7919 Commits

  • Surface TUI config write error causes (#26537)
    ## Summary
    
    TUI config writes currently wrap app-server failures with local context
    like `config/batchWrite failed in TUI`, but several user-visible paths
    only render the outer error. That hides the actionable app-server
    message, such as validation constraints or read-only `CODEX_HOME`
    failures, leaving users with a dead-end diagnostic.
    
    This change adds a small formatter next to the TUI config write helpers
    that renders the error source chain, then uses it for model persistence,
    feature persistence, project trust, status line writes, hook trust, and
    hook enablement.
    
    Fixes #26077
  • [codex] Fix long proxy socket paths (#26553)
    ## Summary
    
    - avoid generating host proxy bridge Unix socket paths that exceed
    Linux's `sockaddr_un.sun_path` limit
    - fall back from a long `$CODEX_HOME/tmp` path to the system temp
    directory, then `/tmp`
    - add focused unit coverage for short and overlong parent paths
    
    ## Root cause
    
    With a sufficiently long `CODEX_HOME`, the generated
    `proxy-route-*.sock` path exceeds Linux's 107-byte pathname limit. The
    host bridge child exits before writing its readiness byte, so the parent
    reports the indirect error `failed to prepare host proxy routing bridge:
    failed to fill whole buffer`.
    
    ## Validation
    
    - reproduced the original error with a long `CODEX_HOME` using
    `codex-cli 0.138.0-alpha.4`
    - `cargo clippy -p codex-linux-sandbox --all-targets`
    - `just fix -p codex-linux-sandbox`
    - `just fmt`
    
    The Linux-only unit test could not execute locally: the arm64 Docker
    build was repeatedly OOM-killed by `rustc` while compiling an unrelated
    `codex-app-server-protocol` dependency, before reaching the test.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat(app-server): expose account token usage [1 of 2] (#25344)
    ## Why
    
    Token activity is useful account-level context, but terminal clients
    need a supported app-server path to fetch it without reaching into
    ChatGPT backend details directly. The API should also live under the
    broader account usage umbrella so future usage surfaces can be added
    without proliferating user-facing concepts.
    
    ## What Changed
    
    - Add `codex-backend-client` support for the ChatGPT profile token-usage
    payload.
    - Add the v2 `account/usage/read` app-server RPC.
    - Map lifetime usage, peak daily usage, streak, longest task duration,
    and daily buckets into app-server protocol types.
    - Gate the request on Codex-backend auth, which supports ChatGPT auth
    tokens and AgentIdentity.
    - Regenerate the app-server JSON and TypeScript schema fixtures.
    
    ## Token Count Source
    
    `account/usage/read` returns the token-usage aggregate supplied by the
    ChatGPT profile backend. App-server maps that backend-owned aggregate
    into protocol fields; it does not recompute cached-token treatment,
    usage multipliers, or raw input/output totals locally.
    
    ## Stack
    
    1. feat(app-server): expose account token usage [1 of 2] (this PR)
    2. [#25345](https://github.com/openai/codex/pull/25345) feat(tui): add
    token activity command [2 of 2]
    
    ## How to Test
    
    1. Start an app-server client from this branch while authenticated with
    ChatGPT or AgentIdentity.
    2. Call `account/usage/read`.
    3. Confirm the response includes `summary` and `dailyUsageBuckets`.
    4. Also verify a session without Codex-backend auth receives the
    existing auth error path.
    
    Targeted tests:
    - `just test -p codex-backend-client -p codex-app-server-protocol -p
    codex-app-server`
    - `just write-app-server-schema`
  • refactor: split agent control modules (#26610)
    ## Summary
    
    Mechanically splits `AgentControl` into focused modules so later agent
    runtime changes are easier to review. The shared lookup, messaging, and
    completion logic remains in `control.rs`, while spawn-specific code and
    V1 legacy close/resume behavior move into dedicated files.
    
    ## Changes
    
    - Extract spawn-agent code into `agent/control/spawn.rs`.
    - Extract V1-only legacy close/resume behavior into
    `agent/control/legacy.rs`.
    - Keep shared control-plane behavior in `agent/control.rs`.
    - Preserve existing behavior; this PR is intended to be mechanical.
    
    ## Stack
    
    1. This PR - Mechanical `AgentControl` split: extracts spawn and V1
    legacy code without behavior changes.
    2. #26614 - Execution slot accounting: separates logical agents from
    active execution slots.
    3. #26611 - Residency and reload runtime: adds resident-agent LRU,
    eviction/reload, durable lookup, and V2 delivery through reload.
    4. #26612 - V2 tool semantics: narrows `close_agent` to interrupt-only
    and updates V2 tool coverage.
  • [codex] Keep v1 spawn metadata visible (#26599)
    ## Summary
    - keep the legacy v1 `spawn_agent` role and model selectors visible
    - add regression coverage for the default v1 tool plan
    
    ## Why
    `hide_spawn_agent_metadata` is a multi-agent v2 setting, but the v1
    planning branch also consumed it. After the default changed to `true`,
    v1 stopped advertising `agent_type`, `model`, `reasoning_effort`, and
    `service_tier`, preventing configured agents from being selected.
    
    This keeps the hidden-metadata default for v2 while opting v1 out of
    that behavior.
    
    Fixes #26363.
    
    ## Validation
    Not run locally, per request; CI will validate the change.
  • [codex] Forward turn moderation metadata through app-server (#25710)
    ## Why
    First-party backends can supply turn-scoped moderation metadata that
    app-server clients need for client-side presentation. Exposing this as
    an experimental typed notification lets opted-in clients consume it
    without interpreting raw Responses API events.
    
    ## What changed
    - forward `response.metadata.openai_chatgpt_moderation_metadata` from
    Responses API SSE and WebSocket streams as turn-scoped moderation
    metadata
    - emit the experimental app-server v2 `turn/moderationMetadata`
    notification with `{ threadId, turnId, metadata }`
    - add app-server integration coverage for the typed moderation metadata
    notification
    
    ## Testing
    - `just test -p codex-core
    build_ws_client_metadata_includes_window_lineage_and_turn_metadata`
    - `just test -p codex-core` (fails locally: 46 failures and 1 timeout,
    primarily missing `test_stdio_server` and shell snapshot timeouts)
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-app-server
    turn_moderation_metadata_emits_typed_notification_v2`
    - `just test -p codex-app-server` (fails locally: 792 passed, 10 failed,
    and 5 timed out; failures are in existing environment-sensitive tests,
    primarily because nested macOS `sandbox-exec` is not permitted)
    - `just write-app-server-schema --experimental --schema-root
    /tmp/codex-app-server-schema-experimental`
  • nit: doc (#26566)
    Matching CBv9
  • Encrypt multi-agent v2 message payloads (#26210)
    ## Why
    
    Multi-agent v2 currently routes agent instructions through normal tool
    arguments and inter-agent context. That means the parent model can emit
    plaintext task text, Codex can persist it in history/rollouts, and the
    recipient can receive it as ordinary assistant-message JSON.
    
    This changes the v2 path so agent instructions stay encrypted between
    model calls: Responses encrypts the `message` argument returned by the
    model, Codex forwards only that ciphertext, and Responses decrypts it
    internally for the recipient model.
    
    ## What changed
    
    - Mark the v2 `message` parameter as encrypted for `spawn_agent`,
    `send_message`, and `followup_task`.
    - Treat multi-agent v2 tool `message` values as ciphertext
    unconditionally.
    - Store v2 inter-agent task text in
    `InterAgentCommunication.encrypted_content` with empty plaintext
    `content`.
    - Convert encrypted inter-agent communications into the Responses
    `agent_message` input item before sending the child request.
    - Preserve `agent_message` items across history, rollout, compaction,
    telemetry, and app-server schema paths.
    - Leave multi-agent v1 unchanged.
    
    ## Message shape
    
    The model still calls the v2 tools with a `message` argument, but that
    value is now ciphertext:
    
    ```json
    {
      "name": "spawn_agent",
      "arguments": {
        "task_name": "worker",
        "message": "<ciphertext>"
      }
    }
    ```
    
    Codex stores the task as encrypted inter-agent communication:
    
    ```json
    {
      "author": "/root",
      "recipient": "/root/worker",
      "content": "",
      "encrypted_content": "<ciphertext>",
      "trigger_turn": true
    }
    ```
    
    When Codex builds the recipient request, it forwards the ciphertext
    using the new Responses input item:
    
    ```json
    {
      "type": "agent_message",
      "author": "/root",
      "recipient": "/root/worker",
      "content": [
        {
          "type": "encrypted_content",
          "encrypted_content": "<ciphertext>"
        }
      ]
    }
    ```
    
    Responses decrypts that item internally for the recipient model.
    
    ## Context impact
    
    - Parent context no longer carries plaintext v2 agent task instructions
    from these tool arguments.
    - Codex rollout/history stores ciphertext for v2 agent instructions.
    - Recipient requests receive an `agent_message` item instead of
    assistant commentary JSON for encrypted task delivery.
    - Plaintext completion/status notifications are still plaintext because
    they are Codex-generated status messages, not encrypted model tool
    arguments.
    
    ## Validation
    
    - `just test -p codex-tools`
    - `just test -p codex-protocol`
    - `just test -p codex-rollout`
    - `just test -p codex-rollout-trace`
    - `just test -p codex-otel`
    - `just write-app-server-schema`
  • [codex] Add environment shell info (#26480)
    ## Why
    
    Shell detection needs to be available through the `Environment`
    abstraction so callers can ask the selected local or remote environment
    for shell metadata without adding a separate HTTP endpoint or parallel
    info-source path. This keeps shell metadata shaped like the existing
    environment-owned filesystem capability and lets remote environments
    answer through exec-server JSON-RPC.
    
    ## What changed
    
    - Added `environment/info` to the exec-server protocol/client/server and
    exposed `Environment::info()`.
    - Added local and remote environment info providers on `Environment`,
    following the existing capability-provider pattern used for filesystem
    access.
    - Moved the shared shell detection logic into `codex-shell-command` and
    kept core shell APIs as wrappers around that implementation.
    - Returned shell metadata as `EnvironmentInfo { shell: ShellInfo }`
    using the existing shell detection path.
    - Added a remote environment test that calls `Environment::info()`
    through an exec-server-backed environment.
    
    ## Validation
    
    - `git diff --check`
    - `just test -p codex-shell-command`
    - `just test -p codex-core -E 'test(/shell::tests::/)'`\n- `just test -p
    codex-exec-server environment`
  • feat(remote-control): allow pairing while disabled (#26215)
    ## Why
    
    `remoteControl/pairing/start` creates authorization for future
    remote-control connections, so it should not require the live websocket
    to already be enabled. Requiring enable first made pairing depend on
    presence instead of the persisted server enrollment that pairing
    actually uses.
    
    Pairing also needs to recover when that persisted server row is stale.
    If `/server/pair` returns `404`, making the first pairing attempt fail
    forces a manual retry even though the client can clear the stale row and
    create a replacement enrollment immediately.
    
    ## What Changed
    
    - Allow `remoteControl/pairing/start` to reuse or create the persisted
    remote-control server enrollment while remote control is disabled.
    - Keep the selected in-memory enrollment across disable and share it
    with websocket connect so a later enable uses the same selected server.
    - Thread the app-server client name through pairing so stdio persistence
    keeps using the websocket-owned enrollment key.
    - Recover pairing server-token auth failures through the existing
    refresh/auth-recovery path.
    - Recover stale pairing enrollment on `/server/pair` `404` by clearing
    the stale selected enrollment, re-enrolling once, and retrying pairing
    once.
    - Add focused disabled-pairing and stale-pairing recovery coverage.
    
    ## Verification
    
    -
    `remote_control_pairing_start_returns_pairing_artifacts_while_disabled`
    exercises pairing before enable.
    - `remote_control_handle_reenrolls_after_stale_pairing_enrollment`
    exercises stale `/server/pair` `404` recovery without a manual retry.
    
    Related: N/A
  • core: derive exec policy filesystem policy from profile (#26499)
    ## Why
    
    `PermissionProfile` already owns the runtime filesystem sandbox policy
    through `file_system_sandbox_policy()`. Keeping a separate
    `FileSystemSandboxPolicy` on exec-policy fallback contexts made it
    possible for callers and tests to construct split states that the
    production permission model should not rely on.
    
    ## What changed
    
    - Removed `file_system_sandbox_policy` from `UnmatchedCommandContext`,
    `ExecApprovalRequest`, and the intercepted Unix exec-policy context.
    - Derived filesystem sandbox policy inside unmatched-command decision
    logic from `PermissionProfile::file_system_sandbox_policy()`.
    - Simplified shell/unified-exec callers and tests that were only
    plumbing the duplicate policy through.
    
    ## Testing
    
    Local tests not run per request; relying on remote CI.
  • [codex] Keep Bazel startup options stable across commands (#26256)
    ## Why
    
    `just bazel-clippy` ran target discovery with
    `--noexperimental_remote_repo_contents_cache`, then ran the build with
    the workspace default `--experimental_remote_repo_contents_cache`. Bazel
    therefore killed and restarted its server on each transition, slowing
    repeated commands and discarding the in-memory analysis cache. An audit
    found the same class of startup-option variation in several CI command
    sequences.
    
    ## What changed
    
    - Keep local lint target-discovery queries on the workspace-default
    Bazel server, while making CI target discovery explicitly use the CI
    startup options.
    - Normalize GitHub Actions launches through the BuildBuddy wrapper to
    share `BAZEL_OUTPUT_USER_ROOT` and
    `--noexperimental_remote_repo_contents_cache`.
    - Route the CI lockfile check and Windows test-shard query through the
    same startup configuration.
    - Document the startup-option invariant and add wrapper regression
    coverage.
    
    ## Validation
    
    - Confirmed consecutive local clippy target-discovery runs retained the
    same Bazel server PID.
  • fix(rmcp): refresh expired OAuth tokens before startup (#26482)
    ## Why
    
    Codex persists OAuth expiry as an absolute `expires_at`, then
    reconstructs RMCP’s relative `expires_in` when credentials are loaded.
    For an already-expired token, Codex reconstructed `expires_in` as
    missing.
    
    [RMCP 0.15 treated a missing `expires_in` as zero when a refresh token
    was
    present](https://github.com/modelcontextprotocol/rust-sdk/blob/9cfc905a9ef17c8bba6748dc0a9bdd2452681733/crates/rmcp/src/transport/auth.rs#L704-L723),
    so this still triggered a refresh. [RMCP 1.7 treats missing expiry
    information as unknown and uses the access token
    as-is](https://github.com/modelcontextprotocol/rust-sdk/blob/3529c3675ff64db805bd947ca6ece6090809e43d/crates/rmcp/src/transport/auth.rs#L1233-L1265),
    causing the stale token to be sent during `initialize`.
    
    ## What changed
    
    - Represent a known-expired persisted token as `expires_in = 0`,
    preserving `None` for genuinely unknown expiry.
    - Add Streamable HTTP coverage requiring the token to refresh before the
    startup handshake.
    
    ## Validation
    
    - The new regression test fails on RMCP 1.7 before the fix and passes
    afterward.
    - The same scenario passes on the commit immediately before the RMCP 1.7
    update, using RMCP 0.15.
    - `just test -p codex-rmcp-client` (63 passed).
  • [codex] Add use_responses_lite 'override' logic (#26487)
    ## Summary
    
    - add a defaulted `ModelInfo.use_responses_lite` catalog field
    - support serializing `reasoning.context` while preserving the existing
    effort and summary path
    - has not been turned on for any models yet
    
    I've added an override to parallel tools if responses_lite is on. I've
    also forced persistent reasoning when using responses_lite. It would be
    ideal if we could centralize all the responses_lite plumbing, but I
    think this is best for now to keep the plumbing & diffs small.
    
    ## Testing
    
    - `cargo test -p codex-protocol
    model_info_defaults_availability_nux_to_none_when_omitted`
    - `RUST_MIN_STACK=8388608 cargo test -p codex-core
    responses_lite_sets_all_turns_context_and_disables_parallel_tool_calls`
    - `RUST_MIN_STACK=8388608 cargo test -p codex-core
    configured_reasoning_summary_is_sent`
    - `cargo check -p codex-core --tests`
    - `RUST_MIN_STACK=8388608 cargo clippy -p codex-core --tests` (passes
    with pre-existing warnings in `codex-code-mode` and
    `codex-core-plugins`)
  • [codex] Emit sandbox outcome telemetry event (#25955)
    ## Summary
    
    Adds a dedicated `codex.sandbox_outcome` telemetry event so we can query
    sandbox edge outcomes without threading sandbox metadata through
    tool-result output types.
    
    This is meant to make sandbox failures and approved escalation retries
    visible in OTEL while keeping the existing `codex.tool_result` event
    shape focused on tool completion data.
    
    ## What changed
    
    - Adds `SessionTelemetry::sandbox_outcome(...)`, which emits
    `codex.sandbox_outcome` as both a log and trace event.
    - Records the tool name, call id, sandbox outcome, initial attempt
    duration, and escalated attempt duration when a retry runs.
    - Emits `denied` when the sandbox blocks execution and no retry is run.
    - Emits `timed_out` and `signal` when those sandbox errors surface from
    tool execution.
    - Emits `escalated` when the initial sandboxed attempt fails and the
    approved unsandboxed retry succeeds.
    - Adds OTEL coverage for the new event payload, including timing fields.
    
    ## Validation
    
    - `RUST_MIN_STACK=8388608 just test -p codex-core
    sandbox_outcome_event_records_outcome
    handle_sandbox_error_user_approves_retry_records_tool_decision`
    - `just test -p codex-otel
    otel_export_routing_policy_routes_tool_result_log_and_trace_events
    runtime_metrics_summary_collects_tool_api_and_streaming_metrics`
    - `just fix -p codex-core`
    - `just fix -p codex-otel`
  • ci: test windows cross build (#25000)
    We cross build when using bazel for windows. This causes a couple
    hiccups in that v8 does a mksnapshot step that is expecting to snapshot
    on the host arch which wasn't matching when we were doing the
    crossbuild. This was causing segfault failiures when starting up
    codemode from a cross built artifact.
    
    This changes things such that we cross build the library and then run
    and link a snapshot on the host machine/arch which is windows. This
    gives us a functional snapshot and library that can start code-mode on
    windows.
    
    This fixes the build and then fixes two test regressions we had.
  • Pull plugin service less frequently (#26431)
    # Summary
    Reduce download traffic to `github.com/openai/plugins` while continuing
    to check for updates on every Codex startup.
    
    # Root cause
    The startup sync replaced the local repository with a fresh shallow
    clone whenever the remote revision changed. At Codex's global scale,
    repeatedly downloading the repository created excessive GitHub traffic.
    
    # Changes
    - Run `git ls-remote` on each startup to read the remote HEAD SHA.
    - Skip all repository downloads when the local and remote SHAs match.
    - Update existing checkouts with an exact-SHA shallow `git fetch`,
    followed by reset and clean.
    - Bootstrap new installations with `git init` plus the same shallow
    fetch, rather than cloning.
    - Keep the existing file lock so concurrent Codex processes serialize
    updates and do not duplicate fetches.
    - Preserve the existing GitHub HTTP and export archive fallback
    behavior.
    
    # Impact
    Each startup makes one lightweight remote HEAD check. Repository objects
    are downloaded only when the revision changes, and existing Git objects
    are reused during updates.
    
    # Validation
    - `just test -p codex-core-plugins startup_sync` (15 tests passed)
    - `just test -p codex-core-plugins` (201 tests passed)
    - `just clippy -p codex-core-plugins` (passes with one pre-existing
    `large_enum_variant` warning)
    - Production app-server smoke test against GitHub:
      - Fresh home: `ls-remote`, `git init`, one exact-SHA shallow fetch
    - Unchanged restart: `ls-remote` and local `rev-parse` only; no fetch or
    clone
    - Bench smoke passed
  • Improve Windows sandbox setup refresh diagnostics (#26471)
    ## Why
    
    Users have been seeing opaque Windows sandbox setup refresh failures
    such as `windows sandbox: spawn setup refresh`, including reports in
    #24391 and #21208. The setup refresh path already runs the Windows
    sandbox setup helper, but it was not using the same structured
    `setup_error.json` reporting path that elevated setup uses. As a result,
    when the helper exited non-zero, Codex only surfaced a generic refresh
    status instead of the helper's `SetupFailure` code and message.
    
    ## What changed
    
    - Clear stale `setup_error.json` before non-elevated setup refresh
    launches the helper.
    - When the refresh helper exits non-zero, read the helper-written report
    through the existing `report_helper_failure` path.
    - Keep a parent-side launch diagnostic for cases where the helper never
    starts, including the helper path, cwd, sandbox log path, and spawn
    error.
    - Clear the setup error report after a successful refresh.
    - Add regression coverage for report consumption and stale-report
    avoidance.
    
    ## Verification
    
    - `cargo test -p codex-windows-sandbox setup::tests::`
  • [codex] Expose unavailable app templates in plugin detail (#26317)
    ## Summary
    - Adds `unavailable_app_templates` to the app-server protocol and
    generated schemas/types.
    - Parses plugin-service `release.unavailable_app_templates` in the
    remote plugin client.
    - Maps remote unavailable templates into app-server `PluginDetail`.
    - Defaults local plugins to an empty unavailable app template list.
    
    ## Validation
    - `just write-app-server-schema`
    - `cargo +1.95.0 fmt --manifest-path codex-rs/Cargo.toml --all --check`
    - `cargo +1.95.0 test --manifest-path codex-rs/Cargo.toml -p
    codex-app-server-protocol schema_fixtures`
    - `cargo +1.95.0 check --manifest-path codex-rs/Cargo.toml -p
    codex-app-server-protocol -p codex-core-plugins -p codex-app-server`
    - `git diff --check`
    
    Note: default `cargo check` uses rustc 1.89 locally and failed because
    dependencies require newer Rust, so validation was rerun with installed
    Rust 1.95.
  • Add skill for pushing CI configuration changes (#26473)
    ## Why
    
    Codex agents that modify GitHub Actions configuration need clear
    guidance when repository push protections require temporary approval.
    Without it, an agent may pursue an unavailable exemption or stop before
    checking whether the user already has access.
    
    ## What
    
    Add a `pushing-ci-changes` skill that explains the restriction, directs
    agents to attempt the push first, and tells them how to involve the user
    when approval is required.
    
    ## Validation
    
    Not run; this change only adds skill documentation.
  • fix(app-server): expose remote MCP servers in plugin read (#26453)
    ## Why
    
    Remote plugin detail responses include MCP server metadata under
    `release.mcp_servers`, but Codex did not deserialize or propagate that
    field. As a result, `plugin/read` always returned an empty `mcpServers`
    list for remote plugins, so the plugin details pane omitted the MCP
    Servers section even when the remote plugin declares one.
    
    This affects uninstalled plugins as well: the remote detail API is the
    source of truth and returns MCP server keys without requiring a local
    plugin bundle.
    
    ## What changed
    
    - Deserialize MCP server entries from remote plugin detail responses.
    - Normalize their keys into a sorted, deduplicated list on
    `RemotePluginDetail`.
    - Return those keys from app-server `plugin/read` instead of hardcoding
    an empty list.
    - Add regression coverage proving an uninstalled remote plugin returns
    its MCP server names.
    
    ## Test plan
    
    - `just test -p codex-core-plugins`
    - `just test -p codex-app-server plugin_read`
  • [codex] Preserve logical paths during AGENTS.md discovery (#26465)
    ## Intent
    
    Follow up on #26205 by avoiding unnecessary filesystem canonicalization
    during `AGENTS.md` discovery. The configured working directory is
    already absolute, and canonicalization incorrectly switches symlinked
    workspaces from their logical parent hierarchy to the target's
    hierarchy.
    
    ## User-facing behavior
    
    For a symlinked working directory such as:
    
    ```text
    test-root/
    |-- logical-repo/
    |   |-- AGENTS.md              ("logical parent doc")
    |   `-- workspace ------------> physical-repo/workspace/
    `-- physical-repo/
        |-- AGENTS.md              ("physical parent doc")
        `-- workspace/
            `-- AGENTS.md          ("workspace doc")
    ```
    
    Before this change, Codex canonicalized `logical-repo/workspace` to
    `physical-repo/workspace` before discovery. It therefore loaded
    `physical-repo/AGENTS.md` and `physical-repo/workspace/AGENTS.md`,
    ignoring the instructions from the repository through which the user
    entered the workspace.
    
    After this change, ancestor discovery walks the configured logical path,
    so Codex loads `logical-repo/AGENTS.md`. Opening
    `logical-repo/workspace/AGENTS.md` still follows the symlink through the
    host filesystem, so the workspace document is also loaded.
    `physical-repo/AGENTS.md` is not loaded.
    
    ## Implementation
    
    Use the logical absolute working directory when discovering project
    instructions and reporting instruction sources. Filesystem reads still
    follow the working-directory symlink, so an `AGENTS.md` in the target
    workspace continues to load while ancestor discovery uses the symlink's
    parents.
    
    ## Validation
    
    Added integration coverage proving that discovery loads the logical
    parent's instructions and the target workspace's instructions, but not
    the target parent's instructions.
  • Use Winget release environment secret (#26466)
    ## Why
    `WINGET_PUBLISH_PAT` now lives as a GitHub environment secret under
    `mainline-release-winget`. The WinGet release job needs to enter that
    environment so `secrets.WINGET_PUBLISH_PAT` resolves during
    stable/mainline Rust releases.
    
    ## What Changed
    - Attach the `winget` job in `.github/workflows/rust-release.yml` to the
    `mainline-release-winget` environment.
    - Set `deployment: false` so the job can read environment secrets
    without creating GitHub deployment records.
    
    ## Operational Note
    The `mainline-release-winget` environment must allow `rust-v*.*.*` tag
    refs before this can run on release tags. The live environment currently
    has a custom policy named `rust-v*.*.*` with type `branch`; add the
    corresponding `tag` policy before relying on this path for a release.
    
    ## Validation
    - `git diff --check origin/main...HEAD --
    .github/workflows/rust-release.yml`
    - `ruby -e 'require "yaml"; ARGV.each { |f| YAML.load_file(f); puts
    "yaml ok: #{f}" }' .github/workflows/rust-release.yml`
  • [codex] Use model-advertised reasoning effort order (#26446)
    ## Summary
    - preserve the model catalog order for app-server
    `supportedReasoningEfforts` and document that client contract
    - render TUI reasoning choices in the advertised order
    - step reasoning shortcuts by adjacent list position instead of deriving
    order from known effort names
    - anchor unsupported configured values to the advertised default, or the
    first option when needed
    - remove canonical effort ordering helpers and the unused upgrade effort
    mapping
    
    ## Validation
    - `just fmt`
    - Local tests and compilation were not run per request; relying on CI.
    
    Stacked on #26444.
  • [codex] Support model-defined reasoning efforts (#26444)
    ## Summary
    - accept non-empty model-defined reasoning effort values while
    preserving built-in effort behavior
    - propagate the non-Copy effort type through core, app-server, TUI,
    telemetry, and persistence call sites
    - preserve string wire encoding and expose an open-string schema for
    clients
    - update model selection and shortcut behavior for model-advertised
    effort values
    
    ## Root cause
    `ReasoningEffort` gained a string-backed custom variant, so it could no
    longer implement `Copy` or rely on derived closed-enum serialization.
    Existing consumers still moved effort values from shared references and
    assumed a fixed built-in value set.
    
    ## Validation
    - `just fmt`
    - Local tests and compilation were not run per request; relying on CI.
  • Cleanup experimentalFeature/enablement/set (#26312)
    ## Why
    
    `experimentalFeature/enablement/set` still allowed several keys that no
    longer need to be managed through this API. Keeping those keys also
    preserved corresponding special-case logic, including refreshing the
    apps list when the `apps` key was enabled.
    
    The endpoint also rejected an entire request when any key was invalid or
    unsupported. That makes clients brittle when they send a mix of current
    and stale keys, even when the valid entries can still be applied safely.
    
    ## What changed
    
    - remove the feature keys that no longer need to be supported by
    `experimentalFeature/enablement/set`
    - remove the corresponding apps-list refresh path and its auth/config
    plumbing
    - ignore and warn on invalid or unsupported keys while still applying
    valid keys from the same request
    - update the app-server documentation and integration coverage for the
    reduced key set and partial-acceptance behavior
    
    ## Test plan
    
    - `just test -p codex-app-server experimental_feature_enablement_set` (6
    passed)
    - `just test -p codex-app-server` exercised the changed tests
    successfully; unrelated sandbox-dependent and watcher/timing tests
    failed locally
  • Remove response.processed websocket request (#26447)
    ## Why
    
    The Responses websocket client no longer needs to send a follow-up
    `response.processed` request after a turn response has already been
    recorded. Keeping that extra acknowledgement path adds feature-gated
    control flow and a second websocket request shape that no longer carries
    useful behavior.
    
    ## What Changed
    
    - Removed the `response.processed` websocket request type and sender.
    - Removed the `responses_websocket_response_processed` feature flag and
    schema entry.
    - Removed turn and remote-compaction plumbing that only tracked response
    IDs to send the acknowledgement.
    - Removed tests that existed solely to cover the deleted feature path.
    
    ## Validation
    
    - `just fix -p codex-core -p codex-api -p codex-features`
  • build: use ThinLTO for release binaries (#23710)
    ## Why
    
    Fat LTO makes release builds substantially slower without providing
    enough measured runtime benefit to justify the release CI long pole. The
    build-profile investigation found that keeping Cargo's default release
    `opt-level=3` and switching from fat LTO to ThinLTO (`3/thin/1`) reduced
    a clean `codex-cli` release build from 2073.893 seconds to 1243.172
    seconds, a 40.06% improvement.
    
    The resulting binary increased from 196.7 MiB to 211.8 MiB (+7.63%).
    Measured runtime changes were small: the worst image workload median was
    +0.86% and app-server startup was +0.31% relative to fat LTO. ThinLTO
    retains cross-crate optimization while avoiding most of the fat-LTO
    build cost.
    
    This deliberately avoids global size optimization: final-executable
    testing showed a substantial regression on the image request path, which
    is expected to become more important as image usage grows.
    
    ## What changed
    
    - Set the workspace release profile to `lto = "thin"`, retaining Cargo's
    default release `opt-level=3`.
    - Remove release and CI workflow-specific LTO overrides so
    release-profile builds consistently use the workspace setting.
    - Remove the now-unused Windows release workflow input and related
    diagnostic output.
    
    ## Validation
    
    - Confirmed the release profile parses with `cargo metadata --no-deps
    --format-version 1`.
    - CI validates release builds across the supported target matrix.
  • [codex] Fix Windows sandbox build script lint (#26445)
    ## Why
    
    The Windows ARM64 Cargo clippy job on `main` is failing because
    workspace lints deny `clippy::expect_used`, and the
    `codex-windows-sandbox` build script used `expect()` while reading
    `CARGO_MANIFEST_DIR`.
    
    ## What changed
    
    `codex-rs/windows-sandbox-rs/build.rs` now returns `Result<(), String>`
    from `main()` and converts a missing `CARGO_MANIFEST_DIR` into an
    explicit build-script error. The non-Windows early return and Windows
    linker argument behavior are unchanged.
    
    ## Verification
    
    - `just clippy -p codex-windows-sandbox -- -D warnings`
    - `just test -p codex-windows-sandbox`
  • Route AGENTS.md loading through environment filesystems (#26205)
    ## Why
    
    Workspace-specific `AGENTS.md` loading needs to use the selected
    environment filesystem so remote workspaces and child agents read
    instructions from their actual environment instead of the host
    filesystem. The app-server should report the same instruction sources
    the initialized thread actually loaded, rather than independently
    rescanning configuration and filesystem state.
    
    ## What changed
    
    - Introduce `LoadedAgentsMd` to retain ordered user, project, and
    internal instructions with their provenance.
    - Load and canonicalize workspace `AGENTS.md` paths through the primary
    `EnvironmentManager` environment, then render the loaded instructions
    when constructing turn context.
    - Expose cached loaded instruction sources from initialized threads and
    use them for app-server start, resume, and fork responses.
    - Preserve global `CODEX_HOME` loading and separator behavior while
    excluding empty project files that did not supply model-visible
    instructions.
    - Add integration coverage for CLI injection, selected-environment
    provenance and rendering, empty environment selection, and cached
    sources on loaded-thread resume.
    
    ## Validation
    
    - `just test -p codex-core agents_md`
    - `just test -p codex-core
    selected_environment_sources_match_model_visible_instructions`
    - `just test -p codex-exec agents_md`
    - `just test -p codex-app-server instruction_sources`
    - `just test -p codex-app-server --status-level fail`
  • Use Azure artifact signing environment secrets (#25945)
    ## Why
    Windows release signing should read Azure signing credentials from the
    `azure-artifact-signing` environment instead of the old repo-level
    `AZURE_TRUSTED_SIGNING_*` names. The smoke runs confirmed the
    environment secrets resolve with the new `AZURE_ARTIFACT_SIGNING_*`
    names once the Windows signing job is attached to that environment.
    
    ## What Changed
    - Put the real Windows signing job in the `azure-artifact-signing`
    environment.
    - Switch the Windows signing action inputs from
    `AZURE_TRUSTED_SIGNING_*` to `AZURE_ARTIFACT_SIGNING_*`.
    - Drop the obsolete `workflow_call.secrets` declarations for the old
    repo-level secret names; the caller continues to use `secrets: inherit`.
    - Remove the temporary branch-trigger and Windows-only smoke-test
    workflow changes before finalizing this PR.
    
    ## Validation
    - `git diff --check -- .github/workflows/rust-release.yml
    .github/workflows/rust-release-windows.yml`
    - `ruby -e 'require "yaml"; ARGV.each { |f| YAML.load_file(f); puts
    "yaml ok: #{f}" }' .github/workflows/rust-release.yml
    .github/workflows/rust-release-windows.yml`
  • core: allow excluding tool namespaces from code mode (#26320)
    ## Why
    
    Research and training setups need to control which tool namespaces
    appear inside code mode's nested `tools` surface without disabling those
    tools entirely. This makes it possible to train against a deliberately
    reduced nested-tool setup while preserving the normal direct and
    deferred tool paths.
    
    ## What
    
    - Extend `features.code_mode` to accept structured configuration while
    preserving the existing boolean syntax.
    - Add an exact `excluded_tool_namespaces` list under
    `[features.code_mode]`:
    
      ```toml
      [features.code_mode]
      enabled = true
      excluded_tool_namespaces = ["mcp__codex_apps", "multi_agent_v1"]
      ```
    
    - Filter matching canonical `ToolName` namespaces when constructing code
    mode's nested router and code-mode-specific direct tool descriptions.
    - Keep excluded tools registered, directly exposed in mixed code mode,
    and discoverable through top-level `tool_search` when otherwise
    eligible.
    - Derive deferred nested-tool guidance after namespace filtering so the
    `exec` description does not advertise excluded-only deferred tools.
    - Preserve the boolean/table representation when materializing config
    locks and update the generated config schema.
    
    ## Testing
    
    - `just test -p codex-features`
    - `just test -p codex-config`
    - `just test -p codex-core load_config_resolves_code_mode_config`
    - `just test -p codex-core
    lock_contains_prompts_and_materializes_features`
    - `just test -p codex-core
    excluded_deferred_namespaces_do_not_enable_nested_tool_guidance`
    - `just test -p codex-core
    code_mode_excludes_configured_nested_tool_namespaces`
    - `cargo check -p codex-thread-manager-sample`
  • [codex-analytics] emit forked thread id on initialization (#26248)
    ## Why
    - Thread initialization analytics do not identify the source thread for
    forked threads.
    - The session viewer needs this lineage to construct thread trees.
    - Depends on openai/openai#987854. Do not release this change before
    that backend schema change is deployed.
    
    ## What Changed
    - Adds optional `forked_from_thread_id` to `codex_thread_initialized`.
    - Populates it from the existing thread fork lineage for app-server and
    in-process subagent initialization paths.
    - Keeps it null for non-forked threads.
    
    ## Verification
    - `just fmt`
    - `just test -p codex-analytics`
    - `just test -p codex-app-server
    thread_fork_tracks_thread_initialized_analytics`
  • external-agent-migration: avoid mixed MCP transport configs (#26435)
    ## Why
    
    MCP migration could recursively merge an imported server into an
    existing same-named Codex server. When one definition used stdio and the
    other used HTTP, this produced an invalid mixed configuration containing
    both `command` and `url`.
    
    ## What changed
    
    - Merge MCP configuration at the server level instead of field by field.
    - Preserve an existing same-named Codex MCP server unchanged.
    - Report only MCP servers that would actually be added during detection.
    - Add regression coverage for mixed command/HTTP source configurations.
    - Use neutral fixture names and reserved `example.com` URLs.
    
    ## Test plan
    
    - `just test -p codex-app-server repo_mcp`
      - 5 tests passed.
    - `just test -p codex-external-agent-migration
    mcp_migration_prefers_command_transport_for_mixed_server_config`
      - 1 test passed.
  • app-server: support -c config overrides (#26436)
    ## Why
    
    The standalone `codex-app-server` binary already routed a
    `CliConfigOverrides` value into app-server startup, but its own clap
    args did not expose the shared `-c/--config` option. That meant
    `codex-app-server -c key=value` was rejected before the existing config
    override path could run, unlike the main `codex` CLI.
    
    ## What Changed
    
    - Flatten `CliConfigOverrides` into `AppServerArgs` in
    `codex-rs/app-server/src/main.rs`.
    - Pass parsed overrides to `run_main_with_transport_options` instead of
    always using `CliConfigOverrides::default()`.
    - Add a binary parser test covering both `-c` and `--config` for the
    standalone app-server.
    
    ## Verification
    
    - `just test -p codex-app-server
    app_server_accepts_cli_config_overrides`
    
    The broader `just test -p codex-app-server` run was also attempted. It
    compiled and ran 812 tests, with 796 passing, but failed in this local
    sandbox on unrelated `sandbox-exec: sandbox_apply: Operation not
    permitted` command-exec/turn integration paths and a skills watcher
    timeout.
  • Expose configured marketplace source in plugin list JSON (#26417)
    ## Summary
    - Follow-up to #25330
    - Add `marketplaceSource` to `codex plugin list --json` entries for
    configured marketplaces
    - Keep the existing per-plugin `source` field unchanged; this still
    reports the local plugin source path
    - Include only the configured marketplace `sourceType` and `source` from
    `config.toml`
    - Keep human-readable output unchanged
    - Add CLI coverage for configured local and git marketplace sources
    
    Example:
    
    ```json
    {
      "source": {
        "source": "local",
        "path": "/path/to/.codex/.tmp/marketplaces/debug/plugins/sample"
      },
      "marketplaceSource": {
        "sourceType": "git",
        "source": "https://example.com/acme/agent-skills.git"
      }
    }
    ```
    
    ## Validation
    - `just fmt`
    - `just fix -p codex-cli`
    - `just test -p codex-cli plugin_list`
  • Bound external agent session detection work (#26291)
    ## Why
    
    External agent migration detection parsed and hashed every JSONL session
    file. For users with many large conversations, launching migration could
    consume substantial CPU and disk resources.
    
    Detection only needs the most recent sessions for the migration UI, so
    full-content work should be bounded.
    
    ## What
    
    - Use file modification metadata to select the 50 most recent eligible
    sessions before parsing JSONL content.
    - Skip unchanged imported sessions using metadata stored in the import
    ledger.
    - Preserve content hashing when metadata indicates a session may have
    changed.
    - Stream SHA-256 calculation through a 64 KiB buffer instead of loading
    an entire session into memory.
    - Continue detecting older sessions in subsequent batches after newer
    sessions are imported.
    
    ## Validation
    
    - `RUST_MIN_STACK=8388608 cargo nextest run --no-fail-fast -p
    codex-external-agent-sessions`
      - 20 tests passed.
    - Benchmarked release builds against 250 valid JSONL sessions totaling
    501 MiB:
      - Median detection time decreased from 1,138.8 ms to 47.0 ms.
      - CPU instructions decreased by 95.8%.
      - Both versions returned the expected 50 sessions.
    
    The benchmark used warm filesystem caches and measured the reduction in
    parsing, hashing, and CPU work.
  • Add saved image path hint to standalone image generation (#25947)
    ## Why
    
    Standalone image generation returns image bytes to the model, but the
    model also needs the host artifact path to reference the generated file
    in follow-up work.
    
    ## What changed
    
    - Append the default saved-image path hint alongside the generated image
    tool output.
    - Reuse the existing core image-generation hint text.
    - Pass the thread ID and Codex home directory needed to compute the
    artifact path.
    - Add app-server and extension coverage for the model-visible hint.
    
    ## Validation
    
    - `just fmt`
    - `just bazel-lock-check`
    - `just test -p codex-app-server
    standalone_image_generation_returns_saved_path_hint_to_model`
  • Simplify Codex CLI README (#26313)
    ## Summary
    
    The codex-rs README was left over from before we moved the docs into the
    developer site. Its contents were very much out of date, and we received
    some bug reports about it.
  • Load plugin hooks without other plugin capabilities (#26272)
    ## Summary
    
    `hooks/list` only consumes plugin hook declarations, but previously
    loaded every enabled plugin's skills, MCP configuration, apps, and
    capability summary before discarding them.
    
    In a local benchmark, this reduced `hooks/list` latency by over 100ms
    (e.g., from 594 to 467ms on startup, and 168 to 16ms when making a
    `hooks/list` call later in the same TUI session). This is on the
    critical path to rendering the TUI, so every 10s of ms should be eyed
    skeptically (IMO).
    
    This change adds a hook-specific plugin loading path that preserves
    plugin enablement, remote/local conflict resolution, deterministic
    ordering, manifest resolution, and hook-loading warnings while skipping
    unrelated capabilities. (I think there's room for a more general design
    here that allows you to project the capabilities you need at load-time,
    but that seems unnecessary right now.)
  • Reduce SQLite contention from OpenTelemetry SDK debug logs (#26396)
    ## Summary
    
    - skip `opentelemetry_sdk` DEBUG and TRACE events before formatting or
    queueing them for the SQLite log sink
    - preserve INFO, WARN, and ERROR events from the SDK, along with TRACE
    events from application targets
    - add a persistence-level regression test for the target and level
    policy
    
    ## Why
    
    OpenTelemetry's batch log processor emits internal
    `BatchLogProcessor.ExportingDueToTimer` meta-events every second per
    Codex process. In measured high-fanout `logs_2.sqlite` databases,
    low-level `opentelemetry_sdk` events accounted for over 30% of retained
    rows (30-60% on the machines of people I asked to check).
    
    Persisting this SDK bookkeeping across many processes adds substantial
    write volume and contention without representing application activity.
    
    ## Validation
    
    - `just test -p codex-state` (132/132 tests passed, plus bench smoke)
    - `just fix -p codex-state`
    - `just fmt`
  • Optimize unbounded byte scans with memchr (#26265)
    ## Summary
    
    This PR adds `memchr` for some low-hanging performance improvements
    (namely, in MCP stdio, Ollama streaming, and full message-history
    newline counts).
    
    Codex produced the following release benchmarks:
    
    | Operation | Before | After | Speedup |
    | --- | ---: | ---: | ---: |
    | MCP 1 MiB chunked line | 2.172 s | 3.984 ms | 545x |
    | Ollama 1 MiB chunked line | 1.673 s | 2.790 ms | 600x |
    | Count newlines in 10 MiB history | 132.83 ms | 20.05 ms | 6.6x |
    
    With a "real" MCP setup (`ExecutorStdioServerLauncher` started a Python
    MCP server, completed `initialize`, requested `tools/list`, and
    deserialized a 1 MiB tool description over newline-delimited stdio),
    it's about 16x faster end-to-end:
    
    | Branch | 50 calls | Per call |
    | --- | ---: | ---: |
    | `main` | 862.53 ms | 17.25 ms |
    | this branch | 53.89 ms | 1.08 ms |
    
    `memchr` is already in our dependency tree and extremely widely used for
    this kind of optimized scanning.
  • Bridge host-loaded skills into the skills extension (#26172)
    ## Why
    
    The skills extension needs to become the path that exposes local host
    skills without losing the behavior already owned by core skill loading.
    Host skill discovery is not just `$CODEX_HOME/skills`: it also includes
    config layers, bundled-skill settings, plugin roots, runtime extra
    roots, and the filesystem for the selected primary environment.
    
    Rather than making the extension reload host skills and risk drifting
    from that authoritative load, this PR bridges the already-loaded
    per-turn skills outcome into the extension. That lets the extension
    advertise host skills and inject explicit `$skill` prompts while
    preserving the same roots, disabled/hidden state, rendered paths, and
    environment-backed file reads that the legacy path uses.
    
    ## What Changed
    
    - Adds `HostLoadedSkills` in `core-skills` to wrap the turn's
    `SkillLoadOutcome` and read `SKILL.md` through the filesystem that
    loaded that skill.
    - Stores `HostLoadedSkills` in turn extension data for normal turns and
    review turns, so the skills extension can consume the loaded host
    catalog without reloading it.
    - Adds `HostSkillProvider` under `ext/skills/src/provider/host.rs`,
    mapping host-loaded skill metadata into the skills-extension
    catalog/read contract.
    - Registers the host provider by default from
    `codex_skills_extension::install()`.
    - Preserves host skill metadata such as dependencies, disabled state,
    hidden-from-prompt policy, and slash-normalized display paths.
    - Passes host-loaded skills through `SkillListQuery` and
    `SkillReadRequest` so explicit skill invocation reads only resources
    from the loaded host catalog.
    - Adds integration coverage for a real legacy
    `$CODEX_HOME/skills/.../SKILL.md` skill being listed and injected
    through the installed extension.
    
    ## Testing
    
    - Added `installed_extension_loads_host_skills_from_legacy_roots` in
    `ext/skills/tests/skills_extension.rs`.
    - `just test -p codex-skills-extension`
  • Gate automatic idle turns in Plan mode (#26147)
    ## Why
    
    Goal idle continuation is extension-triggered model-visible work, so it
    should follow one core-owned rule for when automatic work may start. In
    particular, it should not jump ahead of queued user/client work, start
    while another task is active, or inject a continuation turn while the
    thread is in Plan mode.
    
    Keeping this policy in `try_start_turn_if_idle` avoids passing
    `collaboration_mode` or review-specific state through
    `ThreadLifecycleContributor::on_thread_idle`. Active `/review` is
    covered by the same active-task gate because Review turns are not
    steerable.
    
    ## What Changed
    
    - Teach `Session::try_start_turn_if_idle` to reject automatic idle turns
    in Plan mode, both before reserving an idle turn and after building the
    turn context.
    - Document `CodexThread::try_start_turn_if_idle` as the extension-facing
    gate for automatic idle work, including Plan-mode and active Review-task
    behavior.
    - Add focused coverage for Plan-mode rejection and active Review-task
    rejection without queuing synthetic input.
    
    ## Testing
    
    - `just test -p codex-core try_start_turn_if_idle`
  • chore: calm down (#26367)
    Prompt update to address feedback
  • ci: sign macOS release artifacts with Azure Key Vault (#26252)
    ## Why
    
    The public Codex release workflow needs to sign and notarize macOS
    binaries and DMGs without placing the Developer ID private key in
    GitHub. This moves the private-key operation behind the protected
    `codesigning` environment and uses GitHub OIDC with Azure Key Vault
    PKCS#11, while preserving the existing external `build_unsigned` /
    `promote_signed` fallback.
    
    ## What changed
    
    - Add a reusable AKV PKCS11 setup action that authenticates to Azure
    with OIDC, downloads pinned signing tools, verifies their SHA-256
    digests, and loads the public signing certificate from Key Vault.
    - Replace the legacy macOS signing action with scripts that support
    AKV-backed `rcodesign`, notarize signed binaries and DMGs, and staple
    DMG notarization tickets.
    - Restructure `rust-release.yml` so macOS builds produce unsigned
    artifacts first, protected jobs perform signing and notarization, macOS
    runners package and verify the results, and release publishing waits for
    verified artifacts.
    - Preserve the manual external-signing handoff flow and make manual-mode
    conditions explicit.
    - Move the Codex entitlements file alongside the signing scripts and
    update CODEOWNERS for the new signing surfaces.
    
    ## Verification
    
    - [Live protected signing workflow
    run](https://github.com/openai/codex/actions/runs/26903610631) completed
    successfully for both macOS architectures, including binary
    signing/notarization, DMG signing/notarization, and final artifact
    verification.
    - Downloaded both signed DMGs and independently verified their checksums
    and strict signatures.
    - Confirmed `xcrun stapler validate` succeeds and Gatekeeper accepts
    both DMGs as `Notarized Developer ID`.
    - Mounted both DMGs and confirmed the contained `codex` and
    `codex-responses-api-proxy` binaries have valid Developer ID signatures
    for the expected architectures.
    
    ---------
    
    Co-authored-by: shijie-openai <shijie.rao@openai.com>
  • [codex-analytics] report compaction request token counts (#25946)
    ## Why
    
    Compaction analytics need token counts that better represent the request
    being compacted. The existing session snapshot can diverge from the
    actual remote compaction request after output rewriting, and remote v2
    can use server-side Responses usage when available.
    
    ## What changed
    
    - Add an optional `active_context_tokens_before` override to
    `CompactionAnalyticsAttempt::track(...)` for remote compaction when it
    has a better before-token value than the begin-time session snapshot.
    The local `/compact` path passes no override.
    - For remote v1 `responses_compact`, subtract the estimated token delta
    from pre-compaction output rewriting from the session snapshot, capped
    by locally-added tokens since the last successful API response.
    - For remote v2 `responses_compaction_v2`, use the same bounded
    output-rewrite fallback as remote v1, then overwrite
    `active_context_tokens_before` with server `token_usage.input_tokens`
    from the `response.completed` event when present.
    - Keep the existing v2 compaction-output validation while carrying the
    completed response token usage through `collect_compaction_output`.
    
    ## Verification
    
    - `just fmt`
    - `just test -p codex-core
    collect_compaction_output_accepts_additional_output_items`
    - `git diff --check`
  • cli: add package path from install context (#26189)
    ## Why
    
    Codex package installs include helper binaries in `codex-path`, such as
    the bundled `rg`. Package-layout launches should add that directory
    before user commands run, but standalone launches were missing it while
    npm launches only worked because `codex.js` had its own legacy `PATH`
    rewrite. That made npm and standalone package behavior diverge.
    
    Shell snapshot restoration can also reset `PATH` after runtime setup.
    Any package-owned `PATH` prepend has to be recorded as an explicit
    runtime override so shells, unified exec, and user-shell commands keep
    access to `codex-path` after a snapshot is sourced.
    
    ## Repro
    
    Before this change, a curl-installed package could contain `rg` under
    `codex-path` but still fail to put it on `PATH`:
    
    ```shell
    mkdir /tmp/test-codex-curl
    curl -fsSL https://chatgpt.com/codex/install.sh \
      | CODEX_HOME=/tmp/test-codex-curl CODEX_NON_INTERACTIVE=1 sh
    /tmp/test-codex-curl/packages/standalone/current/bin/codex exec \
      --skip-git-repo-check 'print `which -a rg`'
    find /tmp/test-codex-curl -name rg
    ```
    
    The `which -a rg` output omitted the packaged helper even though `find`
    showed it under
    `/tmp/test-codex-curl/packages/standalone/releases/.../codex-path/rg`.
    
    The npm install path behaved differently only because
    `codex-cli/bin/codex.js` had legacy `PATH` rewriting:
    
    ```shell
    mkdir /tmp/test-codex-npm
    cd /tmp/test-codex-npm
    npm install @openai/codex
    ./node_modules/.bin/codex exec --skip-git-repo-check 'print `which -a rg`'
    ```
    
    That printed the npm package's `vendor/<target>/codex-path/rg` first.
    This PR moves that behavior into Rust-side package launch setup so
    curl/standalone and npm/bun launches agree without JS rewriting `PATH`.
    
    ## What Changed
    
    - `codex-rs/arg0` now uses
    `InstallContext::current().package_layout.path_dir` to prepend the
    package helper directory before any threads are created.
    - Package helper `PATH` setup is independent from the temporary arg0
    alias setup, so `codex-path` is still added even if CODEX_HOME tempdir,
    lock, or symlink setup fails.
    - `codex-rs/install-context` detects the canonical package layout we
    ship: `bin/`, `codex-resources/`, and `codex-path/` next to
    `codex-package.json`.
    - Shell, local unified exec, and user-shell runtimes now record package
    `codex-path` prepends in `explicit_env_overrides`, matching the existing
    zsh-fork behavior so shell snapshots cannot restore over the package
    helper path.
    - Remote unified exec requests do not receive the local app-server
    package path overlay.
    - `codex-cli/bin/codex.js` no longer computes or overrides `PATH`; it
    only locates the native binary in the canonical package layout and
    passes npm/bun management metadata.
    - Added regression tests for `PATH` ordering, package layout detection,
    and shell snapshot preservation of package path prepends.
    
    ## Verification
    
    - `node --check codex-cli/bin/codex.js`
    - `just test -p codex-install-context -p codex-arg0`
    - `just test -p codex-core
    user_shell_snapshot_preserves_package_path_prepend`
    - `just test -p codex-core tools::runtimes::tests`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `just fix -p codex-install-context -p codex-arg0 -p codex-core`