Commit Graph

5530 Commits

  • Log realtime session id (#18571)
    - Log the actual realtime session id when the session.updated event
    arrives.
  • Queue slash and shell prompts in the TUI (#18542)
    ## Why
    
    Users have asked to queue follow-up slash commands while a task is
    running, including in #14081, #14588, #14286, and #13779. The previous
    TUI behavior validated slash commands immediately, so commands that are
    only meaningful once the current turn is idle could not be queued
    consistently.
    
    The queue should preserve what the user typed and defer command parsing
    until the item is actually dispatched. This also gives `/fast`, `/review
    ...`, `/rename ...`, `/model`, `/permissions`, and similar slash
    workflows the same FIFO behavior as plain queued prompts.
    
    ## What Changed
    
    - Added a queued-input action enum so queued items can be dispatched as
    plain prompts, slash commands, or user shell commands.
    - Changed `Tab` queueing to accept slash-led prompts without validating
    them up front, then parse and dispatch them when dequeued.
    - Added `!` shell-command queueing for `Tab` while a task is running,
    while preserving existing `Enter` behavior for immediate shell
    execution.
    - Moved queued slash dispatch through shared slash-command parsing so
    inline commands, unavailable commands, unknown commands, and local
    config commands report at dequeue time.
    - Continued queue draining after local-only actions and after slash menu
    cancellation or selection when no task is running.
    - Preserved slash-popup completion behavior so `/mo<Tab>` completes to
    `/model ` instead of queueing the prefix.
    - Updated pending-input preview snapshots to show queued follow-up
    inputs.
    
    ## Verification
    
    I did a bunch of manual validation (and found and fixed a few bugs along
    the way).
  • Support codex app on macOS (Intel) and Windows (#18500)
    ## Summary
    
    `codex app` should be a platform-aware entry point for opening Codex
    Desktop or helping users install it. Before this change, the command
    only existed on macOS and its default installer URL always pointed at
    the Apple Silicon DMG, which sent Intel Mac users to the wrong build.
    
    This updates the macOS path to choose the Apple Silicon or Intel DMG
    based on the detected processor, while keeping `--download-url` as an
    advanced override. It also enables `codex app` on Windows, where the CLI
    opens an installed Codex Desktop app when available and otherwise opens
    the Windows installer URL.
    
    ---------
    
    Co-authored-by: Felipe Coury <felipe.coury@openai.com>
  • feat(tui): show context used in plan implementation prompt (#18573)
    # Summary
    
    When a user finishes planning, the TUI asks whether to implement in the
    current conversation or start fresh with the approved plan. The
    clear-context choice is easier to evaluate when the prompt shows how
    much context has already been used, because the user can see when
    carrying the full prior conversation is likely to be less useful than
    preserving only the plan.
    
    <img width="1612" height="1312" alt="image"
    src="https://github.com/user-attachments/assets/694bcf87-8be5-4e88-a412-e562af62d5f7"
    />
        
    This PR adds that context signal directly to the clear-context option
    while keeping the copy compact enough for the Plan-mode selection popup.
    
    # What Changed
    
    - Compute an optional context-usage label when opening the plan
    implementation prompt.
    - Show the label only on `Yes, clear context and implement`, where it
    informs the cleanup decision.
    - Prefer a percentage-used label when context-window information is
    available, with a compact token-used fallback when only token totals are
    known.
    - Preserve the original option description when usage is unknown or
    effectively zero.
    - Add rustdoc comments around the prompt-copy boundary so future changes
    keep the context label formatting and selection rendering
    responsibilities clear.
    
    # Testing
    
    - `cargo test -p codex-tui plan_implementation`
    
    # Notes
    
    The footer continues to show context remaining as ambient status. The
    implementation prompt intentionally shows context used because the user
    is choosing whether to clean up the current thread before
    implementation.
  • [5/6] Wire executor-backed MCP stdio (#18212)
    ## Summary
    - Add the executor-backed RMCP stdio transport.
    - Wire MCP stdio placement through the executor environment config.
    - Cover local and executor-backed stdio paths with the existing MCP test
    helpers.
    
    ## Stack
    ```text
    o  #18027 [6/6] Fail exec client operations after disconnect
    │
    @  #18212 [5/6] Wire executor-backed MCP stdio
    │
    o  #18087 [4/6] Abstract MCP stdio server launching
    │
    o  #18020 [3/6] Add pushed exec process events
    │
    o  #18086 [2/6] Support piped stdin in exec process API
    │
    o  #18085 [1/6] Add MCP server environment config
    │
    o  main
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Fix plugin cache panic when cwd is unavailable (#18499)
    ## Summary
    
    Fixes #16637. (I hit this bug after 11h of work on a long-running task.)
    
    Plugin cache initialization could panic when an already-absolute cache
    path was normalized through `AbsolutePathBuf::from_absolute_path`,
    because that path still consulted `current_dir()`.
    
    This changes absolute-path normalization so already-absolute paths do
    not depend on cwd, and makes plugin cache root construction available as
    a fallible path through `PluginStore::try_new()`. Plugin cache subpaths
    now use `AbsolutePathBuf::join()` instead of re-absolutizing derived
    absolute paths.
  • Update image outputs to default to high detail (#18386)
    Do not assume the default `detail`.
  • Revert "[codex] drain mailbox only at request boundaries" (#18325)
    ## Summary
    - Reverts PR #17749 so queued inter-agent mail can again preempt after
    reasoning/commentary output item boundaries.
    - Applies the revert to the current `codex/turn.rs` module layout and
    restores the prior pending-input test expectations/snapshots.
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-core --test all pending_input`
    - `cargo test -p codex-core` failed in unrelated
    `tools::js_repl::tests::js_repl_imported_local_files_can_access_repl_globals`:
    dotslash download hit `mktemp: mkdtemp failed ... Operation not
    permitted` in the sandbox temp dir.
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add max context window model metadata (#18382)
    Adds max_context_window to model metadata and routes core context-window
    reads through resolved model info. Config model_context_window overrides
    are clamped to max_context_window when present; without an override, the
    model context_window is used.
  • [codex] Add marketplace remove command and shared logic (#17752)
    ## Summary
    
    Move the marketplace remove implementation into shared core logic so
    both the CLI command and follow-up app-server RPC can reuse the same
    behavior.
    
    This change:
    - adds a shared `codex_core::plugins::remove_marketplace(...)` flow
    - moves validation, config removal, and installed-root deletion out of
    the CLI
    - keeps the CLI as a thin wrapper over the shared implementation
    - adds focused core coverage for the shared remove path
    
    ## Validation
    
    - `just fmt`
    - focused local coverage for the shared remove path
    - heavier follow-up validation deferred to stacked PR CI
  • [codex] Add owner nudge app-server API (#18220)
    ## Summary
    
    Second PR in the split from #17956. Stacked on #18227.
    
    - adds app-server v2 protocol/schema support for
    `account/sendAddCreditsNudgeEmail`
    - adds the backend-client `send_add_credits_nudge_email` request and
    request body mapping
    - handles the app-server request with auth checks, backend call, and
    cooldown mapping
    - adds the disabled `workspace_owner_usage_nudge` feature flag and
    focused app-server/backend tests
    
    ## Validation
    
    - `cargo test -p codex-backend-client`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server rate_limits`
    - `cargo test -p codex-tui workspace_`
    - `cargo test -p codex-tui status_`
    - `just fmt`
    - `just fix -p codex-backend-client`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-app-server`
    - `just fix -p codex-tui`
  • [codex] Describe uninstalled cross-repo plugin reads (#18449)
    ## Summary
    - Populate `PluginDetail.description` in core for uninstalled cross-repo
    plugins when detailed fields are unavailable until install.
    - Include the source Git URL plus optional path/ref/sha details in that
    fallback description.
    - Keep `details_unavailable_reason` as the structured signal while
    app-server forwards the description normally.
    - Add plugin-read coverage proving the response does not clone the
    remote source just to show the message.
    
    ## Why
    Uninstalled cross-repo plugins intentionally return sparse detail data
    so listing/reading does not clone the plugin source. Without a
    description, Desktop and TUI detail pages look like an ordinary empty
    plugin. This gives users a concrete explanation and source pointer while
    keeping the existing structured reason available for callers.
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-core
    read_plugin_for_config_uninstalled_git_source_requires_install_without_cloning`
    - `cargo test -p codex-app-server plugin_read --test all`
    - `just fix -p codex-core`
    - `just fix -p codex-app-server`
    
    Note: `cargo test -p codex-app-server` was also attempted before the
    latest refactor and failed broadly in unrelated v2
    thread/realtime/review/skills suites; the new plugin-read test passed in
    that run as well.
  • feat: Budget skill metadata and surface trimming as a warning (#18298)
    Cap the model-visible skills section to a small share of the context
    window, with a fallback character budget, and keep only as many implicit
    skills as fit within that budget.
    
    Emit a non-fatal warning when enabled skills are omitted, and add a new
    app-server warning notification
    
    Record thread-start skill metrics for total enabled skills, kept skills,
    and whether truncation happened
    
    ---------
    
    Co-authored-by: Matthew Zeng <mzeng@openai.com>
    Co-authored-by: Codex <noreply@openai.com>
  • Feat/auto review dev message marker (#18369)
    supporting guardian's rebrand to auto-review!
  • [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>
  • fix: trust-gate project hooks and exec policies (#14718)
    ## Summary
    - trust-gate project `.codex` layers consistently, including repos that
    have `.codex/hooks.json` or `.codex/execpolicy/*.rules` but no
    `.codex/config.toml`
    - keep disabled project layers in the config stack so nested trusted
    project layers still resolve correctly, while preventing hooks and exec
    policies from loading until the project is trusted
    - update app-server/TUI onboarding copy to make the trust boundary
    explicit and add regressions for loader, hooks, exec-policy, and
    onboarding coverage
    
    ## Security
    Before this change, an untrusted repo could auto-load project hooks or
    exec policies from `.codex/` as long as `config.toml` was absent. This
    makes trust the single gate for project-local config, hooks, and exec
    policies.
    
    ## Stack
    - Parent of #15936
    
    ## Test
    - cargo test -p codex-core without_config_toml
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • /plugins: Add inline enablement toggles (#18395)
    This PR adds inline enable/disable controls to the new /plugins browse
    menu. Installed plugins can now be toggled directly from the list with
    keyboard interaction, and the associated config-write plumbing is
    included so the UI and persisted plugin state stay in sync. This also
    includes the queued-write handling needed to avoid stale toggle
    completions overwriting newer intent.
    
    - Add toggleable plugin rows for installed plugins in /plugins
    - Support Space to enable or disable without leaving the list
    - Persist plugin enablement through the existing app/config write path
    - Preserve the current selection while the list refreshes after a toggle
    - Add tests and snapshot updates for toggling behavior
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: Add remote plugin fields to plugin API (#17277)
    ## Summary
    Update the plugin API for the new remote plugin model.
    
    The mental model is no longer “keep local plugin state in sync with
    remote.” Instead, local and remote plugins are becoming separate
    sources. Remote catalog entries can be shown directly from the remote
    API before installation; after installation they are still downloaded
    into the local cache for execution, but remote installed state will come
    from the API and be held in memory rather than being read from config.
    
    • ## API changes
    - Remove `forceRemoteSync` from `plugin/list`, `plugin/install`, and
    `plugin/uninstall`.
      - Remove `remoteSyncError` from `plugin/list`.
      - Add remote-capable metadata to `plugin/list` / `plugin/read`:
        - nullable `marketplaces[].path`
        - `source: { type: "remote", downloadUrl }`
        - URL asset fields alongside local path fields:
      `composerIconUrl`, `logoUrl`, `screenshotUrls`
      - Make `plugin/read` and `plugin/install` source-compatible:
        - `marketplacePath?: AbsolutePathBuf | null`
        - `remoteMarketplaceName?: string | null`
        - exactly one source is required at runtime
  • Update image resizing to fit 2048 square bounds (#18384)
    We don't have to downsize to 768 height.
  • bazel: use native rust test sharding (#18082)
    ## Why
    
    The large Rust test suites are slow and include some of our flakiest
    tests, so we want to run them with Bazel native sharding while keeping
    shard membership stable between runs.
    
    This is the simpler follow-up to the explicit-label experiment in
    #17998. Since #18397 upgraded Codex to `rules_rs` `0.0.58`, which
    includes the stable test-name hashing support from
    hermeticbuild/rules_rust#14, this PR only needs to wire Codex's Bazel
    macros into that support.
    
    Using native sharding preserves BuildBuddy's sharded-test UI and Bazel's
    per-shard test action caching. Using stable name hashing avoids
    reshuffling every test when one test is added or removed.
    
    ## What Changed
    
    `codex_rust_crate` now accepts `test_shard_counts` and applies the right
    Bazel/rules_rust attributes to generated unit and integration test
    rules. Matched tests are also marked `flaky = True`, giving them Bazel's
    default three attempts.
    
    This PR shards these labels 8 ways:
    
    ```text
    //codex-rs/core:core-all-test
    //codex-rs/core:core-unit-tests
    //codex-rs/app-server:app-server-all-test
    //codex-rs/app-server:app-server-unit-tests
    //codex-rs/tui:tui-unit-tests
    ```
    
    ## Verification
    
    `bazel query --output=build` over the selected public labels and their
    inner unit-test binaries confirmed the expected `shard_count = 8`,
    `flaky = True`, and `experimental_enable_sharding = True` attributes.
    
    Also verified that we see the shards as expected in BuildBuddy so they
    can be analyzed independently.
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Upgrade rules_rs and llvm to latest BCR versions (#18397)
    ## Why
    This branch brings the Bazel module pins for `rules_rs` and `llvm` up to
    the latest BCR releases and aligns the root direct dependencies with the
    versions the module graph already resolves to.
    
    That gives us a few concrete wins:
    - picks up newer upstream fixes in the `rules_rs` / `rules_rust` stack,
    including work around repo-rule nondeterminism and default Cargo binary
    target generation
    - picks up test sharding support from the newer `rules_rust` stack
    ([hermeticbuild/rules_rust#13](https://github.com/hermeticbuild/rules_rust/pull/13))
    - picks up newer built-in knowledge for common system crates like
    `gio-sys`, `glib-sys`, `gobject-sys`, `libgit2-sys`, and `libssh2-sys`,
    which gives us a future path to reduce custom build-script handling
    - reduces local patch maintenance by dropping fixes that are now
    upstream and rebasing the remaining Windows patch stack onto a newer
    upstream base
    - removes the direct-dependency warnings from `bazel-lock-check` by
    making the root pins match the resolved graph
    
    ## What Changed
    - bump `rules_rs` from `0.0.43` to `0.0.58`
    - bump `llvm` from `0.6.8` to `0.7.1`
    - bump `bazel_skylib` from `1.8.2` to `1.9.0` so the root direct dep
    matches the resolved graph
    - regenerate `MODULE.bazel.lock` for the updated module graph
    - refresh the remaining Windows-specific patch stack against the newer
    upstream sources:
      - `patches/rules_rs_windows_gnullvm_exec.patch`
      - `patches/rules_rs_windows_exec_linker.patch`
      - `patches/rules_rust_windows_exec_std.patch`
      - `patches/rules_rust_windows_msvc_direct_link_args.patch`
    - remove patches that are no longer needed because the underlying fixes
    are upstream now:
      - `patches/rules_rs_delete_git_worktree_pointer.patch`
      - `patches/rules_rust_repository_set_exec_constraints.patch`
    
    ## Validation
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix: fix fs sandbox helper for apply_patch (#18296)
    ## Summary
    
    - pass split filesystem sandbox policy/cwd through apply_patch contexts,
    while omitting legacy-equivalent policies to keep payloads small
    - keep the fs helper compatible with legacy Landlock by avoiding helper
    read-root permission expansion in that mode and disabling helper network
    access
    
    ## Root Cause
    
    `d626dc38950fb40a1a5ad0a8ffab2485e3348c53` routed exec-server filesystem
    operations through a sandboxed helper. That path forwarded legacy
    Landlock into a helper policy shape that could require direct
    split-policy enforcement. Sandboxed `apply_patch` hit that edge through
    the filesystem abstraction.
    
    The same 0.121 edit-regression path is consistent with #18354: normal
    writes route through the `apply_patch` filesystem helper, fail under
    sandbox, and then surface the generic retry-without-sandbox prompt.
    
    Fixes #18069
    Fixes #18354
    
    ## Validation
    
    - `cd codex-rs && just fmt`
    - earlier branch validation before merging current `origin/main` and
    dropping the now-separate PATH fix:
      - `cd codex-rs && cargo test -p codex-exec-server`
    - `cd codex-rs && cargo test -p codex-core file_system_sandbox_context`
      - `cd codex-rs && just fix -p codex-exec-server`
      - `cd codex-rs && just fix -p codex-core`
      - `git diff --check`
      - `cd codex-rs && cargo clean`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • refactor: use cloneable async channels for shared receivers (#18398)
    This is the first mechanical cleanup in a stack whose higher-level goal
    is to enable Clippy coverage for async guards held across `.await`
    points.
    
    The follow-up commits enable Clippy's
    [`await_holding_lock`](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock)
    lint and the configurable
    [`await_holding_invalid_type`](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type)
    lint for Tokio guard types. This PR handles the cases where the
    underlying issue is not protected shared mutable state, but a
    `tokio::sync::mpsc::UnboundedReceiver` wrapped in `Arc<Mutex<_>>` so
    cloned owners can call `recv().await`.
    
    Using a mutex for that shape forces the receiver lock guard to live
    across `.await`. Switching these paths to `async-channel` gives us
    cloneable `Receiver`s, so each owner can hold a receiver handle directly
    and await messages without an async mutex guard.
    
    ## What changed
    
    - In `codex-rs/code-mode`, replace the turn-message
    `mpsc::UnboundedSender`/`UnboundedReceiver` plus `Arc<Mutex<Receiver>>`
    with `async_channel::Sender`/`Receiver`.
    - In `codex-rs/codex-api`, replace the realtime websocket event receiver
    with an `async_channel::Receiver`, allowing `RealtimeWebsocketEvents`
    clones to receive without locking.
    - Add `async-channel` as a dependency for `codex-code-mode` and
    `codex-api`, and update `Cargo.lock`.
    
    ## Verification
    
    - The split stack was verified at the final lint-enabling head with
    `just clippy`.
  • [codex] Add cross-repo plugin sources to marketplace manifests (#18017)
    ## Summary
    - add first-class marketplace support for git-backed plugin sources
    - keep the newer marketplace parsing behavior from `main`, including
    alternate manifest locations and string local sources
    - materialize remote plugin sources during install, detail reads, and
    non-curated cache refresh
    - expose git plugin source metadata through the app-server protocol
    
    ## Details
    This teaches the marketplace parser to accept all of the following:
    - local string sources such as `"source": "./plugins/foo"`
    - local object sources such as
    `{"source":"local","path":"./plugins/foo"}`
    - remote repo-root sources such as
    `{"source":"url","url":"https://github.com/org/repo.git"}`
    - remote subdir sources such as
    `{"source":"git-subdir","url":"owner/repo","path":"plugins/foo","ref":"main","sha":"..."}`
    
    It also preserves the newer tolerant behavior from `main`: invalid or
    unsupported plugin entries are skipped instead of breaking the whole
    marketplace.
    
    ## Validation
    - `cargo test -p codex-core plugins::marketplace::tests`
    - `just fix -p codex-core`
    - `just fmt`
    
    ## Notes
    - A full `cargo test -p codex-core` run still hit unrelated existing
    failures in agent and multi-agent tests during this session; the
    marketplace-focused suite passed after the rebase resolution.
  • refactor: narrow async lock guard lifetimes (#18211)
    Follow-up to https://github.com/openai/codex/pull/18178, where we called
    out enabling the await-holding lint as a follow-up.
    
    The long-term goal is to enable Clippy coverage for async guards held
    across awaits. This PR is intentionally only the first, low-risk cleanup
    pass: it narrows obvious lock guard lifetimes and leaves
    `codex-rs/Cargo.toml` unchanged so the lint is not enabled until the
    remaining cases are fixed or explicitly justified. It intentionally
    leaves the active-turn/turn-state locking pattern alone because those
    checks and mutations need to stay atomic.
    
    ## Common fixes used here
    
    These are the main patterns reviewers should expect in this PR, and they
    are also the patterns to reach for when fixing future `await_holding_*`
    findings:
    
    - **Scope the guard to the synchronous work.** If the code only needs
    data from a locked value, move the lock into a small block, clone or
    compute the needed values, and do the later `.await` after the block.
    - **Use direct one-line mutations when there is no later await.** Cases
    like `map.lock().await.remove(&id)` are acceptable when the guard is
    only needed for that single mutation and the statement ends before any
    async work.
    - **Drain or clone work out of the lock before notifying or awaiting.**
    For example, the JS REPL drains pending exec senders into a local vector
    and the websocket writer clones buffered envelopes before it serializes
    or sends them.
    - **Use a `Semaphore` only when serialization is intentional across
    async work.** The test serialization guards intentionally span awaited
    setup or execution, so using a semaphore communicates "one at a time"
    without holding a mutex guard.
    - **Remove the mutex when there is only one owner.** The PTY stdin
    writer task owns `stdin` directly; the old `Arc<Mutex<_>>` did not
    protect shared access because nothing else had access to the writer.
    - **Do not split locks that protect an atomic invariant.** This PR
    deliberately leaves active-turn/turn-state paths alone because those
    checks and mutations need to stay atomic. Those cases should be fixed
    separately with a design change or documented with `#[expect]`.
    
    ## What changed
    
    - Narrow scoped async mutex guards in app-server, JS REPL, network
    approval, remote-control websocket, and the RMCP test server.
    - Replace test-only async mutex serialization guards with semaphores
    where the guard intentionally lives across async work.
    - Let the PTY pipe writer task own stdin directly instead of wrapping it
    in an async mutex.
    
    ## Verification
    
    - `just fix -p codex-core -p codex-app-server -p codex-rmcp-client -p
    codex-shell-escalation -p codex-utils-pty -p codex-utils-readiness`
    - `just clippy -p codex-core`
    - `cargo test -p codex-core -p codex-app-server -p codex-rmcp-client -p
    codex-shell-escalation -p codex-utils-pty -p codex-utils-readiness` was
    run; the app-server suite passed, and `codex-core` failed in the local
    sandbox on six otel approval tests plus
    `suite::user_shell_cmd::user_shell_command_does_not_set_network_sandbox_env_var`,
    which appear to depend on local command approval/default rules and
    `CODEX_SANDBOX_NETWORK_DISABLED=1` in this environment.
  • Remove the tier constraint from connectors directory requests (#18381)
    We should allow all apps regardless of tier.
  • exec-server: preserve fs helper runtime env (#18380)
    ## Summary
    - preserve a small fs-helper runtime env allowlist (`PATH`, temp vars)
    instead of launching the sandboxed helper with an empty env
    - add unit coverage for the allowlist and transformed sandbox request
    env
    - add a Linux smoke test that starts the test exec-server with a fake
    `bwrap` on `PATH`, runs a sandboxed fs write through the remote fs
    helper path, and asserts that bwrap path was exercised
    
    ## Validation
    - `cd /tmp/codex-worktrees/fs-helper-env-defaults/codex-rs && export
    PATH=$HOME/code/openai/project/dotslash-gen/bin:$HOME/.local/bin:$PATH
    && bazel test --bes_backend= --bes_results_url=
    //codex-rs/exec-server:exec-server-file_system-test
    --test_filter=sandboxed_file_system_helper_finds_bwrap_on_preserved_path`
    - `cd /tmp/codex-worktrees/fs-helper-env-defaults/codex-rs && export
    PATH=$HOME/code/openai/project/dotslash-gen/bin:$HOME/.local/bin:$PATH
    && bazel test --bes_backend= --bes_results_url=
    //codex-rs/exec-server:exec-server-unit-tests
    --test_filter="helper_env|sandbox_exec_request_carries_helper_env"`
    - earlier on this branch before the smoke-test harness adjustment: `cd
    /tmp/codex-worktrees/fs-helper-env-defaults/codex-rs && export
    PATH=$HOME/code/openai/project/dotslash-gen/bin:$HOME/.local/bin:$PATH
    && bazel test --bes_backend= --bes_results_url=
    //codex-rs/exec-server:all`
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Propagate rate limit reached type (#18227)
    ## Summary
    
    First PR in the split from #17956.
    
    - adds the core/app-server `RateLimitReachedType` shape
    - maps backend `rate_limit_reached_type` into Codex rate-limit snapshots
    - carries the field through app-server notifications/responses and
    generated schemas
    - updates existing constructors/tests for the new optional field
    
    ## Validation
    
    - `cargo test -p codex-backend-client`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server rate_limits`
    - `cargo test -p codex-tui workspace_`
    - `cargo test -p codex-tui status_`
    - `just fmt`
    - `just fix -p codex-backend-client`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-app-server`
    - `just fix -p codex-tui`
  • /plugins: Add v2 tabbed marketplace menu (#18222)
    This PR moves `/plugins` onto the shared tabbed selection-list
    infrastructure and introduces the new v2 menu. The menu now groups
    plugins into All Plugins, Installed, OpenAI Curated, and per-marketplace
    tabs.
    
    - Rebuild /plugins on top of the shared tabbed selection list
    - Add All Plugins, Installed, OpenAI Curated, and per-marketplace tabs
    - Preserve active tab and selected-row behavior across popup refreshes
    - Add duplicate marketplace tab-label disambiguation
    - Update browse-mode popup tests and snapshots
    
    Co-authored-by: Codex <noreply@openai.com>
  • perf(tui): defer startup skills refresh (#18370)
    # Summary
    
    This removes startup `skills/list` from the critical path to first
    input. In release measurements, median startup-to-input time improved
    from `307.5 ms` to `191.0 ms` across 30 measured runs with 5 warmups.
    
    # Background
    
    Startup currently waits for a forced `skills/list` app-server request
    before scheduling the first usable TUI frame. That makes skill metadata
    freshness part of the process-launch-to-input path, even though the
    prompt can safely accept normal input before skill metadata has finished
    loading.
    
    I measured startup from process launch until the TUI reports that the
    user can type. The measurement harness watched the startup measurement
    record, killed Codex after a successful sample, and enforced a timeout
    so repeated runs would not leave TUI processes behind. The debug runs
    had enough outliers that I used median as the primary signal and ran a
    baseline self-compare to understand the noise floor.
    
    # Why skills/list
    
    The `skills/list` cut was the best practical optimization because it
    improved startup without changing the important readiness contract: when
    the prompt is shown, it is still backed by an active session. Only
    enrichment data arrives later.
    
    | Candidate | Result | Decision |
    | --- | --- | --- |
    | Defer startup `skills/list` | Debug median improved from `524.0 ms` to
    `348.0 ms`; release median improved from `307.5 ms` to `191.0 ms`. |
    Keep |
    | Defer fresh `thread/start` | Debug median improved from `494.0 ms` to
    `256.0 ms`, but the prompt could appear before an active thread was
    attached. | Reject as too risky for this PR |
    | Avoid forced skills config reload | Debug median moved from `509.0 ms`
    to `512.0 ms`. | Reject as neutral |
    | Skip fresh history metadata | Debug median moved from `496.5 ms` to
    `531.5 ms`. | Reject as regression/noise |
    | Defer app-server startup | Not implemented because it would only
    permit a loading frame unless the TUI gained a deliberate pre-server
    state. | Out of scope |
    
    # Implementation
    
    `App::refresh_startup_skills` now clones the app-server request handle,
    spawns a background task, and issues the same forced `skills/list`
    request after the first frame is scheduled. When the request completes,
    the task sends `AppEvent::SkillsListLoaded` back through the normal app
    event queue.
    
    The existing skills response handling still converts the app-server
    response, updates the chat widget, and emits invalid `SKILL.md`
    warnings. Explicit user-initiated skills refreshes still use the
    existing synchronous app command path, so callers that intentionally
    requested fresh skill state do not race ahead of their own refresh.
    
    # Tradeoffs
    
    The main tradeoff is a narrow theoretical race at startup: skill mention
    completion depends on a background `skills/list` response, so it could
    briefly show stale or empty metadata if opened before that response
    arrives. In manual testing, pressing `$` as soon as possible after
    launch still showed populated skill metadata, so this risk appears
    minimal in normal use. Plain input remains available immediately, and
    the UI updates through the existing skills response path once the
    refresh completes.
    
    This PR does not change how skills are discovered, cached,
    force-reloaded, displayed, enabled, or warned about. It only changes
    when the startup refresh is allowed to complete relative to the first
    usable TUI frame.
    
    # Verification
    
    - `cargo test -p codex-tui`
  • [4/6] Abstract MCP stdio server launching (#18087)
    ## Summary
    - Move local MCP stdio process startup behind a launcher trait.
    - Preserve existing local stdio behavior while making transport creation
    explicit.
    
    ## Stack
    ```text
    o  #18027 [6/6] Fail exec client operations after disconnect
    │
    o  #18212 [5/6] Wire executor-backed MCP stdio
    │
    @  #18087 [4/6] Abstract MCP stdio server launching
    │
    o  #18020 [3/6] Add pushed exec process events
    │
    o  #18086 [2/6] Support piped stdin in exec process API
    │
    o  #18085 [1/6] Add MCP server environment config
    │
    o  main
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Attribute automated PR Babysitter review replies (#18379)
    ## Summary
    PR Babysitter can reply directly to GitHub code review comments when
    feedback is non-actionable, already addressed, or not valid. Those
    replies should be visibly attributed so reviewers do not mistake an
    automated Codex response for a message from the human operator.
    
    This updates the skill instructions to require GitHub code review
    replies from the babysitter to start with `[codex]`.
    
    ## Changes
    - Adds the `[codex]` prefix requirement to the core PR Babysitter
    workflow.
    - Repeats the requirement in the review comment handling guidance where
    agents decide whether to reply to a review thread.
  • Show default reasoning in /status (#18373)
    - Shows the model catalog default reasoning effort when no reasoning
    override is configured.
    - Adds /status coverage for the empty-config fallback.
  • Update models.json (#12640)
    Automated update of models.json.
    
    Co-authored-by: aibrahim-oai <219906144+aibrahim-oai@users.noreply.github.com>
    Co-authored-by: Ahmed Ibrahim <aibrahim@openai.com>
  • [3/6] Add pushed exec process events (#18020)
    ## Summary
    - Add a pushed `ExecProcessEvent` stream alongside retained
    `process/read` output.
    - Publish local and remote output, exit, close, and failure events.
    - Cover the event stream with shared local/remote exec process tests.
    
    ## Testing
    - `cargo check -p codex-exec-server`
    - `cargo check -p codex-rmcp-client`
    - Not run: `cargo test` per repo instruction; CI will cover.
    
    ## Stack
    ```text
    o  #18027 [6/6] Fail exec client operations after disconnect
    │
    o  #18212 [5/6] Wire executor-backed MCP stdio
    │
    o  #18087 [4/6] Abstract MCP stdio server launching
    │
    @  #18020 [3/6] Add pushed exec process events
    │
    o  #18086 [2/6] Support piped stdin in exec process API
    │
    o  #18085 [1/6] Add MCP server environment config
    │
    o  main
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • 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.
  • ci: scope Bazel repository cache by job (#18366)
    ## Why
    
    The Bazel workflow has multiple jobs that run concurrently for the same
    target triple. In particular, the Windows `test`, `clippy`, and
    `verify-release-build` jobs could all miss and then attempt to save the
    same Bazel repository cache key:
    
    ```text
    bazel-cache-${target}-${lockhash}
    ```
    
    Because `actions/cache` entries are immutable, only one job can reserve
    that key. The others can report failures such as:
    
    ```text
    Failed to save: Unable to reserve cache with key bazel-cache-x86_64-pc-windows-gnullvm-..., another job may be creating this cache.
    ```
    
    Adding only the workflow name would not separate these jobs because they
    all run inside the same `Bazel` workflow. The key needs a job-level
    namespace as well.
    
    ## What Changed
    
    - Added a required `cache-scope` input to
    `.github/actions/prepare-bazel-ci/action.yml`.
    - Moved Bazel repository cache key construction into the shared action
    and exposed the computed key as `repository-cache-key`.
    - Exposed the exact restore result as `repository-cache-hit` so save
    steps can skip exact cache hits.
    - Updated `.github/workflows/bazel.yml` to pass `cache-scope: bazel-${{
    github.job }}` for the `test`, `clippy`, and `verify-release-build`
    jobs.
    - The scoped restore key is now the only fallback. This avoids carrying
    a temporary restore path for the old unscoped cache namespace.
    
    ## Verification
    
    - Parsed `.github/actions/prepare-bazel-ci/action.yml` and
    `.github/workflows/bazel.yml` with Ruby's YAML parser.
    - `actionlint` is not installed in this workspace, so I could not run a
    GitHub Actions semantic lint locally.
  • Add core CODEOWNERS (#18362)
    Adds @openai/codex-core-agent-team as the owner for codex-rs/core/ and
    protects .github/CODEOWNERS with the same owner.
  • ci: make Windows Bazel clippy catch core test imports (#18350)
    ## Why
    
    Unused imports in `core/tests/suite/unified_exec.rs` in the Windows
    build were not caught by Bazel CI on
    https://github.com/openai/codex/pull/18096. I spot-checked
    https://github.com/openai/codex/actions/workflows/rust-ci-full.yml?query=branch%3Amain
    and noticed that builds were consistently red. This revealed that our
    Cargo builds _were_ properly catching these issues, identifying a
    Windows-specific coverage hole in the Bazel clippy job.
    
    The Windows Bazel clippy job uses `--skip_incompatible_explicit_targets`
    so it can lint a broad target set without failing immediately on targets
    that are genuinely incompatible with Windows. However, with the default
    Windows host platform, `rust_test` targets such as
    `//codex-rs/core:core-all-test` could be skipped before the clippy
    aspect reached their integration-test modules. As a result, the imports
    in `core/tests/suite/unified_exec.rs` were not being linted by the
    Windows Bazel clippy job at all.
    
    The clippy diagnostic that Windows Bazel should have surfaced was:
    
    ```text
    error: unused import: `codex_config::Constrained`
     --> core\tests\suite\unified_exec.rs:8:5
      |
    8 | use codex_config::Constrained;
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^
      |
      = note: `-D unused-imports` implied by `-D warnings`
      = help: to override `-D warnings` add `#[allow(unused_imports)]`
    
    error: unused import: `codex_protocol::permissions::FileSystemAccessMode`
      --> core\tests\suite\unified_exec.rs:11:5
       |
    11 | use codex_protocol::permissions::FileSystemAccessMode;
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    error: unused import: `codex_protocol::permissions::FileSystemPath`
      --> core\tests\suite\unified_exec.rs:12:5
       |
    12 | use codex_protocol::permissions::FileSystemPath;
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    error: unused import: `codex_protocol::permissions::FileSystemSandboxEntry`
      --> core\tests\suite\unified_exec.rs:13:5
       |
    13 | use codex_protocol::permissions::FileSystemSandboxEntry;
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    error: unused import: `codex_protocol::permissions::FileSystemSandboxPolicy`
      --> core\tests\suite\unified_exec.rs:14:5
       |
    14 | use codex_protocol::permissions::FileSystemSandboxPolicy;
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ```
    
    ## What changed
    
    - Run the Windows Bazel clippy job with the MSVC host platform via
    `--windows-msvc-host-platform`, matching the Windows Bazel test job.
    This keeps `--skip_incompatible_explicit_targets` while ensuring Windows
    `rust_test` targets such as `//codex-rs/core:core-all-test` are still
    linted.
    - Remove the unused imports from `core/tests/suite/unified_exec.rs`.
    - Add `--print-failed-action-summary` to
    `.github/scripts/run-bazel-ci.sh` so Bazel action failures can be
    summarized after the build exits.
    
    ## Failure reporting
    
    Once the coverage issue was fixed, an intentionally reintroduced unused
    import made the Windows Bazel clippy job fail as expected. That exposed
    a separate usability problem: because the job keeps `--keep_going`, the
    top-level Bazel output could still end with:
    
    ```text
    ERROR: Build did NOT complete successfully
    FAILED:
    ```
    
    without the underlying rustc/clippy diagnostic being visible in the
    obvious part of the GitHub Actions log.
    
    To keep `--keep_going` while making failures actionable, the wrapper now
    scans the captured Bazel console output for failed actions and prints
    the matching rustc/clippy diagnostic block. When a diagnostic block is
    found, it is emitted both as a GitHub `::error` annotation and as plain
    expanded log output, rather than being hidden in a collapsed group.
    
    ## Verification
    
    To validate the CI path, I intentionally introduced an unused import in
    `core/tests/suite/unified_exec.rs`. The Windows Bazel clippy job failed
    as expected, confirming that the integration-test module is now covered
    by Bazel clippy. The same failure also verified that the wrapper
    surfaces the matching clippy diagnostics directly in the Actions output.
  • enable tool search over dynamic tools (#18263)
    ## Summary
    
    - Normalize deferred MCP and dynamic tools into `ToolSearchEntry` values
    before constructing `ToolSearchHandler`.
    - Move the tool-search entry adapter out of `tools/handlers` and into
    `tools/tool_search_entry.rs` so the handlers directory stays focused on
    handlers.
    - Keep `ToolSearchHandler` operating over one generic entry list for
    BM25 search, namespace grouping, and per-bucket default limits.
    
    ## Why
    
    Follow-up cleanup for #17849. The dynamic tool-search support made the
    handler juggle source-specific MCP and dynamic tool lists, index
    arithmetic, output conversion, and namespace emission. This keeps source
    adaptation outside the handler so the search loop itself is smaller and
    source-agnostic.
    
    ## Validation
    
    - `just fmt`
    - `cargo test -p codex-core tools::handlers::tool_search::tests`
    - `git diff --check`
    - `cargo test -p codex-core` currently fails in unrelated
    `plugins::manager::tests::list_marketplaces_ignores_installed_roots_missing_from_config`;
    rerunning that single test fails the same way at
    `core/src/plugins/manager_tests.rs:1692`.
    
    ---------
    
    Co-authored-by: pash <pash@openai.com>
  • codex: route thread/read persistence through thread store (#18352)
    Summary
    - replace the thread/read persisted-load helper with
    ThreadStore::read_thread
    - move SQLite/rollout summary, name, fork metadata, and history loading
    for persisted reads into LocalThreadStore
    - leave getConversationSummary unchanged for a later PR
    
    Context
    - Replaces closed stacked PR #18232 after PR #18231 merged and its base
    branch was deleted.
  • feat(tui): add clear-context plan implementation (#17499)
    ## TL;DR
    
    - Adds a second Plan Mode handoff: implement the approved plan after
    clearing context.
    - Keeps the existing same-thread `Yes, implement this plan` action
    unchanged.
    - Reuses the `/clear` thread-start path and submits the approved plan as
    the fresh thread's first prompt.
    - Covers the new popup option, event plumbing, initial-message behavior,
    and disabled states in TUI tests.
    
    ## Problem
    
    Plan Mode already asks whether to implement an approved plan, but the
    only affirmative path continues in the same thread. That is useful when
    the planning conversation itself is still valuable, but it does not
    support the workflow where exploratory planning context is discarded and
    implementation starts from the final approved plan as the only
    model-visible handoff.
    
    <img width="1253" height="869" alt="image"
    src="https://github.com/user-attachments/assets/90023d75-c330-4919-bed8-518671c3474b"
    />
    
    ## Mental model
    
    There are now two implementation choices after a proposed plan. The
    existing choice, `Yes, implement this plan`, is unchanged: it switches
    to Default mode and submits `Implement the plan.` in the current thread.
    The new choice, `Yes, clear context and implement`, treats the proposed
    plan as a handoff artifact. It clears the UI/session context through the
    same thread-start source used by `/clear`, then submits an initial
    prompt containing the approved plan after the fresh thread is
    configured.
    
    The important distinction is that the new path is not compaction. The
    model receives a deliberate implementation prompt built from the
    approved plan markdown, not a summary of the previous planning
    transcript. Both implementation choices require the Default
    collaboration preset to be available, so the popup does not offer a
    coding handoff when the fresh thread would fall back to another mode.
    
    ## Non-goals
    
    This change does not alter `/clear`, `/compact`, or the existing
    same-context Plan Mode implementation option. It does not add protocol
    surface area or app-server schema changes. It also does not carry the
    previous transcript path or a generated planning summary into the new
    model context.
    
    ## Tradeoffs
    
    The fresh-context option relies on the approved plan being sufficiently
    complete. That matches the Plan Mode contract, but it means vague plans
    will produce weaker implementation starts than a compacted transcript
    would. The upside is that rejected ideas, exploratory dead ends, and
    planning corrections do not leak into the implementation turn.
    
    The current implementation stores the latest proposed plan in
    `ChatWidget` rather than deriving it from history cells at selection
    time. This keeps the popup action simple and deterministic, but it makes
    the cache lifecycle important: it must be reset when a new task starts
    so an old plan cannot be submitted later.
    
    ## Architecture
    
    The TUI stores the most recent completed proposed-plan markdown when a
    plan item completes. The Plan Mode approval popup uses that cache to
    enable the fresh-context option and to build a first-turn prompt that
    instructs the model to implement the approved plan in a fresh context.
    
    Selecting the new option emits a TUI-internal
    `ClearUiAndSubmitUserMessage` event. `App` handles that event by reusing
    the existing clear flow: clear terminal state, reset app UI state, start
    a new app-server thread with `ThreadStartSource::Clear`, and attach a
    replacement `ChatWidget` with an initial user message. The existing
    initial-message suppression in `enqueue_primary_thread_session` ensures
    the prompt is submitted only after the new session is configured and any
    startup replay is rendered.
    
    ## Observability
    
    The previous thread remains resumable through the existing clear-session
    summary hint. There is no new telemetry or protocol event for this path,
    so debugging should start at the TUI event boundary: confirm the popup
    emitted `ClearUiAndSubmitUserMessage`, confirm the app-server thread
    start used `ThreadStartSource::Clear`, then confirm the fresh widget
    submitted the initial user message after `SessionConfigured`.
    
    ## Tests
    
    The Plan Mode popup snapshots cover the new option and preserve the
    original option as the first/default action. Unit coverage verifies the
    original same-context option still emits `SubmitUserMessageWithMode`,
    the new option emits `ClearUiAndSubmitUserMessage` with the approved
    plan embedded verbatim, and the clear-context option is disabled when
    Default mode is unavailable or no approved plan exists. The broader
    `codex-tui` test package passes with the updated fresh-thread
    initial-message plumbing.
  • Make app tool hint defaults pessimistic for app policies (#17232)
    ## Summary
    - default missing app tool destructive/open-world hints to true for app
    policies
    - add regression tests for missing MCP annotations under restrictive app
    config
  • feat: config aliases (#18140)
    Rename `no_memories_if_mcp_or_web_search` →
    `disable_on_external_context` with backward compatibility
    
    While doing so, we add a key alias system on our layer merging system.
    What we try to avoid is a case where a company managed config use an old
    name while the user has a new name in it's local config (which would
    make the deserialization fail)
  • Guardian -> Auto-Review (#18021)
    This PR is a user-facing change for our rebranding of guardian to
    auto-review.
  • Fix config-loader tests after filesystem abstraction race (#18351)
    ## Why
    
    `origin/main` picked up two changes that crossed in flight:
    
    - #18209 refactored config loading to read through `ExecutorFileSystem`,
    changing `load_requirements_toml` to take a filesystem handle and an
    `AbsolutePathBuf`.
    - #17740 added managed `deny_read` requirements tests that still called
    `load_requirements_toml` with the previous two-argument signature.
    
    Once both landed, `just clippy` failed because the new tests no longer
    matched the current helper API.
    
    ## What
    
    - Updates the two managed `deny_read` requirements tests to convert the
    fixture path to `AbsolutePathBuf` before loading.
    - Passes `LOCAL_FS.as_ref()` into `load_requirements_toml` so these
    tests follow the filesystem abstraction introduced by #18209.
    
    ## Verification
    
    - `just clippy`
    - `cargo test -p codex-core load_requirements_toml_resolves_deny_read`
    - `cargo test -p codex-core --test all
    unified_exec_enforces_glob_deny_read_policy`
  • Move codex module under session (#18249)
    ## Summary
    - rename the core codex module root to session/mod.rs without using
    #[path]
    - move the codex module directory and tests under core/src/session
    - remove session/mod.rs reexports so call sites use explicit child
    module paths
    
    ## Testing
    - cargo test -p codex-core --lib
    - cargo check -p codex-core --tests
    - just fmt
    - just fix -p codex-core
    - git diff --check
  • feat(config): support managed deny-read requirements (#17740)
    ## Summary
    - adds managed requirements support for deny-read filesystem entries
    - constrains config layers so managed deny-read requirements cannot be
    widened by user-controlled config
    - surfaces managed deny-read requirements through debug/config plumbing
    
    This PR lets managed requirements inject deny-read filesystem
    constraints into the effective filesystem sandbox policy.
    User-controlled config can still choose the surrounding permission
    profile, but it cannot remove or weaken the managed deny-read entries.
    
    ## Managed deny-read shape
    A managed requirements file can declare exact paths and glob patterns
    under `[permissions.filesystem]`:
    
    ```toml
    # /etc/codex/requirements.toml
    [permissions.filesystem]
    deny_read = [
      "/Users/alice/.gitconfig",
      "/Users/alice/.ssh",
      "./managed-private/**/*.env",
    ]
    ```
    
    Those entries are compiled into the effective filesystem policy as
    `access = none` rules, equivalent in shape to filesystem permission
    entries like:
    
    ```toml
    [permissions.workspace.filesystem]
    "/Users/alice/.gitconfig" = "none"
    "/Users/alice/.ssh" = "none"
    "/absolute/path/to/managed-private/**/*.env" = "none"
    ```
    
    The important difference is that the managed entries come from
    requirements, so lower-precedence user config cannot remove them or make
    those paths readable again.
    
    Relative managed `deny_read` entries are resolved relative to the
    directory containing the managed requirements file. Glob entries keep
    their glob suffix after the non-glob prefix is normalized.
    
    ## Runtime behavior
    - Managed `deny_read` entries are appended to the effective
    `FileSystemSandboxPolicy` after the selected permission profile is
    resolved.
    - Exact paths become `FileSystemPath::Path { access: None }`; glob
    patterns become `FileSystemPath::GlobPattern { access: None }`.
    - When managed deny-read entries are present, `sandbox_mode` is
    constrained to `read-only` or `workspace-write`; `danger-full-access`
    and `external-sandbox` cannot silently bypass the managed read-deny
    policy.
    - On Windows, the managed deny-read policy is enforced for direct file
    tools, but shell subprocess reads are not sandboxed yet, so startup
    emits a warning for that platform.
    - `/debug-config` shows the effective managed requirement as
    `permissions.filesystem.deny_read` with its source.
    
    ## Stack
    1. #15979 - glob deny-read policy/config/direct-tool support
    2. #18096 - macOS and Linux sandbox enforcement
    3. This PR - managed deny-read requirements
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix(tui): use BEL for terminal title updates (#18261)
    ## Summary
    
    Fixes #18160.
    
    iTerm2 can append the current foreground process to tab titles, and
    Codex's terminal-title updates were causing that decoration to appear as
    `(codex")` with a stray trailing quote. Codex was writing OSC 0 title
    sequences terminated with ST (`ESC \`). Some terminal title integrations
    appear to accept that title update but still expose the ST terminator in
    their own process/title decoration.
    
    ## Changes
    
    - Update `codex-rs/tui/src/terminal_title.rs` to terminate OSC 0 title
    updates with BEL instead of ST.
    - Update the focused terminal-title encoding test to assert the
    BEL-terminated sequence.
    
    ## Compatibility
    
    This should be low risk: the title payload and update timing are
    unchanged, and BEL is the form already emitted by
    `crossterm::terminal::SetTitle` in the crossterm version used by this
    repository. BEL is also the widely supported xterm-family title
    terminator used by common terminals and multiplexers. The main
    theoretical risk would be a very old or unusual terminal that accepted
    only ST and not BEL for OSC title termination, but that is unlikely
    compared with the observed iTerm2 issue.
    
    ## Verification
    
    - `cargo test -p codex-tui terminal_title`
    - `cargo test -p codex-tui`