mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
74f06dcdfbb9f0c9f93ca0814ee873043e78faa4
45 Commits
-
core tests: submit turns with permission profiles (#20010)
## Summary - Add `PermissionProfile`-based turn submission helpers to `core_test_support`, while keeping the legacy `SandboxPolicy` helper for tests that intentionally exercise legacy fallback behavior. - Switch the default `TestCodex::submit_turn()` path to send a real `PermissionProfile` plus the required legacy compatibility projection in `Op::UserTurn`. - Migrate straightforward app/search/shell/truncation tests from `SandboxPolicy::{DangerFullAccess, ReadOnly}` to `PermissionProfile::{Disabled, read_only}`. - Add a TUI compatibility projection helper for legacy app-server fields so non-legacy writable roots are preserved instead of being downgraded to read-only. - Fix remote start/resume/fork sandbox-mode projection to classify any managed profile with writable roots as workspace-write, not only profiles that can write `cwd`. - Reduce `SandboxPolicy` references in `codex-rs/core/tests` from 47 files to 41 files without changing production behavior. ## Testing - `cargo check -p codex-core --tests` - `cargo test -p codex-tui compatibility_profile_preserves_unbridgeable_write_roots` - `cargo test -p codex-tui sandbox_mode_preserves_non_cwd_write_roots_for_remote_sessions` - `just fmt` - `just fix -p core_test_support` - `just fix -p codex-core`Michael Bolin ·
2026-04-28 23:01:40 +00:00 -
tui: use permission profiles for sandbox state (#20008)
## Summary - Move TUI permission state from legacy `SandboxPolicy` values to canonical `PermissionProfile` values across presets, app events, chat widget state, app commands, thread routing, and cached thread session state. - Keep app-server compatibility boundaries explicit: embedded sessions send `permissionProfile`, while remote sessions send only a legacy `sandbox` projection and fall back to read-only when a custom profile cannot be projected. - Update status/add-dir UI summaries and snapshots to render the active permission profile, including workspace profiles selected by the new built-in defaults. ## Verification - `rg '\bSandboxPolicy\b' codex-rs/tui -n` returns no matches. - `cargo test -p codex-tui` - `cargo check -p codex-tui --tests` - `cargo test -p codex-tui additional_dirs` - `just fmt` - `just fix -p codex-tui` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/20008). * #20041 * #20040 * #20037 * #20035 * #20034 * #20033 * #20032 * #20030 * #20028 * #20027 * #20026 * #20024 * #20021 * #20018 * #20016 * #20015 * #20013 * #20011 * #20010 * __->__ #20008
Michael Bolin ·
2026-04-28 20:36:48 +00:00 -
permissions: store thread sessions as profiles (#19776)
## Why After thread sessions have a required `PermissionProfile`, the TUI no longer needs to cache a separate legacy `SandboxPolicy` in `ThreadSessionState`. Keeping the legacy field would reintroduce two permission authorities in the session cache and make later replay/switching logic easier to get wrong. This PR keeps legacy app-server compatibility at the ingestion boundary: old `sandbox` response values are still accepted, but they are immediately converted to a cwd-anchored profile. ## What Changed - Removes `ThreadSessionState.sandbox_policy`. - Updates active-session permission syncing to write only the current `PermissionProfile`. - Updates thread-read/replay/test fixtures to use profiles as the cached session permission source. - Leaves legacy `sandbox` fields in app-server request/response protocol paths unchanged; those are compatibility boundaries and are converted before entering cached TUI state. ## Verification - `cargo test -p codex-tui thread_session_state::tests --lib` - `cargo test -p codex-tui inactive_thread_started_notification_initializes_replay_session --lib` - `cargo test -p codex-tui thread_events --lib` - `just fix -p codex-tui` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19776). * #19900 * #19899 * __->__ #19776
Michael Bolin ·
2026-04-28 05:49:58 +00:00 -
permissions: require profiles in TUI thread state (#19773)
## Why `ThreadSessionState` is the TUI's cached view of an app-server session. To make `PermissionProfile` the canonical runtime permissions model, cached thread sessions need to always have a profile instead of treating the profile as an optional supplement to a legacy `sandbox` response field. The main compatibility concern is older app-server v2 lifecycle responses that only include `sandbox` and omit `permissionProfile`: - `thread/start` -> `ThreadStartResponse.sandbox` - `thread/resume` -> `ThreadResumeResponse.sandbox` - `thread/fork` -> `ThreadForkResponse.sandbox` Those responses must still hydrate correctly when the TUI is pointed at an older app-server. This PR converts the legacy `sandbox` value into a `PermissionProfile` immediately at response ingestion time, using the response `cwd`, so cached sessions do not carry an optional profile that can later reinterpret cwd-bound grants against a different thread cwd. This fallback is intentionally boundary compatibility. The follow-up PRs in this stack continue the cleanup by making `SessionConfiguredEvent` profile-only, deriving sandbox projections from snapshots only when an API still needs them, and then removing `sandbox_policy` from `ThreadSessionState`. ## What Changed - Makes `ThreadSessionState.permission_profile` required. - Converts legacy app-server response `sandbox` values into a `PermissionProfile` at ingestion time using the response cwd. - Ensures `thread/read` hydration does not reuse a primary session profile that may be anchored to a different cwd; it uses the active widget permission settings for the read thread fallback instead of reusing cached primary-session permissions. - Keeps the app-server request path unchanged: embedded sessions send profiles, while remote sessions continue using legacy sandbox overrides for compatibility. ## Verification - `cargo test -p codex-tui thread_read --lib` - `cargo test -p codex-tui permission_settings_sync_preserves_active_profile_only_rules --lib` - `cargo test -p codex-tui resume_response_restores_turns_from_thread_items --lib` - `cargo test -p codex-tui thread_session_state::tests --lib` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19773). * #19900 * #19899 * #19776 * #19775 * #19774 * __->__ #19773
Michael Bolin ·
2026-04-27 20:39:06 -07:00 -
Add /auto-review-denials retry approval flow (#19058)
## Why Auto-review can deny an action that the user later decides they want to retry. Today there is no TUI surface for selecting a recent denial and sending explicit approval context back into the session, so users have to restate intent manually and the retry can be reviewed without the original denied action context. This adds a narrow TUI-driven path for approving a recent denied action while still keeping the retry inside the normal auto-review flow. ## What Changed - Added `/auto-review-denials` to open a picker of recent denied auto-review actions. - Added a small in-memory TUI store for the 10 most recent denied auto-review events. - Selecting a denial sends the structured denied event back through the existing core/app-server op path. - Core now injects a developer message containing the approved action JSON rather than the full assessment event. - Auto-review transcript collection now preserves this specific approval developer message so follow-up review sessions can see the user approval context. - Added TUI snapshot/unit coverage for the picker and approval dispatch path. - Added core coverage for retaining the approval developer message in the auto-review transcript. ## Verification - `cargo test -p codex-core collect_guardian_transcript_entries_keeps_manual_approval_developer_message` - `cargo test -p codex-tui auto_review_denials` - `cargo test -p codex-tui approving_recent_denial_emits_structured_core_op_once` ## Notes This intentionally keeps retries going through auto-review. The approval signal is context for the exact previously denied action, not a blanket bypass for similar future actions.
Won Park ·
2026-04-27 03:43:53 +00:00 -
permissions: centralize legacy sandbox projection (#19734)
## Why The remaining migration work still needs `SandboxPolicy` at a few compatibility boundaries, but those projections should come from one canonical path. Keeping ad hoc legacy projections scattered through app-server, CLI, and config code makes it easy for behavior to drift as `PermissionProfile` gains fidelity that the legacy enum cannot represent. ## What Changed - Adds `Permissions::legacy_sandbox_policy(cwd)` and `Config::legacy_sandbox_policy()` as the compatibility projection from the canonical `PermissionProfile`. - Adds `Permissions::can_set_legacy_sandbox_policy()` so legacy inputs are checked after they are converted into profile semantics. - Updates app-server command handling, Windows sandbox setup, session configuration, and sandbox summaries to use the centralized projection helper. - Leaves `SandboxPolicy` in place only for boundary inputs/outputs that still speak the legacy abstraction. ## Verification - `cargo check -p codex-config -p codex-core -p codex-sandboxing -p codex-app-server -p codex-cli -p codex-tui` - `cargo test -p codex-tui permissions_selection_history_snapshot_full_access_to_default -- --nocapture` - `cargo test -p codex-tui permissions_selection_sends_approvals_reviewer_in_override_turn_context -- --nocapture` - `bazel test //codex-rs/tui:tui-unit-tests-bin --test_arg=permissions_selection_history_snapshot_full_access_to_default --test_output=errors` - `bazel test //codex-rs/tui:tui-unit-tests-bin --test_arg=permissions_selection_sends_approvals_reviewer_in_override_turn_context --test_output=errors` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19734). * #19737 * #19736 * #19735 * __->__ #19734
Michael Bolin ·
2026-04-26 20:31:23 -07:00 -
permissions: make runtime config profile-backed (#19606)
## Why This supersedes #19391. During stack repair, GitHub marked #19391 as merged into a temporary stack branch rather than into `main`, so the runtime-config change needed a fresh PR. `PermissionProfile` is now the canonical permissions shape after #19231 because it can distinguish `Managed`, `Disabled`, and `External` enforcement while also carrying filesystem rules that legacy `SandboxPolicy` cannot represent cleanly. Core config and session state still needed to accept profile-backed permissions without forcing every profile through the strict legacy bridge, which rejected valid runtime profiles such as direct write roots. The unrelated CI/test hardening that previously rode along with this PR has been split into #19683 so this PR stays focused on the permissions model migration. ## What Changed - Adds `Permissions.permission_profile` and `SessionConfiguration.permission_profile` as constrained runtime state, while keeping `sandbox_policy` as a legacy compatibility projection. - Introduces profile setters that keep `PermissionProfile`, split filesystem/network policies, and legacy `SandboxPolicy` projections synchronized. - Uses a compatibility projection for requirement checks and legacy consumers instead of rejecting profiles that cannot round-trip through `SandboxPolicy` exactly. - Updates config loading, config overrides, session updates, turn context plumbing, prompt permission text, sandbox tags, and exec request construction to carry profile-backed runtime permissions. - Preserves configured deny-read entries and `glob_scan_max_depth` when command/session profiles are narrowed. - Adds `PermissionProfile::read_only()` and `PermissionProfile::workspace_write()` presets that match legacy defaults. ## Verification - `cargo test -p codex-core direct_write_roots` - `cargo test -p codex-core runtime_roots_to_legacy_projection` - `cargo test -p codex-app-server requested_permissions_trust_project_uses_permission_profile_intent` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19606). * #19395 * #19394 * #19393 * #19392 * __->__ #19606
Michael Bolin ·
2026-04-26 13:29:54 -07:00 -
Add goal TUI UX (5 / 5) (#18077)
Adds the TUI user experience for goals on top of the core runtime from PR 4. ## Why Users need a direct TUI control surface for long-running goals. The UI should make the current goal visible, support common goal actions without waiting for a model turn, and avoid confusing end-of-turn notifications while an active goal is immediately continuing. ## What changed - Added `/goal` summary rendering for the current goal, including active, paused, budget-limited, and complete states. - Added `/goal <objective>` creation/replacement through the app-server goal API rather than a model prompt. - Added `/goal clear`, `/goal pause`, and `/goal unpause` command variants. - Added a confirmation menu when the user enters a new goal while another goal already exists. - Updated `/goal` help and summary tip text so it reflects the supported command variants without advertising slash-command token budgets. - Added footer/statusline goal indicators, including elapsed time and token budget display when a budget exists from API/tool-created goals. - Consumes goal updated/cleared notifications so the TUI stays in sync with external app-server changes. - Suppresses end-of-turn desktop notifications only when a goal is still active and follow-up work is expected. - Preserves slash-command history behavior and avoids leaking queued `/goal` state into unrelated submissions. ## Verification - Added TUI unit and snapshot coverage for goal command availability, summary rendering, control commands, replacement menu behavior, status/footer display, notification handling, and command history.
Eric Traut ·
2026-04-24 21:16:45 -07:00 -
permissions: make legacy profile conversion cwd-free (#19414)
## Why The profile conversion path still required a `cwd` even when it was only translating a legacy `SandboxPolicy` into a `PermissionProfile`. That made profile producers invent an ambient `cwd`, which is exactly the anchoring we are trying to remove from permission-profile data. A legacy workspace-write policy can be represented symbolically instead: `:cwd = write` plus read-only `:project_roots` metadata subpaths. This PR creates that cwd-free base so the rest of the stack can stop threading cwd through profile construction. Callers that actually need a concrete runtime filesystem policy for a specific cwd still have an explicitly named cwd-bound conversion. ## What Changed - `PermissionProfile::from_legacy_sandbox_policy` now takes only `&SandboxPolicy`. - `FileSystemSandboxPolicy::from_legacy_sandbox_policy` is now the symbolic, cwd-free projection for profiles. - The old concrete projection is retained as `FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd` for runtime/boundary code that must materialize legacy cwd behavior. - Workspace-write profiles preserve `CurrentWorkingDirectory` and `ProjectRoots` special entries instead of materializing cwd into absolute paths. ## Verification - `cargo check -p codex-protocol -p codex-core -p codex-app-server-protocol -p codex-app-server -p codex-exec -p codex-exec-server -p codex-tui -p codex-sandboxing -p codex-linux-sandbox -p codex-analytics --tests` - `just fix -p codex-protocol -p codex-core -p codex-app-server-protocol -p codex-app-server -p codex-exec -p codex-exec-server -p codex-tui -p codex-sandboxing -p codex-linux-sandbox -p codex-analytics` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19414). * #19395 * #19394 * #19393 * #19392 * #19391 * __->__ #19414
Michael Bolin ·
2026-04-24 13:42:05 -07:00 -
permissions: make profiles represent enforcement (#19231)
## Why `PermissionProfile` is becoming the canonical permissions abstraction, but the old shape only carried optional filesystem and network fields. It could describe allowed access, but not who is responsible for enforcing it. That made `DangerFullAccess` and `ExternalSandbox` lossy when profiles were exported, cached, or round-tripped through app-server APIs. The important model change is that active permissions are now a disjoint union over the enforcement mode. Conceptually: ```rust pub enum PermissionProfile { Managed { file_system: FileSystemSandboxPolicy, network: NetworkSandboxPolicy, }, Disabled, External { network: NetworkSandboxPolicy, }, } ``` This distinction matters because `Disabled` means Codex should apply no outer sandbox at all, while `External` means filesystem isolation is owned by an outside caller. Those are not equivalent to a broad managed sandbox. For example, macOS cannot nest Seatbelt inside Seatbelt, so an inner sandbox may require the outer Codex layer to use no sandbox rather than a permissive one. ## How Existing Modeling Maps Legacy `SandboxPolicy` remains a boundary projection, but it now maps into the higher-fidelity profile model: - `ReadOnly` and `WorkspaceWrite` map to `PermissionProfile::Managed` with restricted filesystem entries plus the corresponding network policy. - `DangerFullAccess` maps to `PermissionProfile::Disabled`, preserving the “no outer sandbox” intent instead of treating it as a lax managed sandbox. - `ExternalSandbox { network_access }` maps to `PermissionProfile::External { network }`, preserving external filesystem enforcement while still carrying the active network policy. - Split runtime policies that legacy `SandboxPolicy` cannot faithfully express, such as managed unrestricted filesystem plus restricted network, stay `Managed` instead of being collapsed into `ExternalSandbox`. - Per-command/session/turn grants remain partial overlays via `AdditionalPermissionProfile`; full `PermissionProfile` is reserved for complete active runtime permissions. ## What Changed - Change active `PermissionProfile` into a tagged union: `managed`, `disabled`, and `external`. - Keep partial permission grants separate with `AdditionalPermissionProfile` for command/session/turn overlays. - Represent managed filesystem permissions as either `restricted` entries or `unrestricted`; `glob_scan_max_depth` is non-zero when present. - Preserve old rollout compatibility by accepting the pre-tagged `{ network, file_system }` profile shape during deserialization. - Preserve fidelity for important edge cases: `DangerFullAccess` round-trips as `disabled`, `ExternalSandbox` round-trips as `external`, and managed unrestricted filesystem + restricted network stays managed instead of being mistaken for external enforcement. - Preserve configured deny-read entries and bounded glob scan depth when full profiles are projected back into runtime policies, including unrestricted replacements that now become `:root = write` plus deny entries. - Regenerate the experimental app-server v2 JSON/TypeScript schema and update the `command/exec` README example for the tagged `permissionProfile` shape. ## Compatibility Legacy `SandboxPolicy` remains available at config/API boundaries as the compatibility projection. Existing rollout lines with the old `PermissionProfile` shape continue to load. The app-server `permissionProfile` field is experimental, so its v2 wire shape is intentionally updated to match the higher-fidelity model. ## Verification - `just write-app-server-schema` - `cargo check --tests` - `cargo test -p codex-protocol permission_profile` - `cargo test -p codex-protocol preserving_deny_entries_keeps_unrestricted_policy_enforceable` - `cargo test -p codex-app-server-protocol permission_profile_file_system_permissions` - `cargo test -p codex-app-server-protocol serialize_client_response` - `cargo test -p codex-core session_configured_reports_permission_profile_for_external_sandbox` - `just fix` - `just fix -p codex-protocol` - `just fix -p codex-app-server-protocol` - `just fix -p codex-core` - `just fix -p codex-app-server`Michael Bolin ·
2026-04-23 23:02:18 -07:00 -
feat: expose AWS account state from account/read (#19048)
## Why AWS/Bedrock mode currently reports `account: null` with `requiresOpenaiAuth: false` from `account/read`. That suppresses the OpenAI-auth requirement, but it does not let app clients distinguish AWS auth from any other non-OpenAI custom provider. For the prototype AWS provider UX, clients need a simple provider-derived signal so they can suppress ChatGPT/API-key login and token-refresh paths without hardcoding Bedrock checks. ## What changed - Adds an `aws` variant to the v2 `Account` protocol union. - Adds `ProviderAccountKind` to `codex-model-provider` so the runtime provider owns the app-visible account classification. - Makes Amazon Bedrock return `ProviderAccountKind::Aws` from the model-provider layer. - Updates app-server `account/read` to map `ProviderAccountKind` to the existing `GetAccountResponse` wire shape. - Preserves the existing `account: null, requiresOpenaiAuth: false` behavior for other non-OpenAI providers. - Regenerates the app-server protocol schema fixtures. - Adds coverage for provider account classification and for the Amazon Bedrock `account/read` response. ## Testing - `cargo test -p codex-model-provider` - `cargo test -p codex-app-server-protocol` - `cargo test -p codex-app-server get_account_with_aws_provider` ## Notes I attempted `just bazel-lock-update` and `just bazel-lock-check`, but both are blocked in my local environment because `bazel` is not installed.
Celia Chen ·
2026-04-24 01:53:13 +00:00 -
tui: sync session permission profiles (#18284)
## Why Once `SessionConfigured` carries the active `PermissionProfile`, the TUI must treat that as authoritative session state. Otherwise the widget can keep stale local permission details after a session is configured or resumed. The TUI also keeps a local `Config` copy used for later operations, so session-sourced profiles and subsequent local sandbox changes need to keep the derived split runtime permissions in sync. Because this PR may land before the follow-up user-turn profile plumbing, embedded app-server turns also need a standalone path for carrying local runtime sandbox overrides. ## What changed - Sync the chat widget runtime filesystem/network permissions from `SessionConfigured.permission_profile`, with the legacy `sandbox_policy` as the fallback. - Recompute split runtime permissions whenever the TUI applies or carries forward a local sandbox-policy override. - Mark feature-driven Auto-review sandbox changes as runtime sandbox overrides so the standalone embedded turn-start profile path is used even without the follow-up user-turn profile PR. - Send a turn-start `permissionProfile` for embedded, non-ExternalSandbox turns when the TUI has a runtime sandbox override; remote and ExternalSandbox turns keep using the legacy sandbox field. - Extend coverage for profile sync, local sandbox changes, ExternalSandbox fallback, feature-driven sandbox overrides, and turn-start permission override selection. ## Verification - `cargo test -p codex-tui update_feature_flags_enabling_guardian_selects_auto_review` - `cargo test -p codex-tui turn_start_permission_overrides_send_profiles_only_for_embedded_runtime_overrides` - `cargo test -p codex-tui permission_settings_sync` - `cargo test -p codex-tui session_configured_external_sandbox_keeps_external_runtime_policy` - `cargo test -p codex-tui session_configured_syncs_widget_config_permissions_and_cwd` - `just fix -p codex-tui` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18284). * #18288 * #18287 * #18286 * #18285 * __->__ #18284
Michael Bolin ·
2026-04-23 09:47:53 -07:00 -
clients: send permission profiles to app-server (#18280)
## Why After app-server can accept `PermissionProfile`, first-party clients should stop preferring legacy sandbox fields when canonical permission information is available. This keeps the migration moving without removing legacy compatibility yet. The client side still has mixed surfaces during the stack: embedded thread start/resume/fork and exec initial turns can derive a profile directly from local config, while TUI remote sessions and some turn-start paths only have a legacy/server-context-safe sandbox projection. Those paths keep sending legacy sandbox fields rather than synthesizing or sending lossy/local-only profiles. ## What changed - Sends `permissionProfile` from exec and embedded TUI thread start/resume/fork requests when config has a representable profile. - Keeps legacy sandbox fallback for external sandbox policies, TUI remote thread lifecycle requests, and TUI turn-start requests that do not yet carry the active profile. - Sends the actual config-derived `permissionProfile` for exec initial turns instead of rebuilding one from the legacy sandbox projection. - Stores response `permissionProfile` as optional in TUI session state so external sandbox responses and compatibility payloads preserve `null`. - Updates tests for request construction and response mapping. ## Verification - `cargo check --tests -p codex-tui -p codex-exec` - `cargo test -p codex-tui app_server_session -- --nocapture` - `cargo test -p codex-exec thread_start_params -- --nocapture` - `cargo test -p codex-tui app_server_session::tests::thread_lifecycle_params -- --nocapture` - `just fix -p codex-tui -p codex-exec` - `just fix -p codex-tui` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18280). * #18288 * #18287 * #18286 * #18285 * #18284 * #18283 * #18282 * #18281 * __->__ #18280
Michael Bolin ·
2026-04-22 16:34:13 -07:00 -
app-server: accept permission profile overrides (#18279)
## Why `PermissionProfile` is becoming the canonical permissions shape shared by core and app-server. After app-server responses expose the active profile, clients need to be able to send that same shape back when starting, resuming, forking, or overriding a turn instead of translating through the legacy `sandbox`/`sandboxPolicy` shorthands. This still needs to preserve the existing requirements/platform enforcement model. A profile-shaped request can be downgraded or rejected by constraints, but the server should keep the user's elevated-access intent for project trust decisions. Turn-level profile overrides also need to retain existing read protections, including deny-read entries and bounded glob-scan metadata, so a permission override cannot accidentally drop configured protections such as `**/*.env = deny`. ## What changed - Adds optional `permissionProfile` request fields to `thread/start`, `thread/resume`, `thread/fork`, and `turn/start`. - Rejects ambiguous requests that specify both `permissionProfile` and the legacy `sandbox`/`sandboxPolicy` fields, including running-thread resume requests. - Converts profile-shaped overrides into core runtime filesystem/network permissions while continuing to derive the constrained legacy sandbox projection used by existing execution paths. - Preserves project-trust intent for profile overrides that are equivalent to workspace-write or full-access sandbox requests. - Preserves existing deny-read entries and `globScanMaxDepth` when applying turn-level `permissionProfile` overrides. - Updates app-server docs plus generated JSON/TypeScript schema fixtures and regression coverage. ## Verification - `cargo test -p codex-app-server-protocol schema_fixtures` - `cargo test -p codex-core session_configuration_apply_permission_profile_preserves_existing_deny_read_entries` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18279). * #18288 * #18287 * #18286 * #18285 * #18284 * #18283 * #18282 * #18281 * #18280 * __->__ #18279
Michael Bolin ·
2026-04-22 13:34:33 -07:00 -
Add plumbing to approve stored Auto-Review denials (#18955)
## Summary This adds the structural plumbing needed for an app-server client to approve a previously denied Guardian review and carry that approval context into the next model turn. This PR does not add the actual `/auto-review-denials` tool ## What Changed - Added app-server v2 RPC `thread/approveGuardianDeniedAction`. - Added generated JSON schema and TypeScript fixtures for `ThreadApproveGuardianDeniedAction*`. - Added core `Op::ApproveGuardianDeniedAction`. - Added a core handler that validates the event is a denied Guardian assessment and injects a developer message containing the stored denial event JSON. - Queues the approval context for the next turn if there is no active turn yet. - Added the TUI app-server bridge so `Op::ApproveGuardianDeniedAction { event }` is routed to the app-server request. ## What This Does Not Do - Does not add `/auto-review-denials`. - Does not add chat widget recent-denial state. - Does not add popup/list UI. - Does not add a product-facing denial lookup/store. - Does not change where Guardian denials are originally emitted or persisted. ## Verification - `cargo test -p codex-tui thread_approve_guardian_denied_action`Won Park ·
2026-04-22 10:38:19 -07:00 -
app-server: expose thread permission profiles (#18278)
## Why The `PermissionProfile` migration needs app-server clients to see the same constrained permission model that core is using at runtime. Before this PR, thread lifecycle responses only exposed the legacy `SandboxPolicy` shape, so clients still had to infer active permissions from sandbox fields. That makes downstream resume, fork, and override flows harder to make `PermissionProfile`-first. External sandbox policies are intentionally excluded from this canonical view. External enforcement cannot be round-tripped as a `PermissionProfile`, and exposing a lossy root-write profile would let clients accidentally change sandbox semantics if they echo the profile back later. ## What changed - Adds the app-server v2 `PermissionProfile` wire shape, including filesystem permissions and glob scan depth metadata. - Adds `PermissionProfileNetworkPermissions` so the profile response does not expose active network state through the older additional-permissions naming. - Returns `permissionProfile` from thread start, resume, and fork responses when the active sandbox can be represented as a `PermissionProfile`. - Keeps legacy `sandbox` in those responses for compatibility and documents `permissionProfile` as canonical when present. - Makes lifecycle `permissionProfile` nullable and returns `null` for `ExternalSandbox` to avoid exposing a lossy profile. - Regenerates the app-server JSON schema and TypeScript fixtures. ## Verification - `cargo test -p codex-app-server-protocol` - `cargo test -p codex-app-server thread_response_permission_profile_omits_external_sandbox -- --nocapture` - `cargo check --tests -p codex-analytics -p codex-exec -p codex-tui` - `just fix -p codex-app-server-protocol -p codex-app-server -p codex-analytics -p codex-exec -p codex-tui` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18278). * #18279 * __->__ #18278
Michael Bolin ·
2026-04-21 23:52:56 -07:00 -
feat: add explicit AgentIdentity auth mode (#18785)
## Summary This PR adds `CodexAuth::AgentIdentity` as an explicit auth mode. An AgentIdentity auth record is a standalone `auth.json` mode. When `AuthManager::auth().await` loads that mode, it registers one process-scoped task and stores it in runtime-only state on the auth value. Header creation stays synchronous after that because the task is initialized before callers receive the auth object. This PR also removes the old feature flag path. AgentIdentity is selected by explicit auth mode, not by a hidden flag or lazy mutation of ChatGPT auth records. Reference old stack: https://github.com/openai/codex/pull/17387/changes ## Design Decisions - AgentIdentity is a real auth enum variant because it can be the only credential in `auth.json`. - The process task is ephemeral runtime state. It is not serialized and is not stored in rollout/session data. - Account/user metadata needed by existing Codex backend checks lives on the AgentIdentity record for now. - `is_chatgpt_auth()` remains token-specific. - `uses_codex_backend()` is the broader predicate for ChatGPT-token auth and AgentIdentity auth. ## Stack 1. https://github.com/openai/codex/pull/18757: full revert 2. https://github.com/openai/codex/pull/18871: isolated Agent Identity crate 3. This PR: explicit AgentIdentity auth mode and startup task allocation 4. https://github.com/openai/codex/pull/18811: migrate Codex backend auth callsites through AuthProvider 5. https://github.com/openai/codex/pull/18904: accept AgentIdentity JWTs and load `CODEX_AGENT_IDENTITY` ## Testing Tests: targeted Rust checks, cargo-shear, Bazel lock check, and CI.
efrazer-oai ·
2026-04-21 22:33:24 -07:00 -
Add turn-scoped environment selections (#18416)
## Summary - add experimental turn/start.environments params for per-turn environment id + cwd selections - pass selections through core protocol ops and resolve them with EnvironmentManager before TurnContext creation - treat omitted selections as default behavior, empty selections as no environment, and non-empty selections as first environment/cwd as the turn primary ## Testing - ran `just fmt` - ran `just write-app-server-schema` - not run: unit tests for this stacked PR --------- Co-authored-by: Codex <noreply@openai.com>
starr-openai ·
2026-04-21 17:48:33 -07:00 -
Use app server metadata for fork parent titles (#18632)
## Problem The TUI resolved fork parent titles from local CODEX_HOME metadata, which could show missing or stale titles when app-server metadata is authoritative. This is a lingering bug left over from the migration of the TUI to the app-server interface. I found it when I asked Codex to review all places where the TUI code was still directly accessing the local CODEX_HOME. ## Solution Route fork parent title metadata through the app-server session state and render only that supplied title, with focused snapshot coverage for stale local metadata. ## Testing I manually tested by renaming a thread then forking it and confirming that the "forked from" message indicated the parent thread's name.
Eric Traut ·
2026-04-20 15:37:31 -07:00 -
Add
/sideconversations (#18190)The TUI supports long-running turns and agent threads, but quick side questions have required interrupting the main flow or manually forking/navigating threads. This PR adds a guarded `/side` flow so users can ask brief side-conversation questions in an ephemeral fork while keeping the primary thread focused. This also helps address the feature request in #18125. The implementation creates one side conversation at a time, lets `/side` open either an empty side thread or immediately submit `/side <question>`, and returns to the parent with Esc or Ctrl+C. Side conversations get hidden developer guardrails that treat inherited history as reference-only and steer the model away from workspace mutations unless explicitly requested in the side conversation. The TUI hides most slash commands while side mode is active, leaving only `/copy`, `/diff`, `/mention`, and `/status` available there.
Eric Traut ·
2026-04-19 11:59:41 -07:00 -
[TUI] add external config migration prompt when start TUI (#17891)
- add a TUI startup migration prompt for external agent config - support migrating external configs including config, skills, AGENTS.md and plugins - gate the prompt behind features.external_migrate (default false) <img width="1037" height="480" alt="Screenshot 2026-04-14 at 9 29 14 PM" src="https://github.com/user-attachments/assets/6060849b-03cb-429a-9c13-c7bb46ad2e65" /> <img width="713" height="183" alt="Screenshot 2026-04-14 at 9 29 26 PM" src="https://github.com/user-attachments/assets/d13f177e-d4c4-479c-8736-ef29636081e1" /> --------- Co-authored-by: Eric Traut <etraut@openai.com>
alexsong-oai ·
2026-04-17 17:58:32 -07:00 -
[codex] Propagate rate limit reached type (#18227)
## Summary First PR in the split from #17956. - adds the core/app-server `RateLimitReachedType` shape - maps backend `rate_limit_reached_type` into Codex rate-limit snapshots - carries the field through app-server notifications/responses and generated schemas - updates existing constructors/tests for the new optional field ## Validation - `cargo test -p codex-backend-client` - `cargo test -p codex-app-server-protocol` - `cargo test -p codex-app-server rate_limits` - `cargo test -p codex-tui workspace_` - `cargo test -p codex-tui status_` - `just fmt` - `just fix -p codex-backend-client` - `just fix -p codex-app-server-protocol` - `just fix -p codex-app-server` - `just fix -p codex-tui`
richardopenai ·
2026-04-17 13:37:25 -07:00 -
[codex] Revoke ChatGPT tokens on logout (#17825)
## Summary This changes Codex logout so managed ChatGPT auth is revoked against AuthAPI before local auth state is removed. CLI logout, TUI `/logout`, and the app-server account logout path now use the token-revoking logout flow instead of only deleting `auth.json` / credential store state. ## Root Cause Logout previously cleared only local auth storage. That removed Codex's local credentials but did not ask the backend to invalidate the refresh/access token state associated with a managed ChatGPT login. ## Behavior For managed ChatGPT auth, logout sends the stored refresh token to `https://auth.openai.com/oauth/revoke` with `token_type_hint: refresh_token` and the Codex OAuth client id, then deletes all local auth stores after revocation succeeds. If only an access token is available, it falls back to revoking that access token. API key auth and externally supplied `chatgptAuthTokens` are still only cleared locally because Codex does not own a refresh token for those modes. Revocation failures are fail-closed: if Codex cannot load stored auth or the backend revoke call fails, logout returns an error and leaves local auth in place so the user can retry instead of silently clearing local state while backend tokens remain valid. ## Validation ran local version of `codex-cli` with staging overrides/harness for auth ran `codex login` then `codex logout`: saw auth.json clear and backend revocation endpoints were called ``` POST /oauth/revoke status: 200 revoking access token should clear auth session clearing auth session due to token revocation successfully revoked session and access token CANONICAL-API-LINE Response: status='200' method='POST' path='/oauth/revoke ``` --------- Co-authored-by: Codex <noreply@openai.com>
sashank-oai ·
2026-04-16 22:51:21 -07:00 -
Fix MCP startup cancellation through app server (#18078)
Addresses https://github.com/openai/codex/issues/17143 Problem: TUI interrupts without an active turn stopped cancelling slow MCP startup after routing through the app-server APIs. Solution: Route no-active-turn interrupts through app-server as startup cancels, acknowledge them immediately, and emit cancelled MCP startup updates. Testing: I manually confirmed that MCP cancellation didn't work prior to this PR and works after the fix was in place.
Eric Traut ·
2026-04-16 00:03:50 -07:00 -
feat: reset memories button (#17937)
<img width="720" height="175" alt="Screenshot 2026-04-15 at 14 35 02" src="https://github.com/user-attachments/assets/041d73ff-8c16-42a9-8e92-c245805084f0" />
jif-oai ·
2026-04-15 15:34:25 +01:00 -
feat: memories menu (#17632)
Add menu that: 1. If memories feature is not enabled, propose to enable it 2. Let you choose if you want to generate memories and to use memories
jif-oai ·
2026-04-15 14:02:35 +01:00 -
Spread AbsolutePathBuf (#17792)
Mechanical change to promote absolute paths through code.
pakrym-oai ·
2026-04-14 14:26:10 -07:00 -
Add realtime output modality and transcript events (#17701)
- Add outputModality to thread/realtime/start and wire text/audio output selection through app-server, core, API, and TUI.\n- Rename the realtime transcript delta notification and add a separate transcript done notification that forwards final text from item done without correlating it with deltas.
Ahmed Ibrahim ·
2026-04-14 00:13:13 -07:00 -
[codex] Remove unused Rust helpers (#17146)
## Summary Removes high-confidence unused Rust helper functions and exports across `codex-tui`, `codex-shell-command`, and utility crates. The cleanup includes dead TUI helper methods, unused path/string/elapsed/fuzzy-match utilities, an unused Windows PowerShell lookup helper, and the unused terminal palette version counter. This keeps the remaining public surface smaller without changing behavior. ## Validation - `just fmt` - `cargo test -p codex-tui -p codex-shell-command -p codex-utils-elapsed -p codex-utils-fuzzy-match -p codex-utils-string -p codex-utils-path` - `just fix -p codex-tui -p codex-shell-command -p codex-utils-elapsed -p codex-utils-fuzzy-match -p codex-utils-string -p codex-utils-path` - `git diff --check`
pakrym-oai ·
2026-04-13 18:27:00 -07:00 -
Expose instruction sources (AGENTS.md) via app server (#17506)
Addresses #17498 Problem: The TUI derived /status instruction source paths from the local client environment, which could show stale <none> output or incorrect paths when connected to a remote app server. Solution: Add an app-server v2 instructionSources snapshot to thread start/resume/fork responses, default it to an empty list when older servers omit it, and render TUI /status from that server-provided session data. Additional context: The app-server field is intentionally named instructionSources rather than AGENTS.md-specific terminology because the loaded instruction sources can include global instructions, project AGENTS.md files, AGENTS.override.md, user-defined instruction files, and future dynamic sources.
Eric Traut ·
2026-04-12 15:50:12 -07:00 -
TUI: enforce core boundary (#17399)
Problem: The TUI still depended on `codex-core` directly in a number of places, and we had no enforcement from keeping this problem from getting worse. Solution: Route TUI core access through `codex-app-server-client::legacy_core`, add CI enforcement for that boundary, and re-export this legacy bridge inside the TUI as `crate::legacy_core` so the remaining call sites stay readable. There is no functional change in this PR — just changes to import targets. Over time, we can whittle away at the remaining symbols in this legacy namespace with the eventual goal of removing them all. In the meantime, this linter rule will prevent us from inadvertently importing new symbols from core.
Eric Traut ·
2026-04-10 20:25:31 -07:00 -
Revert "Option to Notify Workspace Owner When Usage Limit is Reached" (#17391)
Reverts openai/codex#16969 #sev3-2026-04-10-accountscheckversion-500s-for-openai-workspace-7300
Shijie Rao ·
2026-04-10 23:33:13 +00:00 -
Support clear SessionStart source (#17073)
## Motivation The `SessionStart` hook already receives `startup` and `resume` sources, but sessions created from `/clear` previously looked like normal startup sessions. This makes it impossible for hook authors to distinguish between these with the matcher. ## Summary - Add `InitialHistory::Cleared` so `/clear`-created sessions can be distinguished from ordinary startup sessions. - Add `SessionStartSource::Clear` and wire it through core, app-server thread start params, and TUI clear-session flow. - Update app-server protocol schemas, generated TypeScript, docs, and related tests. https://github.com/user-attachments/assets/9cae3cb4-41c7-4d06-b34f-966252442e5c
Abhinav ·
2026-04-10 16:05:21 -07:00 -
Option to Notify Workspace Owner When Usage Limit is Reached (#16969)
## Summary - Replace the manual `/notify-owner` flow with an inline confirmation prompt when a usage-based workspace member hits a credits-depleted limit. - Fetch the current workspace role from the live ChatGPT `accounts/check/v4-2023-04-27` endpoint so owner/member behavior matches the desktop and web clients. - Keep owner, member, and spend-cap messaging distinct so we only offer the owner nudge when the workspace is actually out of credits. ## What Changed - `backend-client` - Added a typed fetch for the current account role from `accounts/check`. - Mapped backend role values into a Rust workspace-role enum. - `app-server` and protocol - Added `workspaceRole` to `account/read` and `account/updated`. - Derived `isWorkspaceOwner` from the live role, with a fallback to the cached token claim when the role fetch is unavailable. - `tui` - Removed the explicit `/notify-owner` slash command. - When a member is blocked because the workspace is out of credits, the error now prompts: - `Your workspace is out of credits. Request more from your workspace owner? [y/N]` - Choosing `y` sends the existing owner-notification request. - Choosing `n`, pressing `Esc`, or accepting the default selection dismisses the prompt without sending anything. - Selection popups now honor explicit item shortcuts, which is how the `y` / `n` interaction is wired. ## Reviewer Notes - The main behavior change is scoped to usage-based workspace members whose workspace credits are depleted. - Spend-cap reached should not show the owner-notification prompt. - Owners and admins should continue to see `/usage` guidance instead of the member prompt. - The live role fetch is best-effort; if it fails, we fall back to the existing token-derived ownership signal. ## Testing - Manual verification - Workspace owner does not see the member prompt. - Workspace member with depleted credits sees the confirmation prompt and can send the nudge with `y`. - Workspace member with spend cap reached does not see the owner-notification prompt. ### Workspace member out of usage https://github.com/user-attachments/assets/341ac396-eff4-4a7f-bf0c-60660becbea1 ### Workspace owner <img width="1728" height="1086" alt="Screenshot 2026-04-09 at 11 48 22 AM" src="https://github.com/user-attachments/assets/06262a45-e3fc-4cc4-8326-1cbedad46ed6" />
richardopenai ·
2026-04-09 21:15:17 -07:00 -
Forward app-server turn clientMetadata to Responses (#16009)
## Summary App-server v2 already receives turn-scoped `clientMetadata`, but the Rust app-server was dropping it before the outbound Responses request. This change keeps the fix lightweight by threading that metadata through the existing turn-metadata path rather than inventing a new transport. ## What we're trying to do and why We want turn-scoped metadata from the app-server protocol layer, especially fields like Hermes/GAAS run IDs, to survive all the way to the actual Responses API request so it is visible in downstream websocket request logging and analytics. The specific bug was: - app-server protocol uses camelCase `clientMetadata` - Responses transport already has an existing turn metadata carrier: `x-codex-turn-metadata` - websocket transport already rewrites that header into `request.request_body.client_metadata["x-codex-turn-metadata"]` - but the Rust app-server never parsed or stored `clientMetadata`, so nothing from the app-server request was making it into that existing path This PR fixes that without adding a new header or a second metadata channel. ## How we did it ### Protocol surface - Add optional `clientMetadata` to v2 `TurnStartParams` and `TurnSteerParams` - Regenerate the JSON schema / TypeScript fixtures - Update app-server docs to describe the field and its behavior ### Runtime plumbing - Add a dedicated core op for app-server user input carrying turn-scoped metadata: `Op::UserInputWithClientMetadata` - Wire `turn/start` and `turn/steer` through that op / signature path instead of dropping the metadata at the message-processor boundary - Store the metadata in `TurnMetadataState` ### Transport behavior - Reuse the existing serialized `x-codex-turn-metadata` payload - Merge the new app-server `clientMetadata` into that JSON additively - Do **not** replace built-in reserved fields already present in the turn metadata payload - Keep websocket behavior unchanged at the outer shape level: it still sends only `client_metadata["x-codex-turn-metadata"]`, but that JSON string now contains the merged fields - Keep HTTP fallback behavior unchanged except that the existing `x-codex-turn-metadata` header now includes the merged fields too ### Request shape before / after Before, a websocket `response.create` looked like: ```json { "type": "response.create", "client_metadata": { "x-codex-turn-metadata": "{\"session_id\":\"...\",\"turn_id\":\"...\"}" } } ``` Even if the app-server caller supplied `clientMetadata`, it was not represented there. After, the same request shape is preserved, but the serialized payload now includes the new turn-scoped fields: ```json { "type": "response.create", "client_metadata": { "x-codex-turn-metadata": "{\"session_id\":\"...\",\"turn_id\":\"...\",\"fiber_run_id\":\"fiber-start-123\",\"origin\":\"gaas\"}" } } ``` ## Validation ### Targeted tests added / updated - protocol round-trip coverage for `clientMetadata` on `turn/start` and `turn/steer` - protocol round-trip coverage for `Op::UserInputWithClientMetadata` - `TurnMetadataState` merge test proving client metadata is added without overwriting reserved built-in fields - websocket request-shape test proving outbound `response.create` contains merged metadata inside `client_metadata["x-codex-turn-metadata"]` - app-server integration tests proving: - `turn/start` forwards `clientMetadata` into the outbound Responses request path - websocket warmup + real turn request both behave correctly - `turn/steer` updates the follow-up request metadata ### Commands run - `just write-app-server-schema` - `cargo test -p codex-app-server-protocol` - `cargo test -p codex-protocol` - `cargo test -p codex-core turn_metadata_state_merges_client_metadata_without_replacing_reserved_fields --lib` - `cargo test -p codex-core --test all responses_websocket_preserves_custom_turn_metadata_fields` - `cargo test -p codex-app-server --test all client_metadata` - `cargo test -p codex-app-server --test all turn_start_forwards_client_metadata_to_responses_websocket_request_body_v2 -- --nocapture` - `just fmt` - `just fix -p codex-core -p codex-protocol -p codex-app-server-protocol -p codex-app-server` - `just fix -p codex-exec -p codex-tui-app-server` - `just argument-comment-lint` ### Full suite note `cargo test` in `codex-rs` still fails in: - `suite::v2::turn_interrupt::turn_interrupt_resolves_pending_command_approval_request` I verified that same failure on a clean detached `HEAD` worktree with an isolated `CARGO_TARGET_DIR`, so it is not caused by this patch.neil-oai ·
2026-04-09 11:52:37 -07:00 -
Add realtime voice selection (#17176)
- Add realtime voice selection for realtime/start. - Expose the supported v1/v2 voice lists and cover explicit, configured, default, and invalid voice paths.
Ahmed Ibrahim ·
2026-04-08 20:19:15 -07:00 -
fix(tui): reduce startup and new-session latency (#17039)
## TL;DR - Fetches account/rateLimits/read asynchronously so the TUI can continue starting without waiting for the rate-limit response. - Fixes the /status card so it no longer leaves a stale “refreshing cached limits...” notice in terminal history. ## Problem The TUI bootstrap path fetched account rate limits synchronously (`account/rateLimits/read`) before the event loop started for ChatGPT/OpenAI-authenticated startups. This added ~670 ms of blocking latency in the measured hot-start case, even though rate-limit data is not needed to render the initial UI or accept user input. The delay was especially noticeable on hot starts where every other RPC (`account/read`, `model/list`, `thread/start`) completed in under 70 ms total. Moving that fetch to the background also exposed a `/status` UI bug: the status card is flattened into terminal scrollback when it is inserted. A transient "refreshing limits in background..." line could not be cleared later, because the async completion updated the retained `HistoryCell`, not the already-written terminal history. ## Mental model Before this change, `AppServerSession::bootstrap()` performed three sequential RPCs: `account/read` → `model/list` → `account/rateLimits/read`. The result of the third call was baked into `AppServerBootstrap` and applied to the chat widget before the event loop began. After this change, `bootstrap()` only performs two RPCs (`account/read` + `model/list`), and rate-limit fetching is kicked off as an async background task immediately after the first frame is scheduled. A new enum, `RateLimitRefreshOrigin`, tags each fetch so the event handler knows whether the result came from the startup prefetch or from a user-initiated `/status` command; they have different completion side-effects. The `get_login_status()` helper (used outside the main app flow) was also decoupled: it previously called the full `bootstrap()` just to check auth mode, wasting model-list and rate-limit work. It now calls the narrower `read_account()` directly. For `/status`, this PR keeps the background refresh request but stops printing transient refresh notices into status history when cached limits are already available. If a refresh updates the cache, the next `/status` command will render the new values. ## Non-goals - This change does not alter the rate-limit data itself. - This change does not introduce caching, retries, or staleness management for rate limits. - This change does not affect the `model/list` or `thread/start` RPCs; they remain on the critical startup path. ## Tradeoffs - **Stale-on-first-render**: The status bar will briefly show no rate-limit info until the background fetch completes; observed background fetches landed roughly in the 400-900 ms range after the UI appeared. This is acceptable because the user cannot meaningfully act on rate-limit data in the first fraction of a second. - **Error silence on startup prefetch**: If the startup prefetch fails, the error is logged but the UI is not notified (unlike `/status` refresh failures, which go through the status-command completion path). This avoids surfacing transient network errors as a startup blocker. - **Static `/status` history**: `/status` output is terminal history, not a live widget. The card now avoids progress-style language that would appear stuck in scrollback; users can run `/status` again to see newly cached values. - **`account_auth_mode` field removed from `AppServerBootstrap`**: The only consumer was `get_login_status()`, which no longer goes through `bootstrap()`. The field was dead weight. ## Architecture ### New types - `RateLimitRefreshOrigin` (in `app_event.rs`): A `Copy` enum distinguishing `StartupPrefetch` from `StatusCommand { request_id }`. Carried through `RefreshRateLimits` and `RateLimitsLoaded` events so the handler applies the right completion behavior. ### Modified types - `AppServerBootstrap`: Lost `account_auth_mode` and `rate_limit_snapshots`; gained `requires_openai_auth: bool` (passed through from the account response so the caller can decide whether to fire the prefetch). ### Control flow 1. `bootstrap()` returns with `requires_openai_auth` and `has_chatgpt_account`. 2. After scheduling the first frame, `App::run_inner` fires `refresh_rate_limits(StartupPrefetch)` if both flags are true. 3. When `RateLimitsLoaded { StartupPrefetch, Ok(..) }` arrives, snapshots are applied and a frame is scheduled to repaint the status bar. 4. When `RateLimitsLoaded { StartupPrefetch, Err(..) }` arrives, the error is logged and no UI update occurs. 5. `/status`-initiated refreshes continue to use `StatusCommand { request_id }` and call `finish_status_rate_limit_refresh` on completion (success or failure). 6. `/status` history cells with cached rate-limit rows no longer render an additional "refreshing limits" notice; the async refresh updates the cache for future status output. ### Extracted method - `AppServerSession::read_account()`: Factored out of `bootstrap()` so that `get_login_status()` can call it independently without triggering model-list or rate-limit work. ## Observability - The existing `tracing::warn!` for rate-limit fetch failures is preserved for the startup path. - No new metrics or spans are introduced. The startup-time improvement is observable via the existing `ready` timestamp in TUI startup logs. ## Tests - Existing tests in `status_command_tests.rs` are updated to match on `RateLimitRefreshOrigin::StatusCommand { request_id }` instead of a bare `request_id`. - Focused `/status` tests now assert that status history avoids transient refresh text, continues to request an async refresh, and uses refreshed cached limits in future status output. - No new tests are added for the startup prefetch path because it is a fire-and-forget spawn with no observable side-effect other than the widget state update, which is already covered by the snapshot-application tests. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>Felipe Coury ·
2026-04-07 22:16:09 -03:00 -
Use model metadata for Fast Mode status (#16949)
Fast Mode status was still tied to one model name in the TUI and model-list plumbing. This changes the model metadata shape so a model can advertise additional speed tiers, carries that field through the app-server model list, and uses it to decide when to show Fast Mode status. For people using Codex, the behavior is intended to stay the same for existing models. Fast Mode still requires the existing signed-in / feature-gated path; the difference is that the UI can now recognize any model the model list marks as Fast-capable, instead of requiring a new client-side slug check.
pash-openai ·
2026-04-07 17:55:40 -07:00 -
Add WebRTC transport to realtime start (#16960)
Adds WebRTC startup to the experimental app-server `thread/realtime/start` method with an optional transport enum. The websocket path remains the default; WebRTC offers create the realtime session through the shared start flow and emit the answer SDP via `thread/realtime/sdp`. --------- Co-authored-by: Codex <noreply@openai.com>
Ahmed Ibrahim ·
2026-04-07 15:43:38 -07:00 -
[codex] reduce module visibility (#16978)
## Summary - reduce public module visibility across Rust crates, preferring private or crate-private modules with explicit crate-root public exports - update external call sites and tests to use the intended public crate APIs instead of reaching through module trees - add the module visibility guideline to AGENTS.md ## Validation - `cargo check --workspace --all-targets --message-format=short` passed before the final fix/format pass - `just fix` completed successfully - `just fmt` completed successfully - `git diff --check` passed
pakrym-oai ·
2026-04-07 08:03:35 -07:00 -
[codex-analytics] add protocol-native turn timestamps (#16638)
--- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16638). * #16870 * #16706 * #16659 * #16641 * #16640 * __->__ #16638
rhan-oai ·
2026-04-06 16:22:59 -07:00 -
Add remote --cd forwarding for app-server sessions (#16700)
Addresses #16124 Problem: `codex --remote --cd <path>` canonicalized the path locally and then omitted it from remote thread lifecycle requests, so remote-only working directories failed or were ignored. Solution: Keep remote startup on the local cwd, forward explicit `--cd` values verbatim to `thread/start`, `thread/resume`, and `thread/fork`, and cover the behavior with `codex-tui` tests. Testing: I manually tested `--remote --cd` with both absolute and relative paths and validated correct behavior. --- Update based on code review feedback: Problem: Remote `--cd` was forwarded to `thread/resume` and `thread/fork`, but not to `thread/list` lookups, so `--resume --last` and picker flows could select a session from the wrong cwd; relative cwd filters also failed against stored absolute paths. Solution: Apply explicit remote `--cd` to `thread/list` lookups for `--last` and picker flows, normalize relative cwd filters on the app-server before exact matching, and document/test the behavior.
Eric Traut ·
2026-04-03 11:26:45 -07:00 -
Fix fork source display in /status (expose forked_from_id in app server) (#16596)
Addresses #16560 Problem: `/status` stopped showing the source thread id in forked TUI sessions after the app-server migration. Solution: Carry fork source ids through app-server v2 thread data and the TUI session adapter, and update TUI fixtures so `/status` matches the old TUI behavior.
Eric Traut ·
2026-04-02 14:05:29 -07:00 -
Fix stale /status rate limits in active TUI sessions (#16201)
Fix stale weekly limit in `/status` (#16194): /status reused the session’s cached rate-limit snapshot, so the weekly remaining limit could stay frozen within an active session. With this change, we now dynamically update the rate limits after status is displayed. I needed to delete a few low-value test cases from the chatWidget tests because the test.rs file is really large, and the new tests in this PR pushed us over the 512K mandated limit. I'm working on a separate PR to refactor that test file.
Eric Traut ·
2026-03-31 17:03:05 -06:00 -
Rename tui_app_server to tui (#16104)
This is a follow-up to https://github.com/openai/codex/pull/15922. That previous PR deleted the old `tui` directory and left the new `tui_app_server` directory in place. This PR renames `tui_app_server` to `tui` and fixes up all references.
Eric Traut ·
2026-03-28 11:23:07 -06:00