From 50eefaed0bcf3e446fece6926a1ec75511dd98f0 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 13 Sep 2026 19:27:50 +0800 Subject: [PATCH] fix(ui): keep chat history idle when run status changes (#146802) --- .../chat-transcript-invalidation.test.ts | 38 +++++++++++++++++++ .../components/chat-transcript-projection.ts | 2 - 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/ui/src/pages/chat/components/chat-transcript-invalidation.test.ts b/ui/src/pages/chat/components/chat-transcript-invalidation.test.ts index 53090901fdb2..11b4d02963e2 100644 --- a/ui/src/pages/chat/components/chat-transcript-invalidation.test.ts +++ b/ui/src/pages/chat/components/chat-transcript-invalidation.test.ts @@ -280,6 +280,44 @@ describe("chat transcript invalidation", () => { }, ); + it.each(["done", "interrupted"] as const)( + "keeps settled history idle when %s status appears, refreshes or clears", + async (phase) => { + vi.spyOn(Date, "now").mockReturnValue(60_000); + const props = threadProps(`pane-terminal-status-${phase}`); + const transcript = createTestTranscript(); + const container = document.body.appendChild(document.createElement("div")); + const rerender = () => { + render(renderChatThread(props, transcript), container); + transcript.hostUpdated(); + }; + try { + rerender(); + transcript.hostConnected(); + await flushDeferredRowPrune(); + const bubbles = Array.from(container.querySelectorAll(".chat-bubble")); + expect(bubbles).toHaveLength(4); + const renderGroup = vi.spyOn(chatMessage, "renderMessageGroup"); + + for (const occurredAt of [59_000, 59_500, null]) { + props.runStatus = + occurredAt === null + ? null + : { phase, runId: "finished-run", sessionKey: props.sessionKey, occurredAt }; + rerender(); + + expect(renderGroup).not.toHaveBeenCalled(); + const currentBubbles = Array.from(container.querySelectorAll(".chat-bubble")); + expect(currentBubbles).toHaveLength(bubbles.length); + currentBubbles.forEach((bubble, index) => expect(bubble).toBe(bubbles[index])); + expect(container.textContent).toContain("reply two"); + } + } finally { + transcript.hostDisconnected(); + } + }, + ); + it("keeps built row identities across an A to B to A presentation reset", () => { const paneId = "pane-session-items"; const messagesA = [{ role: "assistant", content: "session A", timestamp: 1_000 }]; diff --git a/ui/src/pages/chat/components/chat-transcript-projection.ts b/ui/src/pages/chat/components/chat-transcript-projection.ts index 7ba1ec9e55ed..fca472e03136 100644 --- a/ui/src/pages/chat/components/chat-transcript-projection.ts +++ b/ui/src/pages/chat/components/chat-transcript-projection.ts @@ -692,8 +692,6 @@ export function projectChatTranscript( props.replyMessageAccess?.revision ?? 0, props.replyMessageAccess?.navigationId ?? "", turnRecap === null ? "" : `${turnRecap.runtimeMs}:${turnRecap.outputTokens ?? ""}`, - props.runStatus?.phase ?? "", - props.runStatus?.occurredAt ?? 0, ]); state.transcriptRenderContext.onSetReply = props.onSetReply; state.transcriptRenderContext.onAsyncQuestionSubmit = props.onAsyncQuestionSubmit;