Skip to content

fix: map runtime modes to correct permission levels#1587

Merged
juliusmarminge merged 12 commits intopingdotgg:mainfrom
oski646:fix/permission-mode-bypass
Apr 9, 2026
Merged

fix: map runtime modes to correct permission levels#1587
juliusmarminge merged 12 commits intopingdotgg:mainfrom
oski646:fix/permission-mode-bypass

Conversation

@oski646
Copy link
Copy Markdown
Contributor

@oski646 oski646 commented Mar 30, 2026

What Changed

I fixed running Claude in Supervised mode. Fixing that also made me think we should adjust it for the Codex adapter. I also introduced a new mode inspired by the Claude CLI called “Auto accept edits”. I did not know how to handle the fix without adding this mode because having just "Supervised" which should be read only or full accept mode for the user (to my understanding), and "Full Access" which is a full bypass mode, is probably not enough for some users. Adding this one more mode was low hanging fruit for me, so I thought I could do it here as well. I tried to keep everything simple and focus only on fixing the bug and adding this one mode.

I am not sure if the change to the test, running it in a for loop, is necessary. I am open to suggestions about this and, of course, other changes I made.

I also noticed some other things that could be improved related to that change, but I did not want to make this PR big and difficult to review.

Why

Sessions launched without --dangerously-skip-permissions (Claude) crashed when switching away from full-access because the fallback always tried to restore bypassPermissions. Map each runtime mode to its proper Codex approval policy / sandbox pair and Claude SDK permission mode, and expose the new auto-accept-edits option in the UI.

Fixes #1437
Fixes #1241
Fixes #1609

UI Changes

The only change to UI is new "Auto-accept edits" mode added. Nothing else.

CleanShot 2026-03-30 at 21 34 00

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

High Risk
Changes how runtime modes map to permission/sandbox levels for both Codex and Claude, which directly affects command/file-access enforcement and could unintentionally loosen restrictions if incorrect. Also introduces a new runtime mode that must be handled consistently across server, UI, and persistence.

Overview
Fixes runtime-mode permission mapping across providers and adds a new intermediate runtime mode.

RuntimeMode now includes auto-accept-edits, Codex runtime mapping is updated so approval-required becomes untrusted/read-only, auto-accept-edits becomes on-request/workspace-write, and full-access remains never/danger-full-access.

Claude sessions now map auto-accept-edits to SDK acceptEdits, and switching back from plan restores the correct base permission mode (defaulting to default, not bypassPermissions). The web composer replaces the binary access toggle with a dropdown showing all three modes plus descriptions, and draft persistence/normalization is updated to accept the expanded RuntimeMode schema.

Reviewed by Cursor Bugbot for commit 6707b61. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add 'auto-accept-edits' runtime mode and fix permission level mappings

  • Adds 'auto-accept-edits' as a valid RuntimeMode across the schema, store, server, and UI, sitting between supervised and full-access modes.
  • Fixes incorrect permission mappings in codexAppServerManager.ts: 'approval-required' now maps to untrusted/read-only, 'auto-accept-edits' to on-request/workspace-write, and 'full-access' to never/danger-full-access.
  • Replaces the binary runtime mode toggle in ChatView.tsx and CompactComposerControlsMenu.tsx with a labeled dropdown that shows descriptions for all three modes.
  • Behavioral Change: sessions without a basePermissionMode now restore to 'default' after a plan turn instead of 'bypassPermissions'.

Macroscope summarized 6707b61.

…pt-edits mode

Sessions launched without --dangerously-skip-permissions crashed when
switching away from full-access because the fallback always tried to
restore bypassPermissions. Map each runtime mode to its proper Codex
approval policy / sandbox pair and Claude SDK permission mode, and
expose the new auto-accept-edits option in the UI.

Fixes pingdotgg#1437
Fixes pingdotgg#1241
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dadfcdcc-f6a3-4cb7-a3a8-abc8675813b9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 30, 2026
oski646 added 3 commits March 30, 2026 21:50
setRuntimeMode had an explicit allowlist that missed "auto-accept-edits",
converting it to null. Replace with nullish coalescing since TypeScript
already constrains the parameter to RuntimeMode | null | undefined.
Copy link
Copy Markdown
Member

@juliusmarminge juliusmarminge left a comment

Choose a reason for hiding this comment

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

seems mostly fine apart from the test

Comment on lines +2496 to +2501
for (const [runtimeMode, expectedBase] of [
["full-access", "bypassPermissions"],
["approval-required", "default"],
["auto-accept-edits", "acceptEdits"],
] as const) {
it.effect(`restores ${expectedBase} permission mode after plan turn (${runtimeMode})`, () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it.each or similar

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done 🙌

@oski646
Copy link
Copy Markdown
Contributor Author

oski646 commented Apr 1, 2026

Julius, I also noticed that adding this mode caused the MCP tool approval bug to appear. I asked Claude if it is actually connected with the change I made, and here is the response:

The issue is NOT caused by your runtime mode fix. It's a pre-existing gap in MCP tool call handling:

  1. Server side: classifyRequestType() returns "dynamic_tool_call" for MCP tools (line 462 of ClaudeAdapter.ts)
  2. Server side: requestKindFromCanonicalRequestType() doesn't handle "dynamic_tool_call" — returns undefined (line 156 of ProviderRuntimeIngestion.ts)
  3. Activity payload: requestKind is omitted when undefined (line 192: ...(requestKind ? { requestKind } : {}))
  4. Web side: derivePendingApprovals() requires requestKind to be one of "command" | "file-read" | "file-change" — when it's missing, the approval is silently dropped (line 208 of session-logic.ts)

The approval panel never renders → the agent waits forever for a decision that can never come.

Why your PR surfaces this: Before your fix, "auto-accept-edits" wasn't properly mapped, so everything likely fell through to "full-access" behavior (auto-approve all). Now that "auto-accept-edits" correctly maps to permissionMode: "acceptEdits", MCP tools that aren't file-edits actually hit the canUseTool callback and need manual approval — exposing this existing gap.

The fix needs to happen in two places:

  1. Add "dynamic_tool_call" handling to both requestKindFromCanonicalRequestType (server) and requestKindFromRequestType (web) — or better, add a new request kind like "mcp" or "other"
  2. Update the PendingApproval type and ComposerPendingApprovalPanel to handle this new kind

Would you like me to fix that here as well, or create a new PR focusing on fixing the pre existing bug?

@juliusmarminge
Copy link
Copy Markdown
Member

File a new PR please 🙏🏽

void handleRuntimeModeChange(
runtimeMode === "full-access" ? "approval-required" : "full-access",
);
const cycleRuntimeMode = useCallback(() => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is a toggle button the best way? A select with a short description might be better?

https://coss.com/ui/docs/components/select#with-object-values

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah maybe, let me check how it would look and I'll post a screenshot here so we can decide.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

CleanShot 2026-04-06 at 21 01 02
CleanShot.2026-04-06.at.21.02.31.mp4

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It could work, wdyt?

@github-actions github-actions bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Apr 6, 2026
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp bot commented Apr 6, 2026

Approvability

Verdict: Needs human review

This PR changes how existing runtime modes map to permission levels (e.g., 'approval-required' now maps to more restrictive 'untrusted'/'read-only' instead of 'on-request'/'workspace-write') and adds a new 'auto-accept-edits' mode. These are significant runtime behavior changes affecting the permission/sandbox system that warrant human review.

You can customize Macroscope's approvability policy. Learn more.

@juliusmarminge juliusmarminge enabled auto-merge (squash) April 9, 2026 18:08
@juliusmarminge juliusmarminge merged commit 7372184 into pingdotgg:main Apr 9, 2026
11 of 12 checks passed
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6707b61. Configure here.

"auto-accept-edits": "acceptEdits",
"full-access": "bypassPermissions",
};
const permissionMode = runtimeModeToPermission[input.runtimeMode];
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.

Implicit undefined for approval-required lacks exhaustiveness safety

Low Severity

The runtimeModeToPermission map is typed as Record<string, PermissionMode> and intentionally omits "approval-required", relying on undefined to propagate through truthy checks and ?? "default" fallbacks. Unlike the Codex adapter's mapCodexRuntimeMode switch statement which gets TypeScript exhaustiveness checking, this string-keyed Record provides no compile-time guarantee that all RuntimeMode values are accounted for. If a new runtime mode is ever added, it would silently fall through to undefined / "default" behavior without any compiler warning. Making this a complete map over RuntimeMode (mapping "approval-required" explicitly to "default") would make the intent clear and get exhaustiveness checking from TypeScript.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6707b61. Configure here.

rororowyourboat added a commit to rororowyourboat/t3code that referenced this pull request Apr 9, 2026
…threadId (#2)

* Raise slow RPC ack warning threshold to 15s (pingdotgg#1760)

* Use active worktree path for workspace saves (pingdotgg#1762)

* Stream git status updates over WebSocket (pingdotgg#1763)

Co-authored-by: codex <codex@users.noreply.github.com>

* fix(web): unwrap windows shell command wrappers (pingdotgg#1719)

* Rename "Chat" to "Build" in interaction mode toggle (pingdotgg#1769)

Co-authored-by: Julius Marminge <julius0216@outlook.com>

* Assign default capabilities to Codex custom models (pingdotgg#1793)

* Add project rename support in the sidebar (pingdotgg#1798)

* Support multi-select pending user inputs (pingdotgg#1797)

* Add Zed support to Open actions via editor command aliases (pingdotgg#1303)

Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>

* Closes pingdotgg#1795 - Support building and developing in a devcontainer (pingdotgg#1791)

* Add explicit timeouts to CI and release workflows (pingdotgg#1825)

* fix(web): distinguish singular/plural in pending action submit label (pingdotgg#1826)

* Refactor web stores into atomic slices ready to split ChatView (pingdotgg#1708)

* Add VSCode Insiders and VSCodium icons (pingdotgg#1847)

* Prepare datamodel for multi-environment (pingdotgg#1765)

Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>

* Implement server auth bootstrap and pairing flow (pingdotgg#1768)

Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>

* Use dev proxy for loopback auth and environment requests (pingdotgg#1853)

* Refresh local git status on turn completion (pingdotgg#1821)

Co-authored-by: codex <codex@users.noreply.github.com>

* fix(desktop): add Copy Link action for chat links (pingdotgg#1835)

* fix: map runtime modes to correct permission levels (pingdotgg#1587)

Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>

* Fix persisted composer image hydration typo (pingdotgg#1831)

* Clarify environment and workspace picker labels (pingdotgg#1854)

* Scope git toast state by thread ref (pingdotgg#1855)

* fix build (pingdotgg#1859)

* Stabilize keybindings toast stream setup (pingdotgg#1860)

Co-authored-by: Julius Marminge <julius@macmini.local>

* feat(web): add embeddable thread route for canvas tile hosts

Adds /embed/thread/:environmentId/:threadId — a standalone route that
renders the existing ChatView without the app sidebar chrome. This is the
iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3).

- New file-based route embed.thread.\$environmentId.\$threadId.tsx
- __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the
  environment connection + websocket surface + toasts still initialize
  but the sidebar/diff/plan chrome does not render
- minimal=1 search param is parsed and wired to a data attribute on the
  container for future targeted CSS; chrome hiding (BranchToolbar,
  PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass
- routeTree.gen.ts regenerated by the @tanstack/router-plugin

---------

Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: legs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: sonder <168988030+heysonder@users.noreply.github.com>
Co-authored-by: Adem Ben Abdallah <96244394+AdemBenAbdallah@users.noreply.github.com>
Co-authored-by: Kyle Gottfried <6462596+Spitfire1900@users.noreply.github.com>
Co-authored-by: Jacob <589761+jvzijp@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Klemencina <56873773+Klemencina@users.noreply.github.com>
Co-authored-by: Oskar Sekutowicz <me.oski646@gmail.com>
Co-authored-by: Noxire <59626436+noxire-dev@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

2 participants