Skip to content

fix(codex): bidirectional runtime approval-mode switching for codex-native - #6228

Open
marktai wants to merge 1 commit into
omnigent-ai:mainfrom
marktai:mt--codex-permission-mode-persist
Open

fix(codex): bidirectional runtime approval-mode switching for codex-native#6228
marktai wants to merge 1 commit into
omnigent-ai:mainfrom
marktai:mt--codex-permission-mode-persist

Conversation

@marktai

@marktai marktai commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Related issue

No linked issue yet (draft) — codex-native runtime approval-mode switching from the web.

Closes #

Summary

Adds a running-session Approvals picker for codex-native that mirrors Codex's own /permissions popup and syncs both directions:

  • Web → TUI: PATCH /v1/sessions forwards a codex_approval_mode_change; the runner drives the /permissions popup by keystroke (selects the preset by its menu digit, plus the Full Access confirm). Approval is not a thread/settings/update field like model/effort, so the popup is the mechanism.
  • TUI → web: the forwarder maps the approvalPolicy / sandbox / reviewer fields from thread/settings/updated to a preset; the server stamps a read-back label and publishes session.codex_approval_mode, so a /permissions change made inside the TUI tracks in the web picker live (once the thread is subscribed — after the first turn).

Presets mirror the popup in order: Ask for approval / Approve for me / Full Access / Read Only. Codex's set is version-dependent (0.146 omits Read Only), so this is the superset with position-stable menu digits. The config modal's Approvals picker also now resyncs to live store updates while open (until the user edits), so an external change no longer needs a reopen.

ELI5: pick "how much Codex asks before it acts" from the web, and it flips the same switch the codex terminal's /permissions menu does — and flipping it in the terminal makes the web catch up.

flowchart LR
  Web[Web Approvals picker] -- PATCH approval_mode --> Server
  Server -- codex_approval_mode_change --> Runner
  Runner -- /permissions keystrokes --> TUI[codex TUI]
  TUI -- thread/settings/updated --> Forwarder
  Forwarder -- external_codex_approval_mode_change --> Server
  Server -- SSE session.codex_approval_mode + label --> Web
Loading

Test Plan

  • Python: uv run --group test pytest tests/test_codex_approval_modes.py tests/test_codex_native_forwarder.py tests/server/integration/test_sessions_endpoints.py (preset + threadSettings→preset mapping, forwarder derivation/sync, server PATCH + external-event TUI→web integration). ruff check / ruff format / pyrefly check clean on changed files.
  • Web (web/): npx vitest run on touched files, npm run lint, npm run type-check, npm run build — all green, incl. an open-modal live-resync regression test.
  • Manual (live local server + real codex 0.146): web pick → codex TUI switches for all three reachable presets (Full Access includes its confirm sub-dialog); a TUI /permissions change → web picker updates live; the open-modal picker resyncs on an external change.

Demo

  • Visual demo attached below

Running-session Approvals picker mirroring Codex's /permissions presets:

Codex Approvals picker

Type of change

  • Bug fix
  • Feature
  • UI / frontend change
  • Refactor / chore
  • Docs
  • Test / CI
  • Breaking change

Test coverage

  • Unit tests added / updated
  • Integration tests added / updated
  • E2E tests added / updated
  • Manual verification completed
  • Existing tests cover this change
  • Not applicable

Coverage notes

Manual verification ran against a live local server + real codex 0.146: both sync directions and the open-modal live-resync. Two behaviors worth calling out:

  • TUI → web needs the forwarder subscribed, which happens after the first turn — the codex app-server rejects subscribing to an empty rollout, so on a zero-message session a TUI /permissions change isn't observed until a turn runs.
  • Read Only is only runtime-reachable on codex builds that expose it in /permissions (0.146 does not). The picker offers the superset; on a build without it, selecting Read Only keys a non-existent menu row (a no-op).

Changelog

Switch Codex approval mode (Ask for approval / Approve for me / Full Access / Read Only) from the web, kept in sync with the codex /permissions menu both ways.


This pull request and its description were written by Isaac.

@github-actions github-actions Bot added the size/XL Pull request size: XL label Sep 2, 2026
@marktai
marktai force-pushed the mt--codex-permission-mode-persist branch from ae27942 to 4618818 Compare September 2, 2026 23:14
…ative

Add a running-session Approvals picker for codex-native that mirrors Codex's own
/permissions popup and syncs both directions.

- Web -> TUI: PATCH /v1/sessions forwards a codex_approval_mode_change; the runner
  drives the /permissions popup by keystroke (select the preset by its menu digit,
  plus the Full Access confirm) and confirms Codex echoed the switch before
  returning, so an unsupported preset fails loud instead of the label claiming a
  mode the TUI never entered. Approval is not a thread/settings/update field like
  model/effort, so the popup is the mechanism.
- TUI -> web: the forwarder maps the approvalPolicy / sandbox / reviewer fields
  from thread/settings/updated to a preset value, and the server stamps the
  read-back label and publishes session.codex_approval_mode so the picker tracks
  a /permissions change made inside the TUI live (once the thread is subscribed,
  i.e. after the first turn).

Presets mirror the popup in order (Ask for approval / Approve for me / Full Access
/ Read Only). Codex's set is version-dependent (0.146 omits Read Only), so this is
the superset with position-stable menu digits.

The web config modal's Approvals picker now resyncs to live store updates while
open (only until the user edits), so an external change no longer needs a reopen.

Tests: preset + threadSettings->preset mapping units, forwarder derivation/sync,
server PATCH and external-event (TUI->web) integration; web vitest for the reader,
store action + SSE, and the open-modal live-resync regression.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: Mark Tai <mark.tai@databricks.com>
@marktai
marktai force-pushed the mt--codex-permission-mode-persist branch from 4618818 to bb99b03 Compare September 3, 2026 00:29
@marktai
marktai marked this pull request as ready for review September 3, 2026 01:45
@marktai
marktai requested a review from yaoharry September 3, 2026 01:45
@marktai

marktai commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

@yaoharry is this analogous to how you did it? I'm not a huge fan of the terminal input and checking, but there wasn't another way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Pull request size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant