From d4fe9bb583e7d7e3e6e0cfde99e4ebb19c58bb83 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 24 Aug 2026 06:10:18 -0700 Subject: [PATCH] fix(tui): keep foreign session resets from clearing active chats (#128723) --- src/tui/tui-event-handlers.test.ts | 61 ++++++++++++++++++++++++++++++ src/tui/tui-event-handlers.ts | 8 +--- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/tui/tui-event-handlers.test.ts b/src/tui/tui-event-handlers.test.ts index 9005ff33895d..24a7c3d47dab 100644 --- a/src/tui/tui-event-handlers.test.ts +++ b/src/tui/tui-event-handlers.test.ts @@ -1815,6 +1815,67 @@ describe("tui-event-handlers: handleAgentEvent", () => { expect(setActivityStatus).not.toHaveBeenCalledWith("idle"); }); + it.each([ + { name: "another agent's fixed-store session", agentId: "main" }, + { name: "an ownerless default-agent alias", agentId: undefined }, + ])("ignores a reset for $name colliding with the selected session", ({ agentId }) => { + const pendingSubmit = acceptedSubmit("run-pending"); + const { state, loadHistory, setActivityStatus, handleSessionsChangedEvent } = + createHandlersHarness({ + state: { + agentDefaultId: "main", + currentAgentId: "work", + currentSessionKey: "agent:work:support", + currentSessionId: "session-work", + activeChatRunId: "run-work", + activityStatus: "streaming", + pendingSubmit, + sessionInfo: { updatedAt: 100 }, + }, + }); + + handleSessionsChangedEvent({ + sessionKey: "support", + ...(agentId ? { agentId } : {}), + reason: "reset", + sessionId: "session-main-new", + updatedAt: 200, + activeRunIds: [], + }); + + expect(state.activeChatRunId).toBe("run-work"); + expect(state.pendingSubmit).toBe(pendingSubmit); + expect(state.currentSessionId).toBe("session-work"); + expect(state.sessionInfo.updatedAt).toBe(100); + expect(state.activityStatus).toBe("streaming"); + expect(loadHistory).not.toHaveBeenCalled(); + expect(setActivityStatus).not.toHaveBeenCalledWith("idle"); + }); + + it("accepts a reset for the selected non-default agent's owned session alias", () => { + const { state, loadHistory, handleSessionsChangedEvent } = createHandlersHarness({ + state: { + agentDefaultId: "main", + currentAgentId: "work", + currentSessionKey: "agent:work:support", + currentSessionId: "session-work-old", + activeChatRunId: "run-work", + activityStatus: "streaming", + }, + }); + + handleSessionsChangedEvent({ + sessionKey: "support", + agentId: "work", + reason: "reset", + sessionId: "session-work-new", + }); + + expect(state.activeChatRunId).toBeNull(); + expect(state.currentSessionId).toBe("session-work-new"); + expect(loadHistory).toHaveBeenCalledTimes(1); + }); + it("ignores selected-global sessions.changed reset events from other agents", () => { const { state, loadHistory, setActivityStatus, handleSessionsChangedEvent } = createHandlersHarness({ diff --git a/src/tui/tui-event-handlers.ts b/src/tui/tui-event-handlers.ts index 31e327fa18c8..062d21b6628a 100644 --- a/src/tui/tui-event-handlers.ts +++ b/src/tui/tui-event-handlers.ts @@ -399,7 +399,7 @@ export function createEventHandlers(context: EventHandlerContext) { } const evt = payload as SessionChangedEvent; syncSessionKey(); - if (!matchesSelectedTuiSession(state, evt)) { + if (!matchesSelectedTuiSession(state, evt, { requireAliasOwnership: true })) { return; } @@ -408,11 +408,7 @@ export function createEventHandlers(context: EventHandlerContext) { typeof evt.sessionId !== "string" || !state.currentSessionId || evt.sessionId === state.currentSessionId; - if ( - !matchesSelectedTuiSession(state, evt, { requireAliasOwnership: true }) || - !matchesCurrentSessionId || - !isIdentityOnlyTuiSessionInvalidation(evt) - ) { + if (!matchesCurrentSessionId || !isIdentityOnlyTuiSessionInvalidation(evt)) { return; } // Legacy atomic batches expose no replayable message identity. Refresh