Skip to content

Commit 7a548bb

Browse files
committed
fix: terminal event matching — track idempotency keys instead of relying on session key
The gateway sends response events with a session key that may not be 'main', causing the terminal to filter them out. Now tracks idempotency keys from sent requests and matches events by idempotency key OR session key 'main'. - pendingIdempotencyKeys ref tracks all in-flight request keys - Events accepted if idempotency key matches OR sessionKey is 'main'/absent - Keys cleaned up on final/error/aborted states
1 parent d66a4f0 commit 7a548bb

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

components/gateway-terminal.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ export function GatewayTerminal() {
403403
const inputRef = useRef<HTMLInputElement>(null)
404404
const streamBuf = useRef('')
405405
const streamId = useRef<string | null>(null)
406+
const pendingIdempotencyKeys = useRef(new Set<string>())
406407

407408
// Hydrate history
408409
useLayoutEffect(() => {
@@ -442,21 +443,30 @@ export function GatewayTerminal() {
442443
const p = payload as Record<string, unknown>
443444
const state = p.state as string | undefined
444445

445-
// Only process events for the terminal's session (main)
446+
// Match events belonging to terminal requests
446447
const eventSessionKey = (p.sessionKey ??
447448
p.session_key ??
448449
(typeof p.session === 'object' && p.session !== null
449450
? (p.session as Record<string, unknown>).key
450451
: undefined)) as string | undefined
452+
const eventIdempotencyKey = (p.idempotencyKey ?? p.idempotency_key) as string | undefined
451453

452-
// Debug: log all chat events the terminal sees
453-
console.log('[GW-Terminal] chat event:', { state, sessionKey: eventSessionKey, keys: Object.keys(p).join(',') })
454+
// Accept if: idempotency key matches one we sent, OR session is 'main', OR no session key
455+
const idempotencyMatch = eventIdempotencyKey && pendingIdempotencyKeys.current.has(eventIdempotencyKey)
456+
const sessionMatch = !eventSessionKey || eventSessionKey === 'main'
454457

455-
if (eventSessionKey && eventSessionKey !== 'main') {
456-
console.log('[GW-Terminal] skipping event — session mismatch:', eventSessionKey)
458+
console.log('[GW-Terminal] chat event:', { state, sessionKey: eventSessionKey, idempotencyKey: eventIdempotencyKey, idempotencyMatch, sessionMatch })
459+
460+
if (!idempotencyMatch && !sessionMatch) {
461+
console.log('[GW-Terminal] skipping event — no match:', eventSessionKey)
457462
return
458463
}
459464

465+
// Clean up idempotency key on final/error/aborted
466+
if (eventIdempotencyKey && (state === 'final' || state === 'error' || state === 'aborted')) {
467+
pendingIdempotencyKeys.current.delete(eventIdempotencyKey)
468+
}
469+
460470
if (state === 'delta') {
461471
const text = extractEventText(p)
462472
if (text) {
@@ -566,10 +576,12 @@ export function GatewayTerminal() {
566576
}, 180000)
567577

568578
try {
579+
const idempotencyKey = `gw-term-${Date.now()}`
580+
pendingIdempotencyKeys.current.add(idempotencyKey)
569581
const resp = (await sendRequest('chat.send', {
570582
sessionKey: 'main',
571583
message,
572-
idempotencyKey: `gw-term-${Date.now()}`,
584+
idempotencyKey,
573585
})) as Record<string, unknown> | undefined
574586

575587
const respStatus = resp?.status as string | undefined

0 commit comments

Comments
 (0)