From 5e71da88c38ad0ee3a0d7686fc4825556a942bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Mon, 6 Apr 2026 23:01:37 +0800 Subject: [PATCH] fix: preserve intermediate text between tool calls, restore thinking blocks on refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs fixed: 1. Intermediate assistant text (e.g. "项目已经有 node_modules,直接启动") was lost between tool calls because content_start didn't flush streamingText. Now we flush accumulated text as assistant_text before switching to the next content block. 2. History reload (page refresh) now restores thinking blocks alongside tool_use, tool_result, and text blocks. Previously thinking blocks were intentionally dropped — now they're preserved for full conversation replay. Co-Authored-By: Claude Opus 4.6 --- desktop/src/stores/chatStore.test.ts | 4 ++-- desktop/src/stores/chatStore.ts | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/desktop/src/stores/chatStore.test.ts b/desktop/src/stores/chatStore.test.ts index 420228cc..01eb1d72 100644 --- a/desktop/src/stores/chatStore.test.ts +++ b/desktop/src/stores/chatStore.test.ts @@ -57,7 +57,7 @@ describe('chatStore history mapping', () => { }) }) - it('drops persisted thinking blocks when restoring transcript history', () => { + it('preserves thinking blocks when restoring transcript history', () => { const messages: MessageEntry[] = [ { id: 'assistant-1', @@ -83,11 +83,11 @@ describe('chatStore history mapping', () => { const mapped = mapHistoryMessagesToUiMessages(messages) expect(mapped.map((message) => message.type)).toEqual([ + 'thinking', 'assistant_text', 'tool_use', 'tool_result', ]) - expect(mapped.some((message) => message.type === 'thinking')).toBe(false) }) it('sends permission mode updates to the active session only', () => { diff --git a/desktop/src/stores/chatStore.ts b/desktop/src/stores/chatStore.ts index 38daa85a..1b18c75d 100644 --- a/desktop/src/stores/chatStore.ts +++ b/desktop/src/stores/chatStore.ts @@ -202,7 +202,23 @@ export const useChatStore = create((set, get) => ({ } break - case 'content_start': + case 'content_start': { + // Flush any accumulated streamingText as assistant_text BEFORE + // switching to the next block. This preserves intermediate text + // segments between tool calls (e.g. "项目已经有 node_modules,直接启动"). + const pendingText = get().streamingText.trim() + if (pendingText) { + set((s) => ({ + messages: [...s.messages, { + id: nextId(), + type: 'assistant_text', + content: pendingText, + timestamp: Date.now(), + }], + streamingText: '', + })) + } + if (msg.blockType === 'text') { set({ streamingText: '', chatState: 'streaming', activeThinkingId: null }) } else if (msg.blockType === 'tool_use') { @@ -215,6 +231,7 @@ export const useChatStore = create((set, get) => ({ }) } break + } case 'content_delta': if (msg.text !== undefined) { @@ -408,7 +425,14 @@ export function mapHistoryMessagesToUiMessages(messages: MessageEntry[]): UIMess if (msg.type === 'assistant' && Array.isArray(msg.content)) { for (const block of msg.content as AssistantHistoryBlock[]) { - if (block.type === 'text' && block.text) { + if (block.type === 'thinking' && block.thinking) { + uiMessages.push({ + id: nextId(), + type: 'thinking', + content: block.thinking, + timestamp, + }) + } else if (block.type === 'text' && block.text) { uiMessages.push({ id: nextId(), type: 'assistant_text',