feat: Add an Allow All mode that auto-approves Cursor ACP permission prompts#2180
feat: Add an Allow All mode that auto-approves Cursor ACP permission prompts#2180isundaylee wants to merge 2 commits into
Conversation
…uto-approval cursor-agent reports no ACP session modes, and its own approvalMode config still prompts over ACP for web search and MCP calls. Add Paseo-owned synthetic modes for Cursor (Always Ask / Allow All): selecting Allow All makes the ACP session auto-answer session/request_permission with the allow option without surfacing a prompt. The mode is threaded through the generic ACP client as autoApprovePermissionModeIds and resolved locally by a provider mode writer, so no writes ever reach cursor-agent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Ah, I see that #2177 has been recently put up (for Grok); let me know if you want me to wait for that one and then rebase on top of that one. Separately, I chose to go with a more explicit |
|
| Filename | Overview |
|---|---|
| packages/server/src/server/agent/providers/acp-agent.ts | Core change: adds syntheticModes/autoApprovePermissionModeIds fields, appendSyntheticModes helper, isSyntheticMode guard, and the auto-approve fast path in requestPermission; all edge cases handled correctly. |
| packages/server/src/server/agent/providers/cursor-acp-agent.ts | Wires up CURSOR_ALLOW_ALL_MODE with correct metadata (ShieldOff/dangerous/isUnattended) and a local-only writeCursorProviderMode; no ACP writes occur for the synthetic mode. |
| packages/server/src/server/agent/providers/generic-acp-agent.ts | Passes syntheticModes, autoApprovePermissionModeIds, and providerModeWriter through to the base class; clean pass-through with no transformation. |
| packages/server/src/server/agent/providers/acp-agent.test.ts | New test suite covers auto-approve, fallback prompting, local vs ACP mode switching, config-echo stickiness, and explicit switch-out; tests access private state through asInternals and private methods directly. |
| packages/server/src/server/agent/providers/cursor-acp-agent.test.ts | Adds catalog test verifying Allow All is appended to natively reported Cursor modes with correct fields; clean and self-contained. |
Reviews (2): Last reviewed commit: "Address review: set up mode-list state v..." | Re-trigger Greptile
| private handleCurrentModeUpdate(update: CurrentModeUpdate): void { | ||
| // An explicit current_mode_update is a genuine agent-side mode change (e.g. | ||
| // a slash command switching to plan), so it may also switch out of a | ||
| // synthetic mode. | ||
| this.currentMode = this.transformModeId(update.currentModeId); | ||
| } |
There was a problem hiding this comment.
Synthetic mode exit is silent — UI can show stale "Allow All"
When current_mode_update arrives while Paseo is in the Allow All synthetic mode, this.currentMode is silently updated to the native mode id ("agent", "plan", etc.) but no mode_changed event is emitted. Until a subsequent config_option_update echo arrives (which does push an event), the Paseo mode picker still shows "Allow All" while auto-approve is already inactive. A user who typed a slash command in Cursor, expecting to stay in Allow All, would then be surprised by a permission prompt. Adding an event emission here when switching out of a synthetic mode (this.isSyntheticMode(this.currentMode) before the assignment) would close this gap.
There was a problem hiding this comment.
The exit is actually announced already: translateSessionUpdate's current_mode_update case returns a mode_changed event carrying the updated mode immediately after handleCurrentModeUpdate runs, so the mode picker is informed in the same update — there’s no window where it shows Allow All with auto-approve inactive. Added an explicit assertion on that emitted event to the switch-out test (6cf394c) so the behavior is locked in.
…rt mode_changed on synthetic-mode exit - Drive the native-switching test's mode list through the real applySessionState path instead of mutating internal availableModes directly (also covers synthetic-mode injection into session state) - Assert the mode_changed event emitted when current_mode_update switches out of a synthetic mode, locking in that the exit is announced to clients Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Linked issue
Closes #1926
Type of change
What does this PR do
Cursor sessions get a Paseo-owned Allow All mode. When selected, the daemon answers every ACP
session/request_permissionwith the allow option instead of surfacing a prompt. This is implemented client-side because cursor-agent's own config cannot silence prompts over ACP: the built-in web search prompts unconditionally, MCP approval ignores--approve-mcps/--force, and the auto-review classifier doesn't run in ACP mode.The shared ACP adapter gains two generic options.
syntheticModesappends Paseo-owned modes to the agent-reported mode list wherever modes are derived; they only extend a non-empty native list, selection is resolved locally by the provider mode writer, and an active synthetic mode survives config-option echoes of the native mode while an explicitcurrent_mode_updatestill switches out of it.autoApprovePermissionModeIdsmakesrequestPermissionresolve immediately with the allow option while such a mode is active (logged, no prompt event) — the same shape as OpenCode'sauto_acceptand the voice-session speak-tool auto-allow. Cursor wires these up with a single Allow All mode (ShieldOff / dangerous /isUnattended, so unattended launches auto-select it); native Agent/Plan/Ask switching keeps using the normal ACP path.This also fixes a latent bug where a locally-handled mode switch re-derived
availableModesfrom config options, clobbering ACP-reported modes.How did you verify it
rm -rf .git/refs/tags) ran end-to-end with zero permission prompts; the daemon loggedAuto-approving ACP permission request (allow-all mode)for both theweb_search_0tool call and the shell call, and the file-system effects were confirmed on disk.paseo permit ls) until manually denied — prompting behavior unchanged outside Allow All.Agent, Plan, Ask, Allow Allwith a real cursor-agent, and that switching Allow All ↔ native modes mid-session behaves (native switches still issuesession/set_mode; selecting Allow All writes nothing to cursor-agent).cursor-agent acpdirectly to confirm the premise:approvalMode/--forcegaps over ACP, and that mode switches don't retract config options (basis for the config-echo guard).npx vitest run packages/server/src/server/agent/providers/acp-agent.test.ts packages/server/src/server/agent/providers/cursor-acp-agent.test.ts packages/server/src/server/agent/providers/generic-acp-agent.test.ts --bail=1— 91 passed (new coverage: auto-approve in synthetic mode, still-prompts in native mode and when no allow option exists, local vs ACP-path mode switching, config-echo stickiness vs explicit switch-out).npm run typecheck— passed.npm run lint— passed.npm run format— ran (Biome/oxfmt).Checklist
npm run typecheckpassesnpm run lintpassesnpm run formatran (Biome)🤖 Generated with Claude Code