mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
9cd11e9e62c48ea6127f8b9f6ae2e013e4e4ff00
196 Commits
-
canvrno-oai ·
2026-06-09 16:34:38 -07:00 -
fix: Prevent /review crash when entering Esc on steer message (#22879)
This changes the `/review` escape path so `Esc` no longer behaves like the normal queued-follow-up interrupt flow while a review is running. Steering is not currently supported in `/review` mode, without this change users are able to attempt a steer but it leads to a crash (see #22815). If the user has already tried to send additional guidance during `/review`, the TUI now keeps the review running and shows a warning that steer messages are not supported in that mode, while still pointing users to `Ctrl+C` if they actually want to cancel. It also adds regression coverage for the review-specific warning behavior. When users do cancel with Ctrl+C during /review, the TUI now tolerates the active-turn race that can happen during review handoff, and any queued steer messages are restored to the composer instead of being discarded. - Special-case `Esc` during an active `/review` when follow-up steer input is pending or has already been deferred. - Show a clear warning instead of interrupting the running review. - Make the Ctrl+C cancel path during /review resilient to active-turn races, while preserving any queued steer text by restoring it to the composer. - Add review-mode test coverage for the warning path. ## Testing 1. Start a `/review` with a diff large enough that the review stays active for more than a few seconds. 2. While the review is still running, type a follow-up / steer message, submit it, and then press `Esc`. Before: `Esc` causes the TUI to close abruptly. After: the review keeps running and the transcript shows a warning that steer messages are not supported during `/review`, with guidance to use `Ctrl+C` if you want to cancel. 3. Press `Ctrl+C` if you actually want to stop the review. Before: (after restarting the test since Pt. 2 crashed) this is the intentional cancellation path. After: this remains the intentional cancellation path, and any queued follow-up steer text is restored to the composer instead of being lost. ## Note: `/review` mode explicitly does not support steering at this time (as noted in `turn_processer.rs`, if we want to explore that in the future this code will need to be modified). This change keeps unsupported steer attempts from crashing the TUI and preserves queued follow-up text if the user cancels with Ctrl+C.
canvrno-oai ·
2026-06-09 09:41:58 -07:00 -
multi-agent: add path-based v2 activity tracking (#27007)
## Why Multi-agent v2 identifies agents by canonical paths, but its tool handlers still emitted the larger legacy collaboration begin/end events built around nickname and role metadata. App-server, rollout-trace, analytics, and TUI consumers therefore lacked one compact path-based completion signal that behaved consistently across live events and replay. The TUI also needs a bounded `/agent` status surface for v2 agents. It should use recent local activity for previews, refresh liveness without loading full histories, and keep the legacy picker available when no path-backed v2 agent is known. ## What changed - Replace the v2 `spawn_agent`, `send_message`, `followup_task`, and `interrupt_agent` legacy lifecycle emissions with a success-only `SubAgentActivity` event. The event records the tool call ID, occurrence time, affected thread, canonical agent path, and `started`, `interacted`, or `interrupted` kind. - Expose the activity as a completion-only app-server v2 `subAgentActivity` thread item in live notifications and reconstructed history, regenerate the protocol schemas, and count it in sub-agent tool analytics. - Track canonical paths from live activity and loaded-thread metadata in the TUI, and render the activity in live and replayed transcripts. - Make `/agent` list running path-backed agents with summaries from bounded local event buffers. Each summary is capped at 240 graphemes, the scan is capped at six recent items, only the last three wrapped lines are shown, and command output is omitted. Liveness falls back to metadata-only `thread/read` when local turn state is unavailable. - Persist the activity as a terminal rollout-trace runtime payload and reduce it to the corresponding spawn, send, follow-up, or close interaction edge. `interrupt_agent` is classified as a close-edge operation. - Preserve the legacy picker when no path-backed v2 agent is known. ## Compatibility App-server v2 clients that consumed `collabAgentToolCall` begin/end pairs for these tools must handle the new completion-only `subAgentActivity` item. Legacy v1 collaboration behavior is unchanged. ## Screenshot <img width="684" height="288" alt="Screenshot 2026-06-08 at 15 40 47" src="https://github.com/user-attachments/assets/194b3cd0-619d-45fb-b587-cf3e2b1b8a1d" /> ## Testing - `just test -p codex-app-server-protocol` - `just test -p codex-rollout-trace` - Added focused coverage for activity analytics, terminal trace serialization, spawn-edge reduction, `interrupt_agent` classification, TUI status rendering without aggregated command output, and clearing stale running state after a completed turn.
jif ·
2026-06-09 12:14:48 +02:00 -
Preserve cloud requirements across TUI thread resets (#25177)
Fixes a TUI regression where thread transitions such as `/new` and `/clear` could rebuild config without the cloud requirements loader, allowing users to fall back to non-cloud-managed settings. The config refresh path now preserves cloud requirements during thread reinitialization, and config loading is moved off the deep TUI event stack to avoid stack-overflow crashes during those reloads. - Passes the cloud requirements loader through TUI config rebuild paths. - Keeps cloud requirements applied for `/new`, `/clear`, `/fork`, side conversations, and session picker transitions. - Runs config building on a Tokio task so reloads do not occur on the deep TUI caller stack. - Adds regression coverage that cloud requirements survive thread-transition config refreshes. ## Test/Repro: - Start Codex with a cloud requirement applied. - Use `/new` or `/clear`. - The refreshed/fresh-session config should still include the cloud requirements This can be tested with any config item, at this moment for oai staff the easiest item to test is the `mentions_v2` feature. This is currently enabled in cloud requirements, but is not enabled by default. As a result, prior to these changes that feature is disabled after `/new` or `/clear`. Testing the same steps with a binary from this branch should not drop the feature enablement.
canvrno-oai ·
2026-06-08 18:08:48 -07:00 -
fix: preserve auto review across config and delegation (#26230)
## Why Auto Review should remain the effective approval reviewer when settings cross runtime boundaries. A config or app-server round trip must not change the reviewer identity, and delegated work must not silently fall back to user review. This requires both a stable canonical serialized value and propagation of the effective setting. `auto_review` is the canonical value across protocol and app-server output, while `guardian_subagent` remains accepted as backward-compatible input. ## What changed - serialize `ApprovalsReviewer::AutoReview` consistently as `auto_review` across core protocol and app-server v2 - continue accepting `guardian_subagent` when reading existing config or client requests - carry the active turn's approval reviewer into spawned agents - update config/debug expectations and add delegated-task regression coverage ## Scope This does not change Guardian policy or remove compatibility with existing `guardian_subagent` inputs. It preserves the selected reviewer across serialization, config reloads, app-server settings, and delegated task setup. Related Guardian changes are split independently: - #26231 adds denials and soft denials - #26334 retries transient reviewer failures - #26333 reuses narrowly scoped low-risk approvals - #26232 adds TUI denial recovery ## Validation - `just test -p codex-app-server-protocol` (224 passed) - regression coverage for delegated task reviewer propagation - serialization coverage for canonical `auto_review` output and legacy `guardian_subagent` input --------- Co-authored-by: saud-oai <saud@openai.com>
viyatb-oai ·
2026-06-08 18:59:50 +00:00 -
fix(tui): scope MCP startup status by thread (#26639)
## Why MCP startup failures from spawned subagents were rendered as global notifications, so a child thread's failure could pollute the visible parent transcript. Routing the notification to the child exposed two related replay problems: session refresh could discard the buffered event, and a newly created child `ChatWidget` did not know the expected MCP server set, which could leave its startup spinner running after every server had settled. MCP startup diagnostics should remain visible in the thread that owns the startup without affecting other transcripts. The protocol also needs to support a future app-scoped MCP lifecycle where startup is not owned by any thread. ## Reported Behavior The [originating Slack report](https://openai.slack.com/archives/C08JZTV654K/p1780604538859939) called out that using subagents could turn MCP startup failures into a wall of yellow CLI warnings because repeated failures were not deduplicated. The intended behavior is for those diagnostics to remain visible once in the thread that owns the startup, without polluting the parent transcript. ## What Changed - add nullable `threadId` ownership to `mcpServer/startupStatus/updated` - populate it from the app-server conversation ID for the current thread-scoped lifecycle and regenerate the protocol schema and TypeScript artifacts - treat a missing or null `threadId` as app-scoped without injecting it into the active chat transcript - route and buffer thread-owned MCP startup notifications by thread in the TUI - preserve buffered MCP startup events across child session refresh - seed expected MCP servers before replaying a thread snapshot so startup reaches its terminal state - suppress an identical repeated failure warning for the same server within one startup round The owning thread still renders the detailed failure and final `MCP startup incomplete (...)` summary. ## How to Test 1. Configure an optional MCP server named `smoke` that exits during initialization. 2. Launch the TUI with multi-agent support enabled. 3. Confirm the main thread's own startup failure renders one detailed `smoke` warning and one incomplete-startup summary. 4. Spawn exactly one subagent. 5. Confirm the parent transcript does not receive the subagent's MCP startup failure. 6. Switch to the subagent thread and confirm it contains exactly one detailed `smoke` failure and one incomplete-startup summary. 7. Confirm the subagent's MCP startup spinner disappears and the thread remains usable. 8. Switch between the parent and subagent and confirm the warnings neither move nor duplicate. Targeted tests: - `just test -p codex-app-server-protocol` - `just test -p codex-app-server thread_start_emits_mcp_server_status_updated_notifications` - `just test -p codex-tui mcp_startup` The parent/child behavior and spinner completion were also exercised manually in tmux. `just argument-comment-lint` was attempted but blocked by an unrelated local Bazel LLVM empty-glob failure; touched Rust callsites were inspected manually.
Felipe Coury ·
2026-06-07 20:12:05 -07:00 -
[codex] Deduplicate skill load warnings (#26698)
Skill reloads can get noisy when the watcher keeps triggering `skills/list` and the same invalid `SKILL.md` error comes back each time. This keeps the first warning visible, then suppresses repeats while the same `(path, message)` is still active. If the error clears and later comes back, or if the message changes, it will show again. Validation: - `just fmt` - `just test -p codex-tui skill_load_warning_state`
xl-openai ·
2026-06-05 18:37:47 -07:00 -
[codex-rs] support v2 personal access tokens (#25731)
## Summary - add v2 personal access token support for `codex login --with-access-token` and `CODEX_ACCESS_TOKEN` - classify opaque `at-` tokens separately from legacy Agent Identity JWTs - hydrate required ChatGPT account metadata through AuthAPI `/v1/user-auth-credential/whoami` - use PATs directly as bearer tokens while preserving existing ChatGPT account surfaces - expose PAT-backed auth as the explicit `personalAccessToken` app-server auth mode ## Implementation PAT auth is intentionally small and stateless. Loading a PAT performs one AuthAPI metadata request, stores the hydrated metadata in the in-memory auth object, and redacts the secret from debug output. Legacy Agent Identity JWT handling remains unchanged. The shared access-token classifier lives in a private neutral module because it dispatches between both credential types. PAT hydration fails closed when AuthAPI omits any required metadata, including email. Hydrated metadata is intentionally not persisted: startup performs a live `whoami` preflight so revoked tokens or changed account metadata are not accepted from a stale cache. ## Workspace restriction scope This change intentionally does **not** apply `forced_chatgpt_workspace_id` to PAT authentication. The setting is a client-side config guardrail, not an authorization boundary, and PAT does not currently require workspace-ID parity. The PAT login and `CODEX_ACCESS_TOKEN` paths therefore validate through AuthAPI without threading workspace-restriction state through access-token loading. Existing workspace checks for non-PAT auth remain on their established paths. ## App-server compatibility The public app-server `AuthMode` is shared across v1 and v2, and PAT-backed auth reports `personalAccessToken` through both APIs. Following human review, this intentionally removes the temporary v1 compatibility mapping that reported PATs as `chatgpt`; the deprecated v1 API is kept in parity with v2 rather than maintaining a separate closed enum. Clients with exhaustive auth-mode handling in either API version must add the new case and should generally treat it as ChatGPT-backed unless they need PAT-specific behavior. The v1 auth-status response still omits the raw PAT when `includeToken` is requested because that response cannot carry the account metadata needed to reuse the credential safely. Persisted PAT auth also omits the new enum value so older Codex builds can deserialize `auth.json` and infer PAT auth from the credential field after a rollback. ## Validation Latest review-fix validation: - `CARGO_INCREMENTAL=0 just test -p codex-login` (126 passed) - `CARGO_INCREMENTAL=0 just test -p codex-cli` (263 passed) - `CARGO_INCREMENTAL=0 just test -p codex-cli stored_auth_validation_handles_personal_access_token` - `CARGO_INCREMENTAL=0 just test -p codex-app-server-protocol` (226 passed) - `CARGO_INCREMENTAL=0 just test -p codex-models-manager refresh_available_models_uses_remote_only_catalog_for_chatgpt_auth` - `CARGO_INCREMENTAL=0 just test -p codex-tui existing_non_oauth_chatgpt_login_counts_as_signed_in` - `CARGO_INCREMENTAL=0 just fix -p codex-login -p codex-app-server-protocol -p codex-models-manager -p codex-tui -p codex-cli` - `just fmt` - `git diff --check` The broader `codex-tui` suite previously compiled and ran 2,834 tests. Three unrelated environment-sensitive guardian/IDE-socket tests failed after retries; the PAT-relevant TUI coverage passed.
cooper-oai ·
2026-06-05 17:36:18 -07:00 -
Fix
/goalusage text for control commands (#26551)## Why The TUI's `/goal` usage text only advertised the objective form even though `/goal clear`, `/goal edit`, `/goal pause`, and `/goal resume` are implemented. This made the lifecycle controls difficult to discover and allowed the duplicated help text to drift from actual behavior. Fixes #25530. ## What changed - Show the complete `/goal [<objective>|clear|edit|pause|resume]` syntax in usage messages. - Share one usage string across slash-command dispatch and goal-related app messages. - Add inline snapshot coverage for the control-command usage path.
Eric Traut ·
2026-06-05 08:32:53 -07:00 -
Surface TUI config write error causes (#26537)
## Summary TUI config writes currently wrap app-server failures with local context like `config/batchWrite failed in TUI`, but several user-visible paths only render the outer error. That hides the actionable app-server message, such as validation constraints or read-only `CODEX_HOME` failures, leaving users with a dead-end diagnostic. This change adds a small formatter next to the TUI config write helpers that renders the error source chain, then uses it for model persistence, feature persistence, project trust, status line writes, hook trust, and hook enablement. Fixes #26077
Eric Traut ·
2026-06-05 08:32:07 -07:00 -
[codex] Forward turn moderation metadata through app-server (#25710)
## Why First-party backends can supply turn-scoped moderation metadata that app-server clients need for client-side presentation. Exposing this as an experimental typed notification lets opted-in clients consume it without interpreting raw Responses API events. ## What changed - forward `response.metadata.openai_chatgpt_moderation_metadata` from Responses API SSE and WebSocket streams as turn-scoped moderation metadata - emit the experimental app-server v2 `turn/moderationMetadata` notification with `{ threadId, turnId, metadata }` - add app-server integration coverage for the typed moderation metadata notification ## Testing - `just test -p codex-core build_ws_client_metadata_includes_window_lineage_and_turn_metadata` - `just test -p codex-core` (fails locally: 46 failures and 1 timeout, primarily missing `test_stdio_server` and shell snapshot timeouts) - `just test -p codex-app-server-protocol` - `just test -p codex-app-server turn_moderation_metadata_emits_typed_notification_v2` - `just test -p codex-app-server` (fails locally: 792 passed, 10 failed, and 5 timed out; failures are in existing environment-sensitive tests, primarily because nested macOS `sandbox-exec` is not permitted) - `just write-app-server-schema --experimental --schema-root /tmp/codex-app-server-schema-experimental`carlc-oai ·
2026-06-05 02:41:06 -07:00 -
[codex] Use model-advertised reasoning effort order (#26446)
## Summary - preserve the model catalog order for app-server `supportedReasoningEfforts` and document that client contract - render TUI reasoning choices in the advertised order - step reasoning shortcuts by adjacent list position instead of deriving order from known effort names - anchor unsupported configured values to the advertised default, or the first option when needed - remove canonical effort ordering helpers and the unused upgrade effort mapping ## Validation - `just fmt` - Local tests and compilation were not run per request; relying on CI. Stacked on #26444.
Ahmed Ibrahim ·
2026-06-04 14:01:14 -07:00 -
[codex] Support model-defined reasoning efforts (#26444)
## Summary - accept non-empty model-defined reasoning effort values while preserving built-in effort behavior - propagate the non-Copy effort type through core, app-server, TUI, telemetry, and persistence call sites - preserve string wire encoding and expose an open-string schema for clients - update model selection and shortcut behavior for model-advertised effort values ## Root cause `ReasoningEffort` gained a string-backed custom variant, so it could no longer implement `Copy` or rely on derived closed-enum serialization. Existing consumers still moved effort values from shared references and assumed a fixed built-in value set. ## Validation - `just fmt` - Local tests and compilation were not run per request; relying on CI.
Ahmed Ibrahim ·
2026-06-04 13:36:24 -07:00 -
Felipe Coury ·
2026-06-03 20:30:15 -03:00 -
Propagate permission approval environment id (#25862)
## Stack 1. #25850 - Key request-permission grants by environment: stores and applies sticky permission grants per environment id. 2. #25858 - Add `environmentId` to `request_permissions`: lets the model target a selected environment and resolves relative permission paths against it. 3. This PR (#25862) - Propagate permission approval environment id: carries the selected environment id through approval events, app-server requests, TUI prompts, and delegate forwarding. 4. #25867 - Add remote request permissions integration coverage: verifies the selected remote environment across request, approval, grant reuse, and exec. This PR is stacked on #25858, and #25867 is stacked on this PR. ## Why PR2 lets the model bind a `request_permissions` call to a selected environment, but the approval event and client-facing request still needed to carry that binding. For CCA, the user-facing prompt and delegated approval path should know which environment the grant applies to instead of relying on cwd alone. ## What Changed - Added optional `environmentId` to `RequestPermissionsEvent`. - Emit the selected environment id from core permission approval events. - Preserve the environment id through delegate forwarding, including cwd-based delegated requests. - Added `environmentId` to app-server permission approval params, generated schema/TypeScript artifacts, and README examples. - Preserve and display the environment id in TUI permission approval prompts. - Updated focused core, app-server protocol, and TUI conversion coverage. ## Testing Not run locally per instruction. Performed read-only `git diff --check`.
jif ·
2026-06-02 21:09:34 +02:00 -
Reduce stack pressure in session startup and config rebuilds (#25844)
## Why `/clear` starts a fresh thread with `InitialHistory::Cleared`, which re-enters the thread/session startup path. That path now builds large async futures through `ThreadManagerState::spawn_thread_with_source`, `Codex::spawn`, and `Session::new`. Separately, TUI config rebuilds for cwd and permission-profile changes build a similarly heavy `ConfigBuilder::build()` future inside the app task. In debug and Bazel runs, those call chains can put enough state on the caller stack to abort before startup or config refresh completes. This change keeps the behavior the same while moving the heaviest future frames off the caller stack. ## What changed - Box `Codex::spawn(...)` in `codex-rs/core/src/thread_manager.rs` before awaiting it from `spawn_thread_with_source`. - Box `Session::new(...)` in `codex-rs/core/src/session/mod.rs` before awaiting it from `Codex::spawn_internal`. - Route `ConfigBuilder::build()` through a small `tokio::spawn` helper in `codex-rs/tui/src/app/config_persistence.rs` so cwd and permission-profile config rebuilds run on a runtime worker stack while preserving error context. ## Verification CI is running on the PR. No new targeted tests were added. This is a mechanical stack-pressure reduction that keeps the existing behavior and error propagation intact.
jif-oai ·
2026-06-02 15:42:47 +02:00 -
feat: show enterprise monthly credit limits in status (#24812)
## Summary Enterprise users can have an effective monthly credit limit, but Codex `/status` currently drops that metadata from the account-usage response. This change adds the optional `spend_control.individual_limit` projection to the existing rate-limit snapshot flow. The backend client reads the monthly limit, app-server exposes it as `individualLimit`, and the TUI renders a `Monthly credit limit` row through the existing progress-bar renderer. When the backend does not return an effective monthly limit, existing rate-limit behavior is unchanged. ## Existing backend state The account-usage backend already returns the effective monthly limit and current usage together: ```json { "spend_control": { "reached": false, "individual_limit": { "limit": "25000", "used": "8000", "remaining": "17000", "used_percent": 32, "remaining_percent": 68, "reset_after_seconds": 86400, "reset_at": 1778137680 } } } ``` Before this change, Codex projected rolling `primary` and `secondary` windows plus `credits`. It ignored `spend_control.individual_limit`, so app-server clients and `/status` could not render the monthly cap. The updated flow is: ```text account usage backend -> backend-client reads spend_control.individual_limit -> existing rate-limit snapshot carries optional individual_limit -> app-server exposes optional individualLimit -> TUI renders Monthly credit limit ``` ## App-server contract `account/rateLimits/read` and sparse `account/rateLimits/updated` notifications now include an additive nullable `rateLimits.individualLimit` field: ```json { "individualLimit": { "limit": "25000", "used": "8000", "remainingPercent": 68, "resetsAt": 1778137680 } } ``` In an `account/rateLimits/read` response, `null` means no monthly limit is available. `account/rateLimits/updated` remains a sparse rolling notification: clients merge available values into their most recent `account/rateLimits/read` snapshot or refetch. Nullable account metadata in a rolling notification does not clear a previously observed value. ## Design decisions - Extend the existing rate-limit snapshot instead of introducing a separate request or wire-level update protocol. - Keep the Codex projection narrow: `/status` needs the effective limit, current usage, remaining percentage, and reset timestamp. - Render the monthly row through the existing progress-bar renderer, with one optional detail line for `8,000 of 25,000 credits used`. - Keep the backend response optional so existing accounts and older usage states preserve their current behavior. - Preserve cached monthly metadata when sparse rolling notifications omit it. Live account-usage reads remain authoritative and can clear a removed limit. ## Visual evidence ```text Monthly credit limit: [██████████████░░░░░░] 68% left (resets 07:08 on 7 May) 8,000 of 25,000 credits used ``` Snapshot: `codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_enterprise_monthly_credit_limit.snap` ## Testing Tests: generated app-server schema verification, protocol tests, backend-client tests, app-server integration coverage, TUI snapshot coverage, formatting, and workspace lint cleanup.efrazer-oai ·
2026-06-01 21:25:42 -07:00 -
feat(tui): restore output-free cancelled prompts (#25316)
## TL;DR When you press Esc or Ctrl+C after sending a prompt but before any output was rendering, it restores the last composer and the message. ## Summary Cancelling a prompt immediately after submission should behave like returning to edit that prompt, not like discarding the user's draft. Today, pressing `Esc` or `Ctrl+C` before Codex responds leaves the submitted prompt in the transcript and returns an empty composer, forcing the user to recall or retype it. When an interrupted turn has not produced substantive visible output, restore its submitted prompt directly into the composer and roll back that latest turn. This also covers the first prompt in a fresh thread, before the TUI has retained a local user-history cell. The restored draft keeps its text, image attachments, and active collaboration mode so it can be edited and resubmitted in place. Restoration is intentionally suppressed once the turn has produced user-visible activity such as assistant output, tool work, hooks, or patches. A transient thinking status does not make the prompt ineligible. Rollback also rebuilds terminal scrollback from the retained transcript cells so repeated cancellations and terminal resizes do not duplicate history. ## How to Test 1. Start the TUI with `cargo run -p codex-cli --bin codex`. 2. In a fresh thread, submit the first prompt and press `Esc` before Codex emits substantive output. Confirm that the prompt returns to the composer for editing and its submitted transcript row is removed. 3. Repeat with `Ctrl+C`, then repeat after at least one completed turn. Confirm the same behavior. 4. Submit a prompt, wait for assistant output or tool activity, then cancel. Confirm that the transcript remains intact and the prompt is not restored into the composer. 5. Cancel several output-free prompts and resize the terminal between attempts. Confirm that the startup banner, tip, and transcript history do not duplicate in scrollback. Targeted tests: - `just test -p codex-tui cancelled_turn_edit_restores_prompt` - `just test -p codex-tui output_free_interrupted_turn_requests_prompt_restore` - `just test -p codex-tui visible_output_prevents_cancelled_turn_prompt_restore` - `just test -p codex-tui thinking_status_keeps_cancelled_turn_prompt_restore_eligible` - `just test -p codex-tui patch_activity_prevents_cancelled_turn_prompt_restore` The full `just test -p codex-tui` run completed with `2746` passing tests and two unrelated existing guardian feature-flag failures. `just argument-comment-lint` remains blocked locally by the existing Bazel LLVM `compiler-rt` sanitizer-header glob failure; the touched Rust diff was manually audited for positional literal comments.
Felipe Coury ·
2026-06-01 11:49:14 -03:00 -
store and expose parent_thread_id on Threads (#25113)
## Why This PR https://github.com/openai/codex/pull/24161#discussion_r3325692763 revealed a subagent data modeling issue, where we overloaded `forked_from_id` to also mean `parent_thread_id`. That's incorrect since guardian and review subagents can be a subagent and NOT fork the main thread's history. The solution here is to explicitly store a new `parent_thread_id` on `SessionMeta`, alongside `forked_from_id` which already exists. While we're at it, also expose it in the app-server protocol on the `Thread` object. A thread->subagent relationship and a fork of thread history are orthogonal concepts. ## What Changed - Added top-level `parent_thread_id` persistence on `SessionMeta` and runtime/session plumbing through `SessionConfiguredEvent`, `CodexSpawnArgs`, `SessionConfiguration`, `ThreadConfigSnapshot`, `TurnContext`, and `ModelClient`. - Made turn metadata, request headers, analytics, and subagent-start events read the separate runtime/top-level parent field instead of deriving general parent lineage from `SessionSource` or `forked_from_thread_id`. - Passed parent lineage separately at delegated subagent, review, guardian, agent-job, and multi-agent spawn construction sites; copied-history fork lineage remains derived only from `InitialHistory`. - Persisted and exposed parent lineage through rollout/thread-store projections and app-server v2 `Thread.parentThreadId`. - Updated app-server README text and regenerated app-server schema fixtures for the additive `parentThreadId` response field.
Owen Lin ·
2026-06-01 04:33:20 +00:00 -
Add thread archive CLI commands (#25021)
## Problem Saved threads can already be archived through app-server RPCs, but the command line did not expose direct archive or unarchive commands. ## Solution Add `codex archive <thread>` and `codex unarchive <thread>`, resolving UUIDs or exact thread names before calling the existing `thread/archive` and `thread/unarchive` RPCs. The commands support scoped remote flags so callers can target remote app-server endpoints when archiving or unarchiving threads. This also fixes a long-standing bug in `codex resume <thread id>` and `codex fork <thread id>` that I found when testing the new commands. These operations shouldn't be allowed on archived sessions. They now fail with an error that tells the user to run `codex unarchive <thread id>` first. ## Verification Added app-server coverage for rejecting archived thread resume by id and checking that the error includes the matching `codex unarchive <thread id>` command.
Eric Traut ·
2026-05-29 23:37:26 -07:00 -
Constrain Windows sandbox requirements (#23766)
# Why Managed requirements can already constrain sandbox policy choices, but Windows sandbox implementation selection was still resolved independently from those requirements. That left the TUI able to continue through the unelevated fallback even when an organization wants to require the elevated Windows sandbox implementation. # What - Add `[windows].allowed_sandbox_implementations` requirements support for the Windows `elevated` and `unelevated` implementations. - Apply that allowlist during core config resolution so disallowed configured or feature-selected Windows sandbox implementations fall back to an allowed implementation with the existing requirements warning path. - Reuse the existing TUI Windows setup prompts to block disallowed unelevated continuation, keep required elevated setup in front of the user, and refuse to persist a TUI-selected Windows sandbox mode that requirements disallow. # Semantics | Allowed | Selected | Effective | | --- | --- | --- | | `["elevated"]` | `unelevated` / unset | `elevated` | | `["unelevated"]` | `elevated` / unset | `unelevated` | | `["elevated", "unelevated"]` | `elevated` | `elevated` | | `["elevated", "unelevated"]` | `unelevated` | `unelevated` | | `["elevated", "unelevated"]` | unset | `elevated` | Availability is handled by interactive setup surfaces after allowlist resolution. If the effective elevated implementation is not ready, elevated-only requirements block on setup. When unelevated is also allowed, the UI may offer the existing unelevated fallback. ## TUI Screens If elevated setup is not already complete: ``` Your organization requires the default Codex agent sandbox to continue. Set it up to protect your files and control network access. Learn more <https://developers.openai.com/codex/windows> › 1. Set up default sandbox (requires Administrator permissions) 2. Quit ``` If admin setup fails under `["elevated"]`: ``` Couldn't set up your sandbox with Administrator permissions Your organization requires the default sandbox before Codex can continue. Learn more <https://developers.openai.com/codex/windows> › 1. Try setting up admin sandbox again 2. Quit ``` # Next Steps - extend the requirements/readout surface, such as `configRequirements/read`, so clients can inspect the loaded `[windows].allowed_sandbox_implementations` requirement instead of inferring it from Windows setup state - consider extending `windowsSandbox/readiness` as well - update the App startup guide, setup flow, and banner surfaces so an elevated-only requirement omits any continue-unelevated escape hatch and blocks startup until a permitted implementation is ready; - preserve the existing unelevated fallback path when requirements allow it, including the `["unelevated"]` case where elevated is disallowed
Abhinav ·
2026-05-29 16:31:33 -07:00 -
Add
/archiveslash command (#25027)## Why TUI users can archive saved sessions from other surfaces, but there is no in-session command for archiving the active session. Since archiving the active session also exits the TUI, the command should ask for explicit confirmation instead of firing immediately. I'm also working on [a companion PR](https://github.com/openai/codex/pull/25021) that adds `codex archive` and `codex unarchive` top-level CLI commands. ## What changed - Adds a new `/archive` slash command described as `archive this session and exit`. - Shows a confirmation dialog with `No, don't archive` selected first and `Yes, archive and exit` as the explicit action. - On confirmation, calls the existing `thread/archive` app-server RPC for the active main session and exits after success. - Keeps `/archive` disabled while a task is running and unavailable in side conversations. ## Verification Added focused TUI coverage for the `/archive` confirmation flow, disabled-while-task-running behavior, and the `/ar` slash-command popup snapshot.
Eric Traut ·
2026-05-29 11:07:19 -07:00 -
Align TUI permissions labels with app (#25017)
## Summary The desktop app now presents the on-request permissions mode as `Ask for approval` and the manual-review-backed mode as `Approve for me`. The TUI still exposed older/internal labels like `Default` and `Auto-review`, which made the same underlying settings look different across clients. This updates the TUI UX copy to match the app without changing the underlying default behavior. Fresh threads continue to use the existing on-request approval mode, now displayed as `Ask for approval`. The label changes cover `/permissions`, explicit profile permissions menus, status surfaces, config persistence history/error text, and the corresponding TUI snapshots. ### Before <img width="1181" height="119" alt="Screenshot 2026-05-28 at 10 19 47 PM" src="https://github.com/user-attachments/assets/0664846b-b6dd-4931-b4dd-d0af0d42058e" /> <img width="523" height="19" alt="Screenshot 2026-05-28 at 10 21 29 PM" src="https://github.com/user-attachments/assets/7899c33e-b35d-4684-8389-97e357803423" /> ### After <img width="1216" height="117" alt="Screenshot 2026-05-28 at 10 19 32 PM" src="https://github.com/user-attachments/assets/015aab43-ac97-411f-8031-75cdd887251b" /> <img width="567" height="18" alt="Screenshot 2026-05-28 at 10 20 24 PM" src="https://github.com/user-attachments/assets/28b6422c-b823-4298-b221-c83d46d09d66" />
Eric Traut ·
2026-05-29 11:06:40 -07:00 -
windows-sandbox: pass workspace roots to runner (#24108)
## Why #23813 switches the Windows sandbox runner path to `PermissionProfile`, but it still left one runtime anchor for resolving symbolic `:workspace_roots` entries. That is not enough once a turn has multiple effective workspace roots: exact entries and deny globs under `:workspace_roots` need to be materialized for every runtime root before the command runner chooses token mode or builds ACL plans. ## What Changed - Replaces the Windows runner/setup `permission_profile_cwd` plumbing with `workspace_roots: Vec<AbsolutePathBuf>`. - Resolves Windows-local `PermissionProfile` data with `materialize_project_roots_with_workspace_roots(...)` instead of the single-cwd helper. - Threads `Config::effective_workspace_roots()` through core execution, unified exec, TUI setup/read-grant flows, app-server setup, app-server `command/exec`, and `debug sandbox` on Windows. - Preserves those workspace roots through the zsh-fork escalation executor instead of rebuilding them from `sandbox_policy_cwd`. - Makes `ExecRequest::new(...)` and the remaining `build_exec_request(...)` helper path take `windows_sandbox_workspace_roots` explicitly so new call sites cannot silently fall back to `vec![cwd]`. - Clarifies the `debug sandbox` non-Windows comment: remaining cwd-dependent resolution still uses `sandbox_policy_cwd`, while `:workspace_roots` entries are already materialized from config roots. - Updates elevated runner IPC `SpawnRequest` to send `workspace_roots` and bumps the framed IPC protocol version to `3` for the payload shape change. - Adds Windows-local resolver coverage for expanding exact and glob `:workspace_roots` entries across multiple roots, plus core helper coverage proving explicit roots are preserved. ## Verification - `cargo check -p codex-windows-sandbox -p codex-core -p codex-tui -p codex-cli -p codex-app-server` - `cargo test -p codex-windows-sandbox` - `cargo test -p codex-core windows_sandbox` - `cargo test -p codex-core unix_escalation` - `cargo test -p codex-app-server windows_sandbox` - `cargo test -p codex-tui windows_sandbox` - `cargo test -p codex-cli debug_sandbox` - `just test -p codex-core unified_exec` - `just test -p codex-core build_exec_request_preserves_windows_workspace_roots` - `env -u CODEX_NETWORK_PROXY_ACTIVE -u CODEX_NETWORK_ALLOW_LOCAL_BINDING just test -p codex-app-server --lib command_exec` - `just test -p codex-windows-sandbox` - `just test -p codex-exec sandbox` - `just fix -p codex-core -p codex-app-server -p codex-windows-sandbox` A local macOS cross-check with `cargo check --target x86_64-pc-windows-msvc ...` did not reach crate Rust code because native dependencies require Windows SDK headers (`windows.h` / `assert.h`) in this environment; Windows CI remains the real target validation. Two local targeted filters compile but do not run assertions on macOS: `env -u CODEX_NETWORK_PROXY_ACTIVE -u CODEX_NETWORK_ALLOW_LOCAL_BINDING just test -p codex-app-server --lib command_exec_processor` matched zero tests, and `just test -p codex-linux-sandbox landlock` matched zero tests because the landlock suite is Linux-only.
Michael Bolin ·
2026-05-28 15:26:55 -07:00 -
[codex] Add user input client ids (#24653)
## Summary Adds an optional `clientId` field to app-server v2 `UserInput` and carries it through the core `UserInput` model so clients can correlate echoed user input items without relying on payload equality. ## Details - Adds `client_id: Option<String>` to core `UserInput` variants. - Exposes the v2 app-server field as `clientId` on the wire and in generated TypeScript. - Preserves the id when converting between app-server v2 and core protocol types. - Regenerates app-server schema fixtures. ## Validation - `just fmt` - `just write-app-server-schema` - `cargo test -p codex-app-server-protocol` - `cargo test -p codex-protocol` - `just fix -p codex-app-server-protocol` - `just fix -p codex-protocol` - `git diff --check`
Alexi Christakis ·
2026-05-28 14:54:39 -07:00 -
Expose MCP server info as part of server status (#24698)
# Summary Expose MCP server info via App Server (when available) so apps can render a richer MCP experience
Gabriel Peal ·
2026-05-28 09:38:34 -07:00 -
feat(tui): add OSC 8 web links to rich content (#24472)
## Why Wrapped URLs in rich TUI output, especially URLs rendered inside Markdown tables, are split across terminal rows. In terminals that support OSC 8 hyperlinks, treating each visible fragment as part of the complete destination enables reliable open-link and copy-link actions even after table layout wraps the URL. This addresses the semantic-link portion of #12200 and the behavior described in https://github.com/openai/codex/issues/12200#issuecomment-4535452980. It does not change ordinary drag-selection across bordered table rows. ## What Changed - Added shared TUI OSC 8 support that validates `http://` and `https://` destinations, sanitizes terminal payloads, and applies metadata separately from visible line width/layout. - Added semantic web-link annotations to assistant and proposed-plan Markdown, including explicit web links and bare web URLs in prose and table cells while excluding code and non-web Markdown destinations. - Preserved complete URL targets through table wrapping, narrow pipe fallback, streaming, transcript overlay rendering, history insertion, and resize replay. - Routed intentional Codex-owned links in notices, status/setup/app-link, feedback, onboarding, MCP/plugin help, memories, and update surfaces through the shared hyperlink handling. ## How to Test 1. Run Codex in a terminal with OSC 8 link support, such as Ghostty, and request an assistant response containing a Markdown table whose last column contains a long `https://` URL. 2. Make the terminal narrow enough for the URL to wrap across multiple bordered table rows. 3. Use the terminal's open-link or copy-link action on more than one wrapped URL fragment and confirm each fragment resolves to the complete original URL. 4. Resize the terminal after the table is rendered and repeat the link action to confirm the destination survives scrollback replay. 5. Open the transcript overlay while rich output is present and confirm web links remain interactive there. 6. As a regression check, render inline/fenced code containing URL text and a Markdown link such as `[https://example.com](mailto:support@example.com)`; confirm these do not acquire a web OSC 8 destination. Targeted automated coverage exercised Markdown links and exclusions, wrapped and pipe-fallback tables, streaming/transcript overlay propagation, status-link truncation, and rendered word-wrapping cell alignment. `just test -p codex-tui` was also run; it passed the hyperlink coverage and reproduced two unrelated existing guardian feature-flag test failures.
Felipe Coury ·
2026-05-27 20:14:55 +00:00 -
TUI config cleanup: plugin marketplace (#24257)
## Why Plugin and marketplace mutations are applied by the app server, but several TUI follow-up paths still refreshed state from the TUI host config. In remote workspace mode, that can leave plugin UI state tied to stale client-local `config.toml` after the server has already applied the mutation. ## What - Stop reloading the TUI host config after app-server-owned plugin, marketplace, skill, and app mutations. - Use the same app-server-owned refresh path for local and remote sessions: ask the app server to reload user config where the running session needs it, then refetch plugin list/detail state from the app server. - Build plugin mention candidates from existing app-server `plugin/list` and `plugin/read` data in both local and remote sessions instead of TUI-host plugin config. - Avoid the duplicate local config reload after `ReloadUserConfig` asks the app server to reload config. ## Verification Manually launched a local WebSocket app-server with a temp server `CODEX_HOME`, launched the TUI with a separate temp host `CODEX_HOME` and `--remote`, installed a sample plugin from a temp local marketplace through `/plugins`, and confirmed the TUI refreshed to installed state while only the server config gained `[plugins."sample@debug"]`. Trace logs showed the TUI using app-server `plugin/list` and `plugin/read` for the refresh path.
Eric Traut ·
2026-05-27 07:22:30 -07:00 -
Uprev Rust toolchain pins to 1.95.0 (#24684)
## Summary - Bump the workspace Rust toolchain from `1.93.0` to `1.95.0` across Cargo, Bazel, CI, release workflows, devcontainers, and the Codex environment config. - Refresh `MODULE.bazel.lock` so the Bazel Rust toolchain artifacts match the new version. - Leave purpose-specific toolchains unchanged, including the `argument-comment-lint` nightly and the upstream `rusty_v8` `1.91.0` build pin. - Includes fixes for new lints from `just fix` and a few codex-authored fixes for lints without a suggestion.
Adam Perry @ OpenAI ·
2026-05-26 20:59:47 -07:00 -
windows-sandbox: remove SandboxPolicy runner plumbing (#23813)
## Why The Windows sandbox runner still carried the old `SandboxPolicy` compatibility path even though core now computes `PermissionProfile`. That meant Windows command-runner execution could only see the legacy projection, so profile-only filesystem rules such as deny globs were not part of the runner input. ## What Changed - Removed the Windows-local `SandboxPolicy` parser/export and deleted `windows-sandbox-rs/src/policy.rs`. - Changed restricted-token capture/session setup, elevated setup, world-writable audit, read-root grant, and command-runner session APIs to accept `PermissionProfile` plus the profile cwd. - Bumped the elevated command-runner IPC protocol to version 2 because `SpawnRequest` now carries `permission_profile` / `permission_profile_cwd` instead of the legacy `policy_json_or_preset` / `sandbox_policy_cwd` fields. - Updated core exec, unified exec, debug-sandbox, TUI setup/grant flows, and app-server setup to pass the actual effective `PermissionProfile`. - Left regression coverage asserting the old IPC policy fields are absent and the runner serializes tagged `PermissionProfile` JSON. ## Verification - `cargo test -p codex-windows-sandbox` - `cargo test -p codex-core windows_sandbox` - `cargo test -p codex-app-server request_processors::windows_sandbox_processor` - `just fix -p codex-windows-sandbox -p codex-core -p codex-app-server -p codex-cli -p codex-tui` - `just fix -p codex-cli -p codex-tui` - `just fix -p codex-windows-sandbox -p codex-tui` - `rg "\\bSandboxPolicy\\b" codex-rs/windows-sandbox-rs` returned no matches. Note: `cargo test -p codex-cli` was attempted but did not reach crate tests because local disk filled while compiling dependencies (`No space left on device`). The targeted clippy pass compiled the affected CLI/TUI surfaces afterward. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23813). * #24108 * __->__ #23813
Michael Bolin ·
2026-05-26 14:56:27 -07:00 -
TUI config cleanup: plugin mentions (#24266)
## Summary TUI plugin mention refresh still joined app-server plugin inventory with client-local plugin config, which can diverge once plugin state is owned by the app server. This changes the TUI to mirror the GUI client: `plugin/list` is the autocomplete source, and mention candidates are plugin-level entries filtered to installed, enabled, and not disabled by admin. The TUI no longer reads local plugin config or calls `plugin/read` while refreshing plugin mention candidates. ## API shape and limitations The current app-server API does not expose effective per-session plugin capability summaries for mention autocomplete. As in the GUI, autocomplete now trusts `plugin/list` metadata rather than proving which plugin capabilities are loaded in the active session. That avoids stale client-local reads and the cwd/remote detail gaps in `plugin/read`, but intentionally accepts the same list-level tradeoff as the app: if `plugin/list` reports a remote plugin before its local bundle is materialized, the plugin can still appear as a mention candidate.
Eric Traut ·
2026-05-26 14:34:02 -07:00 -
Use thread config for TUI MCP inventory (#24532)
## Summary `/mcp` in the TUI should reflect the current loaded thread, including project-local MCP servers from that thread config. Before this change, `mcpServerStatus/list` only read the latest global MCP config, so the active chat could miss project-local servers. This adds optional `threadId` to `mcpServerStatus/list`. When present, app-server resolves the loaded thread and lists MCP status from the refreshed effective config for that thread; when omitted, existing global config behavior stays unchanged. The TUI now sends the active chat thread id for `/mcp` and `/mcp verbose`, carries that origin through the async inventory result, and ignores stale completions if the user has switched threads before the fetch returns. The app-server schemas were regenerated. ## Follow-up Once this app-server API change lands, the desktop app should make the same `threadId` plumbing so its MCP inventory also uses the current thread config. Fixes #23874
Eric Traut ·
2026-05-26 07:44:04 -07:00 -
TUI config cleanup: MCP inventory (#24265)
## Summary The TUI `/mcp` inventory flow should reflect the app server’s MCP status response. It was also joining those results with the TUI process’s local `config.mcp_servers`, which can diverge once MCP state is owned by a remote app server and cause stale local command, URL, status, or empty-state details to render. This change removes the local config join from the app-server-backed inventory renderer. The TUI now renders directly from the existing `mcpServerStatus/list` payload and treats an empty status response as the empty MCP inventory state. ## Known limitation The existing `mcpServerStatus/list` payload does not include disabled-state or disabled-reason fields. To preserve the current app-server API, this PR does not try to infer that state from client-local config. If remote `/mcp` needs to show disabled/reason details again, that should come from app-server-owned status data in a follow-up. Related to #22914, #22915, and #22916.
Eric Traut ·
2026-05-25 09:56:21 -07:00 -
Show remote connection details in /status (#24420)
## Summary Fixes #24411. `/status` currently has no way to show when the TUI is talking to Codex through a remote transport. That makes embedded local sessions, local daemon sessions, and true remote sessions look the same, and it hides the remote server version when debugging connection-specific behavior. This PR adds a single `Remote` row for non-embedded connections only. The row shows the sanitized connection address and a dimmed version parenthetical, preserving the existing status output for embedded local sessions. <img width="791" height="144" alt="image" src="https://github.com/user-attachments/assets/529d7940-1c45-4586-8b06-f20a1f04b771" /> ## Verification - Manually validated when connecting remotely (either implicitly to local daemon or explicitly)
Eric Traut ·
2026-05-25 09:42:42 -07:00 -
config: remove legacy profile write paths (#24055)
## Why [#23883](https://github.com/openai/codex/pull/23883) moved the user-facing `--profile` flag onto profile v2 and [#23886](https://github.com/openai/codex/pull/23886) removed CLI forwarding for the legacy profile-v1 path. Core and TUI config persistence still carried `active_profile` and `ConfigEditsBuilder::with_profile`, which let later writes continue targeting legacy `[profiles.<name>]` tables after profile selection moved to profile-v2 config files. ## What - Remove legacy profile routing from [`ConfigEditsBuilder`](https://github.com/openai/codex/blob/4b38e9c22e762261d7f7eef49d8a21792e241a06/codex-rs/core/src/config/edit.rs#L1064-L1294), so core config edits no longer carry `with_profile` or infer `[profiles.*]` write targets from a `profile` key. - Drop `active_profile` plumbing from runtime `Config`, TUI startup/state, app-server config override forwarding, and Windows sandbox setup persistence. - Make app-server-backed TUI config edits use unscoped model, service-tier, feature, Auto-review, plan-mode, and Windows sandbox paths through [`tui/src/config_update.rs`](https://github.com/openai/codex/blob/4b38e9c22e762261d7f7eef49d8a21792e241a06/codex-rs/tui/src/config_update.rs#L43-L112). - Update config edit coverage so legacy `profile` state stays untouched by direct model writes, and remove tests whose only contract was the deleted profile-scoped persistence path. ## Testing - Not run locally.
jif-oai ·
2026-05-22 12:50:42 +02:00 -
Fix auto-review permission profile override (#23956)
## Summary The auto-review runtime sync path was assigning a raw `PermissionProfile` into `runtime_permission_profile_override`, whose field now expects `RuntimePermissionProfileOverride`. That broke the TUI Bazel build. This changes the assignment to store `RuntimePermissionProfileOverride::from_config(&self.config)`, matching the other runtime override paths and preserving the active profile and network metadata with the permission profile.
Eric Traut ·
2026-05-21 16:52:36 -07:00 -
[3 of 4] tui: route feature and memory toggles through app server (#22915)
## Why Experimental feature toggles and memory settings can update several related config values in one interaction. Keeping those writes local in a remote TUI session is especially dangerous because the UI can diverge from the app-server config while also leaving behind partially stale supporting keys. This is **[3 of 4]** in a stacked series that moves TUI-owned config mutations onto app-server APIs. ## What changed - Routed feature flag persistence through app-server batch writes, including the supporting reviewer and permission updates used by guardian approval. - Routed Windows sandbox mode persistence and legacy Windows feature cleanup through app-server writes. - Routed memory settings through app-server batch writes and updated the TUI tests to exercise the embedded app-server path. ## Config keys affected - `features.<feature_key>` - `profiles.<profile>.features.<feature_key>` - `approval_policy` - `sandbox_mode` - `approvals_reviewer` - `windows.sandbox` - `features.experimental_windows_sandbox` - `features.elevated_windows_sandbox` - `features.enable_experimental_windows_sandbox` - Profile-scoped Windows legacy feature variants under `profiles.<profile>.features.*` - `memories.use_memories` - `memories.generate_memories` - Profile-scoped memory variants under `profiles.<profile>.memories.*` ## Suggested manual validation - Connect the TUI to a remote app server, toggle guardian approval on and off, and confirm the remote config updates `features.guardian_approval`, reviewer state, approval policy, and sandbox mode coherently. - Toggle a default-false experimental feature at the root level, disable it again, and confirm the key clears instead of lingering as an unnecessary explicit `false`. - Change memory settings and confirm the remote config updates both memory keys while the running TUI reflects the new state. - On Windows, switch sandbox mode through the TUI and confirm `windows.sandbox` is updated while the legacy Windows feature keys are cleared. ## Stack 1. [#22913](https://github.com/openai/codex/pull/22913) `[1 of 4]` primary settings writes 2. [#22914](https://github.com/openai/codex/pull/22914) `[2 of 4]` app and skill enablement 3. [#22915](https://github.com/openai/codex/pull/22915) `[3 of 4]` feature and memory toggles 4. [#22916](https://github.com/openai/codex/pull/22916) `[4 of 4]` startup and onboarding bookkeeping
Eric Traut ·
2026-05-21 16:03:11 -07:00 -
TUI: skip goal replace prompt for completed goals (#23792)
## Why Users reported that the replacement confirmation feels unnecessary when the current thread goal is already complete. In that state, `/goal <objective>` is starting fresh rather than interrupting active work. ## What changed `/goal <objective>` now skips the replace confirmation when the existing goal has `complete` status and uses the existing fresh replacement path. Goals that are active, paused, blocked, usage-limited, or budget-limited still require confirmation before being replaced.
Eric Traut ·
2026-05-21 10:45:43 -07:00 -
Improve
/goalerror messages for ephemeral sessions (#23796)## Why When a user runs `/goal` in a temporary session, the TUI can currently surface an internal app-server failure such as `thread/goal/get failed in TUI`. That message is technically true, but it does not explain the actual constraint: goals require a saved session because goal state is persisted with the thread. This is especially confusing when `codex doctor` reports the background app-server as running in ephemeral mode, since that wording is easy to conflate with ephemeral thread/session behavior. ## What changed - Added a TUI-side formatter for thread-goal RPC failures in `codex-rs/tui/src/app/thread_goal_actions.rs`. - Detects app-server/core errors that indicate goals are unsupported for an ephemeral thread/session. - Replaces the internal RPC failure with a user-facing explanation: ```text Goals need a saved session. This session is temporary. Run `codex` to start a saved session, or `codex resume` / `/resume` to reopen one. ``` - Preserves the existing generic failure wording for non-ephemeral goal errors. ## Verification - `cargo test -p codex-tui thread_goal_error_message --lib` I also tried `cargo test -p codex-tui`; it built successfully but the test runner aborted in an unrelated side-thread stack overflow (`app::tests::discard_side_thread_removes_agent_navigation_entry`), which reproduced when run by itself.
Eric Traut ·
2026-05-21 09:33:17 -07:00 -
tui: plumb permission profile selection (#23708)
## Why The named-profile `/permissions` picker needs a small TUI action path that can select permission profiles without folding the menu UI and profile metadata into the same review. ## What changed - Carry permission-profile selections through the TUI app event flow. - Persist selected profiles while preserving the existing approval settings and guardrail prompts. - Keep the legacy `/permissions` picker behavior in this layer; the profile-mode menu stays in the follow-up PR. ## Stack 1. [#22931](https://github.com/openai/codex/pull/22931): runtime/session/network propagation for active permission profiles. 2. **This PR**: TUI selection plumbing and guardrail flow. 3. [#21559](https://github.com/openai/codex/pull/21559): profile-aware `/permissions` menu and custom profile display. <img width="1632" height="1186" alt="image" src="https://github.com/user-attachments/assets/69ddcd5e-b57c-468d-8c1d-246916323c15" /> ## Validation - `git diff --cached --check` before commit. - Full test run skipped at the user request while pushing the split stack.
viyatb-oai ·
2026-05-21 12:26:36 -03:00 -
Honor client-resolved service tier defaults (#23537)
## Why Model catalog responses can now advertise a nullable `default_service_tier` for each model. Codex needs to preserve three distinct states all the way from config/app-server inputs to inference: - no explicit service tier, so the client may apply the current model catalog default when FastMode is enabled - explicit `default`, meaning the user intentionally wants standard routing - explicit catalog tier ids such as `priority`, `flex`, or future tiers Keeping those states distinct prevents the UI from showing one tier while core sends another, especially after model switches or app-server `thread/start` / `turn/start` updates. ## What Changed - Plumbed `default_service_tier` through model catalog protocol types, app-server model responses, generated schemas, model cache fixtures, and provider/model-manager conversions. - Added the request-only `default` service tier sentinel and normalized legacy config spelling so `fast` in `config.toml` still materializes as the runtime/request id `priority`. - Moved catalog default resolution to the TUI/client side, including recomputing the effective service tier when model/FastMode-dependent surfaces change. - Updated app-server thread lifecycle config construction so `serviceTier: null` preserves explicit standard-routing intent by mapping to `default` instead of internal `None`. - Kept core responsible for validating explicit tiers against the current model and stripping `default` before `/v1/responses`, without applying catalog defaults itself. ## Validation - `CARGO_INCREMENTAL=0 cargo build -p codex-cli` - `CARGO_INCREMENTAL=0 cargo test -p codex-app-server model_list` - `cargo test -p codex-tui service_tier` - `cargo test -p codex-protocol service_tier_for_request` - `cargo test -p codex-core get_service_tier` - `RUST_MIN_STACK=8388608 CARGO_INCREMENTAL=0 cargo test -p codex-core service_tier`
Shijie Rao ·
2026-05-20 15:57:50 -07:00 -
core: refresh active permission profiles at runtime (#22931)
## Why Once a named permission profile is selected, runtime state has to keep that profile identity intact instead of collapsing back to anonymous effective permissions. The session refresh path also needs to rebuild profile-derived network proxy state so active profile switches take effect consistently. ## What changed - Preserve the active permission profile through session updates. - Rebuild profile-derived runtime/network configuration when the active profile changes. - Keep the runtime path aligned with the current session configuration APIs. - Tighten the affected tests, including the Windows delete-pending memory-file case that was intermittently tripping CI. ## Stack 1. **This PR**: runtime/session/network propagation for active permission profiles. 2. [#23708](https://github.com/openai/codex/pull/23708): TUI selection plumbing and guardrail flow. 3. [#21559](https://github.com/openai/codex/pull/21559): profile-aware `/permissions` menu and custom profile display. <img width="1296" height="906" alt="image" src="https://github.com/user-attachments/assets/077fa3a7-80cb-4925-80b1-d2395018d90a" />
viyatb-oai ·
2026-05-20 21:55:21 +00:00 -
Sync TUI thread settings through app server (#23507)
Builds on #23502. ## Why #23502 adds the app-server `thread/settings/update` API and matching `thread/settings/updated` notification. The TUI already lets users change thread-scoped settings such as model, reasoning effort, service tier, approvals, permissions, personality, and collaboration mode, but those updates need to flow through the app server so embedded and connected clients observe the same thread state. This is a rework (simplification) of PR https://github.com/openai/codex/pull/22510. It has the same functionality, but the underlying `thread/settings/update` api is now simpler in that it no longer returns the effective settings as a response. Now, clients receive the effective settings only through the `thread/settings/updated` notification. ## What Changed This updates the TUI to send `thread/settings/update` whenever those thread-scoped settings change and to treat the RPC response as the authoritative acknowledgement. It also routes `thread/settings/updated` notifications back into cached session state and the visible chat widget so active and inactive threads stay in sync after app-server-originated changes. The implementation is kept to the TUI layer: settings conversion and merge logic live under `codex-rs/tui/src/app/thread_settings.rs`, with dispatch/routing hooks in the existing app and chat widget paths. ## Verification I manually tested using `codex app-server --listen unix://` and then launching two copies of the TUI that use the same local app server. I then resumed the same thread on both and verified that changes like plan mode, fast mode, model, reasoning effort, etc. are reflected "live" in the second client when modified in the first and vice versa.
Eric Traut ·
2026-05-20 11:05:14 -07:00 -
Add thread/settings/update app-server API (#23502)
## Why App-server clients need a way to update a thread's next-turn settings without starting a turn, adding transcript content, or waiting for turn lifecycle events. This gives settings UI a direct path for durable thread settings while clients observe the eventual effective state through a notification. This is a simplified rework of PR https://github.com/openai/codex/pull/22509. In particular, it changes the `thread/settings/update` api to return immediately rather than waiting and returning the effective (updated) thread settings. This makes the new api consistent with `turn/start` and greatly reduces the complexity of the implementation relative to the earlier attempt. ## What Changed - Adds experimental `thread/settings/update` with partial-update request fields and an empty acknowledgment response. - Adds experimental `thread/settings/updated`, carrying full effective `ThreadSettings` and scoped by `threadId` to subscribed clients for the affected thread. - Shares durable settings validation with `turn/start`, including `sandboxPolicy` plus `permissions` rejection and `serviceTier: null` clearing. - Emits the same settings notification when `turn/start` overrides change the stored effective thread settings. - Regenerates app-server protocol schema fixtures and updates `app-server/README.md`.
Eric Traut ·
2026-05-20 11:03:20 -07:00 -
[2 of 2] Start fresh TUI thread in background (#23176)
## Why After the terminal-probe work in #23175, fresh-session startup still waits for `thread/start` before the chat input can become usable. The chat widget already has the machinery to hold early submissions until a session is configured, so fresh `thread/start` does not need to stay on the input-ready hot path. Refs #16335. ## What This PR starts fresh app-server threads in a background task, reports completion through a startup app event, and attaches the primary session once `thread/start` returns. Resume and fork startup paths remain synchronous. ## Benchmark In the local pty startup benchmark, this PR's pre-optimization base branch, #23175, measured about 152ms median from launch to accepted chat input. The stacked result measured about 66ms median, for an approximate additional savings of 85-95ms. For broader context, the original `main` baseline before either startup optimization was about 250.5ms median. We also measured Codex 0.117.0 on the same machine at about 64.6ms median, so the stacked branch is back in the old-startup-time range. ## Stack 1. [#23175: [1 of 2] Optimize TUI startup terminal probes](https://github.com/openai/codex/pull/23175) — base PR 2. [#23176: [2 of 2] Start fresh TUI thread in background](https://github.com/openai/codex/pull/23176) — this PR ## Verification - `cargo test -p codex-tui`
Eric Traut ·
2026-05-20 10:00:33 -07:00 -
Fix: TUI starting in wrong CWD (#23538)
This fixes a regression wher codex could start in the wrong directory when a live local app-server socket was present. The issue was that implicit local socket reuse was being treated like an explicit remote workspace session, which dropped the invoking cwd unless --cd was passed. The change separates local socket transport from true remote workspace semantics. - Plain local startup keeps local cwd, trust, resume, picker, and config-refresh behavior. - Explicit --remote keeps the existing remote cwd behavior. - Added coverage for launch target selection and local-session filtering/cwd behavior. Steps to test: - Start a local app-server from a different directory than the repo you want to use. - Launch codex from a project/worktree without --cd. - Confirm the session starts in the invoking directory, not the app-server process directory. - Confirm explicit codex --remote ... still preserves existing remote behavior.
canvrno-oai ·
2026-05-19 15:48:40 -07:00 -
Make
denycanonical for filesystem permission entries (#23493)## Why Filesystem permission profiles used `none` for deny-read entries, which is less direct than the action the entry actually represents. This change makes `deny` the canonical filesystem permission spelling while preserving compatibility for older configs that still send `none`. ## What changed - rename `FileSystemAccessMode::None` to `Deny` - serialize and generate schemas with `deny` as the canonical value - retain `none` only as a legacy input alias for temporary config compatibility - update filesystem glob diagnostics and regression coverage to use the canonical spelling - refresh config and app-server schema fixtures to match the new wire shape ## Validation - `cargo test -p codex-protocol` - `cargo test -p codex-app-server-protocol` - `cargo test -p codex-core config_toml_deserializes_permission_profiles --lib` - `cargo test -p codex-core read_write_glob_patterns_still_reject_non_subpath_globs --lib` Earlier in the session, a broad `cargo test -p codex-core` run reached unrelated pre-existing failures in timing/snapshot/git-info tests under this environment; the targeted surfaces touched by this PR passed cleanly.
viyatb-oai ·
2026-05-19 11:03:47 -07:00 -
[2 of 4] tui: route app and skill enablement through app server (#22914)
## Why App and skill toggles are user config mutations too. When the TUI is attached to a remote app server, writing those toggles into the local `config.toml` makes the UI report success without updating the server that actually owns the session. This is **[2 of 4]** in a stacked series that moves TUI-owned config mutations onto app-server APIs. ## What changed - Routed app enable/disable persistence through app-server config batch writes. - Routed skill enable/disable persistence through `skills/config/write`. - Avoided refreshing local config from disk after these writes when the TUI is connected to a remote app server. ## Config keys affected - `apps.<app_id>.enabled` - `apps.<app_id>.disabled_reason` - `[[skills.config]]` entries keyed by `path`, with `enabled = false` used for persisted disables ## Suggested manual validation - Connect the TUI to a remote app server, disable an app, reconnect, and confirm the app remains disabled from remote config rather than local disk state. - Re-enable the same app and confirm both `apps.<app_id>.enabled` and `apps.<app_id>.disabled_reason` are cleared remotely. - Disable a skill in the manage-skills UI and confirm a remote `[[skills.config]]` disable entry appears. - Re-enable that skill and confirm the disable entry is removed and the effective enabled state updates without relying on local config reloads. ## Stack 1. [#22913](https://github.com/openai/codex/pull/22913) `[1 of 4]` primary settings writes 2. [#22914](https://github.com/openai/codex/pull/22914) `[2 of 4]` app and skill enablement 3. [#22915](https://github.com/openai/codex/pull/22915) `[3 of 4]` feature and memory toggles 4. [#22916](https://github.com/openai/codex/pull/22916) `[4 of 4]` startup and onboarding bookkeeping
Eric Traut ·
2026-05-19 10:21:07 -07:00 -
Clarify resume hints for renamed threads (#23234)
Addresses #23181 ## Why Renamed threads can share names, so hints that suggest resuming directly by name are ambiguous. Issue #23181 asks for the picker hint to include the thread name and thread ID in parens so users can disambiguate safely. ## What - Adds a shared resume hint formatter for named threads: run `codex resume`, then select `<name> (<thread-id>)`. - Uses that hint for /rename confirmations, TUI session summaries, and CLI/TUI exit messages. - Keeps direct `codex resume <thread-id>` guidance for unnamed threads. ## Verification Manually verified that message after `/rename` and after `/exit` include session ID in parens. --------- Co-authored-by: Felipe Coury <felipe.coury@openai.com>
Eric Traut ·
2026-05-18 11:32:02 -07:00 -
goal: pause continuation loops on usage limits and blockers (#23094)
Addresses #22833, #22245, #23067 ## Why `/goal` can keep synthesizing turns even when the next turn cannot make meaningful progress. Hard usage exhaustion can replay failing turns, and repeated permission or external-resource blockers can keep burning tokens while waiting for user or system intervention. ## What changed - Add resumable `blocked` and `usageLimited` goal states. As with `paused`, goal continuation stops with these states. - Move to `usageLimited` after usage-limit failures. - Allow the built-in `update_goal` tool to set `blocked` only under explicit repeated-impasse guidance. Updated goal continuation prompt to specify that agent should use `blocked` only when it has made at least three attempts to get past an impasse. Most of the files touched by this PR are because of the small app server protocol update. ## Validation I manually reproduced a number of situations where an agent can run into a true impasse and verified that it properly enters `blocked` state. I then resumed and verified that it once again entered `blocked` state several turns later if the impasse still exists. I also manually reproduced the usage-limit condition by creating a simulated responses API endpoint that returns 429 errors with the appropriate error message. Verified that the goal runtime properly moves the goal into `usageLimited` state and TUI UI updates appropriately. Verified that `/goal resume` resumes (and immediately goes back into `ussageLImited` state if appropriate). ## Follow-up PRs Small changes will be needed to the GUI clients to properly handle the two new states.
Eric Traut ·
2026-05-18 11:28:53 -07:00