Skip to content

Commit deed268

Browse files
committed
Fix researcher-web
1 parent f4c1093 commit deed268

File tree

3 files changed

+75
-4
lines changed

3 files changed

+75
-4
lines changed

cli/src/utils/__tests__/message-block-helpers.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,41 @@ describe('extractSpawnAgentResultContent', () => {
331331
})
332332
expect(result).toEqual({ content: '', hasError: false })
333333
})
334+
335+
test('handles allMessages output mode', () => {
336+
const result = extractSpawnAgentResultContent({
337+
type: 'allMessages',
338+
value: [
339+
{
340+
role: 'assistant',
341+
content: [{ type: 'text', text: 'First response' }],
342+
},
343+
{
344+
role: 'user',
345+
content: [{ type: 'text', text: 'Follow up' }],
346+
},
347+
{
348+
role: 'assistant',
349+
content: [{ type: 'text', text: 'Second response' }],
350+
},
351+
],
352+
})
353+
expect(result).toEqual({
354+
content: 'First response\nSecond response',
355+
hasError: false,
356+
})
357+
})
358+
359+
test('handles structuredOutput with message field', () => {
360+
const result = extractSpawnAgentResultContent({
361+
type: 'structuredOutput',
362+
value: { message: 'Structured output message' },
363+
})
364+
expect(result).toEqual({
365+
content: 'Structured output message',
366+
hasError: false,
367+
})
368+
})
334369
})
335370

336371
describe('appendInterruptionNotice', () => {

cli/src/utils/message-block-helpers.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ export const extractSpawnAgentResultContent = (
170170
return { content: String((obj.value as any).errorMessage), hasError: true }
171171
}
172172

173-
// Handle lastMessage output mode: { type: "lastMessage", value: [Message array] }
173+
// Handle lastMessage and allMessages output modes: { type: "lastMessage"|"allMessages", value: [Message array] }
174174
// This is common for agents like researcher-web
175-
if (obj.type === 'lastMessage' && Array.isArray(obj.value)) {
175+
if ((obj.type === 'lastMessage' || obj.type === 'allMessages') && Array.isArray(obj.value)) {
176176
const messages = obj.value as Array<{ role?: string; content?: unknown }>
177177
const textContent = messages
178178
.filter((msg) => msg?.role === 'assistant')
@@ -182,6 +182,30 @@ export const extractSpawnAgentResultContent = (
182182
return { content: textContent, hasError: false }
183183
}
184184

185+
// Handle structuredOutput mode: { type: "structuredOutput", value: any }
186+
if (obj.type === 'structuredOutput') {
187+
const value = obj.value
188+
// Check for message field in structured output
189+
if (value && typeof value === 'object') {
190+
const valueObj = value as Record<string, unknown>
191+
if (typeof valueObj.message === 'string') {
192+
return { content: valueObj.message, hasError: false }
193+
}
194+
// Check for data.message pattern
195+
if (valueObj.data && typeof valueObj.data === 'object') {
196+
const dataObj = valueObj.data as Record<string, unknown>
197+
if (typeof dataObj.message === 'string') {
198+
return { content: dataObj.message, hasError: false }
199+
}
200+
}
201+
}
202+
// Fall through to format as JSON
203+
return {
204+
content: formatToolOutput([{ type: 'json', value: obj.value }]),
205+
hasError: false,
206+
}
207+
}
208+
185209
// Handle nested string value: { value: "..." }
186210
if (typeof obj.value === 'string') {
187211
return { content: obj.value, hasError: false }

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,12 +354,24 @@ const updateSpawnAgentBlocks = (
354354

355355
if (result?.value) {
356356
const { content, hasError } = extractSpawnAgentResultContent(result.value)
357-
// Preserve streamed content (agents like commander stream their output)
357+
358+
// Check if there's meaningful text content already streamed
359+
const existingTextContent = block.blocks
360+
.filter((b): b is { type: 'text'; content: string } => b.type === 'text')
361+
.map(b => b.content)
362+
.join('')
363+
.trim()
364+
365+
// Use extracted content from result if it's more substantial than existing streamed content.
366+
// This ensures agents with lastMessage output mode (like researcher-web) show their final
367+
// result inside the box, even if they had tool blocks or partial content during execution.
368+
const shouldUseExtractedContent = content && (!existingTextContent || content.length > existingTextContent.length)
369+
358370
const hasStreamedContent = block.blocks.length > 0
359371
if (hasError || content || hasStreamedContent) {
360372
return {
361373
...block,
362-
blocks: hasStreamedContent ? block.blocks : [{ type: 'text', content } as ContentBlock],
374+
blocks: shouldUseExtractedContent ? [{ type: 'text', content } as ContentBlock] : block.blocks,
363375
status: hasError ? ('failed' as const) : ('complete' as const),
364376
}
365377
}

0 commit comments

Comments
 (0)