Skip to content

feat: Add an Allow All mode that auto-approves Cursor ACP permission prompts#2180

Open
isundaylee wants to merge 2 commits into
getpaseo:mainfrom
isundaylee:cursor-allow-all-mode
Open

feat: Add an Allow All mode that auto-approves Cursor ACP permission prompts#2180
isundaylee wants to merge 2 commits into
getpaseo:mainfrom
isundaylee:cursor-allow-all-mode

Conversation

@isundaylee

Copy link
Copy Markdown

Linked issue

Closes #1926

Type of change

  • Bug fix
  • New feature (with prior issue + design alignment)
  • Refactor / code improvement
  • Docs

What does this PR do

Cursor sessions get a Paseo-owned Allow All mode. When selected, the daemon answers every ACP session/request_permission with 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. syntheticModes appends 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 explicit current_mode_update still switches out of it. autoApprovePermissionModeIds makes requestPermission resolve immediately with the allow option while such a mode is active (logged, no prompt event) — the same shape as OpenCode's auto_accept and 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 availableModes from config options, clobbering ACP-reported modes.

How did you verify it

  • Manual testing against a dev daemon connected over a Paseo client; in Allow All, verified that command gets auto-approved; in default Agent mode, verified that commands require permission; also verified that switching mode in the middle of a session respects the new mode.
  • Live against cursor-agent 2026.07.16 with an isolated dev daemon: in Allow All, a prompt that triggers a web search plus a non-allowlisted destructive shell command (rm -rf .git/refs/tags) ran end-to-end with zero permission prompts; the daemon logged Auto-approving ACP permission request (allow-all mode) for both the web_search_0 tool call and the shell call, and the file-system effects were confirmed on disk.
  • Control run in native Agent mode with the same web-search prompt: the request parked as a pending permission (visible in paseo permit ls) until manually denied — prompting behavior unchanged outside Allow All.
  • Verified the provider catalog reports Agent, Plan, Ask, Allow All with a real cursor-agent, and that switching Allow All ↔ native modes mid-session behaves (native switches still issue session/set_mode; selecting Allow All writes nothing to cursor-agent).
  • Probed cursor-agent acp directly to confirm the premise: approvalMode/--force gaps 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

  • One focused change. Unrelated cleanups split out.
  • npm run typecheck passes
  • npm run lint passes
  • npm run format ran (Biome)
  • UI changes include screenshots or video for every affected platform — not applicable; no app/UI changes (the mode surfaces through the existing mode picker)
  • Tests added or updated where it made sense

🤖 Generated with Claude Code

…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>
@isundaylee isundaylee changed the title Add an Allow All mode that auto-approves Cursor ACP permission prompts feat: Add an Allow All mode that auto-approves Cursor ACP permission prompts Jul 18, 2026
@isundaylee

isundaylee commented Jul 18, 2026

Copy link
Copy Markdown
Author

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 syntheticModes handling rather than patching session response via sessionResponseTransformer - but happy to switch to the latter if you prefer.

@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a Paseo-owned "Allow All" synthetic mode for Cursor ACP sessions that auto-approves every session/request_permission prompt client-side, working around cursor-agent's inability to silence prompts (web search, MCP) via its own ACP config. It also fixes a latent bug where a locally-handled mode switch re-derived availableModes from config options, clobbering ACP-reported modes.

  • syntheticModes and autoApprovePermissionModeIds are added as generic options on ACPAgentSession/ACPAgentClient; synthetic modes are appended only to a non-empty native mode list so the picker always has a native exit path.
  • isSyntheticMode guard in applySessionState and handleConfigOptionUpdate prevents config-option echoes from evicting an active synthetic mode, while an explicit current_mode_update (genuine agent slash-command) may still switch out of it.
  • CursorACPAgentClient wires up the Allow All mode with isUnattended: true so unattended launches auto-select it; writeCursorProviderMode resolves the selection locally without any ACP write.

Confidence Score: 5/5

Safe to merge; the synthetic mode logic is well-isolated, all identified edge cases are handled, and the 91 new and existing tests pass.

The change is cleanly contained within the ACP provider layer. The isSyntheticMode guard correctly preserves the allow-all state through config echoes while allowing genuine agent-side switches to exit it. The auto-approve path falls back to normal prompting when no allow option exists, preventing silent failures.

No files require special attention. The test files use internal state access via asInternals (previously flagged pattern), but no new production-code risks were identified.

Important Files Changed

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

Comment on lines 2645 to 2650
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);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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.

Comment thread packages/server/src/server/agent/providers/acp-agent.test.ts Outdated
…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>
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.

feat: YOLO mode / auto-bypass permissions for Cursor ACP

1 participant