fix(cursor): map taskToolCall so subagent dispatches are named - #13
Merged
Merged
Conversation
Cursor reports a subagent dispatch as:
{"tool_call": {"taskToolCall": {"args": {"description": ..., "prompt": ...}}}}
`taskToolCall` was absent from `_CURSOR_TOOL_TYPE_MAP`, so
`_extract_cursor_tool()` fell through every branch and returned `("", {})`
even though the payload carries both `description` and `prompt`.
Three consequences of the empty name:
1. Deterministic rule scorers cannot observe the dispatch. `tools_invoked`,
`tools_invoked_in_order`, `tool_args_contain` and `tool_result_contains`
all key on `tc.name`, so a scenario asserting a dispatch fails as though
the agent never delegated -- a false negative. `tool_name_in_cli()` does
not rescue it either, since the raw stream contains `taskToolCall` rather
than the tool name.
2. LLM judges are shown `[TOOL CALL: ]` with `Input: {}` and are left to
guess. Observed in a downstream repo: identical agent behaviour, opposite
verdicts across runs -- "the tool sequence ends with an empty call
consistent with a Task dispatch" (pass) versus "the tool calls are all
Read/Glob/Grep/Shell ... no Task dispatch is present" (fail). The dispatch
had in fact occurred; the planner sub-transcript was present under
`tool_calls[N].result.success.conversationSteps`.
3. `parse_stream_event()` returns None on an empty name, so the single most
significant event in a subagent flow is dropped from live progress output.
Name it `Task` to match Claude Code's name for the same primitive, so
scenarios can assert on it uniformly across agents.
Tests: assert the dispatch surfaces as `Task` with args intact, plus a
regression test that unmapped subtype keys still return `("", {})`.
|
All contributors have signed the CLA ✍️ ✅ |
Collaborator
Author
|
recheck |
Collaborator
Author
|
I have read the CLA Document and I hereby sign the CLA |
1 similar comment
Collaborator
Author
|
I have read the CLA Document and I hereby sign the CLA |
dorringel
approved these changes
Jul 29, 2026
Collaborator
Author
|
recheck |
3 tasks
dorrfrog
approved these changes
Jul 30, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problem
Cursor reports a subagent dispatch as:
{"tool_call": {"taskToolCall": {"args": {"description": "...", "prompt": "..."}}}}taskToolCallis absent from_CURSOR_TOOL_TYPE_MAP, so_extract_cursor_tool()falls through every branch and returns("", {})— even though the payload carries bothdescriptionandprompt. The data was always there; only the name lookup was missing.Impact
The empty
ToolCall.namebreaks three things:1. Deterministic rule scorers cannot observe the dispatch.
tools_invoked,tools_invoked_in_order,tool_args_containandtool_result_containsall key ontc.nameinscorer/rules/trajectory.py. A scenario asserting delegation fails as though the agent never delegated — a false negative, the worst direction for a test.tool_name_in_cli()does not rescue it, since the raw stream containstaskToolCall, not the tool name.Measured against a real recorded payload:
tools_invoked: [Task]tool_args_contain: {Task: …}skill_invoked(implementation-planner)(False, '')(True, 'via Task tool args')2. LLM judges are shown
[TOOL CALL: ]withInput: {}(scorer/llm/scorer.py:145) and are left to guess, which makes any delegation criterion nondeterministic. Observed downstream: identical agent behaviour, opposite verdicts across runs — "the tool sequence ends with an empty call consistent with a Task dispatch" (pass) vs "the tool calls are all Read/Glob/Grep/Shell … no Task dispatch is present" (fail). The dispatch had in fact occurred; the planner sub-transcript was present undertool_calls[N].result.success.conversationSteps.3. Live progress silently drops it.
parse_stream_event()doesif not name: return None, so the single most significant event in a subagent flow renders as nothing.The practical cost downstream is that delegation can only be asserted through prose the agent happens to emit, never through what it actually did.
Fix
One map entry. Named
Taskto match Claude Code's name for the same primitive, so scenarios can assert on it uniformly across agents.Tests
test_parses_cursor_task_tool_call— the dispatch surfaces asTaskwith args intact, mirroring the existingreadToolCall/shellToolCall/grepToolCallcases.test_unknown_tool_call_subtype_stays_unnamed— regression guard that unmapped subtype keys still return("", {}).Verification
uv run make check— 3212 passed, 45 skipped, lint clean.uv run belt agent info cursor— CLI healthy (per CONTRIBUTING's "unit tests can pass while the CLI is broken").