From cc8ed8d25be04397076bdaf2f42732da6b64d60f Mon Sep 17 00:00:00 2001 From: joelnishanth <140015627+joelnishanth@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:28:03 -0700 Subject: [PATCH] fix(tui): preserve user message during slow model responses (#53115) When a local run ends with an empty final event while another run is active, skip history reload to prevent clearing the user's pending message from the chat log. This fixes the 'message disappears' issue with slow models like Ollama. --- src/tui/tui-event-handlers.test.ts | 17 +++++++++++++++++ src/tui/tui-event-handlers.ts | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/src/tui/tui-event-handlers.test.ts b/src/tui/tui-event-handlers.test.ts index 2073afe308d..48140fc9a97 100644 --- a/src/tui/tui-event-handlers.test.ts +++ b/src/tui/tui-event-handlers.test.ts @@ -590,4 +590,21 @@ describe("tui-event-handlers: handleAgentEvent", () => { expect(loadHistory).toHaveBeenCalledTimes(1); }); + + it("does not reload history for local run with empty final when another run is active (#53115)", () => { + const { state, loadHistory, noteLocalRunId, handleChatEvent } = createHandlersHarness({ + state: { activeChatRunId: "run-main" }, + }); + + noteLocalRunId("run-local-empty"); + + handleChatEvent({ + runId: "run-local-empty", + sessionKey: state.currentSessionKey, + state: "final", + }); + + expect(state.activeChatRunId).toBe("run-main"); + expect(loadHistory).not.toHaveBeenCalled(); + }); }); diff --git a/src/tui/tui-event-handlers.ts b/src/tui/tui-event-handlers.ts index 6fda2d85163..e0c92608de8 100644 --- a/src/tui/tui-event-handlers.ts +++ b/src/tui/tui-event-handlers.ts @@ -158,9 +158,18 @@ export function createEventHandlers(context: EventHandlerContext) { const isLocalRun = isLocalRunId?.(runId) ?? false; if (isLocalRun) { forgetLocalRunId?.(runId); + // Never reload history for local runs that ended without displayable output. + // This prevents the user's message from disappearing when the backend is slow + // (e.g., Ollama) and sends an empty final event before the response is ready. if (!opts?.allowLocalWithoutDisplayableFinal) { return; } + // Skip history reload if a DIFFERENT run is still active. + // This prevents clearing the user's pending message when a stale/concurrent + // empty final event arrives while a new message is being processed. + if (state.activeChatRunId && state.activeChatRunId !== runId) { + return; + } } if (hasConcurrentActiveRun(runId)) { return;