mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-09 08:21:50 +00:00
fix(desktop): require session ids for scoped gateway events (#42178)
* fix(desktop): require session ids for scoped gateway events Drop unscoped stream, tool, and subagent events in the desktop renderer so async activity cannot attach to whichever chat is currently focused. * fix(desktop): preserve unscoped session info events Keep session.info out of the scoped-event drop list so global desktop runtime broadcasts still initialize UI state before a session is active.
This commit is contained in:
parent
a77efada5f
commit
de80d28f38
3 changed files with 52 additions and 0 deletions
|
|
@ -14,6 +14,7 @@ import {
|
|||
upsertToolPart
|
||||
} from '@/lib/chat-messages'
|
||||
import { coerceGatewayText, coerceThinkingText, normalizePersonalityValue } from '@/lib/chat-runtime'
|
||||
import { gatewayEventRequiresSessionId } from '@/lib/gateway-events'
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
import { isProviderSetupErrorMessage } from '@/lib/provider-setup-errors'
|
||||
import { setClarifyRequest } from '@/store/clarify'
|
||||
|
|
@ -613,6 +614,9 @@ export function useMessageStream({
|
|||
(event: RpcEvent) => {
|
||||
const payload = event.payload as GatewayEventPayload | undefined
|
||||
const explicitSid = event.session_id || ''
|
||||
if (!explicitSid && gatewayEventRequiresSessionId(event.type)) {
|
||||
return
|
||||
}
|
||||
const sessionId = explicitSid || activeSessionIdRef.current
|
||||
const isActiveEvent = !!sessionId && sessionId === activeSessionIdRef.current
|
||||
|
||||
|
|
|
|||
19
apps/desktop/src/lib/gateway-events.test.ts
Normal file
19
apps/desktop/src/lib/gateway-events.test.ts
Normal file
|
|
@ -0,0 +1,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)
|
||||
expect(gatewayEventRequiresSessionId('subagent.progress')).toBe(true)
|
||||
expect(gatewayEventRequiresSessionId('approval.request')).toBe(true)
|
||||
})
|
||||
|
||||
it('allows global events to remain unscoped', () => {
|
||||
expect(gatewayEventRequiresSessionId('gateway.ready')).toBe(false)
|
||||
expect(gatewayEventRequiresSessionId('preview.restart.progress')).toBe(false)
|
||||
expect(gatewayEventRequiresSessionId('session.info')).toBe(false)
|
||||
expect(gatewayEventRequiresSessionId(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
@ -7,10 +7,39 @@ 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>) : {}
|
||||
}
|
||||
|
||||
export function gatewayEventRequiresSessionId(eventType: string | undefined): boolean {
|
||||
if (!eventType) {
|
||||
return false
|
||||
}
|
||||
|
||||
return SESSION_SCOPED_EVENT_TYPES.has(eventType) || eventType.startsWith('tool.')
|
||||
}
|
||||
|
||||
export function gatewayEventCompletedFileDiff(event: RpcEventLike): boolean {
|
||||
if (event.type !== 'tool.complete') {
|
||||
return false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue