mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-08 07:17:39 +00:00
fix(web): keep global SSE alive for session list status updates (#694)
When a session is open, the web app now keeps an always-on all:true SSE connection for sidebar session-updated events while using a second session-scoped stream for message delivery. Also bump session activity on hub sendMessage so web-originated sends refresh list timestamps. Fixes tiann/hapi#693 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isGlobalScopedMessageStreamEvent } from './useSSE'
|
||||
|
||||
describe('useSSE scope handling', () => {
|
||||
it('treats message stream events as global-scoped skips', () => {
|
||||
expect(isGlobalScopedMessageStreamEvent('global', 'message-received')).toBe(true)
|
||||
expect(isGlobalScopedMessageStreamEvent('global', 'messages-consumed')).toBe(true)
|
||||
expect(isGlobalScopedMessageStreamEvent('global', 'message-cancelled')).toBe(true)
|
||||
})
|
||||
|
||||
it('does not skip session lifecycle events on the global connection', () => {
|
||||
expect(isGlobalScopedMessageStreamEvent('global', 'session-updated')).toBe(false)
|
||||
expect(isGlobalScopedMessageStreamEvent('global', 'session-added')).toBe(false)
|
||||
expect(isGlobalScopedMessageStreamEvent('global', 'session-removed')).toBe(false)
|
||||
})
|
||||
|
||||
it('processes message stream events on full-scoped connections', () => {
|
||||
expect(isGlobalScopedMessageStreamEvent('full', 'message-received')).toBe(false)
|
||||
})
|
||||
})
|
||||
+25
-3
@@ -21,6 +21,18 @@ type SSESubscription = {
|
||||
machineId?: string
|
||||
}
|
||||
|
||||
export type SSEScope = 'global' | 'full'
|
||||
|
||||
const MESSAGE_STREAM_EVENT_TYPES = new Set<SyncEvent['type']>([
|
||||
'message-received',
|
||||
'messages-consumed',
|
||||
'message-cancelled'
|
||||
])
|
||||
|
||||
export function isGlobalScopedMessageStreamEvent(scope: SSEScope, eventType: SyncEvent['type']): boolean {
|
||||
return scope === 'global' && MESSAGE_STREAM_EVENT_TYPES.has(eventType)
|
||||
}
|
||||
|
||||
type VisibilityState = 'visible' | 'hidden'
|
||||
|
||||
type ToastEvent = Extract<SyncEvent, { type: 'toast' }>
|
||||
@@ -105,6 +117,7 @@ export function useSSE(options: {
|
||||
token: string
|
||||
baseUrl: string
|
||||
subscription?: SSESubscription
|
||||
scope?: SSEScope
|
||||
onEvent: (event: SyncEvent) => void
|
||||
onConnect?: () => void
|
||||
onDisconnect?: (reason: string) => void
|
||||
@@ -151,10 +164,11 @@ export function useSSE(options: {
|
||||
}, [options.onToast])
|
||||
|
||||
const subscription = options.subscription ?? {}
|
||||
const scope = options.scope ?? 'full'
|
||||
|
||||
const subscriptionKey = useMemo(() => {
|
||||
return `${subscription.all ? '1' : '0'}|${subscription.sessionId ?? ''}|${subscription.machineId ?? ''}`
|
||||
}, [subscription.all, subscription.sessionId, subscription.machineId])
|
||||
return `${scope}|${subscription.all ? '1' : '0'}|${subscription.sessionId ?? ''}|${subscription.machineId ?? ''}`
|
||||
}, [scope, subscription.all, subscription.sessionId, subscription.machineId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!options.enabled) {
|
||||
@@ -425,6 +439,14 @@ export function useSSE(options: {
|
||||
return
|
||||
}
|
||||
|
||||
if (scope === 'global' && MESSAGE_STREAM_EVENT_TYPES.has(event.type)) {
|
||||
if (event.type === 'message-received') {
|
||||
queueSessionListInvalidation()
|
||||
}
|
||||
onEventRef.current(event)
|
||||
return
|
||||
}
|
||||
|
||||
if (event.type === 'messages-consumed') {
|
||||
markMessagesConsumed(event.sessionId, event.localIds, event.invokedAt)
|
||||
}
|
||||
@@ -575,7 +597,7 @@ export function useSSE(options: {
|
||||
}
|
||||
setSubscriptionId(null)
|
||||
}
|
||||
}, [options.baseUrl, options.enabled, options.token, subscriptionKey, queryClient, reconnectNonce])
|
||||
}, [options.baseUrl, options.enabled, options.scope, options.token, scope, subscriptionKey, queryClient, reconnectNonce])
|
||||
|
||||
return { subscriptionId }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user