Commit Graph

5451 Commits

  • fix(app-server): replay token usage after resume and fork (#18023)
    ## Problem
    
    When a user resumed or forked a session, the TUI could render the
    restored thread history immediately, but it did not receive token usage
    until a later model turn emitted a fresh usage event. That left the
    context/status UI blank or stale during the exact window where the user
    expects resumed state to look complete. Core already reconstructed token
    usage from the rollout; the missing behavior was app-server lifecycle
    replay to the client that just attached.
    
    ## Mental model
    
    Token usage has two representations. The rollout is the durable source
    of historical `TokenCount` events, and the core session cache is the
    in-memory snapshot reconstructed from that rollout on resume or fork.
    App-server v2 clients do not read core state directly; they learn about
    usage through `thread/tokenUsage/updated`. The fix keeps those roles
    separate: core exposes the restored `TokenUsageInfo`, and app-server
    sends one targeted notification after a successful `thread/resume` or
    `thread/fork` response when that restored snapshot exists.
    
    This notification is not a new model event. It is a replay of
    already-persisted state for the client that just attached. That
    distinction matters because using the normal core event path here would
    risk duplicating `TokenCount` entries in the rollout and making future
    resumes count historical usage twice.
    
    ## Non-goals
    
    This change does not add a new protocol method or payload shape. It
    reuses the existing v2 `thread/tokenUsage/updated` notification and the
    TUI’s existing handler for that notification.
    
    This change does not alter how token usage is computed, accumulated,
    compacted, or written during turns. It only exposes the token usage that
    resume and fork reconstruction already restored.
    
    This change does not broadcast historical usage replay to every
    subscribed client. The replay is intentionally scoped to the connection
    that requested resume or fork so already-attached clients are not
    surprised by an old usage update while they may be rendering live
    activity.
    
    ## Tradeoffs
    
    Sending the usage notification after the JSON-RPC response preserves a
    clear lifecycle order: the client first receives the thread object, then
    receives restored usage for that thread. The tradeoff is that usage is
    still a notification rather than part of the `thread/resume` or
    `thread/fork` response. That keeps the protocol shape stable and avoids
    duplicating usage fields across response types, but clients must
    continue listening for notifications after receiving the response.
    
    The helper selects the latest non-in-progress turn id for the replayed
    usage notification. This is conservative because restored usage belongs
    to completed persisted accounting, not to newly attached in-flight work.
    The fallback to the last turn preserves a stable wire payload for
    unusual histories, but histories with no meaningful completed turn still
    have a weak attribution story.
    
    ## Architecture
    
    Core already seeds `Session` token state from the last persisted rollout
    `TokenCount` during `InitialHistory::Resumed` and
    `InitialHistory::Forked`. The new core accessor exposes the complete
    `TokenUsageInfo` through `CodexThread` without giving app-server direct
    session mutation authority.
    
    App-server calls that accessor from three lifecycle paths: cold
    `thread/resume`, running-thread resume/rejoin, and `thread/fork`. In
    each path, the server sends the normal response first, then calls a
    shared helper that converts core usage into
    `ThreadTokenUsageUpdatedNotification` and sends it only to the
    requesting connection.
    
    The tests build fake rollouts with a user turn plus a persisted token
    usage event. They then exercise `thread/resume` and `thread/fork`
    without starting another model turn, proving that restored usage arrives
    before any next-turn token event could be produced.
    
    ## Observability
    
    The primary debug path is the app-server JSON-RPC stream. After
    `thread/resume` or `thread/fork`, a client should see the response
    followed by `thread/tokenUsage/updated` when the source rollout includes
    token usage. If the notification is absent, check whether the rollout
    contains an `event_msg` payload of type `token_count`, whether core
    reconstruction seeded `Session::token_usage_info`, and whether the
    connection stayed attached long enough to receive the targeted
    notification.
    
    The notification is sent through the existing
    `OutgoingMessageSender::send_server_notification_to_connections` path,
    so existing app-server tracing around server notifications still
    applies. Because this is a replay, not a model turn event, debugging
    should start at the resume/fork handlers rather than the turn event
    translation in `bespoke_event_handling`.
    
    ## Tests
    
    The focused regression coverage is `cargo test -p codex-app-server
    emits_restored_token_usage`, which covers both resume and fork. The core
    reconstruction guard is `cargo test -p codex-core
    record_initial_history_seeds_token_info_from_rollout`.
    
    Formatting and lint/fix passes were run with `just fmt`, `just fix -p
    codex-core`, and `just fix -p codex-app-server`. Full crate test runs
    surfaced pre-existing unrelated failures in command execution and plugin
    marketplace tests; the new token usage tests passed in focused runs and
    within the app-server suite before the unrelated command execution
    failure.
  • fix: fix clippy issue in examples/ folder (#18184)
    I believe this use of `expect()` was introduced in
    https://github.com/openai/codex/pull/17826, but was not flagged by CI.
    Though I did see it in the diagnostics panel in VS Code, so it's worth
    cleaning up.
    
    I guess our current CI does include `examples/` when running Clippy?
  • Add codex_hook_run analytics event (#17996)
    # Why
    Add product analytics for hook handler executions so we can understand
    which hooks are running, where they came from, and whether they
    completed, failed, stopped, or blocked work.
    
    # What
    - add the new `codex_hook_run` analytics event and payload plumbing in
    `codex-rs/analytics`
    - emit hook-run analytics from the shared hook completion path in
    `codex-rs/core`
    - classify hook source from the loaded hook path as `system`, `user`,
    `project`, or `unknown`
    
    ```
    {
      "event_type": "codex_hook_run",
      "event_params": {
        "thread_id": "string",
        "turn_id": "string",
        "model_slug": "string",
        "hook_name": "string, // any HookEventName
        "hook_source": "system | user | project | unknown",
        "status": "completed | failed | stopped | blocked"
      }
    }
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Make thread unsubscribe test deterministic (#18000)
    ## Summary
    - replace the unsubscribe-during-turn test's sleep/polling flow with a
    gated streaming SSE response
    - add request-count notification support to the streaming SSE test
    server so the test can wait for the in-flight Responses request
    deterministically
    
    ## Scope
    - codex-rs/app-server/tests/suite/v2/thread_unsubscribe.rs
    - codex-rs/core/tests/common/streaming_sse.rs
    
    ## Validation
    - Not run locally; this is a narrow extraction from the prior CI-green
    branch.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix: drop lock earlier; was held across send_event().await unnecessarily (#18178)
    This was flagged by the Codex Security tool: the `state` lock was held
    longer than necessary, which included being held across an `async` call,
    increasing the potential for deadlock.
    
    While this was flagged by the Codex Security tool, I will look into
    enabling
    https://rust-lang.github.io/rust-clippy/stable/index.html#await_holding_lock
    in a follow-up PR (though unfortunately, that Clippy rule claims it
    reports false positives when `drop()` is used to drop a guard instead of
    using the end of block scope to drop). Though I can't seem to find a
    Clippy rule that checks for opportunities to drop a guard as soon as it
    is no longer referenced, in general.
  • Add server-level approval defaults for custom MCP servers (#17843)
    ## Summary
    - Add `default_tools_approval_mode` support for custom MCP server
    configs, matching the existing `codex_apps` behavior
    - Apply approval precedence as per-tool override, then server default,
    then `auto`
    - Update config serialization, CLI display, schema generation, docs, and
    tests
    
    ## Testing
    - `cargo check -p codex-config`
    - `cargo check -p codex-core`
    - `just write-config-schema`
    - `just fmt`
    - `cargo test -p codex-config`
    - Targeted `codex-core` tests for config parsing, config writes, and MCP
    approval precedence
    - `just fix -p codex-config -p codex-core`
  • Refactor AGENTS.md discovery into AgentsMdManager (#18035)
    Encapsulate Agents MD processing a bit and drop user_instructions_path
    from config.
  • Auto-upgrade configured marketplaces (#17425)
    ## Summary
    - Add best-effort auto-upgrade for user-configured Git marketplaces
    recorded in `config.toml`.
    - Track the last activated Git revision with `last_revision` so
    unchanged marketplace sources skip clone work.
    - Trigger the upgrade from plugin startup and `plugin/list`, while
    preserving existing fail-open plugin behavior with warning logs rather
    than new user-visible errors.
    
    ## Details
    - Remote configured marketplaces use `git ls-remote` to compare the
    source/ref against the recorded revision.
    - Upgrades clone into a staging directory, validate that
    `.agents/plugins/marketplace.json` exists and that the manifest name
    matches the configured marketplace key, then atomically activate the new
    root.
    - Local `.agents/plugins/marketplace.json` marketplaces remain live
    filesystem state and are not auto-pulled.
    - Existing non-curated plugin cache refresh is kicked after successful
    marketplace root upgrades.
    
    ## Validation
    - `just write-config-schema`
    - `cargo test -p codex-core marketplace_upgrade`
    - `cargo check -p codex-cli -p codex-app-server`
    - `just fix -p codex-core`
    
    Did not run the complete `cargo test` suite because the repo
    instructions require asking before a full core workspace run.
  • 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>
  • Avoid fatal TUI errors on skills list failure (#18061)
    Addresses #17951
    
    Problem: The TUI treated skills/list failures as fatal during refresh,
    so proxy/firewall responses that break plugin discovery could crash the
    session.
    
    Solution: Route startup and refresh skills/list responses through shared
    graceful handling that logs a warning and keeps the TUI running.
  • [2/8] Support piped stdin in exec process API (#18086)
    ## Summary
    - Add an explicit stdin mode to process/start.
    - Keep normal non-interactive exec stdin closed while allowing
    pipe-backed processes.
    
    ## Stack
    ```text
    o  #18027 [8/8] Fail exec client operations after disconnect
    │
    o  #18025 [7/8] Cover MCP stdio tests with executor placement
    │
    o  #18089 [6/8] Wire remote MCP stdio through executor
    │
    o  #18088 [5/8] Add executor process transport for MCP stdio
    │
    o  #18087 [4/8] Abstract MCP stdio server launching
    │
    o  #18020 [3/8] Add pushed exec process events
    │
    @  #18086 [2/8] Support piped stdin in exec process API
    │
    o  #18085 [1/8] Add MCP server environment config
    │
    o  main
    ```
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Add remote thread store implementation (#17826)
    - Add a "remote" thread store implementation
    - Implement the remote thread store as a thin wrapper that makes grpc
    calls to a configurable service endpoint
    - Implement only the thread/list method to start
    - Encode the grpc method/param shape as protobufs in the remote
    implementation
    
    A wart: the proto generation script is an "example" binary target. This
    is an example target only because Cargo lets examples use
    dev-dependencies, which keeps tonic-prost-build out of the normal
    codex-thread-store dependency surface. A regular bin would either need
    to add proto generation deps as normal runtime deps, or use a
    feature-gated optional dep, which this repo’s manifest checks explicitly
    reject.
  • fix: model menu pop (#18154)
    Fix the `/model` menu looping on itself
  • Make yolo skip managed-network tool enforcement (#18042)
    ## Summary
    
    This makes `DangerFullAccess` / yolo tool execution fully opt out of
    managed-network enforcement.
    
    Previously, yolo turns could have `turn.network` stripped while tool
    orchestration still derived `enforce_managed_network=true` from
    `requirements.toml.network`. That created an inconsistent state where
    the turn had no managed proxy attached, but tool execution still behaved
    like managed networking was active.
    
    This updates the tool orchestration and JS REPL paths to treat managed
    networking as active only when the current turn actually has
    `turn.network`.
    
    ## Behavior
    
    - Yolo / `DangerFullAccess`: no managed proxy, no managed-network
    enforcement.
    - Guardian / workspace-write with managed proxy: managed-network
    enforcement still applies.
    - Avoids the half-state where yolo has no proxy but still gets
    managed-network sandbox behavior.
    
    ## Tests
    
    - `just fmt`
    - `cargo test -p codex-core
    danger_full_access_tool_attempts_do_not_enforce_managed_network --
    --nocapture`
    - `cargo test -p codex-core danger_full_access -- --nocapture`
    - `just fix -p codex-core`
    
    Co-authored-by: jgershen-oai <jgershen@openai.com>
  • Launch image generation by default (#17153)
    ## Summary
    - Promote `image_generation` from under-development to stable
    - Enable image generation by default in the feature registry
    - Update feature coverage for the new launch-state expectation
    - Add the missing image-generation auth fixture field in a tool registry
    test
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-features`
    - `cargo test -p codex-tools` currently fails:
    `test_full_toolset_specs_for_gpt5_codex_unified_exec_web_search` needs
    its expected default tool list updated for `image_generation`
  • Fix invalid TUI resume hints (#18059)
    Addresses #18011
    
    Problem: #16987 allowed zero-token TUI exits to print resume hints,
    which exposed precomputed thread ids before their rollout files were
    persisted; #17222 made the same invalid hint visible when switching
    sessions via `/resume`.
    
    Solution: Only include resume commands for TUI sessions backed by a
    materialized non-empty rollout, and cover both missing-rollout and
    persisted-rollout summary behavior.
    
    Testing: Manually verified by pressing Ctrl+D before the first prompt
    and confirming that no "to continue this session" message was generated.
  • Prefill rename prompt with current thread name (#18057)
    Addresses #12178
    
    Problem: The TUI /rename prompt opened blank even when the current
    thread already had a custom name, making small edits awkward.
    
    Solution: Let custom prompts receive initial text and prefill /rename
    with the existing thread name while preserving the empty prompt for
    unnamed threads.
    
    Testing: Manually verified that the feature works by using `/rename`
    with unnamed and already-named threads.
  • [code mode] defer mcp tools from exec description (#17287)
    ## Summary
    - hide deferred MCP/app nested tool descriptions from the `exec` prompt
    in code mode
    - add short guidance that omitted nested tools are still available
    through `ALL_TOOLS`
    - cover the code_mode_only path with an integration test that discovers
    and calls a deferred app tool
    
    ## Motivation
    `code_mode_only` exposes only top-level `exec`/`wait`, but the `exec`
    description could still include a large nested-tool reference. This
    keeps deferred nested tools callable while avoiding that prompt bloat.
    
    ## Tests
    - `just fmt`
    - `just fix -p codex-code-mode`
    - `just fix -p codex-tools`
    - `cargo test -p codex-code-mode
    exec_description_mentions_deferred_nested_tools_when_available`
    - `cargo test -p codex-tools
    create_code_mode_tool_matches_expected_spec`
    - `cargo test -p codex-core
    code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools`
  • fix(tui): remove duplicate context statusline item (#18054)
    Addresses #18045
    
    Problem: `/statusline` exposed both `context-remaining` and
    `context-remaining-percent` after conflicting PRs attempted to address
    the same context-status issue, including #17637, allowing duplicate
    footer segments.
    
    Solution: Remove the duplicate `context-remaining-percent` status-line
    item and update status-line tests and snapshots to use only canonical
    `context-remaining`.
  • [1/8] Add MCP server environment config (#18085)
    ## Summary
    - Add an MCP server environment setting with local as the default.
    - Thread the default through config serialization, schema generation,
    and existing config fixtures.
    
    ## Stack
    ```text
    o  #18027 [8/8] Fail exec client operations after disconnect
    │
    o  #18025 [7/8] Cover MCP stdio tests with executor placement
    │
    o  #18089 [6/8] Wire remote MCP stdio through executor
    │
    o  #18088 [5/8] Add executor process transport for MCP stdio
    │
    o  #18087 [4/8] Abstract MCP stdio server launching
    │
    o  #18020 [3/8] Add pushed exec process events
    │
    o  #18086 [2/8] Support piped stdin in exec process API
    │
    @  #18085 [1/8] Add MCP server environment config
    │
    o  main
    ```
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix: auth preflight (#18117)
    Fix app-server startup when `remote_control = true` is enabled without
    ChatGPT auth.
    
    Remote control now starts in a degraded/retrying state instead of
    failing app-server initialization, so Desktop is not stranded before the
    initial initialize handshake.
  • nit: get rid of an expect (#18144)
    Get rid of an `expect()` that caused a `panic` in the TUI
    
    <img width="1320" height="415" alt="Screenshot 2026-04-16 at 15 30 20"
    src="https://github.com/user-attachments/assets/588aaf6f-b009-4b58-8daf-56c3a9d6fe3b"
    />
    
    
    Basically in `from_absolute_path` there is a `absolutize::absolutize`
    that calls a `current_dir()` . But the dir in which Codex was running
    got re-generated (because of Codex I guess but I can't exactly see the
    source). So `current_dir()` returns an `ENOENT` and 💥
  • chore: unify memory drop endpoints (#18134)
    Unify all the memories drop behind a single implementation that drops
    both the main memories and the extensions
  • debug: windows flake (#18135)
    Make sure Bazel logs shows every errors so that we can debug flakes +
    fix a small flake on Windows by updating the sleep command to a
    `Start-Sleep` instead of a PowerShell nested command (otherwise we had
    double nesting which is absurdely slow)
  • nit: add min values for memories (#18137)
    Just add min values to some memories config fields
  • fix: windows flake (#18127)
    Fix `sqlite_feedback_logs_match_feedback_formatter_shape` by explicitly
    flushing the async log DB layer before querying SQLite.
  • fix: more flake (#18006)
    Stabilizes the Responses API proxy header test by splitting the coverage
    at the right boundary:
    - Core integration test now verifies parent/subagent identity headers
    directly from captured `/responses` requests.
    - Proxy dump unit test now verifies those identity headers are preserved
    in dumped request JSON.
    - Removes the flaky real proxy process + temp-file dump polling path
    from the core test.
  • [codex] Route Fed ChatGPT auth through Fed edge (#17151)
    ## Summary
    - parse chatgpt_account_is_fedramp from signed ChatGPT auth metadata
    - add _account_is_fedramp=true to ChatGPT backend-api requests only for
    FedRAMP ChatGPT-auth accounts
  • Fix MCP startup cancellation through app server (#18078)
    Addresses https://github.com/openai/codex/issues/17143
    
    Problem: TUI interrupts without an active turn stopped cancelling slow
    MCP startup after routing through the app-server APIs.
    
    Solution: Route no-active-turn interrupts through app-server as startup
    cancels, acknowledge them immediately, and emit cancelled MCP startup
    updates.
    
    Testing: I manually confirmed that MCP cancellation didn't work prior to
    this PR and works after the fix was in place.
  • Extract plugin loading and marketplace logic into codex-core-plugins (#18070)
    Split plugin loading, marketplace, and related infrastructure out of
    core into codex-core-plugins, while keeping the core-facing
    configuration and orchestration flow in codex-core.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex][mcp] Add resource uri meta to tool call item. (#17831)
    - [x] Add resource uri meta to tool call item so that the app-server
    client can start prefetching resources immediately without loading mcp
    server status.
  • Update ToolSearch to be enabled by default (#17854)
    ## Summary
    - Promote `Feature::ToolSearch` to `Stable` and enable it in the default
    feature set
    - Update feature tests and tool registry coverage to match the new
    default
    - Adjust the search-tool integration test to assert the default-on path
    and explicit disable fallback
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-features`
    - `cargo test -p codex-core --test all search_tool`
    - `cargo test -p codex-tools`
  • Async config loading (#18022)
    Parts of config will come from executor. Prepare for that by making
    config loading methods async.
  • Display YOLO mode permissions if set when launching TUI (#17877)
    - When launching the TUI client, if YOLO mode is enabled, display this
    in the header.
    - Eligibility is determined by `approval_policy = "never"` and
    `sandbox_mode = "danger-full-access"`
    
    <img width="886" height="230" alt="image"
    src="https://github.com/user-attachments/assets/d7064778-e32c-4123-8e44-ca0c9016ab09"
    />
  • feat: introduce codex-pr-body skill (#18033)
    ## Motivation
    
    Codex needs a repeatable workflow for updating PR metadata after a pull
    request already exists. This is more specific than generic GitHub
    handling: the assistant needs to preserve author-provided body content,
    explain why the PR exists before listing implementation details, and
    describe only the net change under review, including when Sapling stacks
    put a PR on top of another PR instead of `main`.
    
    ## Changes
    
    - Adds `.codex/skills/codex-pr-body/SKILL.md`.
    - Documents how to infer the target PR from the current branch or
    commit, including Sapling-specific PR metadata and `sl sl` output.
    - Defines the expected PR body update behavior: inspect the existing
    body, preserve key content such as images, avoid local absolute paths,
    use Markdown formatting, include relevant issue/PR references, and call
    out developer docs follow-up only when applicable.
    - Captures stacked-PR handling so generated PR text describes the change
    between the PR's base and head, rather than unrelated ancestor changes.
    
    ## Verification
    
    Not run; this is a Codex skill documentation addition.
  • [codex] Make command exec delta tests chunk tolerant (#17999)
    ## Summary
    - Make command/exec output-delta tests accumulate streamed chunks
    instead of assuming complete logical output in a single notification.
    - Collect stdout and stderr independently so stream interleaving does
    not fail the pipe streaming test.
    
    ## Why
    The command/exec protocol exposes output as deltas, so tests should not
    rely on chunk boundaries being stable. A line like `out-start\n` may
    arrive split across multiple notifications, and stdout/stderr
    notifications may interleave.
    
    ## Validation
    - `just fmt`
    - `git diff --check`
    - `cargo test -p codex-app-server suite::v2::command_exec`
  • removing network proxy for yolo (#17742)
    **Summary**
    - prevent managed requirements.toml network settings from leaking into
    DangerFullAccess / yolo turns by gating managed proxy attachment on
    sandbox mode
    - keep guardian/sandboxed modes on the managed proxy path, while making
    true yolo bypass the proxy entirely, including /shell full-access
    commands
  • Clarify realtime v2 context and handoff messages (#17896)
    ## Summary
    - wrap realtime startup context in
    `<startup_context>...</startup_context>` tags
    - prefix V2 mirrored user text and relayed backend text with `[USER]` /
    `[BACKEND]`
    - remove the V2 progress suffix and replace the final V2 handoff output
    with a short completion acknowledgement while preserving the existing V1
    wrapper
    
    ## Testing
    - cargo test -p codex-api
    realtime_v2_session_update_includes_background_agent_tool_and_handoff_output_item
    -- --exact
    - cargo test -p codex-app-server webrtc_v2_background_agent_
    - cargo test -p codex-app-server webrtc_v2_text_input_is_
    - cargo test -p codex-core conversation_user_text_turn_is_
  • [mcp] Add dummy tools for previously called but currently missing tools. (#17853)
    - [x] Add dummy tools for previously called but currently missing tools.
    Currently supporting MCP tools only.
  • Significantly improve standalone installer (#17022)
    ## Summary
    
    This PR significantly improves the standalone installer experience.
    
    The main changes are:
    
    1. We now install the codex binary and other dependencies in a
    subdirectory under CODEX_HOME.
    (`CODEX_HOME/packages/standalone/releases/...`)
    
    2. We replace the `codex.js` launcher that npm/bun rely on with logic in
    the Rust binary that automatically resolves its dependencies (like
    ripgrep)
    
    ## Motivation
    
    A few design constraints pushed this work.
    
    1. Currently, the entrypoint to codex is through `codex.js`, which
    forces a node dependency to kick off our rust app. We want to move away
    from this so that the entrypoint to codex does not rely on node or
    external package managers.
    2. Right now, the native script adds codex and its dependencies directly
    to user PATH. Given that codex is likely to add more binary dependencies
    than ripgrep, we want a solution which does not add arbitrary binaries
    to user PATH -- the only one we want to add is the `codex` command
    itself.
    3. We want upgrades to be atomic. We do not want scenarios where
    interrupting an upgrade command can move codex into undefined state (for
    example, having a new codex binary but an old ripgrep binary). This was
    ~possible with the old script.
    4. Currently, the Rust binary uses heuristics to determine which
    installer created it. These heuristics are flaky and are tied to the
    `codex.js` launcher. We need a more stable/deterministic way to
    determine how the binary was installed for standalone.
    5. We do not want conflicting codex installations on PATH. For example,
    the user installing via npm, then installing via brew, then installing
    via standalone would make it unclear which version of codex is being
    launched and make it tough for us to determine the right upgrade
    command.
    
    ## Design
    
    ### Standalone package layout
    
    Standalone installs now live under `CODEX_HOME/packages/standalone`:
    
    ```text
    $CODEX_HOME/
      packages/
        standalone/
          current -> releases/0.111.0-x86_64-unknown-linux-musl
          releases/
            0.111.0-x86_64-unknown-linux-musl/
              codex
              codex-resources/
                rg
    ```
    
    where `standalone/current` is a symlink to a release directory.
    
    On Windows, the release directory has the same shape, with `.exe` names
    and Windows helpers in `codex-resources`:
    
    ```text
    %CODEX_HOME%\
      packages\
        standalone\
          current -> releases\0.111.0-x86_64-pc-windows-msvc
          releases\
            0.111.0-x86_64-pc-windows-msvc\
              codex.exe
              codex-resources\
                rg.exe
                codex-command-runner.exe
                codex-windows-sandbox-setup.exe
    ```
    
    This gives us:
    - atomic upgrades because we can fully stage a release before switching
    `standalone/current`
    - a stable way for the binary to recognize a standalone install from its
    canonical `current_exe()` path under CODEX_HOME
    - a clean place for binary dependencies like `rg`, Windows sandbox
    helpers, and, in the future, our custom `zsh` etc
    
    ### Command location
    
    On Unix, we add a symlink at `~/.local/bin/codex` which points directly
    to the `$CODEX_HOME/packages/standalone/current/codex` binary. This
    becomes the main entrypoint for the CLI.
    
    On Windows, we store the link at
    `%LOCALAPPDATA%\Programs\OpenAI\Codex\bin`.
    
    ### PATH persistence
    
    This is a tricky part of the PR, as there's no ~super reliable way to
    ensure that we end up on PATH without significant tradeoffs.
    
    Most Unix variants will have `~/.local/bin` on PATH already, which means
    we *should* be fine simply registering the command there in most cases.
    However, there are cases where this is not the case. In these cases, we
    directly edit the profile depending on the shell we're in.
    
    - macOS zsh: `~/.zprofile`
    - macOS bash: `~/.bash_profile`
    - Linux zsh: `~/.zshrc`
    - Linux bash: `~/.bashrc`
    - fallback: `~/.profile`
    
    On Windows, we update the User `Path` environment variable directly and
    we don't need to worry about shell profiles.
    
    ### Standalone runtime detection
    
    This PR adds a new shared crate, `codex-install-context`, which computes
    install ownership once per process and caches it in a `OnceLock`.
    
    That context includes:
    - install manager (`Standalone`, `Npm`, `Bun`, `Brew`, `Other`)
    - the managed standalone release directory, when applicable
    - the managed standalone `codex-resources` directory, when present
    - the resolved `rg_command`
    
    The standalone path is detected by canonicalizing `current_exe()`,
    canonicalizing CODEX_HOME via `find_codex_home()`, and checking whether
    the binary is running from under
    `$CODEX_HOME/packages/standalone/releases`.
    
    We intentionally do not use a release metadata file. The binary path is
    the source of truth.
    
    ### Dependency resolution
    
    For standalone installs, `grep_files` now resolves bundled `rg` from
    `codex-resources` next to the Codex binary.
    
    For npm/bun/brew/other installs, `grep_files` falls back to resolving
    `rg` from PATH.
    
    For Windows standalone installs, Windows sandbox helpers are still found
    as direct siblings when present. If they are not direct siblings, the
    lookup also checks the sibling `codex-resources` directory.
    
    ### TUI update path
    
    The TUI now has `UpdateAction::StandaloneUnix` and
    `UpdateAction::StandaloneWindows`, which rerun the standalone install
    commands.
    
    Unix update command:
    
    ```sh
    sh -c "curl -fsSL https://chatgpt.com/codex/install.sh | sh"
    ```
    
    Windows update command:
    
    ```powershell
    powershell -c "irm https://chatgpt.com/codex/install.ps1|iex"
    ```
    
    The Windows updater runs PowerShell directly. We do this because `cmd
    /C` would parse the `|iex` as a cmd pipeline instead of passing it to
    PowerShell.
    
    ## Additional installer behavior
    
    - standalone installs now warn about conflicting npm/bun/brew-managed
    `codex` installs and offer to uninstall them
    - same-version reruns do not redownload the release if it is already
    staged locally
    
    ## Testing
    
    Installer smoke tests run:
    - macOS: fresh install into isolated `HOME` and `CODEX_HOME` with
    `scripts/install/install.sh --release latest`
    - macOS: reran the installer against the same isolated install to verify
    the same-version/update path and PATH block idempotence
    - macOS: verified the installed `codex --version` and bundled
    `codex-resources/rg --version`
    - Windows: parsed `scripts/install/install.ps1` with PowerShell via
    `[scriptblock]::Create(...)`
    - Windows: verified the standalone update action builds a direct
    PowerShell command and does not route the `irm ...|iex` command through
    `cmd /C`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Support original-detail metadata on MCP image outputs (#17714)
    ## Summary
    - honor `_meta["codex/imageDetail"] == "original"` on MCP image content
    and map it to `detail: "original"` where supported
    - strip that detail back out when the active model does not support
    original-detail image inputs
    - update code-mode `image(...)` to accept individual MCP image blocks
    - teach `js_repl` / `codex.emitImage(...)` to preserve the same hint
    from raw MCP image outputs
    - document the new `_meta` contract and add generic RMCP-backed coverage
    across protocol, core, code-mode, and js_repl paths
  • [docs] Revert extra changes from PR 17848 (#18003)
    ## Summary
    
    1. Revert https://github.com/openai/codex/pull/17848 so the Bazel and
    `BUILD` file changes leave `main`.
    2. Prepare for a narrower follow up that restores only `SECURITY.md`.
    
    ## Validation
    
    1. Reviewed the revert diff against `main`.
    2. Ran a clean diff check before push.
  • feat: Support alternate marketplace manifests and local string (#17885)
    - Discover marketplace manifests from different supported layout paths
    instead of only .agents/plugins/marketplace.json.
    - Accept local plugin sources written either as { source: "local", path:
    ... } or as a direct string path.
    - Skip unsupported or invalid plugin source entries without failing the
    entire marketplace, and keep valid local plugins loadable.