Skip to content

fix(cursor): nest ACP extension outcome so plan approvals aren't cancelled#80

Open
heavygee wants to merge 1 commit into
mainfrom
fix/cursor-create-plan-outcome-envelope
Open

fix(cursor): nest ACP extension outcome so plan approvals aren't cancelled#80
heavygee wants to merge 1 commit into
mainfrom
fix/cursor-create-plan-outcome-envelope

Conversation

@heavygee

Copy link
Copy Markdown
Owner

Summary

Fixes #79. Approving a Cursor plan-mode CreatePlan request from the HAPI web UI relayed User cancelled back to the agent instead of an approval, so the plan → execute handoff never fired (plan mode unusable for Cursor sessions over HAPI).

Root cause: Cursor's ACP blocking extension methods (cursor/ask_question, cursor/create_plan) require the JSON-RPC result to nest the outcome under an outcome key (see https://cursor.com/docs/cli/acp):

interface CursorCreatePlanResponse {
  outcome:
    | { outcome: "accepted"; planUri?: string }
    | { outcome: "rejected"; reason?: string }
    | { outcome: "cancelled" };
}

cli/src/cursor/utils/cursorExtensionAdapter.ts returned the outcome flat ({ outcome: "accepted" }), so Cursor read response.outcome.outcome as undefined and fell back to a cancellation. The hub still recorded the request as approved (green checkmark), which is why the symptom looked like "approved in the UI, cancelled to the agent". The same flat shape affected cursor/ask_question (identical latent bug).

The standard ACP session/request_permission responses in AcpSdkBackend already use the nested { outcome: { outcome: ... } } shape, so the cursor extension responses were the outlier.

Changes

  • Wrap all cursor/ask_question and cursor/create_plan responses (accept / reject / cancel / answered, incl. cancelAll) in the nested { outcome: { ... } } envelope via a small wrapOutcome helper.
  • Update existing adapter tests to assert the nested wire shape.
  • Add regression tests: affirmative approval → CreatePlan (approved and approved_for_session) resolves to { outcome: { outcome: 'accepted' } }, plus reject → rejected and abort → cancelled.

Test plan

  • bun typecheck (cli/web/hub) green
  • bun run test green (cli + hub)
  • cursorExtensionAdapter.test.ts — 12/12 pass
  • Manual: Cursor plan-mode session on a hub, click Yes on a CreatePlan approval, confirm the agent proceeds (plan accepted) rather than receiving User cancelled.

…elled

Cursor's ACP blocking extension methods (cursor/ask_question,
cursor/create_plan) expect the JSON-RPC result to nest the outcome under
an `outcome` key, e.g. `{ outcome: { outcome: "accepted" } }`. The
adapter returned it flat (`{ outcome: "accepted" }`), so Cursor read
`response.outcome.outcome` as undefined and fell back to a cancellation
— an approved plan was relayed to the agent as `User cancelled`, making
plan mode unusable over HAPI for Cursor sessions.

Wrap every ask_question / create_plan response in the nested envelope
and add regression tests asserting the exact wire shape for the
affirmative approval -> CreatePlan path (plus approved_for_session,
reject, and abort).

Fixes #79

Co-authored-by: Cursor <cursoragent@cursor.com>
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 plan-mode approval relayed to agent as "User cancelled"

1 participant