From 4c4f0aff74186dab7ce2375c39441b84c1ee3e78 Mon Sep 17 00:00:00 2001 From: dimakis Date: Wed, 20 May 2026 11:18:19 +0100 Subject: [PATCH] fix(server): add sessionId to user_message echo to prevent cross-session bleed user_message events were echoed to clients without sessionId, bypassing the v2 session filter in the frontend store. The filter treated them as global events, causing user input from other sessions to appear in the active session view. Adding sessionId + v:2 to the echo payload at all three emission sites (resume, send, interrupt) aligns user_message with every other session-scoped v2 event. Co-Authored-By: Claude Opus 4.6 --- frontend/src/types/ws-messages.ts | 1 + server/chat.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/types/ws-messages.ts b/frontend/src/types/ws-messages.ts index a16d8fe1..aa22e1c6 100644 --- a/frontend/src/types/ws-messages.ts +++ b/frontend/src/types/ws-messages.ts @@ -170,6 +170,7 @@ interface UserMessageMsg { v: 2; messageId: string; text: string; + sessionId?: string; } interface SessionRenamedMsg { diff --git a/server/chat.ts b/server/chat.ts index 819914f9..dcb8c7fd 100644 --- a/server/chat.ts +++ b/server/chat.ts @@ -929,7 +929,7 @@ async function _startChatInner( }); eventStore.updateLastSpeaker(options.resume, 'user'); _onSessionChange?.(clientId, 'user_message'); - const echo = { type: 'user_message', messageId, text: fullPrompt }; + const echo = { type: 'user_message', v: 2, messageId, text: fullPrompt, sessionId: options.resume }; send(transport, echo); broadcastToObservers(session.observers, echo); } @@ -1055,7 +1055,7 @@ export function sendToChat( /* errors logged internally */ }); } - const echo = { type: 'user_message', messageId, text: fullPrompt }; + const echo = { type: 'user_message', v: 2, messageId, text: fullPrompt, ...(session.sessionId ? { sessionId: session.sessionId } : {}) }; send(session.transport, echo); broadcastToObservers(session.observers, echo); session.inputQueue.push(makeUserMessage(fullPrompt, 'next')); @@ -1089,7 +1089,7 @@ export async function interruptChat( eventStore.updateLastSpeaker(session.sessionId, 'user'); _onSessionChange?.(clientId, 'user_message'); } - const echo = { type: 'user_message', messageId, text: fullPrompt }; + const echo = { type: 'user_message', v: 2, messageId, text: fullPrompt, ...(session.sessionId ? { sessionId: session.sessionId } : {}) }; send(session.transport, echo); broadcastToObservers(session.observers, echo); // Stop all active subagent tasks before interrupting the parent query.