Skip to content

feat(acp): handle Cursor cursor/* extension methods - #785

Merged
xintaofei merged 2 commits into
xintaofei:mainfrom
goon-13:fix/cursor-acp-ext-methods
Sep 21, 2026
Merged

xintaofei merged 2 commits into
xintaofei:mainfrom
goon-13:fix/cursor-acp-ext-methods

Conversation

@goon-13

@goon-13 goon-13 commented Sep 19, 2026

Copy link
Copy Markdown

Fixes #783

What

Register Cursor's ACP client-extension methods so the host no longer replies -32601 Method not found:

method reply
cursor/ask_question shared ask card → { outcome: answered | skipped }
cursor/create_plan shared plan-approval card → { outcome: accepted | rejected | cancelled }
cursor/update_todos { outcome: accepted }
cursor/task { outcome: completed } (echo agentId / durationMs when present)
cursor/generate_image { outcome: generated } if filePath is set, else rejected

Same pattern as _x.ai/ask_user_question / _x.ai/exit_plan_mode / elicitation/create. New methods go in src-tauri/src/acp/cursor_ext.rs plus one .on_receive_request in connection.rs.

Disconnect / malformed plan or ask never silently acceptedcancelled / skipped, matching the Grok disconnect replies.

Test

cd src-tauri
cargo test --features test-utils cursor_ext

CI: this PR's Test workflow (cargo test + clippy -D warnings on desktop/server × linux/mac/windows).

Live Cursor + Opus 5 subagents are out of unit-test scope; after merge, a Task spawn should no longer log Method not found for cursor/task.

Files

  • src-tauri/src/acp/cursor_ext.rs (new)
  • src-tauri/src/acp/connection.rs
  • src-tauri/src/acp/mod.rs

goon and others added 2 commits September 20, 2026 00:50
Cursor's CLI sends cursor/ask_question, cursor/create_plan,
cursor/update_todos, cursor/task, and cursor/generate_image as JSON-RPC
requests. Without handlers, sacp replies -32601 and a live turn shows a
red banner on every Task spawn.

Blocking methods reuse the existing ask and plan-approval cards (same
path as Grok). The other three return the documented outcome envelope so
the agent is not left waiting.

Refs xintaofei#783.
…chema

Review fixes on top of the cursor extension-method bridge.

* `cursor_ext`'s test module called `matches_method` without importing
  `sacp::JsonRpcMessage` (the supertrait that defines it, not the
  `JsonRpcRequest` the derive is named after), so `cargo test` — and
  `clippy --all-targets`, which CI runs — failed to build the lib test
  target. Import it.

* `build_cursor_ask_response` replied `answered` with an empty
  `selectedOptionIds` whenever the user used the card's free-text "Other"
  row — which single-select uses INSTEAD of an option, so it is the
  ordinary path, not an edge case. Cursor then reads "the user was asked
  and chose nothing" and the typed answer is dropped. `answered` is now
  used only when every selected label maps to one of Cursor's option ids;
  otherwise the reply is the documented `skipped` with the user's own
  words in `reason`. Mirrors `pi_select_option_id`, which refuses to fake
  an option id for the same reason.

* Cursor's `CursorUpdateTodosResponse` `accepted` variant is
  `{ outcome, todos }` with `todos` required; the reply omitted it. Echo
  back the list the request carried.

* Every ask early-return (bridge absent, ask disabled, malformed params,
  card could not open) replied a bare `skipped`, which is also what a user
  dismissing the card sends. Carry a structural `reason` — never payload —
  so the agent can tell "the user passed" from "this host never asked".

* The question chip read a constant "Cursor" when the request carried no
  `title`; use `synthesize_header`, as the grok and pi bridges do.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@xintaofei

Copy link
Copy Markdown
Owner

codeg work task 208 is done — #785 (3 files, +235/-30).

@xintaofei
xintaofei merged commit c8f8343 into xintaofei:main Sep 21, 2026
7 checks passed
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.

Cursor ACP: cursor/task / cursor/update_todos return Method not found; long Opus turns die with HTTP/2 CANCEL

2 participants