fix(server): classify ACP Task tool calls as collab agent tool calls - #8443
fix(server): classify ACP Task tool calls as collab agent tool calls#8443NoahLinckeScout wants to merge 1 commit into
Conversation
A thread on an ACP-backed provider that launched subagents via its Task tool showed zero subagents in the UI. The launches were never dropped: over ACP they arrive as ordinary background tool calls titled "Task: ..." with `rawInput._toolName === "task"` and no agent identity on the wire, so `canonicalItemTypeFromAcpToolKind` filed them under its `dynamic_tool_call` default. Every consumer that makes delegated work visible keys off `collab_agent_tool_call`, so the rows sat in the timeline as anonymous tool noise. The other adapters already classify the same work as `collab_agent_tool_call` (`ClaudeAdapter.classifyToolItemType`, `OpenCodeAdapter`, `CodexAdapter`), so the shared ACP helper was the outlier. Recognize the Task spellings there so the ACP providers get the same classification: `rawInput._toolName === "task"` (case-insensitive) or a title matching `/^task:/i`. All other kinds are unchanged. Identity limits remain: ACP carries no model, agent type, or subagent prompt for these calls, so per-subagent attribution is still not possible. The row now at least reports that it was agent work.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c5af99eea9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (isAcpTaskToolCall(toolCall)) { | ||
| return "collab_agent_tool_call"; |
There was a problem hiding this comment.
Render reclassified Task calls distinctly on mobile
On the mobile client, this classification still does not expose Task launches as agent work: apps/mobile/src/lib/threadActivity.ts:653-655 maps both dynamic_tool_call and collab_agent_tool_call to the same hammer icon, and a repo-wide search finds no other mobile presentation branch for the collab type. Consequently, Cursor/Grok Task calls remain visually unchanged on mobile even though web now gives them bot styling and agent grouping; update the mobile derivation and its focused tests to render this type as agent work too.
AGENTS.md reference: AGENTS.md:L67-L70
Useful? React with 👍 / 👎.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This is a narrow ACP classification fix that preserves execution and schema behavior while changing existing timeline presentation for Task calls. An unresolved review comment identifies that mobile still renders the new agent classification like a generic tool, leaving a customer-facing behavior gap to resolve or explicitly accept. You can add or adjust custom eligibility rules. Learn more. |
What Changed
canonicalItemTypeFromAcpToolKindinAcpCoreRuntimeEvents.ts(the helper shared by the Cursor and Grok adapters) now recognises Task tool calls and classifies themcollab_agent_tool_callinstead of falling through to itsdynamic_tool_calldefault.A Task call is recognised by
rawInput._toolName === "task"(case-insensitive) or a title matching/^task:/i. Every other kind is unchanged. One helper plus its single call site, and tests.Why
A thread on an ACP-backed provider launched several subagents through its
Tasktool. All of them ran to completion. The timeline showed them as anonymous tool rows, indistinguishable from ordinary tool noise.The launches are never dropped. Over ACP a Task launch arrives as a plain background tool call:
session/update→tool_call/tool_call_update,kind: "other"title: "Task: ..."rawInput: { _toolName: "task" },rawOutput: { isBackground: true }canonicalItemTypeFromAcpToolKindkeys only offkind, and has no Task branch, sokind: "other"lands in thedynamic_tool_calldefault.This is an inconsistency between adapters, not a new policy. Every other adapter already classifies the same work as
collab_agent_tool_call:ClaudeAdapter.classifyToolItemType—normalized === "task" || normalized.includes("agent") || ...OpenCodeAdapter—normalized.includes("task") || normalized.includes("agent") || ...CodexAdapter—type.includes("collab")The shared ACP helper was the only one without it, so Cursor and Grok were the odd ones out. This brings them in line.
Repro: start a thread on an ACP-backed provider (Cursor or Grok), ask it to delegate something via its Task tool, and watch the timeline. Before this change the launches render with the generic tool icon;
projection_thread_activitiesshowstool.completedrows withitemType: dynamic_tool_calland aTask: ...summary.Known limitation
ACP carries no model, agent type, or subagent prompt for these calls, so per-subagent attribution still is not possible for these providers. The row now at least reports that it was agent work.
UI Changes
No UI code is touched, but the reclassification does change how these rows render, via two existing call sites:
MessagesTimeline.tsx:2475— icon goes fromhammer(dynamic_tool_call) tobot(collab_agent_tool_call)MessagesTimeline.logic.ts:413— group summary kind goes fromdynamic-tooltoagent-toolI have not attached before/after screenshots: reproducing the row requires a live Cursor or Grok session that actually delegates through its Task tool, which I could not capture cleanly. The rendering delta above is the whole of the visible change, and it comes entirely from those two existing branches rather than from anything in this diff. Happy to add screenshots if you want them before reviewing.
Tests
Added to the existing
AcpCoreRuntimeEvents.test.ts: three Task spellings (both recognizers, plus one with no title) assertcollab_agent_tool_call, with negative controls (_toolName: "mcp__x"→dynamic_tool_call,kind: "search"→web_search) so the new branch cannot swallow unrelated calls.Verified red/green rather than assuming: with the call site reverted to
canonicalItemTypeFromAcpToolKind, the new test fails withexpected collab_agent_tool_call, received dynamic_tool_call; with the fix it passes.Verified locally:
vp test run src/provider/acp/AcpCoreRuntimeEvents.test.ts→ 4 passedapps/serversuite → 245 files passed, 2816 passed / 10 skipped (baseline on this commit's parent: 245 files, 2815 passed)vp run typecheck→ exit 0vp fmt --check→ clean;vp lintreports nothing new for the changed filesChecklist
Note
Low Risk
Narrow classification change in ACP event mapping with targeted tests; no auth, data, or API surface changes.
Overview
ACP-backed providers (Cursor, Grok) were mapping Task subagent launches to
dynamic_tool_callbecause classification only looked atkind: "other". The shared ACP runtime mapper now detects Task calls viarawInput._toolName(case-insensitive"task") or a title matchingTask:, and emitscollab_agent_tool_callso timeline rows align with Claude/Codex/OpenCode.makeAcpToolCallEventuses the newcanonicalItemTypeFromAcpToolCallhelper instead of kind-only mapping; other tool kinds are unchanged. Tests cover the three Task spellings plus controls so MCP and search tools still map todynamic_tool_callandweb_search.Reviewed by Cursor Bugbot for commit c5af99e. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Classify ACP Task tool calls as
collab_agent_tool_callinmakeAcpToolCallEventAdds
isAcpTaskToolCallto detect Task tool calls by matchingrawInput._toolNametotask(case-insensitive) ortitlestarting withtask:. The newcanonicalItemTypeFromAcpToolCallreturnscollab_agent_tool_callfor these calls and falls back tocanonicalItemTypeFromAcpToolKindfor everything else.makeAcpToolCallEventnow uses this new classifier instead of the kind-only lookup.othertool calls whose_toolNameortitlematch the Task patterns now emitcollab_agent_tool_callinstead ofdynamic_tool_call; non-matchingmcp__calls andsearchkind are unchanged.Macroscope summarized c5af99e.