Skip to content

Commit 9dde8dd

Browse files
committed
Fix for showing thinekr output
1 parent efcb10b commit 9dde8dd

2 files changed

Lines changed: 149 additions & 4 deletions

File tree

cli/src/utils/__tests__/sdk-event-handlers.test.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,142 @@ describe('sdk-event-handlers', () => {
251251
})
252252
expect(getStreamingAgents().size).toBe(0)
253253
})
254+
255+
test('handles spawn_agents tool results for agents with tool blocks (lastMessage mode)', () => {
256+
const { ctx, getMessages, getStreamingAgents } = createTestContext()
257+
258+
// Create an agent block with an existing tool block (simulating thinker agent's read_files)
259+
ctx.message.updater.updateAiMessageBlocks(() => [
260+
{
261+
type: 'agent',
262+
agentId: 'tool-1-0',
263+
agentName: 'Thinker',
264+
agentType: 'thinker-with-files-gemini',
265+
content: '',
266+
status: 'running',
267+
blocks: [
268+
{
269+
type: 'tool',
270+
toolCallId: 'read-1',
271+
toolName: 'read_files',
272+
input: { paths: ['package.json'] },
273+
output: 'package contents',
274+
},
275+
],
276+
initialPrompt: 'Think about this',
277+
spawnToolCallId: 'tool-1',
278+
spawnIndex: 0,
279+
} as any,
280+
])
281+
ctx.streaming.setStreamingAgents(() => new Set(['tool-1-0']))
282+
283+
const handleEvent = createEventHandler(ctx)
284+
const toolResultEvent: ToolResultEvent = {
285+
type: 'tool_result',
286+
toolCallId: 'tool-1',
287+
toolName: 'spawn_agents',
288+
output: [
289+
{
290+
type: 'json',
291+
value: [
292+
{
293+
agentName: 'thinker-with-files-gemini',
294+
value: {
295+
type: 'lastMessage',
296+
value: [
297+
{
298+
role: 'assistant',
299+
content: [
300+
{ type: 'text', text: 'Here is the analysis result.' },
301+
],
302+
},
303+
],
304+
},
305+
},
306+
],
307+
},
308+
],
309+
}
310+
handleEvent(toolResultEvent)
311+
312+
const agentBlock = (getMessages()[0].blocks ?? [])[0] as AgentContentBlock
313+
expect(agentBlock.status).toBe('complete')
314+
// Should have the tool block AND the final text content
315+
expect(agentBlock.blocks).toHaveLength(2)
316+
expect(agentBlock.blocks?.[0]).toMatchObject({
317+
type: 'tool',
318+
toolName: 'read_files',
319+
})
320+
expect(agentBlock.blocks?.[1]).toMatchObject({
321+
type: 'text',
322+
content: 'Here is the analysis result.',
323+
})
324+
expect(getStreamingAgents().size).toBe(0)
325+
})
326+
327+
test('preserves streamed text content and skips duplicate final content', () => {
328+
const { ctx, getMessages, getStreamingAgents } = createTestContext()
329+
330+
// Create an agent block with existing text blocks (simulating streamed output like basher)
331+
ctx.message.updater.updateAiMessageBlocks(() => [
332+
{
333+
type: 'agent',
334+
agentId: 'tool-1-0',
335+
agentName: 'Basher',
336+
agentType: 'basher',
337+
content: '',
338+
status: 'running',
339+
blocks: [
340+
{
341+
type: 'text',
342+
content: 'Streamed output from basher',
343+
textType: 'text',
344+
},
345+
],
346+
initialPrompt: 'Run a command',
347+
spawnToolCallId: 'tool-1',
348+
spawnIndex: 0,
349+
} as any,
350+
])
351+
ctx.streaming.setStreamingAgents(() => new Set(['tool-1-0']))
352+
353+
const handleEvent = createEventHandler(ctx)
354+
const toolResultEvent: ToolResultEvent = {
355+
type: 'tool_result',
356+
toolCallId: 'tool-1',
357+
toolName: 'spawn_agents',
358+
output: [
359+
{
360+
type: 'json',
361+
value: [
362+
{
363+
agentName: 'basher',
364+
value: {
365+
type: 'lastMessage',
366+
value: [
367+
{
368+
role: 'assistant',
369+
content: [
370+
{ type: 'text', text: 'Streamed output from basher' },
371+
],
372+
},
373+
],
374+
},
375+
},
376+
],
377+
},
378+
],
379+
}
380+
handleEvent(toolResultEvent)
381+
382+
const agentBlock = (getMessages()[0].blocks ?? [])[0] as AgentContentBlock
383+
expect(agentBlock.status).toBe('complete')
384+
// Should NOT duplicate the streamed text — only the original text block
385+
expect(agentBlock.blocks).toHaveLength(1)
386+
expect(agentBlock.blocks?.[0]).toMatchObject({
387+
type: 'text',
388+
content: 'Streamed output from basher',
389+
})
390+
expect(getStreamingAgents().size).toBe(0)
391+
})
254392
})

cli/src/utils/sdk-event-handlers.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,12 +371,19 @@ const updateSpawnAgentBlocks = (
371371

372372
if (result?.value) {
373373
const { content, hasError } = extractSpawnAgentResultContent(result.value)
374-
// Preserve streamed content (agents like basher stream their output)
375-
const hasStreamedContent = block.blocks.length > 0
376-
if (hasError || content || hasStreamedContent) {
374+
// Check if the agent already streamed text content (e.g., basher).
375+
// Agents like thinker return all output at the end via lastMessage,
376+
// so we should add final content even if they have tool blocks.
377+
const hasStreamedTextContent = block.blocks.some(
378+
(b) => b.type === 'text' && b.textType === 'text'
379+
)
380+
const finalBlocks = content && !hasStreamedTextContent
381+
? [...block.blocks, { type: 'text', content } as ContentBlock]
382+
: block.blocks
383+
if (hasError || finalBlocks.length > 0) {
377384
return {
378385
...block,
379-
blocks: hasStreamedContent ? block.blocks : [{ type: 'text', content } as ContentBlock],
386+
blocks: finalBlocks,
380387
status: hasError ? ('failed' as const) : ('complete' as const),
381388
}
382389
}

0 commit comments

Comments
 (0)