mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
5bb193aa88fef0f5ef3fbbd2c6253ba93d3f6521
278 Commits
-
[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 -
Add sorting/backwardsCursor to thread/list and new thread/turns/list api (#17305)
To improve performance of UI loads from the app, add two main improvements: 1. The `thread/list` api now gets a `sortDirection` request field and a `backwardsCursor` to the response, which lets you paginate forwards and backwards from a window. This lets you fetch the first few items to display immediately while you paginate to fill in history, then can paginate "backwards" on future loads to catch up with any changes since the last UI load without a full reload of the entire data set. 2. Added a new `thread/turns/list` api which also has sortDirection and backwardsCursor for the same behavior as `thread/list`, allowing you the same small-fetch for immediate display followed by background fill-in and resync catchup.
David de Regt ·
2026-04-17 11:49:02 -07:00 -
feat(permissions): add glob deny-read policy support (#15979)
## Summary - adds first-class filesystem policy entries for deny-read glob patterns - parses config such as :project_roots { "**/*.env" = "none" } into pattern entries - enforces deny-read patterns in direct read/list helpers - fails closed for sandbox execution until platform backends enforce glob patterns in #18096 - preserves split filesystem policy in turn context only when it cannot be reconstructed from legacy sandbox policy ## Stack 1. This PR - glob deny-read policy/config/direct-tool support 2. #18096 - macOS and Linux sandbox enforcement 3. #17740 - managed deny-read requirements ## Verification - just fmt - cargo check -p codex-core -p codex-sandboxing --tests --------- Co-authored-by: Codex <noreply@openai.com>viyatb-oai ·
2026-04-16 10:31:51 -07:00 -
fix: propagate log db (#17953)
It restores the TRACE logs in the DB and `/feedback` Fix https://github.com/openai/codex/pull/16184 Result: https://openai.sentry.io/issues/6972946529/?project=4510195390611458&query=019d91e9-f931-7451-8852-c5240514a419&referrer=issue-stream
jif-oai ·
2026-04-15 20:25:53 +01:00 -
Use AbsolutePathBuf in skill loading and codex_home (#17407)
Helps with FS migration later
pakrym-oai ·
2026-04-13 10:26:51 -07:00 -
Run exec-server fs operations through sandbox helper (#17294)
## Summary - run exec-server filesystem RPCs requiring sandboxing through a `codex-fs` arg0 helper over stdin/stdout - keep direct local filesystem execution for `DangerFullAccess` and external sandbox policies - remove the standalone exec-server binary path in favor of top-level arg0 dispatch/runtime paths - add sandbox escape regression coverage for local and remote filesystem paths ## Validation - `just fmt` - `git diff --check` - remote devbox: `cd codex-rs && bazel test --bes_backend= --bes_results_url= //codex-rs/exec-server:all` (6/6 passed) --------- Co-authored-by: Codex <noreply@openai.com>
starr-openai ·
2026-04-12 18:36:03 -07:00 -
Fix thread/list cwd filtering for Windows verbatim paths (#17414)
Addresses #17302 Problem: `thread/list` compared cwd filters with raw path equality, so `resume --last` could miss Windows sessions when the saved cwd used a verbatim path form and the current cwd did not. Solution: Normalize cwd comparisons through the existing path comparison utilities before falling back to direct equality, and add Windows regression coverage for verbatim paths. I made this a general utility function and replaced all of the duplicated instance of it across the code base.
Eric Traut ·
2026-04-10 23:08:02 -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 -
fix(permissions): fix symlinked writable roots in sandbox permissions (#15981)
## Summary - preserve logical symlink paths during permission normalization and config cwd handling - bind real targets for symlinked readable/writable roots in bwrap and remap carveouts and unreadable roots there - add regressions for symlinked carveouts and nested symlink escape masking ## Root cause Permission normalization canonicalized symlinked writable roots and cwd to their real targets too early. That drifted policy checks away from the logical paths the sandboxed process can actually address, while bwrap still needed the real targets for mounts. The mismatch caused shell and apply_patch failures on symlinked writable roots. ## Impact Fixes #15781. Also fixes #17079: - #17079 is the protected symlinked carveout side: bwrap now binds the real symlinked writable-root target and remaps carveouts before masking. Related to #15157: - #15157 is the broader permission-check side of this path-identity problem. This PR addresses the shared logical-vs-canonical normalization issue, but the reported Darwin prompt behavior should be validated separately before auto-closing it. This should also fix #14672, #14694, #14715, and #15725: - #14672, #14694, and #14715 are the same Linux symlinked-writable-root/bwrap family as #15781. - #15725 is the protected symlinked workspace path variant; the PR preserves the protected logical path in policy space while bwrap applies read-only or unreadable treatment to the resolved target so file-vs-directory bind mismatches do not abort sandbox setup. ## Notes - Added Linux-only regressions for symlinked writable ancestors and protected symlinked directory targets, including nested symlink escape masking without rebinding the escape target writable. --------- Co-authored-by: Codex <noreply@openai.com>
viyatb-oai ·
2026-04-10 17:00:58 -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 -
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 -
feat(tui): Ctrl+O copy hotkey and harden copy-as-markdown behavior (#16966)
## TL;DR - New `Ctrl+O` shortcut on top of the existing `/copy` command, allowing users to copy the latest agent response without having to cancel a plan or type `/copy` - Copy server clipboard to the client over SSH (OSC 52) - Fixes linux copy behavior: a clipboard handle has to be kept alive while the paste happens for the contents to be preserved - Uses arboard as primary mechanism on Windows, falling back to PowerShell copy clipboard function - Works with resumes, rolling back during a session, etc. Tested on macOS, Linux/X11, Windows WSL2, Windows cmd.exe, Windows PowerShell, Windows VSCode PowerShell, Windows VSCode WSL2, SSH (macOS -> macOS). ## Problem The TUI's `/copy` command was fragile. It relied on a single `last_copyable_output` field that was bluntly cleared on every rollback and thread reconfiguration, making copied content unavailable after common operations like backtracking. It also had no keyboard shortcut, requiring users to type `/copy` each time. The previous clipboard backend mixed platform selection policy with low-level I/O in a way that was hard to test, and it did not keep the Linux clipboard owner alive — meaning pasted content could vanish once the process that wrote it dropped its `arboard::Clipboard`. This addresses the text-copy failure modes reported in #12836, #15452, and #15663: native Linux clipboard access failing in remote or unreachable-display environments, copy state going blank even after visible assistant output, and local Linux X11 reporting success while leaving the clipboard empty. ## Shortcut rationale The copy hotkey is `Ctrl+O` rather than `Alt+C` because Alt/Option combinations are not delivered consistently by macOS terminal emulators. Terminal.app and iTerm2 can treat Option as text input or as a configurable Meta/Esc prefix, and Option+C may be consumed or transformed before the TUI sees an `Alt+C` key event. `Ctrl+O` is a stable control-key chord in Terminal.app, iTerm2, SSH, and the existing cross-platform terminal stack. ## Mental model Agent responses are now tracked as a bounded, ordinal-indexed history (`agent_turn_markdowns: Vec<AgentTurnMarkdown>`) rather than a single nullable string. Each completed agent turn appends an entry keyed by its ordinal (the number of user turns seen so far). Rollbacks pop entries whose ordinal exceeds the remaining turn count, then use the visible transcript cells as a best-effort fallback if the ordinal history no longer has a surviving entry. This means `/copy` and `Ctrl+O` reflect the most recent surviving agent response after a backtrack, instead of going blank. The clipboard backend was rewritten as `clipboard_copy.rs` with a strategy-injection design: `copy_to_clipboard_with` accepts closures for the OSC 52, arboard, and WSL PowerShell paths, making the selection logic fully unit-testable without touching real clipboards. On Linux, the `Clipboard` handle is returned as a `ClipboardLease` stored on `ChatWidget`, keeping X11/Wayland clipboard ownership alive for the lifetime of the TUI. When native copy fails under WSL, the backend now tries the Windows clipboard through PowerShell before falling back to OSC 52. ## Non-goals - This change does not introduce rich-text (HTML) clipboard support; the copied content is raw markdown. - It does not add a paste-from-history picker or multi-entry clipboard ring. - WSL support remains a best-effort fallback, not a new configuration surface or guarantee for every terminal/host combination. ## Tradeoffs - **Bounded history (256 entries)**: `MAX_AGENT_COPY_HISTORY` caps memory. For sessions with thousands of turns this silently drops the oldest entries. The cap is generous enough for realistic sessions. - **`saw_copy_source_this_turn` flag**: Prevents double-recording when both `AgentMessage` and `TurnComplete.last_agent_message` fire for the same turn. The flag is reset on turn start and on turn complete, creating a narrow window where a race between the two events could theoretically skip recording. In practice the protocol delivers them sequentially. - **Transcript fallback on rollback**: `last_agent_markdown_from_transcript` walks the visible transcript cells to reconstruct plain text when the ordinal history has been fully truncated. This path uses `AgentMessageCell::plain_text()` which joins rendered spans, so it reconstructs display text rather than the original raw markdown. It keeps visible text copyable after rollback, but responses with markdown-specific syntax can diverge from the original source. - **Clipboard fallback ordering**: SSH still uses OSC 52 exclusively because native/PowerShell clipboard access would target the wrong machine. Local sessions try native clipboard first, then WSL PowerShell when running under WSL, then OSC 52. This adds one process-spawn fallback for WSL users but keeps the normal desktop and SSH paths simple. ## Architecture ``` chatwidget.rs ├── agent_turn_markdowns: Vec<AgentTurnMarkdown> // ordinal-indexed history ├── last_agent_markdown: Option<String> // always == last entry's markdown ├── completed_turn_count: usize // incremented when user turns enter history ├── saw_copy_source_this_turn: bool // dedup guard ├── clipboard_lease: Option<ClipboardLease> // keeps Linux clipboard owner alive │ ├── record_agent_markdown(&str) // append/update history entry ├── truncate_agent_turn_markdowns_to_turn_count() // rollback support ├── copy_last_agent_markdown() // public entry point (slash + hotkey) └── copy_last_agent_markdown_with(fn) // testable core clipboard_copy.rs ├── copy_to_clipboard(text) -> Result<Option<ClipboardLease>> ├── copy_to_clipboard_with(text, ssh, wsl, osc52_fn, arboard_fn, wsl_fn) ├── ClipboardLease { _clipboard on linux } ├── arboard_copy(text) // platform-conditional native clipboard path ├── wsl_clipboard_copy(text) // WSL PowerShell fallback ├── osc52_copy(text) // /dev/tty -> stdout fallback ├── SuppressStderr // macOS stderr redirect guard ├── is_ssh_session() └── is_wsl_session() app_backtrack.rs ├── last_agent_markdown_from_transcript() // reconstruct from visible cells └── truncate call sites in trim/apply_confirmed_rollback ``` ## Observability - `tracing::warn!` on native clipboard failure before OSC 52 fallback. - `tracing::debug!` on `/dev/tty` open/write failure before stdout fallback. - History cell messages: "Copied last message to clipboard", "Copy failed: {error}", "No agent response to copy" appear in the TUI transcript. ## Tests - `clipboard_copy.rs`: Unit tests cover OSC 52 encoding roundtrip, payload size rejection, writer output, SSH-only OSC52 routing, non-WSL native-to-OSC52 fallback, WSL native-to-PowerShell fallback, WSL PowerShell-to-OSC52 fallback, and all-error reporting via strategy injection. - `chatwidget/tests/slash_commands.rs`: Updated existing `/copy` tests to use `last_agent_markdown_text()` accessor. Added coverage for the Linux clipboard lease lifecycle, missing `TurnComplete.last_agent_message` fallback through completed assistant items, replayed legacy agent messages, stale-output prevention after rollback, and the `Ctrl+O` no-output hotkey path. - `app_backtrack.rs`: Added `agent_group_count_ignores_context_compacted_marker` verifying that info-event cells don't inflate the agent group count. --------- Co-authored-by: Felipe Coury <felipe.coury@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Felipe Coury ·
2026-04-09 18:10:38 -03:00 -
chore: merge name and title (#17116)
Merge title and name concept to leverage the sqlite title column and have more efficient queries --------- Co-authored-by: Codex <noreply@openai.com>
jif-oai ·
2026-04-09 18:44:26 +01:00 -
[codex] Support remote exec cwd in TUI startup (#17142)
When running with remote executor the cwd is the remote path. Today we check for existence of a local directory on startup and attempt to load config from it. For remote executors don't do that.
pakrym-oai ·
2026-04-08 13:09:28 -07:00 -
feat: single app-server bootstrap in TUI (#16582)
Before this, the TUI was starting 2 app-server. One to check the login status and one to actually start the session This PR make only one app-server startup and defer the login check in async, outside of the frame rendering path --------- Co-authored-by: Codex <noreply@openai.com>
jif-oai ·
2026-04-08 13:49:06 +01: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 -
[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 -
Refactor config types into a separate crate (#16962)
Move config types into a separate crate because their macros expand into a lot of new code.
pakrym-oai ·
2026-04-07 00:32:41 +00: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(tui): handle zellij redraw and composer rendering (#16578)
## TL;DR Fixes the issues when using Codex CLI with Zellij multiplexer. Before this PR there would be no scrollback when using it inside a zellij terminal. ## Problem Addresses #2558 Zellij does not support ANSI scroll-region manipulation (`DECSTBM` / Reverse Index) or the alternate screen buffer in the way traditional terminals do. When codex's TUI runs inside Zellij, two things break: (1) inline history insertion corrupts the display because the scroll-region escape sequences are silently dropped or mishandled, and (2) the composer textarea renders with inherited background/foreground styles that produce unreadable text against Zellij's pane chrome. ## Mental model The fix introduces a **Zellij mode** — a runtime boolean detected once at startup via `codex_terminal_detection::terminal_info().is_zellij()` — that gates two subsystems onto Zellij-safe terminal strategies: - **History insertion** (`insert_history.rs`): Instead of using `DECSTBM` scroll regions and Reverse Index (`ESC M`) to slide content above the viewport, Zellij mode scrolls the screen by emitting `\n` at the bottom row and then writes history lines at absolute positions. This avoids every escape sequence Zellij mishandles. - **Viewport expansion** (`tui.rs`): When the viewport grows taller than available space, the standard path uses `scroll_region_up` on the backend. Zellij mode instead emits newlines at the screen bottom to push content up, then invalidates the ratatui diff buffer so the next draw is a full repaint. - **Composer rendering** (`chat_composer.rs`, `textarea.rs`): All text rendering in the input area uses an explicit `base_style` with `Color::Reset` foreground, preventing Zellij's pane styling from bleeding into the textarea. The prompt chevron (`›`) and placeholder text use explicit color constants instead of relying on `.bold()` / `.dim()` modifiers that render inconsistently under Zellij. ## Non-goals - This change does not fix or improve Zellij's terminal emulation itself. - It does not rearchitect the inline viewport model; it adds a parallel code path gated on detection. - It does not touch the alternate-screen disable logic (that already existed and continues to use `is_zellij` via the same detection). ## Tradeoffs - **Code duplication in `insert_history.rs`**: The Zellij and Standard branches share the line-rendering loop (color setup, span merging, `write_spans`) but differ in the scrolling preamble. The duplication is intentional — merging them would force a complex conditional state machine that's harder to reason about than two flat sequences. - **`invalidate_viewport` after every Zellij history flush or viewport expansion**: This forces a full repaint on every draw cycle in Zellij, which is more expensive than ratatui's normal diff-based rendering. This is necessary because Zellij's lack of scroll-region support means the diff buffer's assumptions about what's on screen are invalid after we manually move content. - **Explicit colors vs semantic modifiers**: Replacing `.bold()` / `.dim()` with `Color::Cyan` / `Color::DarkGray` / `Color::White` in the Zellij branch sacrifices theme-awareness for correctness. If the project ever adopts a theming system, Zellij styling will need to participate. ## Architecture The Zellij detection flag flows through three layers: 1. **`codex_terminal_detection`** — `TerminalInfo::is_zellij()` (new convenience method) reads the already-detected `Multiplexer` variant. 2. **`Tui` struct** — caches `is_zellij` at construction; passes it into `update_inline_viewport`, `flush_pending_history_lines`, and `insert_history_lines_with_mode`. 3. **`ChatComposer` struct** — independently caches `is_zellij` at construction; uses it in `render_textarea` for style decisions. The two caches (`Tui.is_zellij` and `ChatComposer.is_zellij`) are read from the same global `OnceLock<TerminalInfo>`, so they always agree. ## Observability No new logging, metrics, or tracing is introduced. Diagnosis depends on: - Whether `ZELLIJ` or `ZELLIJ_SESSION_NAME` env vars are set (the detection heuristic). - Visual inspection of the rendered TUI inside Zellij vs a standard terminal. - The insta snapshot `zellij_empty_composer` captures the Zellij-mode render path. ## Tests - `terminal_info_reports_is_zellij` — unit test in `terminal-detection` confirming the convenience method. - `zellij_empty_composer_snapshot` — insta snapshot in `chat_composer` validating the Zellij render path for an empty composer. - `vt100_zellij_mode_inserts_history_and_updates_viewport` — integration test in `insert_history` verifying that Zellij-mode history insertion writes content and shifts the viewport. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fcoury-oai ·
2026-04-02 18:07:05 -03:00 -
core: remove cross-crate re-exports from lib.rs (#16512)
## Why `codex-core` was re-exporting APIs owned by sibling `codex-*` crates, which made downstream crates depend on `codex-core` as a proxy module instead of the actual owner crate. Removing those forwards makes crate boundaries explicit and lets leaf crates drop unnecessary `codex-core` dependencies. In this PR, this reduces the dependency on `codex-core` to `codex-login` in the following files: ``` codex-rs/backend-client/Cargo.toml codex-rs/mcp-server/tests/common/Cargo.toml ``` ## What - Remove `codex-rs/core/src/lib.rs` re-exports for symbols owned by `codex-login`, `codex-mcp`, `codex-rollout`, `codex-analytics`, `codex-protocol`, `codex-shell-command`, `codex-sandboxing`, `codex-tools`, and `codex-utils-path`. - Delete the `default_client` forwarding shim in `codex-rs/core`. - Update in-crate and downstream callsites to import directly from the owning `codex-*` crate. - Add direct Cargo dependencies where callsites now target the owner crate, and remove `codex-core` from `codex-rs/backend-client`.
Michael Bolin ·
2026-04-01 23:06:24 -07:00 -
Fix TUI app-server permission profile conversions (#16284)
Addresses #16283 Problem: TUI app-server permission approvals could drop filesystem grants because request and response payloads were round-tripped through mismatched camelCase and snake_case JSON shapes. Solution: Replace the lossy JSON round-trips with typed app-server/core permission conversions so requested and granted permission profiles, including filesystem paths and scope, are preserved end to end.
Eric Traut ·
2026-04-01 22:00:27 -06:00 -
tui: remove the voice-input crate feature (#16467)
## Why `voice-input` is the only remaining TUI crate feature, but it is also a default feature and nothing in the workspace selects it explicitly. In practice it is just acting as a proxy for platform support, which is better expressed with target-specific dependencies and cfgs. ## What changed - remove the `voice-input` feature from `codex-tui` - make `cpal` a normal non-Linux target dependency - replace the feature-based voice and audio cfgs with pure Linux-vs-non-Linux cfgs - shrink the workspace-manifest verifier allowlist to remove the remaining `codex-tui` exception ## How tested - `python3 .github/scripts/verify_cargo_workspace_manifests.py` - `cargo test -p codex-tui` - `just bazel-lock-check` - `just argument-comment-lint -p codex-tui`
Michael Bolin ·
2026-04-01 13:03:59 -07:00 -
Remove TUI voice transcription feature (#16114)
Removes the partially-completed TUI composer voice transcription flow, including its feature flag, app events, and hold-to-talk state machine.
Eric Traut ·
2026-03-29 00:20:25 +00: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 -
Remove the legacy TUI split (#15922)
This is the part 1 of 2 PRs that will delete the `tui` / `tui_app_server` split. This part simply deletes the existing `tui` directory and marks the `tui_app_server` feature flag as removed. I left the `tui_app_server` feature flag in place for now so its presence doesn't result in an error. It is simply ignored. Part 2 will rename the `tui_app_server` directory `tui`. I did this as two parts to reduce visible code churn.
Eric Traut ·
2026-03-27 22:56:44 +00:00 -
ci: add Bazel clippy workflow for codex-rs (#15955)
## Why `bazel.yml` already builds and tests the Bazel graph, but `rust-ci.yml` still runs `cargo clippy` separately. This PR starts the transition to a Bazel-backed lint lane for `codex-rs` so we can eventually replace the duplicate Rust build, test, and lint work with Bazel while explicitly keeping the V8 Bazel path out of scope for now. To make that lane practical, the workflow also needs to look like the Bazel job we already trust. That means sharing the common Bazel setup and invocation logic instead of hand-copying it, and covering the arm64 macOS path in addition to Linux. Landing the workflow green also required fixing the first lint findings that Bazel surfaced and adding the matching local entrypoint. ## What changed - add a reusable `build:clippy` config to `.bazelrc` and export `codex-rs/clippy.toml` from `codex-rs/BUILD.bazel` so Bazel can run the repository's existing Clippy policy - add `just bazel-clippy` so the local developer entrypoint matches the new CI lane - extend `.github/workflows/bazel.yml` with a dedicated Bazel clippy job for `codex-rs`, scoped to `//codex-rs/... -//codex-rs/v8-poc:all` - run that clippy job on Linux x64 and arm64 macOS - factor the shared Bazel workflow setup into `.github/actions/setup-bazel-ci/action.yml` and the shared Bazel invocation logic into `.github/scripts/run-bazel-ci.sh` so the clippy and build/test jobs stay aligned - fix the first Bazel-clippy findings needed to keep the lane green, including the cross-target `cmsghdr::cmsg_len` normalization in `codex-rs/shell-escalation/src/unix/socket.rs` and the no-`voice-input` dead-code warnings in `codex-rs/tui` and `codex-rs/tui_app_server` ## Verification - `just bazel-clippy` - `RUNNER_OS=macOS ./.github/scripts/run-bazel-ci.sh -- build --config=clippy --build_metadata=COMMIT_SHA=local-check --build_metadata=TAG_job=clippy -- //codex-rs/... -//codex-rs/v8-poc:all` - `bazel build --config=clippy //codex-rs/shell-escalation:shell-escalation` - `CARGO_TARGET_DIR=/tmp/codex4-shell-escalation-test cargo test -p codex-shell-escalation` - `ruby -e 'require "yaml"; YAML.load_file(".github/workflows/bazel.yml"); YAML.load_file(".github/actions/setup-bazel-ci/action.yml")'` ## Notes - `CARGO_TARGET_DIR=/tmp/codex4-tui-app-server-test cargo test -p codex-tui-app-server` still hits existing guardian-approvals test and snapshot failures unrelated to this PR's Bazel-clippy changes. Related: #15954Michael Bolin ·
2026-03-27 12:02:41 -07:00 -
fix: fix old system bubblewrap compatibility without falling back to vendored bwrap (#15693)
Fixes #15283. ## Summary Older system bubblewrap builds reject `--argv0`, which makes our Linux sandbox fail before the helper can re-exec. This PR keeps using system `/usr/bin/bwrap` whenever it exists and only falls back to vendored bwrap when the system binary is missing. That matters on stricter AppArmor hosts, where the distro bwrap package also provides the policy setup needed for user namespaces. For old system bwrap, we avoid `--argv0` instead of switching binaries: - pass the sandbox helper a full-path `argv0`, - keep the existing `current_exe() + --argv0` path when the selected launcher supports it, - otherwise omit `--argv0` and re-exec through the helper's own `argv[0]` path, whose basename still dispatches as `codex-linux-sandbox`. Also updates the launcher/warning tests and docs so they match the new behavior: present-but-old system bwrap uses the compatibility path, and only absent system bwrap falls back to vendored. ### Validation 1. Install Ubuntu 20.04 in a VM 2. Compile codex and run without bubblewrap installed - see a warning about falling back to the vendored bwrap 3. Install bwrap and verify version is 0.4.0 without `argv0` support 4. run codex and use apply_patch tool without errors <img width="802" height="631" alt="Screenshot 2026-03-25 at 11 48 36 PM" src="https://github.com/user-attachments/assets/77248a29-aa38-4d7c-9833-496ec6a458b8" /> <img width="807" height="634" alt="Screenshot 2026-03-25 at 11 47 32 PM" src="https://github.com/user-attachments/assets/5af8b850-a466-489b-95a6-455b76b5050f" /> <img width="812" height="635" alt="Screenshot 2026-03-25 at 11 45 45 PM" src="https://github.com/user-attachments/assets/438074f0-8435-4274-a667-332efdd5cb57" /> <img width="801" height="623" alt="Screenshot 2026-03-25 at 11 43 56 PM" src="https://github.com/user-attachments/assets/0dc8d3f5-e8cf-4218-b4b4-a4f7d9bf02e3" /> --------- Co-authored-by: Michael Bolin <mbolin@openai.com>
viyatb-oai ·
2026-03-25 23:51:39 -07:00 -
Add non-interactive resume filter option (#15339)
## Summary - add `codex resume --include-non-interactive` to include non-interactive sessions in the picker and `--last` - keep current-provider and cwd filtering behavior unchanged - replace the picker API boolean with a `SessionSourceFilter` enum to avoid a boolean trap ## Tests - `cargo test -p codex-cli` - `cargo test -p codex-tui` - `just fmt` - `just fix -p codex-cli` - `just fix -p codex-tui`
Jeremy Rose ·
2026-03-25 11:05:07 -07:00 -
Use AbsolutePathBuf for cwd state (#15710)
Migrate `cwd` and related session/config state to `AbsolutePathBuf` so downstream consumers consistently see absolute working directories. Add test-only `.abs()` helpers for `Path`, `PathBuf`, and `TempDir`, and update branch-local tests to use them instead of `AbsolutePathBuf::try_from(...)`. For the remaining TUI/app-server snapshot coverage that renders absolute cwd values, keep the snapshots unchanged and skip the Windows-only cases where the platform-specific absolute path layout differs.
pakrym-oai ·
2026-03-25 16:02:22 +00:00 -
tui: always restore the terminal on early exit (#15671)
## Summary Fixes early TUI exit paths that could leave the terminal in a dirty state and cause a stray `%` prompt marker after the app quit. ## Root cause Both `tui` and `tui_app_server` had early returns after `tui::init()` that did not guarantee terminal restore. When that happened, shells like `zsh` inherited the altered terminal state. ## Changes - Add a restore guard around `run_ratatui_app()` in both `tui` and `tui_app_server` - Route early exits through the guard instead of relying on scattered manual restore calls - Ensure terminal restore still happens on normal shutdown
Eric Traut ·
2026-03-24 14:29:29 -06:00 -
Initial plugins TUI menu - list and read only. tui + tui_app_server (#15215)
### Preliminary /plugins TUI menu - Adds a preliminary /plugins menu flow in both tui and tui_app_server. - Fetches plugin list data asynchronously and shows loading/error/cached states. - Limits this first pass to the curated ChatGPT marketplace. - Shows available plugins with installed/status metadata. - Supports in-menu search over plugin display name, plugin id, plugin name, and marketplace label. - Opens a plugin detail view on selection, including summaries for Skills, Apps, and MCP Servers, with back navigation. ### Testing - Launch codex-cli with plugins enabled (`--enable plugins`). - Run /plugins and verify: - loading state appears first - plugin list is shown - search filters results - selecting a plugin opens detail view, with a list of skills/connectors/MCP servers for the plugin - back action returns to the list. - Verify disabled behavior by running /plugins without plugins enabled (shows “Plugins are disabled” message). - Launch with `--enable tui_app_server` (and plugins enabled) and repeat the same /plugins flow; behavior should match.canvrno-oai ·
2026-03-19 21:28:33 -07:00 -
Use released DotSlash package for argument-comment lint (#15199)
## Why The argument-comment lint now has a packaged DotSlash artifact from [#15198](https://github.com/openai/codex/pull/15198), so the normal repo lint path should use that released payload instead of rebuilding the lint from source every time. That keeps `just clippy` and CI aligned with the shipped artifact while preserving a separate source-build path for people actively hacking on the lint crate. The current alpha package also exposed two integration wrinkles that the repo-side prebuilt wrapper needs to smooth over: - the bundled Dylint library filename includes the host triple, for example `@nightly-2025-09-18-aarch64-apple-darwin`, and Dylint derives `RUSTUP_TOOLCHAIN` from that filename - on Windows, Dylint's driver path also expects `RUSTUP_HOME` to be present in the environment Without those adjustments, the prebuilt CI jobs fail during `cargo metadata` or driver setup. This change makes the checked-in prebuilt wrapper normalize the packaged library name to the plain `nightly-2025-09-18` channel before invoking `cargo-dylint`, and it teaches both the wrapper and the packaged runner source to infer `RUSTUP_HOME` from `rustup show home` when the environment does not already provide it. After the prebuilt Windows lint job started running successfully, it also surfaced a handful of existing anonymous literal callsites in `windows-sandbox-rs`. This PR now annotates those callsites so the new cross-platform lint job is green on the current tree. ## What Changed - checked in the current `tools/argument-comment-lint/argument-comment-lint` DotSlash manifest - kept `tools/argument-comment-lint/run.sh` as the source-build wrapper for lint development - added `tools/argument-comment-lint/run-prebuilt-linter.sh` as the normal enforcement path, using the checked-in DotSlash package and bundled `cargo-dylint` - updated `just clippy` and `just argument-comment-lint` to use the prebuilt wrapper - split `.github/workflows/rust-ci.yml` so source-package checks live in a dedicated `argument_comment_lint_package` job, while the released lint runs in an `argument_comment_lint_prebuilt` matrix on Linux, macOS, and Windows - kept the pinned `nightly-2025-09-18` toolchain install in the prebuilt CI matrix, since the prebuilt package still relies on rustup-provided toolchain components - updated `tools/argument-comment-lint/run-prebuilt-linter.sh` to normalize host-qualified nightly library filenames, keep the `rustup` shim directory ahead of direct toolchain `cargo` binaries, and export `RUSTUP_HOME` when needed for Windows Dylint driver setup - updated `tools/argument-comment-lint/src/bin/argument-comment-lint.rs` so future published DotSlash artifacts apply the same nightly-filename normalization and `RUSTUP_HOME` inference internally - fixed the remaining Windows lint violations in `codex-rs/windows-sandbox-rs` by adding the required `/*param*/` comments at the reported callsites - documented the checked-in DotSlash file, wrapper split, archive layout, nightly prerequisite, and Windows `RUSTUP_HOME` requirement in `tools/argument-comment-lint/README.md`
Michael Bolin ·
2026-03-20 03:19:22 +00:00 -
Split features into codex-features crate (#15253)
- Split the feature system into a new `codex-features` crate. - Cut `codex-core` and workspace consumers over to the new config and warning APIs. Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com> Co-authored-by: Codex <noreply@openai.com>
Ahmed Ibrahim ·
2026-03-19 20:12:07 -07:00 -
Move auth code into login crate (#15150)
- Move the auth implementation and token data into codex-login. - Keep codex-core re-exporting that surface from codex-login for existing callers. --------- Co-authored-by: Codex <noreply@openai.com>
Ahmed Ibrahim ·
2026-03-19 18:58:17 -07:00 -
Move terminal module to terminal-detection crate (#15216)
- Move core/src/terminal.rs and its tests into a standalone terminal-detection workspace crate. - Update direct consumers to depend on codex-terminal-detection and import terminal APIs directly. --------- Co-authored-by: Codex <noreply@openai.com>
Ahmed Ibrahim ·
2026-03-19 14:08:04 -07:00 -
feat(tui): add /title terminal title configuration (#12334)
## Problem When multiple Codex sessions are open at once, terminal tabs and windows are hard to distinguish from each other. The existing status line only helps once the TUI is already focused, so it does not solve the "which tab is this?" problem. This PR adds a first-class `/title` command so the terminal window or tab title can carry a short, configurable summary of the current session. ## Screenshot <img width="849" height="320" alt="image" src="https://github.com/user-attachments/assets/8b112927-7890-45ed-bb1e-adf2f584663d" /> ## Mental model `/statusline` and `/title` are separate status surfaces with different constraints. The status line is an in-app footer that can be denser and more detailed. The terminal title is external terminal metadata, so it needs short, stable segments that still make multiple sessions easy to tell apart. The `/title` configuration is an ordered list of compact items. By default it renders `spinner,project`, so active sessions show lightweight progress first while idle sessions still stay easy to disambiguate. Each configured item is omitted when its value is not currently available rather than forcing a placeholder. ## Non-goals This does not merge `/title` into `/statusline`, and it does not add an arbitrary free-form title string. The feature is intentionally limited to a small set of structured items so the title stays short and reviewable. This also does not attempt to restore whatever title the terminal or shell had before Codex started. When Codex clears the title, it clears the title Codex last wrote. ## Tradeoffs A separate `/title` command adds some conceptual overlap with `/statusline`, but it keeps title-specific constraints explicit instead of forcing the status line model to cover two different surfaces. Title refresh can happen frequently, so the implementation now shares parsing and git-branch orchestration between the status line and title paths, and caches the derived project-root name by cwd. That keeps the hot path cheap without introducing background polling. ## Architecture The TUI gets a new `/title` slash command and a dedicated picker UI for selecting and ordering terminal-title items. The chosen ids are persisted in `tui.terminal_title`, with `spinner` and `project` as the default when the config is unset. `status` remains available as a separate text item, so configurations like `spinner,status` render compact progress like `⠋ Working`. `ChatWidget` now refreshes both status surfaces through a shared `refresh_status_surfaces()` path. That shared path parses configured items once, warns on invalid ids once, synchronizes shared cached state such as git-branch lookup, then renders the footer status line and terminal title from the same snapshot. Low-level OSC title writes live in `codex-rs/tui/src/terminal_title.rs`, which owns the terminal write path and last-mile sanitization before emitting OSC 0. ## Security Terminal-title text is treated as untrusted display content before Codex emits it. The write path strips control characters, removes invisible and bidi formatting characters that can make the title visually misleading, normalizes whitespace, and caps the emitted length. References used while implementing this: - [xterm control sequences](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html) - [WezTerm escape sequences](https://wezterm.org/escape-sequences.html) - [CWE-150: Improper Neutralization of Escape, Meta, or Control Sequences](https://cwe.mitre.org/data/definitions/150.html) - [CERT VU#999008 (Trojan Source)](https://kb.cert.org/vuls/id/999008) - [Trojan Source disclosure site](https://trojansource.codes/) - [Unicode Bidirectional Algorithm (UAX #9)](https://www.unicode.org/reports/tr9/) - [Unicode Security Considerations (UTR #36)](https://www.unicode.org/reports/tr36/) ## Observability Unknown configured title item ids are warned about once instead of repeatedly spamming the transcript. Live preview applies immediately while the `/title` picker is open, and cancel rolls the in-memory title selection back to the pre-picker value. If terminal title writes fail, the TUI emits debug logs around set and clear attempts. The rendered status label intentionally collapses richer internal states into compact title text such as `Starting...`, `Ready`, `Thinking...`, `Working...`, `Waiting...`, and `Undoing...` when `status` is configured. ## Tests Ran: - `just fmt` - `cargo test -p codex-tui` At the moment, the red Windows `rust-ci` failures are due to existing `codex-core` `apply_patch_cli` stack-overflow tests that also reproduce on `main`. The `/title`-specific `codex-tui` suite is green.
Yaroslav Volovich ·
2026-03-19 19:26:36 +00:00 -
feat: support product-scoped plugins. (#15041)
1. Added SessionSource::Custom(String) and --session-source. 2. Enforced plugin and skill products by session_source. 3. Applied the same filtering to curated background refresh.
xl-openai ·
2026-03-19 00:46:15 -07:00 -
Handle realtime conversation end in the TUI (#14903)
- close live realtime sessions on errors, ctrl-c, and active meter removal - centralize TUI realtime cleanup and avoid duplicate follow-up close info --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com>
Ahmed Ibrahim ·
2026-03-17 21:04:58 -07:00 -
Gate realtime audio interruption logic to v2 (#14984)
- thread the realtime version into conversation start and app-server notifications - keep playback-aware mic gating and playback interruption behavior on v2 only, leaving v1 on the legacy path
Ahmed Ibrahim ·
2026-03-17 15:24:37 -07:00 -
Revert tui code so it does not rely on in-process app server (#14899)
PR https://github.com/openai/codex/pull/14512 added an in-process app server and started to wire up the tui to use it. We were originally planning to modify the `tui` code in place, converting it to use the app server a bit at a time using a hybrid adapter. We've since decided to create an entirely new parallel `tui_app_server` implementation and do the conversion all at once but retain the existing `tui` while we work the bugs out of the new implementation. This PR undoes the changes to the `tui` made in the PR #14512 and restores the old initialization to its previous state. This allows us to modify the `tui_app_server` without the risk of regressing the old `tui` code. For example, we can start to remove support for all legacy core events, like the ones that PR https://github.com/openai/codex/pull/14892 needed to ignore. Testing: * I manually verified that the old `tui` starts and shuts down without a problem.
Eric Traut ·
2026-03-17 00:56:32 -06:00 -
[stack 4/4] Reduce realtime self-interruptions during playback (#14827)
## Stack Position 4/4. Top-of-stack sibling built on #14830. ## Base - #14830 ## Sibling - #14829 ## Scope - Gate low-level mic chunks while speaker playback is active, while still allowing spoken barge-in. --------- Co-authored-by: Codex <noreply@openai.com>
Ahmed Ibrahim ·
2026-03-17 05:19:51 +00:00 -
Apply argument comment lint across codex-rs (#14652)
## Why Once the repo-local lint exists, `codex-rs` needs to follow the checked-in convention and CI needs to keep it from drifting. This commit applies the fallback `/*param*/` style consistently across existing positional literal call sites without changing those APIs. The longer-term preference is still to avoid APIs that require comments by choosing clearer parameter types and call shapes. This PR is intentionally the mechanical follow-through for the places where the existing signatures stay in place. After rebasing onto newer `main`, the rollout also had to cover newly introduced `tui_app_server` call sites. That made it clear the first cut of the CI job was too expensive for the common path: it was spending almost as much time installing `cargo-dylint` and re-testing the lint crate as a representative test job spends running product tests. The CI update keeps the full workspace enforcement but trims that extra overhead from ordinary `codex-rs` PRs. ## What changed - keep a dedicated `argument_comment_lint` job in `rust-ci` - mechanically annotate remaining opaque positional literals across `codex-rs` with exact `/*param*/` comments, including the rebased `tui_app_server` call sites that now fall under the lint - keep the checked-in style aligned with the lint policy by using `/*param*/` and leaving string and char literals uncommented - cache `cargo-dylint`, `dylint-link`, and the relevant Cargo registry/git metadata in the lint job - split changed-path detection so the lint crate's own `cargo test` step runs only when `tools/argument-comment-lint/*` or `rust-ci.yml` changes - continue to run the repo wrapper over the `codex-rs` workspace, so product-code enforcement is unchanged Most of the code changes in this commit are intentionally mechanical comment rewrites or insertions driven by the lint itself. ## Verification - `./tools/argument-comment-lint/run.sh --workspace` - `cargo test -p codex-tui-app-server -p codex-tui` - parsed `.github/workflows/rust-ci.yml` locally with PyYAML --- * -> #14652 * #14651
Michael Bolin ·
2026-03-16 16:48:15 -07:00 -
Move TUI on top of app server (parallel code) (#14717)
This PR replicates the `tui` code directory and creates a temporary parallel `tui_app_server` directory. It also implements a new feature flag `tui_app_server` to select between the two tui implementations. Once the new app-server-based TUI is stabilized, we'll delete the old `tui` directory and feature flag.
Eric Traut ·
2026-03-16 10:49:19 -06:00 -
Start TUI on embedded app server (#14512)
This PR is part of the effort to move the TUI on top of the app server. In a previous PR, we introduced an in-process app server and moved `exec` on top of it. For the TUI, we want to do the migration in stages. The app server doesn't currently expose all of the functionality required by the TUI, so we're going to need to support a hybrid approach as we make the transition. This PR changes the TUI initialization to instantiate an in-process app server and access its `AuthManager` and `ThreadManager` rather than constructing its own copies. It also adds a placeholder TUI event handler that will eventually translate app server events into TUI events. App server notifications are accepted but ignored for now. It also adds proper shutdown of the app server when the TUI terminates.
Eric Traut ·
2026-03-13 12:04:41 -06:00 -
jif-oai ·
2026-03-07 03:48:36 -08:00 -
fix(tui) remove config check for trusted setting (#11874)
## Summary Simplify the trusted directory flow. This logic was originally designed several months ago, to determine if codex should start in read-only or workspace-write mode. However, that's no longer the purpose of directory trust - and therefore we should get rid of this logic. ## Testing - [x] Unit tests pass
Dylan Hurd ·
2026-03-05 22:29:34 -08:00 -
feat(core): persist trace_id for turns in RolloutItem::TurnContext (#13602)
This PR adds a durable trace linkage for each turn by storing the active trace ID on the rollout TurnContext record stored in session rollout files. Before this change, we propagated trace context at runtime but didn’t persist a stable per-turn trace key in rollout history. That made after-the-fact debugging harder (for example, mapping a historical turn to the corresponding trace in datadog). This sets us up for much easier debugging in the future. ### What changed - Added an optional `trace_id` to TurnContextItem (rollout schema). - Added a small OTEL helper to read the current span trace ID. - Captured `trace_id` when creating `TurnContext` and included it in `to_turn_context_item()`. - Updated tests and fixtures that construct TurnContextItem so older/no-trace cases still work. ### Why this approach TurnContext is already the canonical durable per-turn metadata in rollout. This keeps ownership clean: trace linkage lives with other persisted turn metadata.
Owen Lin ·
2026-03-05 13:26:48 -08:00 -
config: enforce enterprise feature requirements (#13388)
## Why Enterprises can already constrain approvals, sandboxing, and web search through `requirements.toml` and MDM, but feature flags were still only configurable as managed defaults. That meant an enterprise could suggest feature values, but it could not actually pin them. This change closes that gap and makes enterprise feature requirements behave like the other constrained settings. The effective feature set now stays consistent with enterprise requirements during config load, when config writes are validated, and when runtime code mutates feature flags later in the session. It also tightens the runtime API for managed features. `ManagedFeatures` now follows the same constraint-oriented shape as `Constrained<T>` instead of exposing panic-prone mutation helpers, and production code can no longer construct it through an unconstrained `From<Features>` path. The PR also hardens the `compact_resume_fork` integration coverage on Windows. After the feature-management changes, `compact_resume_after_second_compaction_preserves_history` was overflowing the libtest/Tokio thread stacks on Windows, so the test now uses an explicit larger-stack harness as a pragmatic mitigation. That may not be the ideal root-cause fix, and it merits a parallel investigation into whether part of the async future chain should be boxed to reduce stack pressure instead. ## What Changed Enterprises can now pin feature values in `requirements.toml` with the requirements-side `features` table: ```toml [features] personality = true unified_exec = false ``` Only canonical feature keys are allowed in the requirements `features` table; omitted keys remain unconstrained. - Added a requirements-side pinned feature map to `ConfigRequirementsToml`, threaded it through source-preserving requirements merge and normalization in `codex-config`, and made the TOML surface use `[features]` (while still accepting legacy `[feature_requirements]` for compatibility). - Exposed `featureRequirements` from `configRequirements/read`, regenerated the JSON/TypeScript schema artifacts, and updated the app-server README. - Wrapped the effective feature set in `ManagedFeatures`, backed by `ConstrainedWithSource<Features>`, and changed its API to mirror `Constrained<T>`: `can_set(...)`, `set(...) -> ConstraintResult<()>`, and result-returning `enable` / `disable` / `set_enabled` helpers. - Removed the legacy-usage and bulk-map passthroughs from `ManagedFeatures`; callers that need those behaviors now mutate a plain `Features` value and reapply it through `set(...)`, so the constrained wrapper remains the enforcement boundary. - Removed the production loophole for constructing unconstrained `ManagedFeatures`. Non-test code now creates it through the configured feature-loading path, and `impl From<Features> for ManagedFeatures` is restricted to `#[cfg(test)]`. - Rejected legacy feature aliases in enterprise feature requirements, and return a load error when a pinned combination cannot survive dependency normalization. - Validated config writes against enterprise feature requirements before persisting changes, including explicit conflicting writes and profile-specific feature states that normalize into invalid combinations. - Updated runtime and TUI feature-toggle paths to use the constrained setter API and to persist or apply the effective post-constraint value rather than the requested value. - Updated the `core_test_support` Bazel target to include the bundled core model-catalog fixtures in its runtime data, so helper code that resolves `core/models.json` through runfiles works in remote Bazel test environments. - Renamed the core config test coverage to emphasize that effective feature values are normalized at runtime, while conflicting persisted config writes are rejected. - Ran `compact_resume_after_second_compaction_preserves_history` inside an explicit 8 MiB test thread and Tokio runtime worker stack, following the existing larger-stack integration-test pattern, to keep the Windows `compact_resume_fork` test slice from aborting while a parallel investigation continues into whether some of the underlying async futures should be boxed. ## Verification - `cargo test -p codex-config` - `cargo test -p codex-core feature_requirements_ -- --nocapture` - `cargo test -p codex-core load_requirements_toml_produces_expected_constraints -- --nocapture` - `cargo test -p codex-core compact_resume_after_second_compaction_preserves_history -- --nocapture` - `cargo test -p codex-core compact_resume_fork -- --nocapture` - Re-ran the built `codex-core` `tests/all` binary with `RUST_MIN_STACK=262144` for `compact_resume_after_second_compaction_preserves_history` to confirm the explicit-stack harness fixes the deterministic low-stack repro. - `cargo test -p codex-core` - This still fails locally in unrelated integration areas that expect the `codex` / `test_stdio_server` binaries or hit existing `search_tool` wiremock mismatches. ## Docs `developers.openai.com/codex` should document the requirements-side `[features]` table for enterprise and MDM-managed configuration, including that it only accepts canonical feature keys and that conflicting config writes are rejected.
Michael Bolin ·
2026-03-04 04:40:22 +00:00 -
Record realtime close marker on replacement (#13058)
## Summary - record a realtime close developer message when a new realtime session replaces an active one - assert the replacement marker through the mocked responses request path --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Charles Cunningham <ccunningham@openai.com>
Ahmed Ibrahim ·
2026-03-01 13:54:12 -08:00