fix(#611): make the send_to contract true, and stop the errors hiding the fix (S) - #614
Conversation
… the fix The schema declared mode .optional() while the runtime threw when it was omitted, so a correct reading of the published contract produced a failing call. Agents rediscovered this by trial and error at 2-3 wasted turns each; one said out loud that 'the coordination tool rejected its documented default mode'. It was never a regression -- it was always wrong. - mode now has a real default of "agent" that the runtime honours, so the contract and the behaviour agree. agent mode is what essentially every caller means. - the remaining guards carry the copy-pasteable example the enum errorMap already produced. Previously a BOGUS mode got an example and an OMITTED mode got a bare string -- backwards, since omission is the case agents actually hit. - read_screen accepts surface_id as an alias for surface, because OUR OWN output taught that name: spawn_agent's output schema and every list_agents row emit surface_id, so the natural workflow hands it straight back. Not compatibility -- we taught it. - no message alias: that field never existed. - trimmed the two worst parameter descriptions (allow_busy described a deprecated no-op in 301 chars; report_path ran to 1023). Tests go through a real MCP client because the defect was in the served contract. Calibrated against origin/main: both new tests FAIL there and pass here, while the 8 surrounding tests pass either way. Tool surface: 31,674 -> 30,993 bytes (~7,919 -> ~7,748 tokens/turn). Closes #611. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four tests failed, all of them asserting the behaviour the fix removes: - 'returns the exact missing-mode error' asserted the bare refusal that IS the bug. Inverted: omission must now be accepted. - 'requires mode ...' was named after the contract lie. Renamed and inverted. - the allow_busy doc test pinned 300+ chars of prose per site, twice. It now guards the two things that matter -- still marked deprecated, and the safety gates it does NOT bypass are named -- not the wording. - the coordination-footer test CAUGHT ME OVER-TRIMMING. My report_path cut dropped the documented coordination_footer_delivered=false branches, which callers need. Restored that substance (1023 -> 838, not 412) and left the guard's regex untouched. Also trimmed the second allow_busy site, which was the same 200+ chars of no-op prose duplicated on send_to_agent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_7b28ce96-738b-4a2d-818e-2b3ba2972bf2) |
|
Warning Review limit reachedNext included review available in 56 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| ), | ||
| allow_busy: SendToArgsSchema.shape.allow_busy.describe( | ||
| "Deprecated no-op retained for compatibility: send_to always attempts immediate delivery. Input landing behind an active turn is reported as queued_behind_turn, not a nonterminal queued state. Picker/menu and permission-prompt safety gates still refuse text; use mode=key for deliberate menu driving.", | ||
| "Deprecated no-op. Safety gates still refuse text at a picker/menu or permission prompt; use mode=key to drive those deliberately.", |
There was a problem hiding this comment.
🟠 High src/server.ts:17606
allow_busy: true still changes submission verification by selecting BUSY_AGENT_SUBMIT_VERIFY_TIMEOUT_MS, so callers are told the flag is a no-op even though it changes timeout behavior and can produce a SubmitVerificationError. Remove the flag from the deliverAgentInput call or update this description to document its actual effect.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/server.ts around line 17606:
`allow_busy: true` still changes submission verification by selecting `BUSY_AGENT_SUBMIT_VERIFY_TIMEOUT_MS`, so callers are told the flag is a no-op even though it changes timeout behavior and can produce a `SubmitVerificationError`. Remove the flag from the `deliverAgentInput` call or update this description to document its actual effect.
Daemon performance budget: GREENReplay: 8 clients x 12 rounds. Runner regression ratio: 1.25x. Sampled rows use max(2 x (p95 - p50), 3 sigma of p50 after five green main runs); single-shot or untrusted-history rows retain +300 ms. Every row keeps the baseline x 1.25 floor and its sanity cap.
15 rows unchanged. Full table
|
Make the
send_tocontract truesize:S. Closes #611.Etan sent a screenshot of three consecutive
send_tocalls failing, with the agent's own line under them:That sentence is the bug. The schema declared
mode.optional()(server.ts:726) while the runtime threw when it was omitted (:17590,:17612). A correct reading of the published contract produced a failing call, so every agent rediscovered it by trial and error at 2–3 wasted turns each. It was never a regression — it was always wrong.What changed
modenow has a real.default("agent")that the runtime honours. Agent mode is what essentially every caller means. Verified off the wire against the built server:mode default: "agent".The remaining guards carry the copy-pasteable example. Previously a bogus mode got
Example: send_to({ mode: "agent", agent_id: "...", text: "hello" })from the enumerrorMap, and an omitted mode got a bare string. That is backwards — omission is the case agents actually hit. The constant already existed at:723; the throws just did not use it.read_screenacceptssurface_idas an alias forsurface— because our own output taught that name.spawn_agent's output schema (:860) and everylist_agentsrow (:4339) emitsurface_id, so the natural workflow (list agents, read the surface it named) hands the key straight back and got a validation error. The value was always right; only the name was, and the tool that taught the wrong name was ours. This is an alias for that reason — not backwards compatibility.No
messagealias.git log -S'message: z.string()' -- src/server.tsreturns nothing; that field never existed, so an alias would invent an API that was never real. The improved error namestextinstead.Description bloat
Etan raised this twice. Measured, rather than guessed — the whole tool surface, off the wire:
send_tospawn_agent−2.2%. That is a start, not a win, and I am not dressing it up. The measurement produced a more useful finding than the trim:
spawn_agent(~2,736 tok) is 63% BIGGER thansend_to(~1,676), and the bulk is in PARAMETER descriptions, not the tool description. Etan namedsend_totwice; the bigger cost is next door. That baseline is now recorded for the real lane.Trimmed here:
allow_busy, which spent 301 characters describing a deprecated no-op (twice — the same prose was duplicated onsend_to_agent), andreport_path.The irony worth naming: the description is enormous and still failed to convey the one field a caller had to supply. Length is not clarity; here it bought neither.
Tests
New tests go through a real MCP client, because the defect was in the served contract rather than an internal helper.
Calibrated against
origin/main: both new tests FAIL there and pass here, while the 8 surrounding tests pass either way — so the new arms are the only thing moving.Four existing tests encoded the old contract and were updated, not deleted — including one literally named "requires mode", which asserted the refusal that was the bug.
One of them caught me over-trimming. The coordination-footer test pinned that
report_pathdocuments everycoordination_footer_delivered: falseoutcome. My first cut (1023 → 412 chars) dropped branches callers need. I restored the substance (→ 838) and left that guard's regex untouched. It did exactly its job, on me.Full suite: 159 files, 3799 passed, 1 skipped.
— cmuxlayerClaude-70bfff64 (lead) · claude/claude-opus-5[1m]
Note
Medium Risk
Changes default parsing and validation for widely used coordination tools (
send_to,read_screen); behavior is intentional but affects every agent caller omittingmodeor passingsurface_id.Overview
Fixes #611 by aligning the MCP tool contract with runtime behavior so agents stop failing on “correct” calls.
send_to:modeis now.default("agent")instead of optional while the handler still required it. Omittedmodeno longer throws before parse; invalid modes and wrong payload keys still error with the sharedSEND_TO_WORKING_EXAMPLE.allow_busydescriptions onsend_to/send_to_agentare shortened but still note picker/permission safety gates.read_screen: Acceptssurface_idas an alias forsurface(matchinglist_agents/spawn_agentoutput), resolves one ref, and errors clearly if neither is provided.spawn_agent: Trimsreport_pathparameter docs without dropping coordination-footer guidance.Tests: Real MCP client coverage for omitted-
modedelivery and published schema default; prior tests that encoded the bogus “mode required” refusal are inverted or relaxed.Reviewed by Cursor Bugbot for commit aa58772. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Default
send_tomode toagentinSendToArgsSchemaand acceptsurface_idinread_screenSendToArgsSchemato setmodedefault toagent, fixing a mismatch between the published contract and runtime behavior; omitted mode now reaches the agent path instead of being rejected.read_screento acceptsurface_id(emitted bylist_agentsandspawn_agent) as an alias forsurface, resolving both into one reference and erroring when neither is provided.send_tousage example to errors caused by legacy payload parameter names, and shortensallow_busydescriptions to mark the option as a deprecated no-op while noting picker/menu/permission safety gates remain active.modenow get agent-mode handling;read_screencallers must provide eithersurfaceorsurface_idor receive an explicit target error.📊 Macroscope summarized aa58772. 1 file reviewed, 1 issue evaluated, 0 issues filtered, 1 comment posted
🗂️ Filtered Issues