Skip to content

Commit 30490d2

Browse files
committed
feat(ai-chat): surface subagent tool calls to the browser UI
The Claude Code SDK delivers the parent agent's tool calls as a stream of stream_event messages (content_block_start / delta / stop), but the new Agent subagent dispatcher's internal tool calls arrive batched on a single assistant message with parent_tool_use_id set — there is no streaming path. The existing stream_event subagent branch was dead code under the new dispatcher, so the user saw the parent Agent card finish immediately and then nothing until the subagent returned. Extract tool_use blocks from subagent assistant messages and emit aiProgress + aiToolInfo back-to-back per block, registering each tool_use id in _toolUseIdToCounter so the existing tool_result handler can route the response back to the right indicator card. The cards appear once each subagent tool completes (no streaming preview — the SDK never gave us one), but they appear instead of being invisible.
1 parent f61fefa commit 30490d2

1 file changed

Lines changed: 39 additions & 0 deletions

File tree

src-node/claude-code-agent.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,6 +1612,45 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale,
16121612
_log("Session:", currentSessionId);
16131613
}
16141614

1615+
// Subagent tool extraction. The SDK delivers the parent
1616+
// agent's tool calls as a stream of stream_event messages
1617+
// (content_block_start / content_block_delta / content_block_
1618+
// stop), but the new Agent dispatcher's *subagent* tool calls
1619+
// arrive batched on a single assistant message with
1620+
// parent_tool_use_id set — there is no streaming path. We have
1621+
// to fish them out here, otherwise the UI sees the parent
1622+
// Agent card finish and then nothing until the subagent
1623+
// returns. Each tool_use block emits aiProgress + aiToolInfo
1624+
// back-to-back (no streaming preview — the SDK never gave us
1625+
// one); the tool_use id is registered in _toolUseIdToCounter
1626+
// so the existing tool_result handler routes the response
1627+
// back to the right indicator card.
1628+
if (message.type === "assistant" &&
1629+
message.parent_tool_use_id &&
1630+
message.message && Array.isArray(message.message.content)) {
1631+
for (const block of message.message.content) {
1632+
if (block && block.type === "tool_use") {
1633+
toolCounter++;
1634+
if (block.id) {
1635+
_toolUseIdToCounter[block.id] = toolCounter;
1636+
}
1637+
_log("Subagent tool:", block.name, "#" + toolCounter);
1638+
nodeConnector.triggerPeer("aiProgress", {
1639+
requestId: requestId,
1640+
toolName: block.name,
1641+
toolId: toolCounter,
1642+
phase: "tool_use"
1643+
});
1644+
nodeConnector.triggerPeer("aiToolInfo", {
1645+
requestId: requestId,
1646+
toolName: block.name,
1647+
toolId: toolCounter,
1648+
toolInput: block.input || {}
1649+
});
1650+
}
1651+
}
1652+
}
1653+
16151654
// Per-turn token usage: each SDKAssistantMessage carries the
16161655
// wrapped Anthropic API message whose `.usage` reflects what
16171656
// that single turn consumed. Useful for diagnosing runaway

0 commit comments

Comments
 (0)