mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-09 08:21:50 +00:00
fix(desktop): don't drop the focused chat's own stream when unscoped (#42359)
#42178 dropped every session-scoped gateway event that arrived without an explicit session_id, to stop background activity attaching to the focused chat. But the gateway already stamps background sessions with their own id, so an unscoped message/reasoning/tool/prompt event can only be the focused turn's own output. Dropping those swallowed the live answer — it reappeared only after a transcript refetch (manual refresh). Narrow the guard to subagent.* (the only genuinely background/async family); everything else falls back to the active session as before.
This commit is contained in:
parent
e88116256c
commit
6e7033bb4c
2 changed files with 25 additions and 30 deletions
|
|
@ -3,11 +3,19 @@ import { describe, expect, it } from 'vitest'
|
|||
import { gatewayEventRequiresSessionId } from './gateway-events'
|
||||
|
||||
describe('gateway event routing', () => {
|
||||
it('requires explicit session ids for async session-scoped events', () => {
|
||||
expect(gatewayEventRequiresSessionId('message.delta')).toBe(true)
|
||||
expect(gatewayEventRequiresSessionId('tool.start')).toBe(true)
|
||||
it('drops only unscoped subagent events (genuinely background work)', () => {
|
||||
expect(gatewayEventRequiresSessionId('subagent.progress')).toBe(true)
|
||||
expect(gatewayEventRequiresSessionId('approval.request')).toBe(true)
|
||||
expect(gatewayEventRequiresSessionId('subagent.start')).toBe(true)
|
||||
})
|
||||
|
||||
it('attributes unscoped foreground turn events to the active chat', () => {
|
||||
// These must NOT be dropped when unscoped — they are the focused turn's own
|
||||
// output, and dropping them loses the live response until a refetch (#42178).
|
||||
expect(gatewayEventRequiresSessionId('message.delta')).toBe(false)
|
||||
expect(gatewayEventRequiresSessionId('message.complete')).toBe(false)
|
||||
expect(gatewayEventRequiresSessionId('reasoning.delta')).toBe(false)
|
||||
expect(gatewayEventRequiresSessionId('tool.start')).toBe(false)
|
||||
expect(gatewayEventRequiresSessionId('approval.request')).toBe(false)
|
||||
})
|
||||
|
||||
it('allows global events to remain unscoped', () => {
|
||||
|
|
|
|||
|
|
@ -7,37 +7,24 @@ interface RpcEventLike {
|
|||
type?: string
|
||||
}
|
||||
|
||||
const SESSION_SCOPED_EVENT_TYPES = new Set([
|
||||
'approval.request',
|
||||
'clarify.request',
|
||||
'error',
|
||||
'message.complete',
|
||||
'message.delta',
|
||||
'message.start',
|
||||
'reasoning.available',
|
||||
'reasoning.delta',
|
||||
'secret.request',
|
||||
'status.update',
|
||||
'subagent.complete',
|
||||
'subagent.progress',
|
||||
'subagent.spawn_requested',
|
||||
'subagent.start',
|
||||
'subagent.thinking',
|
||||
'subagent.tool',
|
||||
'sudo.request',
|
||||
'thinking.delta'
|
||||
])
|
||||
|
||||
function asRecord(payload: unknown): Record<string, unknown> {
|
||||
return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether an unscoped event (no `session_id`) must be dropped rather than
|
||||
* attributed to the focused chat.
|
||||
*
|
||||
* Only `subagent.*` qualifies: it describes background/async work that must
|
||||
* never attach to whichever chat happens to be focused. Every other scoped
|
||||
* event — message/reasoning/thinking/tool/status/prompt — is, when unscoped,
|
||||
* the active turn's own output. The gateway always stamps a *background*
|
||||
* session's events with that session's id, so a missing id can only mean "the
|
||||
* focused turn". #42178 dropped those too, which silently swallowed the live
|
||||
* answer; it then reappeared only after a transcript refetch (manual refresh).
|
||||
*/
|
||||
export function gatewayEventRequiresSessionId(eventType: string | undefined): boolean {
|
||||
if (!eventType) {
|
||||
return false
|
||||
}
|
||||
|
||||
return SESSION_SCOPED_EVENT_TYPES.has(eventType) || eventType.startsWith('tool.')
|
||||
return eventType?.startsWith('subagent.') ?? false
|
||||
}
|
||||
|
||||
export function gatewayEventCompletedFileDiff(event: RpcEventLike): boolean {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue