From 25424660d302ec34a8f60139b638b351c30c9b6b Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 20 May 2026 16:41:21 +0530 Subject: [PATCH 1/5] feat(ai-chat): allow renamed Agent subagent dispatcher tool Pair with the phoenix-pro Agent icon mapping. Keep Task in the list for backwards compatibility with older sessions / SDKs. --- src-node/claude-code-agent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js index 4d2350f513..3c2aee2df3 100644 --- a/src-node/claude-code-agent.js +++ b/src-node/claude-code-agent.js @@ -824,7 +824,7 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale, }, allowedTools: [ "Read", "Edit", "Write", "Glob", "Grep", "Bash", - "AskUserQuestion", "Task", + "AskUserQuestion", "Task", "Agent", "TodoRead", "TodoWrite", "TaskCreate", "TaskUpdate", "TaskList", "TaskGet", "WebFetch", "WebSearch", From 7044fcd3ec623d5ccac4cb0280946658b0a28854 Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 20 May 2026 17:23:44 +0530 Subject: [PATCH 2/5] feat(ai-chat): surface subagent tool calls to the browser UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src-node/claude-code-agent.js | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js index 3c2aee2df3..23f5e93784 100644 --- a/src-node/claude-code-agent.js +++ b/src-node/claude-code-agent.js @@ -1612,6 +1612,45 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale, _log("Session:", currentSessionId); } + // Subagent tool extraction. The SDK delivers the parent + // agent's tool calls as a stream of stream_event messages + // (content_block_start / content_block_delta / content_block_ + // stop), but the new Agent dispatcher's *subagent* tool calls + // arrive batched on a single assistant message with + // parent_tool_use_id set — there is no streaming path. We have + // to fish them out here, otherwise the UI sees the parent + // Agent card finish and then nothing until the subagent + // returns. Each tool_use block emits aiProgress + aiToolInfo + // back-to-back (no streaming preview — the SDK never gave us + // one); the tool_use id is registered in _toolUseIdToCounter + // so the existing tool_result handler routes the response + // back to the right indicator card. + if (message.type === "assistant" && + message.parent_tool_use_id && + message.message && Array.isArray(message.message.content)) { + for (const block of message.message.content) { + if (block && block.type === "tool_use") { + toolCounter++; + if (block.id) { + _toolUseIdToCounter[block.id] = toolCounter; + } + _log("Subagent tool:", block.name, "#" + toolCounter); + nodeConnector.triggerPeer("aiProgress", { + requestId: requestId, + toolName: block.name, + toolId: toolCounter, + phase: "tool_use" + }); + nodeConnector.triggerPeer("aiToolInfo", { + requestId: requestId, + toolName: block.name, + toolId: toolCounter, + toolInput: block.input || {} + }); + } + } + } + // Per-turn token usage: each SDKAssistantMessage carries the // wrapped Anthropic API message whose `.usage` reflects what // that single turn consumed. Useful for diagnosing runaway From e47be08fc54b90773aaa19a5c9727ca345fc542c Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 20 May 2026 17:50:30 +0530 Subject: [PATCH 3/5] feat(ai-chat): tag subagent tool events with parentToolId, style nested rows Pairs with the phoenix-pro inline-subagent-steps renderer. The backend now looks up the dispatching Agent's toolCounter from _toolUseIdToCounter[message.parent_tool_use_id] and includes it on both aiProgress and aiToolInfo events so the browser knows where to render each step. Styling adds .ai-subagent-steps (left border + indent for the nested block) and .ai-subagent-step / -icon / -label (compact one-line rows with the secondary text size and ellipsis truncation). --- src-node/claude-code-agent.js | 6 +++++- src/styles/Extn-AIChatPanel.less | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js index 23f5e93784..3bda06f8b9 100644 --- a/src-node/claude-code-agent.js +++ b/src-node/claude-code-agent.js @@ -1628,23 +1628,27 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale, if (message.type === "assistant" && message.parent_tool_use_id && message.message && Array.isArray(message.message.content)) { + const parentToolId = _toolUseIdToCounter[message.parent_tool_use_id]; for (const block of message.message.content) { if (block && block.type === "tool_use") { toolCounter++; if (block.id) { _toolUseIdToCounter[block.id] = toolCounter; } - _log("Subagent tool:", block.name, "#" + toolCounter); + _log("Subagent tool:", block.name, "#" + toolCounter, + "parent=#" + (parentToolId !== undefined ? parentToolId : "?")); nodeConnector.triggerPeer("aiProgress", { requestId: requestId, toolName: block.name, toolId: toolCounter, + parentToolId: parentToolId, phase: "tool_use" }); nodeConnector.triggerPeer("aiToolInfo", { requestId: requestId, toolName: block.name, toolId: toolCounter, + parentToolId: parentToolId, toolInput: block.input || {} }); } diff --git a/src/styles/Extn-AIChatPanel.less b/src/styles/Extn-AIChatPanel.less index b3bf169495..2811ed0fe0 100644 --- a/src/styles/Extn-AIChatPanel.less +++ b/src/styles/Extn-AIChatPanel.less @@ -1126,6 +1126,39 @@ } } +/* ── Subagent step rows ────────────────────────────────────────────── + Compact one-line entries inside an Agent dispatcher card showing + what the subagent is doing (Read foo.json, Searched: *.md, etc). + Indented with a left border so the nesting reads clearly. */ +.ai-subagent-steps { + margin-top: 4px; + padding-left: 8px; + border-left: 2px solid fade(@project-panel-text-2, 25%); +} + +.ai-subagent-step { + display: flex; + align-items: center; + gap: 6px; + padding: 2px 0; + font-size: @ai-text-secondary; +} + +.ai-subagent-step-icon { + width: 14px; + text-align: center; + flex-shrink: 0; + font-size: @ai-text-secondary; +} + +.ai-subagent-step-label { + color: @project-panel-text-2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} + @keyframes ai-spin { to { transform: rotate(360deg); } } From e0dc92f6ec6fc434949f7bd46e86de117f7e58de Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 20 May 2026 17:53:20 +0530 Subject: [PATCH 4/5] chore: update pro deps --- tracking-repos.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracking-repos.json b/tracking-repos.json index 2ad56dbe5e..55786aee98 100644 --- a/tracking-repos.json +++ b/tracking-repos.json @@ -1,5 +1,5 @@ { "phoenixPro": { - "commitID": "21edc7c2963b6b73076dfa7b362a76ae71db7879" + "commitID": "3058f78dd3851d0194df3fc0aeb271fb315471be" } } From 8382004f2449807aab9e863372c97f1d2971b4e5 Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 20 May 2026 18:34:41 +0530 Subject: [PATCH 5/5] chore: update pro deps --- tracking-repos.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracking-repos.json b/tracking-repos.json index 55786aee98..731d64b832 100644 --- a/tracking-repos.json +++ b/tracking-repos.json @@ -1,5 +1,5 @@ { "phoenixPro": { - "commitID": "3058f78dd3851d0194df3fc0aeb271fb315471be" + "commitID": "79aeedfe7690aa5a166dd8e5b988900f6bf71468" } }