Skip to content

fix(tools): map cursor-agent fileText arg to canonical content - #119

Draft
Nomadcxx wants to merge 2 commits into
devfrom
fix/filetext-arg-alias
Draft

fix(tools): map cursor-agent fileText arg to canonical content#119
Nomadcxx wants to merge 2 commits into
devfrom
fix/filetext-arg-alias

Conversation

@Nomadcxx

Copy link
Copy Markdown
Owner

Summary

cursor-agent 2026.07.17 carries full-file edit/write bodies under fileText (it replaced the earlier streamContent field). ARG_KEY_ALIASES in src/provider/tool-schema-compat.ts had no mapping for it, so the content was dropped and the write/edit reroute lost the body.

Adds ["filetext", "content"]. Key normalization (lowercase + strip non-alphanumerics) means this one entry covers fileText, file_text, and filetext.

How it was found

Reading the cursor-agent client bundle directly (the binary is a shell launcher over readable Node.js). Its edit builder constructs full-file edits from r.fileText, and streamContent is absent from the 2026.07.17 build while present in 2026.07.09 — live protocol drift between builds.

Test

  • New: normalizes fileText (cursor-agent full-file content) to content
  • bun run test:ci:unit: 540 pass / 0 fail

cursor-agent 2026.07.17 carries full-file edit/write bodies under `fileText`,
replacing the earlier `streamContent` field. ARG_KEY_ALIASES had no mapping for
it, so the body was dropped and the write/edit reroute lost the content. Add
`filetext -> content`; key normalization also covers `file_text`.

Found by reading the cursor-agent client bundle directly.
@Nomadcxx
Nomadcxx marked this pull request as draft July 21, 2026 12:23
@Nomadcxx

Copy link
Copy Markdown
Owner Author

Draft for refinement, not merge-ready. Verified by unit test (540/0), but still needs:

  • Live capture on cursor-agent 2026.07.17 confirming a full-file edit/write actually carries fileText on the wire (the bundle read shows the field; a real stream fixture would confirm the drift end-to-end).
  • Decide whether to keep the now-superseded streamContent alias for back-compat with older agents (leaning keep).

@Nomadcxx Nomadcxx left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: fileText alias is correct, but it re-opens the edit→write reroute with the anti-clobber guard disabled

Verdict: Not merge-ready yet. The alias itself is right, but as written this PR re-enables edit→write reroutes for 2026.07.17+ agents while the suspicious-overwrite guard stays dark for exactly those payloads. Details below.

What checks out

  • Correct, minimal fix. resolveCanonicalArgKey lowercases + strips non-alphanumerics (tool-schema-compat.ts:263-266), so ["filetext", "content"] covers fileText, file_text, and filetext as described.
  • Schema-respect and collision handling are safe. A tool that legitimately declares fileText is skipped by normalization; an explicit content key wins over fileText with the drop recorded in collisionKeys.
  • End-to-end path verified by tracing: edit {path, fileText} → alias → content → edit repair sets new_stringmissing: [old_string] only → full-file hint → tryRerouteEditToWrite → write carrying the content. Works for write/oc_write, snake_/camelCase schemas.
  • Correctly alias-only. Adding fileText to the local edit schema (defaults.ts:187) instead would trip the schema-respect skip and strand the body, since hasEditBody doesn't know fileText. This is the right layer.
  • streamContent back-compat retained (alias, reroute chain at lines 200-207, hasEditBody at 738, local schema) — right call for agents ≤ 2026.07.09.
  • Tests reproduced locally: 540 pass / 0 fail.

Important — must fix before merge

1. detectSuspiciousStreamContentWrite never fires for fileText payloads.

hasStreamContentArg (runtime-interception.ts:877-879) only matches keys normalizing to streamcontent:

return Object.keys(args).some((key) => key.toLowerCase().replace(/[^a-z0-9]/g, "") === "streamcontent");

Before this PR, fileText payloads never rerouted at all (body dropped, hint emitted — the bug being fixed), so the guard's blindness was moot. After this PR, fileText payloads reroute to write — but the guard that blocks a reroute when it would shrink an existing ≥5-line file to ≤10% lines and ≤10% bytes never fires for them. The hazard the guard models (suspicious/truncated full-file body clobbering a real file) is protocol-field-agnostic, and it's tested for streamContent (provider-runtime-interception.test.ts:649,687) but not fileText.

Fix:

function hasStreamContentArg(args: Record<string, unknown>): boolean {
  return Object.keys(args).some((key) => {
    const token = key.toLowerCase().replace(/[^a-z0-9]/g, "");
    return token === "streamcontent" || token === "filetext";
  });
}

plus a regression test mirroring the existing suspicious-reroute tests with a fileText payload.

Minor

2. No reroute-path test for fileText. The new test covers only the direct write path, but the headline failure is the edit→write reroute. One test — edit call {path, fileText} with no old_stringtryRerouteEditToWrite → write carrying the content — would lock in the behavior.

3. Dead fileText fallback in the guard. runtime-interception.ts:823-824 reads args.fileText from the rerouted write args, but buildWriteArguments only ever emits {path|filePath, content}fileText can never appear there. It reads as if the guard were already fileText-aware when it isn't; remove it when fixing #1.

Process notes

  • The two open checklist items in the draft comment remain the right gates: live capture on 2026.07.17 confirming fileText on the wire (the premise is currently inferred from bundle reading), and keeping the streamContent alias (yes, keep).
  • Worth documenting the streamContentfileText drift window (2026.07.09 → 2026.07.17) — this codebase now has three layers (ARG_KEY_ALIASES, the reroute chain, the guard) that each had to learn about protocol field renames independently.

The filetext alias re-enables edit-to-write reroutes for cursor-agent
2026.07.17+ full-file edit payloads, but hasStreamContentArg only matched
the streamcontent token, so the anti-clobber guard (blocks reroutes that
would shrink an existing file) never fired for those payloads. Match both
tokens so the guard applies regardless of protocol field name.

Also remove the dead fileText fallback when reading rerouted write args
(buildWriteArguments only ever emits content), and add regression tests:
fileText edit payloads reroute to write carrying the content, and
suspicious fileText reroutes that would shrink an existing file are
skipped. The guard test fails without the hasStreamContentArg change.
@Nomadcxx

Copy link
Copy Markdown
Owner Author

Follow-up to the review above: all three findings are now addressed on this branch in d6c00cc.

Suite: 542 pass / 0 fail (bun run test:ci:unit).

Still open before merge, per the draft checklist: a live capture on cursor-agent 2026.07.17 confirming fileText actually appears on the wire (the premise is currently inferred from the client bundle). Full audit in PR-119-review.md on the dev machine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant