Repository navigation
feat(settings): Agent Permissions offers the same four modes as the chat button - #26117
Closed
brennanb2025 wants to merge 33 commits into
Closed
brennanb2025 wants to merge 33 commits into
brennanb2025 wants to merge 33 commits into
Conversation
…m free-text arguments Settings stores a per-agent permission mode (a shared default plus per-agent overrides). One function turns it into the agent's bypass flag at launch, so a model or other text in Arguments no longer silently turns Yolo off, and Settings shows each agent's real posture instead of 'mixed' as Yolo. Refs STA-8902, STA-8699, #23853, #23387
…liases - Keep the bypass flag inside text that also sets permissions, since a launch adds no flag beside it - Read codex --yolo and claude --permission-mode bypassPermissions as bypass - Detect permission options under every launch grammar - Paired-client writes replace the whole record, as before, and don't flip the mode of text that decides itself - Show the env editor for env-mode agents; summarize installed agents only; resolve each posture once
- Paired web client keeps the typed shape when merging the host's settings reply - Detect Gemini/Qwen approval options and Codex attached short options; read Goose's typed env as its posture - Source Control preview composes args the same way the launch does - One normalizer for the permission fields; memoize postures; split argument reading into its own module
- Lift the flag from Windows-quoted legacy text - Paired writes change only the agents they name - Devin's second bypass option counts as a permission option - Web client keeps its own agent launch settings when merging a host reply - Rows with a per-agent permission choice open expanded; narrower memo deps
- Lift the flag after Windows paths POSIX mis-splits (PowerShell grammar fallback) - Apply the older Devin bypass rewrite in the shared lift (web and paired paths too) - Cache published launch records per settings snapshot and option names per agent - Line-specific SAFETY notes on fixture casts in edited test statements
…yolo as bypass Settings and structured sessions now agree with the launch when these are typed into Arguments under Manual. Also pin that Codex --search keeps Yolo.
Launch composition and Settings/structured/migration read an agent's free-text Arguments with two different rules, so some bypass spellings (Devin --permission-mode bypass, Grok/Cline --opt=value, Codex -a never -s danger-full-access) launched without prompts while Settings showed Manual, and Claude --allow-dangerously-skip-permissions stopped Yolo from adding the flag. One per-agent table next to the bypass flags and one classifier (none / bypass / other) now serve launch, Settings, structured Claude and Codex, the migration and the settings.update lift. It reads --opt value and --opt=value under POSIX, PowerShell and cmd grammars and never treats a quoted word as an option. The migration records an agent in Yolo through an alias as Yolo and keeps its text. Every load now moves a bypass flag or env found in a typed profile's text into that agent's mode, so text an older build writes back cannot latch. Any stored mode marks the profile migrated; one this build does not know is kept and read as Manual. The per-agent badge is gone; the summary line, the selected option and the warning already say it.
The classifier skipped quoted words, but the launch strips quotes before the agent sees argv, so Yolo added a bypass flag beside a quoted Codex `"-a" on-request` and Settings hid a quoted Claude bypass. It now reads the unquoted words the CLI receives; only the lift refuses to cut a quoted span, so stored text stays as written. Devin's --respect-workspace-trust only skips the folder-trust prompt, so it no longer decides the permission verdict. It still rides along with Devin's bypass flag, but composition leaves it out when the user's Arguments already set it, and the lift keeps the user's own value. The settings.update write lift and the load lift now share one per-agent lift: Devin's old short default is rewritten only by the legacy pass, as main did. An entry an older client writes back unchanged is left alone, and overrides change only for agents the write changed, so get, write, get is a fixed point. Per-agent modes a newer build stored are kept on load and read as ask, like the shared mode.
New profiles stored agentDefaultArgs as {}, and older builds read a
missing agent entry as "launch with the bypass flag". A paired web
client set to Manual whose host was downgraded got the older web app,
which launched every agent with bypass and Goose with GOOSE_MODE=auto.
Stored profiles, on the host and in the web client, now always hold an
explicit '' for every agent with a bypass flag and {} for env-bypass
agents. One pair of normalizers builds that shape for defaults, the
per-load lift, host and renderer writes and the web merge. A record
missing entries is saved once; clean data still loads unchanged, and
settings.get publishes the same composed values.
Also refreshes two comments left stale by the quoted-option and
unchanged-write changes.
… the card The Agent Permissions switch cleared every per-agent choice, even when the selected option was clicked again, so a Codex set to Manual quietly flipped to Yolo. The switch and onboarding's answer now set only the shared default; an agent keeps its own choice until its card says Default. Clicking the selected option writes nothing. The tooltip now says the switch applies to agents without a setting of their own. Launches that bring their own arguments (Source Control actions, fix-checks) replaced the configured Arguments and then took the shared mode, so an agent whose Arguments ask (Codex -a on-request) got the bypass flag. The composer now follows the agent's effective mode, the one its card shows: Arguments that ask keep asking, an alias Yolo (Gemini -y) still gets the flag, and caller args that set permissions themselves still decide. The Source Control preview uses the same composer, so it shows what launches.
The Settings card read an agent's Arguments with the local shell while launches that bring their own arguments read them with no shell and ignored grammars that saw nothing, so on macOS Claude `--model opus ^--dangerously-skip-permissions` showed Manual while a Source Control launch added the bypass flag. Both now read one posture: typed text counts as bypass only when POSIX, PowerShell and cmd parsing all read it so, since the same text can launch under any of them. "Set separately" now names every agent the switch won't move: each agent with its own choice, even one equal to the current default or not installed here (it may run on an SSH host), plus installed agents whose Arguments or env launch them differently. An agent that isn't installed but has its own choice shows its Permissions control so Default can clear it.
The permission reading of typed Arguments changed rule three times, and each rule broke a different input: requiring every shell grammar to agree showed a Windows path ending in a backslash before a bypass option (Gemini --include-directories C:\code\ -y) as Manual while the Windows launch bypassed, and the load-time lift still cut escaped flags that the card read as Manual. Arguments are now read with the shell that will launch them. Settings and structured Claude/Codex read them with this machine's local launch shell, resolved as local launches resolve it, and every composer call passes its launch's own platform and shell, so a local launch always agrees with the card. A launch on an SSH or paired host reads them with that host's shell. The lift that moves the flag out of stored text on migration and every load cuts only the flag's exact unquoted, unescaped words, so the cut means the same under every shell; any other spelling stays in the text and is read by the launch's shell.
The load-time lift found word boundaries with POSIX, then PowerShell. On macOS a flag between escaped quotes inside a double-quoted prompt is part of the prompt, but PowerShell splits it out, so every restart cut it from the prompt and turned that agent's Manual into Yolo. The lift now cuts the flag only where POSIX, PowerShell and cmd all read it as a whole, unquoted, unescaped word. If any grammar can't parse the text or reads the flag inside another word, the text stays whole and the launch shell reads it, so a wrong call keeps prompting. The Windows path tests no longer certify a cmd launch with a path that ends in a backslash: cmd quoting doesn't double that backslash, so the agent never gets the option after it. That is a separate fix.
Cutting the stored bypass flag trimmed the gap around it, which could drop a space escaped by a trailing backslash, backtick or caret and glue two words together: `--add-dir C:\code\ --dangerously-skip-permissions --model opus` became `--add-dir C:\code\ --model opus`, which POSIX reads as one word `C:code --model`. A cut is now kept only if POSIX, PowerShell and cmd each read the cut text as exactly the original words minus the flag's. Otherwise the text stays whole and the launch shell reads it, so a wrong call still means more prompts, never fewer.
Conflict: src/shared/global-settings-types.ts imports (kept both the permission-fields type and main's ZcodePlanSite type). Adapted main's new Antigravity reference startup (ai-vault-antigravity-reference-startup.ts) to the typed permission mode: it now composes with the full settings and the launch's own platform and shell, like every other launch path, instead of passing the raw agentDefaultArgs record. Updated two tests main added for the typed mode: main's Cursor AI Vault resume test relied on Cursor's shipped Yolo default, so its fixture now states Yolo; and a paired-host declined-chat launch now expects the caller's arguments combined with the client's permission mode, as every other paired-host caller-args launch in this PR does.
"Set separately: Claude (Manual), Codex (Manual)." read like an instruction to set those agents manually, and said neither why nor where to change it. The line under Agent Permissions now reads "These agents don't follow this switch:" with one entry per agent: "Claude runs Manual: it has its own setting.", "Codex runs Manual: its Arguments set -a on-request.", "Goose runs Manual: its environment sets GOOSE_MODE=approve." Each name is a link button that opens the agent's row and focuses the control to change: its Permissions choice, or its Arguments or Environment field when those decide. It works for agents listed under "Available to install" too. That line is now the one place stating each exception: the row's warning box is gone, and the switch's rule moves from a tooltip into its description. Adds an `inline` Button size for link buttons inside a sentence.
An agent that isn't installed here but whose Arguments decide its mode was listed as "its Arguments set ...", yet its link landed on the Permissions choice because that row had no Arguments field. The row under "Available to install" now shows the same Arguments or Environment field the installed row uses when it decides, and the link focuses it. While agent detection is pending or has failed no rows render, so the names in the line were links that did nothing (and a click while pending was dropped). They now show as plain text until detection finishes, then become links.
Clicking an agent's name in the Agent Permissions line with the mouse focused its Permissions radio, but the radio's ring only draws on :focus-visible, which a focus after a mouse click doesn't trigger, so the user couldn't see where they landed. The reveal now focuses with `focusVisible: true` (supported by the Chromium in Electron 43), using the existing ring. A normal mouse click on the radio looks the same.
…2025/sta-8902-typed-permission-mode # Conflicts: # src/main/claude/claude-structured-launch-resolution.test.ts # src/main/codex/codex-structured-launch-resolution.test.ts # src/main/runtime/orca-runtime-get-structured-agent-session-create-support.ts # src/main/runtime/orca-runtime-get-worktree-ps.ts # src/renderer/src/i18n/locales/es.json # src/renderer/src/i18n/locales/zh.json # src/renderer/src/lib/ai-vault-resume-command.ts # src/shared/global-settings-types.ts
Adds AgentChatPermissionMode ('ask' | 'accept-edits' | 'auto' | 'bypass'), a
superset of the stored Agent Permissions mode, with each agent's offered
subset, the default a setting gives a new chat, and the optional
permissionModes field a host publishes on agentSession.options. An older
host omits the field, which is what keeps a client from showing a picker.
Linear: STA-8261
A new chat records the mode the Agent Permissions setting gives it (create seeds permissionMode), and every later start prefers the chat's stored mode over the setting; a chat with none still follows the setting. Claude: chat modes map to default / acceptEdits / auto / bypassPermissions and apply live through set_permission_mode. The CLI refuses bypass on a session launched without --dangerously-skip-permissions, so that pick is kept as the chat's mode and the delivery path puts the idle child to rest (the idle sweep's silent stop) before the next send, which starts a child launched with the flag and resumes the conversation. A child that still owes work keeps running under its old launch. Codex: thread/start and thread/resume state the chat's policy (approval, sandbox, reviewer); a changed mode rides the next turn/start as approvalPolicy / approvalsReviewer, plus sandboxPolicy only when the sandbox changes. An app-server that refuses approvalsReviewer is retried without it, and one that never reports a reviewer offers no Approve for me. agentSession.options reports permissionModes live and at rest, and the at-rest write path refuses a mode the agent cannot run. Linear: STA-8261
…its model The structured option state holds the chat's mode beside the per-model record, so a model switch never drops it. It comes from the host's permissionModes report; before a session exists, a create seed that names a mode is the host saying it offers the picker. A pick made before the fence is held by the launch, survives a model pick, and is applied after the per-model picks. A permission pick is never remembered as the next chat's launch default: the next chat starts where the setting points. structuredAgentSessionOptionPicks moves to its own module so the options module stays within its size limit. Linear: STA-8261
Its own pill beside the model and options pills, shown only where the host offers a picker (a terminal-backed chat never does). Ask for approval, Accept edits (Claude only), Approve for me where the agent supports it, and Full access in the warning colour on the pill and in the menu. A pick applies from the next message for Codex and at once for Claude. Linear: STA-8261
Its own pill beside the options pill, opening a bottom drawer with the same modes and copy as desktop. Full access reads in the warning colour. Shown only where the paired host reports the chat's permission modes. Linear: STA-8261
Move Claude's fast-mode support helper to its own module so the session options file stays under 300 lines, and read the host's permission report and a test style through narrowed property access instead of Reflect.get.
…025/sta-8261-permission-setting-widen
brennanb2025
force-pushed
the
brennanb2025/sta-8261-permission-picker
branch
from
October 7, 2026 21:07
66d4ff4 to
da54347
Compare
Contributor
Author
|
Closing: the plan changed. Chats now get their own permission setting (Settings → Chat → "Permissions for new chats") in #26116, and the terminal Agent Permissions setting is no longer changed by this work. Widening the terminal setting is not needed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ELI5
Settings → Agents → Agent Permissions decides how much agents may do without asking. It offered only two choices, Ask or Bypass. The new permission button in the chat composer (#26116) offers four. So a user who wanted every new chat to start in Claude's Auto mode, or Accept edits, had to pick it again in every chat. Terminal tabs couldn't use the middle modes at all.
Agent Permissions now offers the same four options as the chat button, with the same labels: Ask for approval, Accept edits, Approve for me and Full access. Whatever you pick there is what new chats and new agent terminals start with.
What Changed
Before
defaultMode: autofor Claude got Ask in chat (STA-9517), and Orca had no way to choose Auto by default.After
--permission-mode acceptEdits--permission-mode auto--dangerously-skip-permissions(unchanged)-a on-request -s workspace-write -c approvals_reviewer=auto_review--dangerously-bypass-approvals-and-sandbox(unchanged)Flags were checked against the installed CLIs' own help (Claude Code 2.1.280, Codex CLI 0.159.0). For Codex, the long form above is what Codex's own
--approve-for-meshortcut expands to (checked in Codex's source). It is used instead of the shortcut because older paired Orca hosts recognise-a/-sas a permission choice and won't add their own bypass flag on top.Mechanism
ask,bypass) keep working with no migration.Why
The common pattern has one default-permission setting that offers the same options as the chat's permission control. This makes Orca's single setting complete instead of adding a second one for chat. It also covers the request behind STA-9517 and STA-3680: running in Auto mode, or Codex's automatic review, by default, without reading the CLI's own config files.
Alternatives considered:
defaultMode, Codexapproval_policy). Rejected: chat states the mode explicitly so the button always shows what the chat really runs in. Codex chat already overridesconfig.tomlon purpose (Make the Agent Permissions toggle apply to Codex chat #20977).--approve-for-meshortcut. Rejected for the mixed-version reason above.Differences from the common pattern:
Linked Issue
Linear STA-8261 follow-up (also STA-9517, STA-3680). No GitHub issue: maintainer change.
Visual Proof
QA ran in a hidden, isolated Orca built from this branch, with stand-in Claude and Codex processes and never the real CLIs.
Agent Permissions with the four options:
Codex's own override offers only what Codex supports:
With the setting on Accept edits, a new Claude chat starts in Accept edits. The stand-in's log shows
set_permission_mode acceptEditsarriving before any message:With the setting on Approve for me, a new Claude chat starts in Approve for me (
set_permission_mode autologged before any message):Testing
2,712 tests across 29 test files pass, run as explicit files. They cover:
65 compatibility checks run the previous branch's actual code against the new saved values and launch inputs, including an older host receiving a middle mode.
tc:nodeandtc:webpass. The changed-code quality gate passes.Real Claude CLI (
ORCA_REAL_CLAUDE_CLI_TEST=1, both files): 11 passed.Electron QA on macOS. Not covered: the earliest Claude/Codex versions that accept these flags, live Windows/Linux/SSH, and real Codex automatic-review sessions.
I manually tested these changes locally
Automated tests added/updated
Review
Agent skill upstream boundary
Notes
Stacked on #26116 (chat permission button), which is stacked on #24508. Merge order: #24508 → #26116 → this PR.
Checklist
pnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (CI will cover)