diff --git a/packages/web-shell/client/adapters/transcriptToMessages.test.ts b/packages/web-shell/client/adapters/transcriptToMessages.test.ts index ed1a386fc41..9a243a591ab 100644 --- a/packages/web-shell/client/adapters/transcriptToMessages.test.ts +++ b/packages/web-shell/client/adapters/transcriptToMessages.test.ts @@ -4641,6 +4641,42 @@ describe('transcriptBlocksToDaemonMessages', () => { expect(messages).toEqual([]); }); + it.each([' ', '\n', '\r\n', '\t'])( + 'omits a standalone whitespace assistant between tools and thinking: %j', + (whitespace) => { + const messages = transcriptBlocksToDaemonMessages([ + toolBlock('tool-1', 'call-1', 'completed', 1), + textBlock('blank', 'assistant', whitespace, 2, true), + textBlock('thought', 'thought', 'Check the next file.', 3, true), + toolBlock('tool-2', 'call-2', 'completed', 4), + ]); + + expect(messages.map((message) => message.role)).toEqual([ + 'tool_group', + 'thinking', + 'tool_group', + ]); + }, + ); + + it('preserves whitespace chunks within existing assistant prose and usage', () => { + const messages = transcriptBlocksToDaemonMessages([ + textBlock('first', 'assistant', 'First paragraph.', 1, true), + textBlock('spacing', 'assistant', '\n\n', 2, true, { + usage: { inputTokens: 10, outputTokens: 2 }, + }), + textBlock('last', 'assistant', 'Second paragraph.\n', 3), + ]); + + expect(messages).toMatchObject([ + { + role: 'assistant', + content: 'First paragraph.\n\nSecond paragraph.\n', + usage: { inputTokens: 10, outputTokens: 2 }, + }, + ]); + }); + it('keeps status blocks in the main transcript while subagent is active', () => { const messages = transcriptBlocksToDaemonMessages([ toolBlock('agent-start', 'agent-1', 'in_progress', 10, { diff --git a/packages/web-shell/client/adapters/transcriptToMessages.ts b/packages/web-shell/client/adapters/transcriptToMessages.ts index 761a4f70743..77fd6cf3e43 100644 --- a/packages/web-shell/client/adapters/transcriptToMessages.ts +++ b/packages/web-shell/client/adapters/transcriptToMessages.ts @@ -986,7 +986,7 @@ export function transcriptBlocksToDaemonMessages( }; needsNewContentMessage = false; currentThinkingIdx = null; - } else if (!isTextBlockEmpty(textBlock)) { + } else if (textBlock.text.trim().length > 0) { messages.push({ id: block.id, role: 'assistant',