Skip to content

Commit b030439

Browse files
committed
fix: subagent/tool call structure
1 parent 1b54844 commit b030439

File tree

9 files changed

+477
-104
lines changed

9 files changed

+477
-104
lines changed

backend/src/llm-apis/vercel-ai-sdk/ai-sdk.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export type StreamChunk =
3131
| {
3232
type: 'text'
3333
text: string
34+
agentId?: string
3435
}
3536
| {
3637
type: 'reasoning'
@@ -114,6 +115,7 @@ export const promptAiSdkStream = async function* (
114115
yield {
115116
type: 'text',
116117
text: flushed,
118+
agentId: options.agentId,
117119
}
118120
}
119121
}
@@ -166,6 +168,7 @@ export const promptAiSdkStream = async function* (
166168
yield {
167169
type: 'text',
168170
text: chunk.text,
171+
agentId: options.agentId,
169172
}
170173
}
171174
continue
@@ -177,6 +180,7 @@ export const promptAiSdkStream = async function* (
177180
yield {
178181
type: 'text',
179182
text: stopSequenceResult.text,
183+
agentId: options.agentId,
180184
}
181185
}
182186
}
@@ -187,6 +191,7 @@ export const promptAiSdkStream = async function* (
187191
yield {
188192
type: 'text',
189193
text: flushed,
194+
agentId: options.agentId,
190195
}
191196
}
192197

backend/src/run-agent-step.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ export const runAgentStep = async (
227227
fingerprintId,
228228
userInputId,
229229
userId,
230-
agentId: agentState.agentId,
230+
agentId: agentState.parentId ? agentState.agentId : undefined,
231231
template: agentTemplate,
232232
onCostCalculated: async (credits: number) => {
233233
try {

backend/src/run-programmatic-step.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,6 @@ export async function runProgrammaticStep(
250250
role: 'assistant' as const,
251251
content: toolCallString,
252252
})
253-
state.sendSubagentChunk({
254-
userInputId,
255-
agentId: state.agentState.agentId,
256-
agentType: state.agentState.agentType!,
257-
chunk: toolCallString,
258-
})
259253
}
260254

261255
// Execute the tool synchronously and get the result immediately

backend/src/tools/handlers/tool/spawn-agent-utils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ export async function executeSubagent({
328328
}) {
329329
onResponseChunk({
330330
type: 'subagent_start',
331-
agentId: agentTemplate.id,
331+
agentId: agentState.agentId,
332+
agentType: agentTemplate.id,
332333
displayName: agentTemplate.displayName,
333334
onlyChild: isOnlyChild,
334335
})
@@ -354,7 +355,8 @@ export async function executeSubagent({
354355

355356
onResponseChunk({
356357
type: 'subagent_finish',
357-
agentId: agentTemplate.id,
358+
agentId: agentState.agentId,
359+
agentType: agentTemplate.id,
358360
displayName: agentTemplate.displayName,
359361
onlyChild: isOnlyChild,
360362
})

backend/src/tools/stream-parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ export async function processStreamWithTools(options: {
273273
reasoning = false
274274
onResponseChunk(`"\n}${endToolTag}\n\n`)
275275
}
276-
onResponseChunk(chunk.text)
276+
onResponseChunk(chunk)
277277
fullResponseChunks.push(chunk.text)
278278
} else if (chunk.type === 'error') {
279279
onResponseChunk(chunk)

cli/src/components/message-block.tsx

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ export const MessageBlock = ({
181181
: ''
182182

183183
const finishedPreview =
184-
!isStreaming && isCollapsed
185-
? lastLine.replace(/[#*_`~\[\]()]/g, '').trim()
184+
!isStreaming && isCollapsed && block.initialPrompt
185+
? block.initialPrompt.replace(/[#*_`~\[\]()]/g, '').trim()
186186
: ''
187187

188188
const agentCodeBlockWidth = Math.max(10, availableWidth - 12)
@@ -195,16 +195,88 @@ export const MessageBlock = ({
195195
codeBlockWidth: agentCodeBlockWidth,
196196
palette: agentPalette,
197197
}
198-
const displayContent = hasMarkdown(block.content)
199-
? renderMarkdown(block.content, agentMarkdownOptions)
200-
: block.content
198+
199+
const displayContent = block.content
200+
? (hasMarkdown(block.content)
201+
? renderMarkdown(block.content, agentMarkdownOptions)
202+
: block.content)
203+
: ''
204+
205+
const nestedToolBlocks = block.blocks && block.blocks.length > 0 && !isCollapsed
206+
? block.blocks.map((nestedBlock, nestedIdx) => {
207+
if (nestedBlock.type === 'tool') {
208+
const displayInfo = getToolDisplayInfo(nestedBlock.toolName)
209+
const isNestedCollapsed = collapsedAgents.has(nestedBlock.toolCallId)
210+
const isNestedStreaming = streamingAgents.has(nestedBlock.toolCallId)
211+
212+
const inputContent = `\`\`\`json\n${JSON.stringify(nestedBlock.input, null, 2)}\n\`\`\``
213+
const codeBlockLang =
214+
nestedBlock.toolName === 'run_terminal_command' ? '' : 'yaml'
215+
const resultContent = nestedBlock.output
216+
? `\n\n**Result:**\n\`\`\`${codeBlockLang}\n${nestedBlock.output}\n\`\`\``
217+
: ''
218+
const fullNestedContent = inputContent + resultContent
219+
220+
const nestedLines = fullNestedContent.split('\n').filter((line) => line.trim())
221+
const firstNestedLine = nestedLines[0] || ''
222+
const lastNestedLine = nestedLines[nestedLines.length - 1] || firstNestedLine
223+
224+
const nestedStreamingPreview = isNestedStreaming
225+
? firstNestedLine.replace(/[#*_`~\[\]()]/g, '').trim() + '...'
226+
: ''
227+
228+
let nestedFinishedPreview = ''
229+
if (!isNestedStreaming && isNestedCollapsed) {
230+
if (nestedBlock.toolName === 'run_terminal_command' && nestedBlock.output) {
231+
const outputLines = nestedBlock.output
232+
.split('\n')
233+
.filter((line) => line.trim())
234+
const lastThreeLines = outputLines.slice(-3)
235+
const hasMoreLines = outputLines.length > 3
236+
nestedFinishedPreview = hasMoreLines
237+
? '...\n' + lastThreeLines.join('\n')
238+
: lastThreeLines.join('\n')
239+
} else {
240+
nestedFinishedPreview = lastNestedLine
241+
.replace(/[#*_`~\[\]()]/g, '')
242+
.trim()
243+
}
244+
}
245+
246+
const nestedDisplayContent = hasMarkdown(fullNestedContent)
247+
? renderMarkdown(fullNestedContent, agentMarkdownOptions)
248+
: fullNestedContent
249+
250+
const nextNestedBlock = block.blocks![nestedIdx + 1]
251+
const isLastNestedBranch = !nextNestedBlock
252+
const nestedBranchChar = isLastNestedBranch ? ' └─ ' : ' ├─ '
253+
254+
return (
255+
<box key={`${messageId}-agent-${block.agentId}-tool-${nestedBlock.toolCallId}`}>
256+
<BranchItem
257+
name={displayInfo.name}
258+
content={nestedDisplayContent}
259+
isCollapsed={isNestedCollapsed}
260+
isStreaming={isNestedStreaming}
261+
branchChar={nestedBranchChar}
262+
streamingPreview={nestedStreamingPreview}
263+
finishedPreview={nestedFinishedPreview}
264+
theme={theme}
265+
onToggle={() => onToggleCollapsed(nestedBlock.toolCallId)}
266+
/>
267+
</box>
268+
)
269+
}
270+
return null
271+
})
272+
: null
201273

202274
const nextBlock = blocks[idx + 1]
203275
const isLastBranch = !nextBlock || nextBlock.type === 'text'
204276
const branchChar = isLastBranch ? '└─ ' : '├─ '
205277

206278
return (
207-
<box key={`${messageId}-agent-${block.agentId}`}>
279+
<box key={`${messageId}-agent-${block.agentId}`} style={{ flexDirection: 'column', gap: 0 }}>
208280
<BranchItem
209281
name={block.agentName}
210282
content={displayContent}
@@ -216,6 +288,7 @@ export const MessageBlock = ({
216288
theme={theme}
217289
onToggle={() => onToggleCollapsed(block.agentId)}
218290
/>
291+
{nestedToolBlocks}
219292
</box>
220293
)
221294
}

0 commit comments

Comments
 (0)