Commit Graph

6705 Commits

  • windows-sandbox: share bundled helper lookup (#23735)
    ## Summary
    
    Follow-up to #23636 review feedback: the Windows sandbox had two copies
    of the same bundled-helper lookup order, one for
    `codex-command-runner.exe` in `helper_materialization.rs` and one for
    `codex-windows-sandbox-setup.exe` in `setup.rs`.
    
    This PR centralizes that lookup in
    `helper_materialization::bundled_executable_path_for_exe()` and has
    setup reuse it for `codex-windows-sandbox-setup.exe`. The lookup
    behavior is unchanged: direct sibling first, package-root
    `codex-resources/` when running from `bin/`, then legacy sibling
    `codex-resources/`.
    
    ## Test plan
    
    - `cargo test -p codex-windows-sandbox`
    
    ## Notes
    
    I also attempted `cargo check -p codex-windows-sandbox --target
    x86_64-pc-windows-gnullvm`, but this local host is missing
    `x86_64-w64-mingw32-clang`.
  • windows-sandbox: send permission profiles to elevated runner (#22918)
    ## Why
    
    This is the next PR in the Windows sandbox migration stack after #22896.
    The bottom PR introduces a Windows-local resolved permissions helper
    while existing callers still start from legacy `SandboxPolicy`. This PR
    moves the elevated runner IPC boundary to `PermissionProfile`, which
    makes the direction of the stack visible without changing the public
    core call sites yet.
    
    Because that changes the CLI-to-command-runner message shape, the framed
    IPC protocol version is bumped in the same PR so the boundary change is
    explicit.
    
    ## What changed
    
    - Replaced elevated IPC `policy_json_or_preset`/`sandbox_policy_cwd`
    fields with `permission_profile`/`permission_profile_cwd`.
    - Bumped the elevated command-runner IPC protocol to
    `IPC_PROTOCOL_VERSION = 2` and switched parent/runner frames to use the
    shared constant.
    - Converted the parent elevated paths from the parsed legacy policy into
    a materialized `PermissionProfile` before sending the runner request.
    - Added `WindowsSandboxTokenMode` resolution for managed
    `PermissionProfile` values and made the runner choose read-only vs
    writable-root capability tokens from that resolved profile.
    - Rejected disabled, external, unrestricted, and full-disk-write
    profiles before token selection.
    - Added IPC JSON coverage for tagged `PermissionProfile` payloads and
    token-mode unit coverage for the resolved permission helper.
    
    ## Verification
    
    - `cargo test -p codex-windows-sandbox`
    - `just fix -p codex-windows-sandbox`
    - `cargo check -p codex-windows-sandbox --target x86_64-pc-windows-msvc
    --tests` was attempted locally but blocked before crate type-checking
    because the macOS compiler environment lacks Windows C headers such as
    `windows.h` and `assert.h`; GitHub Windows CI is the required
    verification for the runner path.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22918).
    * #23715
    * #23714
    * #23167
    * #22923
    * __->__ #22918
  • dotslash: publish Codex entrypoints from package archives (#23638)
    ## Summary
    
    DotSlash should resolve the same canonical package archives used by
    standalone installers and npm platform packages, rather than continuing
    to point at single-binary zstd artifacts or the older Linux bundle
    archive.
    
    This updates the Codex CLI and `codex-app-server` DotSlash release
    config entries to match `codex-package-<target>.tar.gz` and
    `codex-app-server-package-<target>.tar.gz`, with paths that select
    `bin/codex` or `bin/codex-app-server` inside the extracted package. The
    other helper outputs stay on their existing per-binary artifacts for
    now.
    
    ## Test plan
    
    - `python3 -m json.tool .github/dotslash-config.json > /dev/null`
    - Ran a Python regex smoke test that checked every updated `codex` and
    `codex-app-server` platform entry against the archive names emitted by
    `.github/scripts/build-codex-package-archive.sh`.
  • fix(config): resolve cloud requirements deny-read globs (#23729)
    ## Why
    
    Cloud-managed `requirements.toml` contents were deserialized without an
    `AbsolutePathBuf` base directory. Relative managed
    `permissions.filesystem.deny_read` glob entries therefore failed while
    the equivalent local system requirements path succeeded under its
    `AbsolutePathBufGuard`. This follows the `codex_home` base path
    convention clarified in https://github.com/openai/codex/pull/15707.
    
    ## What changed
    
    - Resolve cloud requirements TOML under an `AbsolutePathBufGuard` rooted
    at `codex_home`.
    - Reuse the same base for cloud requirements loaded from the signed
    cache.
    - Add a regression test for a relative cloud-managed `deny_read` glob.
    
    ## Validation
    
    - `just fmt`
    - `cargo test -p codex-cloud-requirements`
    - `cargo clippy -p codex-cloud-requirements --all-targets --no-deps`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `git diff --check`
  • npm: ship platform packages in Codex package layout (#23637)
    ## Summary
    
    The npm platform packages should stop carrying a bespoke native layout
    now that the release workflow builds canonical Codex package archives.
    Keeping npm on the same `bin/`, `codex-resources/`, and `codex-path/`
    structure lets the Rust package-layout detection behave consistently
    across standalone, npm, and future DotSlash installs.
    
    This changes platform npm packages to stage the `codex-package` artifact
    for each target under `vendor/<target>`. The Node launcher now resolves
    `bin/codex` and prepends `codex-path`, while retaining legacy
    `vendor/<target>/codex` and `vendor/<target>/path` fallback support for
    local development and migration. The npm staging helper downloads
    `codex-package` archives instead of rebuilding the CLI payload from
    individual `codex`, `rg`, `bwrap`, and sandbox helper artifacts.
    
    CI still needs to stage npm packages from historical rust-release
    workflow artifacts that predate package archives, so the staging scripts
    expose an explicit `--allow-legacy-codex-package` fallback. That
    fallback synthesizes the canonical package layout from legacy per-binary
    artifacts and is wired only into the CI smoke path; release staging
    remains strict and continues to require real package archives.
    
    For direct local use, `install_native_deps.py` now points its built-in
    default workflow at the same recent artifact run used by CI and
    automatically enables legacy package synthesis only when
    `--workflow-url` is omitted. Explicit workflow URLs remain strict unless
    callers opt in with `--allow-legacy-codex-package`.
    
    ## Test plan
    
    - `python3 -m py_compile codex-cli/scripts/build_npm_package.py
    codex-cli/scripts/install_native_deps.py scripts/stage_npm_packages.py
    scripts/codex_package/cli.py`
    - `node --check codex-cli/bin/codex.js`
    - `ruby -e 'require "yaml";
    YAML.load_file(".github/workflows/rust-release.yml");
    YAML.load_file(".github/workflows/ci.yml"); puts "ok"'`
    - Staged a synthetic `codex-linux-x64` platform package from a canonical
    vendor tree and verified it copied only `bin/`, `codex-path/`,
    `codex-resources/`, and `codex-package.json`.
    - Imported `install_native_deps.py` and extracted a synthetic
    `codex-package-x86_64-unknown-linux-musl.tar.gz` into `vendor/<target>`.
    - Ran legacy-layout conversion smokes for Linux, Windows, and unsigned
    macOS artifact naming.
    - Ran a synthetic `install_native_deps.py` default-workflow smoke that
    verifies legacy package synthesis is automatic only when
    `--workflow-url` is omitted.
    - `NPM_CONFIG_CACHE="$tmp_dir/npm-cache" python3
    ./scripts/stage_npm_packages.py --release-version 0.125.0 --workflow-url
    https://github.com/openai/codex/actions/runs/26131514935 --package codex
    --allow-legacy-codex-package --output-dir "$tmp_dir"`
    - `node codex-cli/bin/codex.js --version`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23637).
    * #23638
    * __->__ #23637
  • Fix thread settings clippy failure (#23724)
    ## Why
    
    `main` picked up two small Rust build failures after nearby merges:
    
    - #23507 added a real handler for
    `ServerNotification::ThreadSettingsUpdated`, but the same variant was
    still listed in the ignored-notification match arm. Full Clippy runs
    treat the resulting unreachable-pattern warning as an error.
    - #23666 added `turn_id` and `truncation_policy` to
    `codex_tools::ToolCall`, while the goal extension backend test fixtures
    from the goal-extension work still used the old shape. That left
    `codex-goal-extension` tests unable to compile once the branches met on
    `main`.
    
    ## What changed
    
    Removed the duplicate `ThreadSettingsUpdated` match pattern from
    `tui/src/chatwidget/protocol.rs`.
    
    Updated the goal extension test `tool_call` helper to populate the new
    `ToolCall` fields, and reused that helper for the one direct literal
    that still had the old field list.
    
    ## Verification
    
    - `just fix -p codex-tui`
    - `cargo test -p codex-goal-extension`
  • add standalone websearch api client (#23655)
    add standalone web search request types and a `codex-api` client ahead
    of the extension-contributed search tool.
    
    this adds typed commands/settings and opaque encrypted output handling
    for the new standalone search flow. the endpoint types are close to
    finalized but may still shift slightly as that API settles.
  • [codex] Preserve failed goal accounting flushes (#23717)
    ## What
    - Preserve database accounting failures from the goal extension instead
    of collapsing them into `None`
    - Warn with turn/tool context when a flush fails
    - Keep stop/abort accounting snapshots alive when the final flush did
    not persist
    
    ## Why
    PR #23696 can finish and discard a turn snapshot after
    `account_thread_goal_usage` fails. That loses the final accumulated
    accounting state silently. This follow-up keeps that failure explicit
    and avoids deleting the local snapshot in the failing path.
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-goal-extension`
  • install: consume Codex package archives (#23636)
    ## Summary
    
    Standalone installs should exercise the same canonical package archive
    layout that release builds produce, rather than unpacking npm platform
    packages and reconstructing a parallel install tree.
    
    This updates `install.sh` and `install.ps1` to prefer
    `codex-package-<target>.tar.gz` plus `codex-package_SHA256SUMS`
    introduced in https://github.com/openai/codex/pull/23635, authenticate
    the checksum manifest against GitHub release metadata, verify the
    selected package archive against the authenticated manifest, and install
    the package archive directly.
    
    ## Compatibility Notes
    
    Package installs still leave a compatibility command at `current/codex`
    for managed daemon flows, while visible command shims point at
    `bin/codex` inside the package layout.
    
    Recent releases that predate package archives still publish per-platform
    npm artifacts, so both installers keep a legacy platform npm fallback
    for those versions and verify those archives against release metadata
    directly.
    
    Releases old enough to publish only the single root
    `codex-npm-<version>.tgz` archive are intentionally out of scope. The
    installers fail clearly when neither package archives nor per-platform
    npm archives are present.
    
    On Windows, the runtime helper lookups now recognize package-layout
    installs where `codex.exe` runs from `bin/`, so
    `codex-command-runner.exe` and `codex-windows-sandbox-setup.exe` resolve
    from the top-level `codex-resources/` directory. The direct-sibling and
    older sibling-resource fallbacks are preserved.
    
    ## Test plan
    
    - `sh -n scripts/install/install.sh`
    - `bash -n scripts/install/install.sh`
    - `pwsh -NoProfile -Command '$tokens=$null; $errors=$null; $null =
    [System.Management.Automation.Language.Parser]::ParseFile("scripts/install/install.ps1",
    [ref]$tokens, [ref]$errors); if ($errors.Count) { $errors | Format-List
    *; exit 1 }'`
    - `HOME="$home_dir" CODEX_HOME="$tmp_dir/codex-home"
    CODEX_INSTALL_DIR="$bin_dir" PATH="$bin_dir:$PATH" sh
    scripts/install/install.sh --release 0.125.0`
    - Verified the 0.125.0 isolated install leaves the visible command
    pointed at `current/codex` and includes the legacy `codex-resources/rg`
    payload.
    - `cargo test -p codex-windows-sandbox`
    - `just fix -p codex-windows-sandbox`
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23636).
    * #23638
    * #23637
    * __->__ #23636
  • feat: add turn_id and truncation_policy to extension tool calls (#23666)
    ## Why
    
    Extension-owned tools currently receive a stripped `ToolCall` with only
    `call_id`, `tool_name`, and `payload`.
    That makes extension work that needs turn-local execution context
    awkward, especially web-search extension work that needs the active
    `truncation_policy` at tool invocation time.
    
    Reconstructing that value from config or `ExtensionData` would be
    indirect and could drift from the actual turn context, so the cleaner
    fix is to pass the needed turn metadata directly on the extension-facing
    invocation type.
    
    ## What changed
    
    - added `turn_id` and `truncation_policy` to `codex_tools::ToolCall`
    - populated those fields when core adapts `ToolInvocation` into an
    extension tool call
    - added a focused adapter test that verifies extension executors receive
    the forwarded turn metadata
    - updated the memories extension tests to construct the richer
    `ToolCall`
    - added the `codex-utils-output-truncation` dependency to `codex-tools`
    and refreshed lockfiles
    
    ## Testing
    
    - `cargo test -p codex-tools`
    - `cargo test -p codex-memories-extension`
    - `cargo test -p codex-core passes_turn_fields_to_extension_call`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
  • Sync TUI thread settings through app server (#23507)
    Builds on #23502.
    
    ## Why
    
    #23502 adds the app-server `thread/settings/update` API and matching
    `thread/settings/updated` notification. The TUI already lets users
    change thread-scoped settings such as model, reasoning effort, service
    tier, approvals, permissions, personality, and collaboration mode, but
    those updates need to flow through the app server so embedded and
    connected clients observe the same thread state.
    
    This is a rework (simplification) of PR
    https://github.com/openai/codex/pull/22510. It has the same
    functionality, but the underlying `thread/settings/update` api is now
    simpler in that it no longer returns the effective settings as a
    response. Now, clients receive the effective settings only through the
    `thread/settings/updated` notification.
    
    ## What Changed
    
    This updates the TUI to send `thread/settings/update` whenever those
    thread-scoped settings change and to treat the RPC response as the
    authoritative acknowledgement. It also routes `thread/settings/updated`
    notifications back into cached session state and the visible chat widget
    so active and inactive threads stay in sync after app-server-originated
    changes.
    
    The implementation is kept to the TUI layer: settings conversion and
    merge logic live under `codex-rs/tui/src/app/thread_settings.rs`, with
    dispatch/routing hooks in the existing app and chat widget paths.
    
    ## Verification
    
    I manually tested using `codex app-server --listen unix://` and then
    launching two copies of the TUI that use the same local app server. I
    then resumed the same thread on both and verified that changes like plan
    mode, fast mode, model, reasoning effort, etc. are reflected "live" in
    the second client when modified in the first and vice versa.
  • Add thread/settings/update app-server API (#23502)
    ## Why
    
    App-server clients need a way to update a thread's next-turn settings
    without starting a turn, adding transcript content, or waiting for turn
    lifecycle events. This gives settings UI a direct path for durable
    thread settings while clients observe the eventual effective state
    through a notification.
    
    This is a simplified rework of PR
    https://github.com/openai/codex/pull/22509. In particular, it changes
    the `thread/settings/update` api to return immediately rather than
    waiting and returning the effective (updated) thread settings. This
    makes the new api consistent with `turn/start` and greatly reduces the
    complexity of the implementation relative to the earlier attempt.
    
    ## What Changed
    
    - Adds experimental `thread/settings/update` with partial-update request
    fields and an empty acknowledgment response.
    - Adds experimental `thread/settings/updated`, carrying full effective
    `ThreadSettings` and scoped by `threadId` to subscribed clients for the
    affected thread.
    - Shares durable settings validation with `turn/start`, including
    `sandboxPolicy` plus `permissions` rejection and `serviceTier: null`
    clearing.
    - Emits the same settings notification when `turn/start` overrides
    change the stored effective thread settings.
    - Regenerates app-server protocol schema fixtures and updates
    `app-server/README.md`.
  • windows-sandbox: add resolved permissions helper (#22896)
    ## Why
    
    The Windows sandbox migration away from the legacy `SandboxPolicy`
    abstraction needs a small local bridge before IPC and core wiring can
    move to `PermissionProfile`. Leaf helpers currently branch directly on
    `WorkspaceWrite`, which spreads legacy assumptions through path planning
    and token setup code.
    
    This PR introduces a Windows-local resolved permissions view so those
    helpers can ask Windows-specific questions about runtime
    filesystem/network permissions without matching on the legacy policy
    enum everywhere.
    
    ## What changed
    
    - Added `ResolvedWindowsSandboxPermissions` in
    `windows-sandbox-rs/src/resolved_permissions.rs`, with legacy
    `SandboxPolicy` constructors for the current call sites.
    - Moved `allow.rs` writable-root and read-only-subpath planning onto the
    resolved permissions type.
    - Preserved Windows `TEMP`/`TMP` writable-root behavior when the
    effective policy includes writable tmpdir access.
    - Avoided resolving Unix `:slash_tmp` or parent-process `TMPDIR` while
    computing Windows writable roots.
    - Reused the shared allow-path result for setup write-root gathering and
    routed network-block selection through the resolved abstraction.
    
    ## Verification
    
    - `cargo test -p codex-windows-sandbox`
    - `just fix -p codex-windows-sandbox`
    - GitHub CI restarted on the amended commit; Windows Bazel is the
    required signal for the Windows-only code paths.
    
    
    
    
    
    
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22896).
    * #23715
    * #23714
    * #23167
    * #22923
    * #22918
    * __->__ #22896
  • fix(app-server): speed up shutdown (#23578)
    ## Why
    
    Pressing `Ctrl+C` or `Ctrl+D` in the TUI could make Codex pause during
    shutdown when app-server background work still held outbound sender
    clones.
    
    Shutdown tracing against the current `~/.codex` path found three
    relevant holders:
    
    - `SkillsWatcher` kept its event-loop task alive until the shutdown
    timeout path.
    - `AppServerAttestationProvider` retained a strong
    `Arc<OutgoingMessageSender>`, which could keep outbound teardown waiting
    after the processor task had exited.
    - A background `apps/list` task could still own an outbound sender when
    shutdown began, causing the in-process app-server runtime to wait for
    its outbound channel to close.
    
    ## What Changed
    
    - Give `SkillsWatcher` an explicit shutdown `CancellationToken` and
    cancel it from app-server teardown so its event loop drops the outbound
    sender promptly.
    - Change `AppServerAttestationProvider` to keep a
    `Weak<OutgoingMessageSender>` and return immediately when it can no
    longer be upgraded.
    - Give `AppsRequestProcessor` a shutdown `CancellationToken` and cancel
    in-flight background `apps/list` work during teardown.
    
    ## How to Test
    
    1. Start Codex TUI from a real home configuration.
    2. Press `Ctrl+C`.
    3. Confirm Codex exits promptly instead of pausing during shutdown.
    4. Repeat with `Ctrl+D` and confirm the same prompt exit path.
    
    Focused manual trace validation from the investigation:
    
    - Before the full fix, reproduced shutdown traces showed outbound
    teardown waiting on lingering owners, including `attestation.provider=1`
    and later `apps.list.task=1`.
    - After the fix, fresh real-home `Ctrl+D` traces showed
    `app_server.runtime.outbound_state_after_processor_join` with
    `owners=none`, `app_server.runtime.wait_outbound_handle = 0ms`, and
    total TUI app-server shutdown around `18ms`.
    
    Targeted validation:
    
    - `RUST_MIN_STACK=8388608 cargo test -p codex-app-server`
  • [2 of 2] Start fresh TUI thread in background (#23176)
    ## Why
    
    After the terminal-probe work in #23175, fresh-session startup still
    waits for `thread/start` before the chat input can become usable. The
    chat widget already has the machinery to hold early submissions until a
    session is configured, so fresh `thread/start` does not need to stay on
    the input-ready hot path.
    
    Refs #16335.
    
    ## What
    
    This PR starts fresh app-server threads in a background task, reports
    completion through a startup app event, and attaches the primary session
    once `thread/start` returns. Resume and fork startup paths remain
    synchronous.
    
    ## Benchmark
    
    In the local pty startup benchmark, this PR's pre-optimization base
    branch, #23175, measured about 152ms median from launch to accepted chat
    input. The stacked result measured about 66ms median, for an approximate
    additional savings of 85-95ms. For broader context, the original `main`
    baseline before either startup optimization was about 250.5ms median. We
    also measured Codex 0.117.0 on the same machine at about 64.6ms median,
    so the stacked branch is back in the old-startup-time range.
    
    ## Stack
    
    1. [#23175: [1 of 2] Optimize TUI startup terminal
    probes](https://github.com/openai/codex/pull/23175) — base PR
    2. [#23176: [2 of 2] Start fresh TUI thread in
    background](https://github.com/openai/codex/pull/23176) — this PR
    
    ## Verification
    
    - `cargo test -p codex-tui`
  • feat: account active goal progress in the goal extension (#23696)
    ## Why
    
    The goal extension can create and surface goals, but the live
    turn-accounting path still stopped short of persisting active-goal
    progress. That leaves token and wall-clock usage, plus
    `ThreadGoalUpdated` events, out of sync with the extension boundary once
    work actually advances or a goal transitions out of active state.
    
    ## What changed
    
    - Teach `GoalAccountingState` to track the current turn, active goal,
    token deltas, and wall-clock progress snapshots against the persisted
    goal id.
    - Flush active-goal accounting from tool-finish, turn-stop, and
    turn-abort lifecycle hooks, and emit `ThreadGoalUpdated` events when
    persisted progress changes.
    - Route `create_goal` and `update_goal` through the same accounting
    state so new goals start from the right baseline, final progress is
    flushed before status changes, and `update_goal` can mark a goal
    `blocked` as well as `complete`.
    - Keep budget-limited goals accruing through the end of the turn while
    clearing local active-goal state once a turn or explicit update is
    finished.
    - Expand backend and lifecycle coverage around store ids, baseline
    reset, tool-finish accounting, budget-limited carry-through, and
    blocked-goal updates.
    
    ## Testing
    
    - Added focused backend coverage in
    `codex-rs/ext/goal/tests/goal_extension_backend.rs` for baseline reset,
    tool-finish accounting, budget-limited turns, and blocked-goal updates.
    - Extended `codex-rs/core/src/session/tests.rs` to assert that lifecycle
    inputs expose the expected session, thread, and turn store ids.
  • release: publish Codex package archive checksums (#23635)
    ## Summary
    
    Standalone installers and other downstream package consumers need a
    stable checksum source for the canonical package archives. Relying on
    per-asset metadata makes that harder to consume uniformly, especially
    when several package archives are produced in the same release.
    
    This keeps the `codex-package-*.tar.gz` and
    `codex-app-server-package-*.tar.gz` assets in the GitHub Release upload
    set and adds `codex-package_SHA256SUMS` to `dist/` before the release is
    created. The manifest contains one SHA-256 line per package archive and
    fails the release job if no package archives are present.
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23635).
    * #23638
    * #23637
    * #23636
    * __->__ #23635
  • runtime: use install context for bundled bwrap (#23634)
    ## Summary
    
    The Linux sandbox should find bundled `bwrap` through the same
    package-layout abstraction as the rest of the runtime, instead of
    maintaining a separate standalone-specific lookup path.
    
    This adds an `InstallContext` helper for bundled resources and updates
    `codex-linux-sandbox` to ask the current install context for
    `codex-resources/bwrap` before falling back to the old
    executable-relative probes. The tests cover npm-style, standalone, and
    canonical package layouts so `bwrap` lookup follows the package
    structure introduced earlier in the stack.
    
    ## Test plan
    
    - `cargo test -p codex-install-context`
    - `cargo test -p codex-linux-sandbox --lib`
    - `just fix -p codex-install-context -p codex-linux-sandbox`
    - `just bazel-lock-check`
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23634).
    * #23638
    * #23637
    * #23636
    * #23635
    * __->__ #23634
  • [codex] Hide deferred tools from code mode prompt (#23605)
    ## Why
    
    `code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools`
    was failing because code-mode prompt generation used the same nested
    tool spec list for both the model-visible `exec` guide and the runtime
    `ALL_TOOLS` surface. That allowed deferred MCP/app tools, such as
    `calendar_timezone_option_99`, to leak into the `exec` description even
    though they should only be discoverable through `ALL_TOOLS` at runtime.
    
    ## What changed
    
    Split code-mode nested tool planning into two sets in
    `core/src/tools/spec_plan.rs`:
    
    - runtime nested tool specs still include deferred tools, so
    `tools[...]` and `ALL_TOOLS` can call them
    - `exec` prompt docs only render non-deferred tools, so deferred app
    tools stay out of the model-visible guide
    
    ## Validation
    
    - `cargo test -p codex-core --test all
    code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools --
    --nocapture`
    - looped the same focused test 5 additional times with `cargo test -q -p
    codex-core --test all
    code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools`
  • feat: expose turn-start metadata to extensions (#23688)
    ## Why
    
    The goal extension needs more context when a turn starts than
    `turn_store` alone provides.
    
    In particular, goal accounting needs the stable turn id, the effective
    collaboration mode, and the cumulative token-usage baseline captured at
    turn start so it can:
    
    - suppress goal accounting for plan-mode turns
    - compute exact per-turn deltas from cumulative `total_token_usage`
    snapshots instead of relying on the most recent usage event alone
    - keep the extension-owned accounting path aligned with the host turn
    lifecycle
    
    ## What
    
    - extend `codex_extension_api::TurnStartInput` to expose `turn_id`,
    `collaboration_mode`, and `token_usage_at_turn_start`
    - pass the full `TurnContext` plus the captured token-usage baseline
    through the turn-start lifecycle emission path
    - initialize goal turn accounting from the turn-start baseline and
    collaboration mode
    - switch goal token accounting to compute deltas from cumulative
    `total_token_usage` snapshots
    - add coverage for the new turn-start lifecycle fields and for
    goal-accounting baseline behavior
    
    ## Testing
    
    - added `turn_start_lifecycle_exposes_turn_metadata_and_token_baseline`
    in `codex-rs/core/src/session/tests.rs`
    - added `ext/goal/tests/accounting.rs` coverage for baseline-aware goal
    accounting and plan-mode suppression
  • feat: wire goal extension tools to the dedicated goal store (#23685)
    ## Why
    
    `ext/goal` already had the tool specs and contributor wiring for
    `/goal`, but the installed tools still depended on a placeholder backend
    that always errored. That meant the extension could not actually own
    goal persistence even though the dedicated `thread_goals` store already
    exists.
    
    This change wires the extension tools directly to the dedicated goal
    store so the extension can create, read, and complete goals against real
    state instead of falling back to host-side placeholders.
    
    ## What changed
    
    - make `install_with_backend(...)` require
    `Arc<codex_state::StateRuntime>` so goal storage is always available
    when the extension is installed
    - remove the unused no-backend/public backend abstraction from
    `ext/goal` and have the tool executors talk directly to `StateRuntime`
    - map `thread_goals` rows into the existing protocol response shape for
    `get_goal`, `create_goal`, and `update_goal`
    - preserve current thread-list behavior by filling an empty thread
    preview from the goal objective when a goal is created through the
    extension path
    - add integration coverage for the installed tool surface, including
    successful goal creation and duplicate-create rejection
    
    ## Testing
    
    - `cargo test -p codex-goal-extension`
  • fix: main (#23675)
    Fix main due to conflicting merges
    This is only fixing some imports and mechanics
  • feat: rename 2 (#23668)
    Just a mechanical renaming
  • feat: rename 3 (#23669)
    Just a mechanical renaming
  • feat: rename 1 (#23667)
    Just a mechanical renaming
  • Add timeout for remote compaction requests (#23451)
    ## Why
    
    Remote compaction currently sends a unary `POST /responses/compact` and
    waits for the full response before replacing history or emitting the
    completed `ContextCompaction` item. Unlike normal `/responses` streaming
    requests, this unary compact request had no timeout boundary. If the
    backend accepts the request and then stalls before returning a body, the
    existing request retry policy never sees a transport error, so the
    compact turn can remain stuck after the started item with no completion
    or actionable error.
    
    That matches the reported hang shape in issues such as #18363, where
    logs show `responses/compact` was posted but no corresponding compact
    completion followed. A bounded request timeout gives the existing retry
    policy a concrete timeout error to retry instead of letting the user sit
    indefinitely on automatic context compaction.
    
    ## What
    
    - Add a request timeout to legacy `/responses/compact` calls.
    - Size that timeout from the provider stream idle timeout with a
    conservative multiplier, so the default compact attempt gets 20 minutes
    rather than the 5 minute stream idle window.
    - Map API transport timeouts to a request timeout error instead of the
    child-process timeout message.
    
    ## Testing
    
    - Not run (per request; CI will cover).
  • Migrate exec-server remote registration to environments (#23633)
    ## Summary
    - migrate exec-server remote registration naming from executor to
    environment
    - align CLI, public Rust exports, registry error messages, and relay
    test fixtures with the environment registry contract
    - keep the live registration path and response model consistent with
    `/cloud/environment/{environment_id}/register`
    
    ## Verification
    - `cargo test -p codex-exec-server
    remote::tests::register_environment_posts_with_auth_provider_headers
    --manifest-path /Users/richardlee/code/codex/codex-rs/Cargo.toml`
    - `cargo test -p codex-exec-server --test relay
    multiplexed_remote_environment_routes_independent_virtual_streams
    --manifest-path /Users/richardlee/code/codex/codex-rs/Cargo.toml`
    - `cargo check -p codex-cli --manifest-path
    /Users/richardlee/code/codex/codex-rs/Cargo.toml` (still running when PR
    opened; will update after completion if needed)
  • add encryptedcontent to functioncalloutput (#23500)
    add new `EncryptedContent` variant to `FunctionCallOutputContentItem`
    ahead of standalone websearch.
    
    we need to be able to receive and pass encrypted function call output
    from the new web search endpoint back to responsesapi, as we cannot
    expose direct search results.
  • runtime: detect Codex package layout (#23596)
    ## Why
    
    The package-builder stack now creates a canonical Codex package
    directory where the entrypoint lives under `bin/`, bundled helper
    resources live under `codex-resources/`, and bundled PATH-style tools
    live under `codex-path/`. That layout is not specific to the standalone
    installer: npm, brew, install scripts, and manually unpacked artifacts
    should all be able to use the same package shape.
    
    The Rust runtime still only knew about the legacy standalone release
    layout, where resources sit next to the executable. A packaged binary
    therefore would not identify its package root or prefer the bundled `rg`
    from `codex-path/`.
    
    ## What changed
    
    - Adds `CodexPackageLayout` to `codex-install-context` and detects it
    from an executable path shaped like `<package>/bin/<entrypoint>` when
    `<package>/codex-package.json` is present.
    - Splits `InstallContext` into an install `method` plus an optional
    package layout so the layout is shared across npm, bun, brew,
    standalone, and other launch contexts.
    - Stores package-layout paths as `AbsolutePathBuf` values.
    - Keeps `codex-resources/` and `codex-path/` optional so Codex can still
    run with degraded behavior if sidecar directories are missing.
    - Updates `InstallContext::rg_command()` to prefer bundled
    `codex-path/rg` or `rg.exe`, then fall back to the legacy standalone
    resources location, then system `rg`.
    - Updates `codex doctor` reporting so package installs show package,
    bin, resources, and path directories, and so bundled search detection
    recognizes `codex-path/` for any install method.
    
    ## Test plan
    
    - `cargo test -p codex-install-context`
    - `cargo test -p codex-cli`
    - `cargo test -p codex-tui
    update_action::tests::maps_install_context_to_update_action`
    - `just bazel-lock-check`
  • ci: build Codex package archives in release workflow (#23582)
    ## Why
    
    Release CI already builds the Codex entrypoints before staging
    artifacts, and the package builder can now package those prebuilt
    binaries directly. The workflow should produce package-shaped sidecar
    archives from the same staged entrypoints that downstream distribution
    channels will eventually consume, without rebuilding `codex` or
    `codex-app-server` inside the packaging step.
    
    This intentionally does **not** publish the new package archives as
    GitHub Release assets yet. The archives are kept with workflow artifacts
    until npm, Homebrew, `install.sh`, winget, and related consumers are
    ready to switch over.
    
    ## What changed
    
    - Adds a `Build Codex package archive` step to
    `.github/workflows/rust-release.yml` after target artifacts are staged.
    - Runs `scripts/build_codex_package.py` for both release bundles:
    - `primary` builds `codex-package-${TARGET}.tar.gz` with `--variant
    codex`.
    - `app-server` builds `codex-app-server-package-${TARGET}.tar.gz` with
    `--variant codex-app-server`.
    - Passes `--entrypoint-bin target/${TARGET}/release/<entrypoint>` so
    packages contain the entrypoint already built by the workflow.
    - Deletes both package archive names before the final GitHub Release
    upload so they remain workflow artifacts only for now.
    
    ## Verification
    
    - Parsed `.github/workflows/rust-release.yml` with Ruby's YAML loader.
    
    
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23582).
    * #23596
    * __->__ #23582
  • build: package prebuilt Codex entrypoints (#23586)
    ## Why
    
    The package builder should describe the binaries it is actually
    packaging, not require callers to restate release metadata out of band.
    A caller-provided `--version` flag can drift from the workspace version,
    but running the target entrypoint to discover its version breaks
    cross-target packages when the produced binary cannot execute on the
    build host.
    
    This PR keeps package metadata tied to the repository source of truth by
    reading `[workspace.package].version` from `codex-rs/Cargo.toml`. It
    also prepares the package layout for `codex-app-server` packages: the
    same package structure can now represent either the CLI entrypoint or
    the app-server entrypoint while keeping shared sidecars such as `rg`,
    `bwrap`, and Windows sandbox helpers in the existing package
    directories.
    
    ## What changed
    
    - Removes the `--version` CLI flag from
    `scripts/build_codex_package.py`.
    - Adds Cargo.toml version discovery for `codex-package.json.version` via
    `codex-rs/Cargo.toml`.
    - Adds `--entrypoint-bin` so callers can package a prebuilt entrypoint
    instead of rebuilding it with Cargo.
    - Makes `--variant` an explicit choice between `codex` and
    `codex-app-server`, and uses it to select the cargo binary and packaged
    `bin/` entrypoint name.
    - Updates `scripts/codex_package/README.md` to document variants,
    prebuilt entrypoints, and Cargo.toml version detection.
    
    ## Verification
    
    - Compiled `scripts/build_codex_package.py` and
    `scripts/codex_package/*.py` with `PYTHONDONTWRITEBYTECODE=1`.
    - Ran `scripts/build_codex_package.py --help` and verified `--version`
    is gone while `--variant` and `--entrypoint-bin` are present.
    - Verified the package builder reads version `0.0.0` from
    `codex-rs/Cargo.toml`.
    - Built a fake cross-target `codex-app-server` package using a
    non-executable `--entrypoint-bin`; verified metadata records version
    `0.0.0`, variant `codex-app-server`, and `bin/codex-app-server` as the
    entrypoint.
  • feat: Add vertical remote plugin collection support (#23584)
    - Adds an explicit vertical marketplace kind for plugin/list that
    fail-open fetches collection=vertical only when full remote plugins are
    disabled.
    
    - Renames the global remote marketplace/cache identity to
    openai-curated-remote and materializes remote installs with backend
    release versions and app manifests.
  • Warn on invalid UTF-8 in AGENTS.md files (#23232)
    Fixes #23223.
    
    ## Why
    
    Malformed AGENTS instructions should not fail silently. The reported
    issue had invalid UTF-8 in a global `AGENTS.md`; before this change,
    Codex treated that decode failure like a missing file, so the personal
    instructions disappeared without a user-visible explanation and the
    rollout had no `# AGENTS.md instructions` block.
    
    Project-level AGENTS files already used lossy decoding, so their
    instructions still appeared, but invalid bytes were replaced without
    telling the user. Global and project AGENTS files should behave
    consistently: keep usable instruction text when possible, and surface a
    diagnostic when bytes had to be replaced.
    
    ## What changed
    
    Global `AGENTS.override.md` and `AGENTS.md` loading now reads bytes and
    decodes with replacement characters on invalid UTF-8, matching
    project-level AGENTS behavior. Both global and project AGENTS loading
    now emit a startup warning when invalid UTF-8 is found, and both keep
    the instruction text with invalid byte sequences replaced.
    
    Missing files, non-file candidates, empty files, and the existing
    `AGENTS.override.md` before `AGENTS.md` precedence keep their current
    behavior.
    
    ## How users see it
    
    The warnings flow through the existing startup warning surface.
    App-server clients receive config-time startup warnings as
    `configWarning` notifications during initialization, and thread startup
    emits startup warnings as thread-scoped `warning` notifications.
    
    Global AGENTS invalid UTF-8 warnings can appear on both surfaces.
    Project-level AGENTS invalid UTF-8 warnings are discovered while
    building thread instructions, so they appear as thread-scoped `warning`
    notifications. Clients that render warning notifications in the
    conversation surface show the message as a visible diagnostic instead of
    silently hiding or altering instructions.
  • [codex] Preserve raw code-mode exec output by default (#23564)
    ## Why
    Code mode can use nested unified exec calls as data sources. When those
    calls omit `max_output_tokens`, code mode should receive raw command
    output so the script can parse or summarize it itself. When code mode
    does provide `max_output_tokens`, that explicit nested budget should be
    respected, including values above the default unified exec limit, rather
    than being capped before code mode sees the result.
    
    ## What
    - Preserve direct unified exec truncation behavior, while letting
    code-mode exec/write_stdin keep `max_output_tokens` as `None` unless
    explicitly supplied.
    - Make code-mode tool results use raw output when no explicit limit is
    present, and use the explicit nested limit directly when one is
    specified.
    - Refactor unified exec output formatting so `truncated_output` takes
    the caller-selected token budget.
    - Add e2e integration coverage for explicit nested exec limits, omitted
    nested exec limits, outer exec limit propagation, omitted-limit outputs
    that exceed both the default and a small truncation policy, explicit
    nested limits above those caps, and high explicit limits that still
    compact larger command output.
    - Reuse the code-mode turn setup helper while directly asserting the
    exact exec output item in each test.
    
    ## Testing
    - `just fmt`
    - `git diff --check`
    - Not run locally per repo guidance; CI should validate the e2e
    integration tests.
  • Fix stale background terminal poll events (#23231)
    ## Why
    
    Issue #23214 reports `/ps` showing no background terminals while the
    status line still says it is waiting for a background terminal. The race
    is in core: `write_stdin` can poll a process that exits before the
    response returns. The process manager correctly returns `process_id:
    None`, but the handler still emitted a `TerminalInteraction` event using
    the requested session id, causing clients to believe a dead process was
    still being polled.
    
    Fixes #23214.
    
    ## What changed
    
    - Suppress `TerminalInteraction` events for empty `write_stdin` polls
    once `response.process_id` is `None`.
    - Continue emitting interactions for non-empty stdin, even if that input
    causes the process to exit before the response returns.
    - Extend the unified exec integration test to assert completed empty
    polls do not emit terminal interactions.
    
    ## Verification
    
    - `cargo test -p codex-core --test all
    unified_exec_emits_one_begin_and_one_end_event`
    - `cargo test -p codex-core --test all
    unified_exec_emits_terminal_interaction_for_write_stdin`
    
    `cargo test -p codex-core` currently aborts in unrelated
    `agent::control::tests::resume_agent_from_rollout_uses_edge_data_when_descendant_metadata_source_is_stale`
    with a reproducible stack overflow.
  • Move plugin and skill warmup into session startup (#23535)
    ## Why
    
    Plugin and skill loading is useful as warmup and early validation, but
    session startup does not need to wait for that work before it can
    continue building the session. Keeping it on the serial startup path
    adds avoidable latency to every fresh thread start.
    
    We still want invalid skill configurations to show up quickly, and we
    want the warmup to exercise the same plugin and skill manager caches
    that the normal turn path uses.
    
    ## What changed
    
    - moved plugin and skill warmup into the session startup async path
    instead of eagerly awaiting it on the serial setup path
    - kept the warmup using the session's resolved filesystem/environment
    context so skill loading still sees the right roots
    - preserved early skill-load error logging so broken skill
    configurations still surface during startup
    - left the per-turn plugin and skill loading path unchanged, so turns
    still use the normal cached managers
    
    ## Testing
    
    - Not run locally; relying on CI for validation.
  • feat: add permission profile list api (#23412)
    ## Why
    
    Clients need a typed permission-profile catalog instead of
    reconstructing that state from config internals.
    
    ## What changed
    
    - Added `permissionProfile/list` to the app-server v2 protocol with
    cursor pagination and optional `cwd`.
    - The list response includes built-in permission profiles plus
    config-defined `[permissions.<id>]` profiles from the effective config
    for the request context.
    - Permission profiles keep optional `description` metadata for display
    purposes.
    - App-server docs and schema fixtures are updated for the new RPC.
  • feat: expose codex-app-server version flag (#23593)
    ## Why
    
    `codex-app-server` is published as a standalone release binary, so it
    should support the same basic version inspection behavior users expect
    from command-line tools. This is independent of package assembly:
    package metadata now comes from `codex-rs/Cargo.toml`, but the
    standalone app-server binary should still answer `--version` directly.
    
    ## What changed
    
    - Enables Clap's generated `--version` flag for the `codex-app-server`
    binary by adding `#[command(version)]` to its top-level parser.
    
    ## Verification
    
    - Ran `cargo run -p codex-app-server --bin codex-app-server --
    --version` and verified it prints `codex-app-server 0.0.0`.
  • Fan out rust-ci-full nextest by platform (#23358)
    ## Why
    
    `rust-ci-full` was paying the full Cargo nextest build-and-run cost once
    per platform, with Windows ARM64 as the long pole. This change moves the
    heavy work into one reusable per-platform flow: build a nextest archive
    once, then replay it across four shards so the platform lane spends less
    time running tests serially. For Windows ARM64, the archive is
    cross-compiled on Windows x64 and replayed on native Windows ARM64
    shards so the slow ARM64 machine is used for execution rather than
    compilation.
    
    ## What changed
    
    - split the `rust-ci-full` nextest matrix into five explicit
    per-platform reusable-workflow calls
    - add `.github/workflows/rust-ci-full-nextest-platform.yml` to build one
    archive, upload timings/helpers, replay four nextest shards, upload
    per-shard JUnit, and roll the shard status back up per platform
    - add Windows CI helpers for Dev Drive setup and MSVC ARM64 linker
    environment export so the Windows ARM64 archive can be produced on
    Windows x64
    - keep the existing Cargo git CLI fetch hardening inside the reusable
    workflow, since caller workflow-level `env` does not flow through
    `workflow_call`
    - document the archive-backed shard shape in
    `.github/workflows/README.md`
    - raise the default nextest slow timeout to 30s so the sharded full-CI
    path does not treat every >15s test as stuck
    
    ## Verification
    
    - validated the archive/shard flow with live GitHub Actions runs on this
    PR branch
    - Windows ARM64 cross-compile latency on completed runs:
    - https://github.com/openai/codex/actions/runs/26118759651: `34m30s`
    lane e2e, `17m16s` archive build, `9m55s` shard phase
    - https://github.com/openai/codex/actions/runs/26120777976: `30m36s`
    lane e2e, `17m21s` archive build, `6m50s` shard phase
    - comparable pre-cross-compile sharded Windows ARM64 runs were `55m01s`,
    `50m21s`, and `46m42s`, so the completed cross-compile runs improved the
    lane by roughly `12m` to `24m` versus the prior range
    - latest corrected cross-compile run:
    https://github.com/openai/codex/actions/runs/26120777976
      - Windows ARM64 archive built successfully on Windows x64
    - native Windows ARM64 shards started immediately after the archive
    upload
    - 3/4 Windows ARM64 shards passed; the failing shard hit the same
    existing `code_mode` test failure seen outside this lane
    - downloaded failed-shard JUnit XML from the validation runs and
    confirmed the remaining red is from known test failures, not
    archive/shard wiring
    - no local Codex tests run per repo guidance
    
    ## Notes
    
    - this PR does not change developers.openai.com documentation
  • build: default Codex package target and output (#23541)
    ## Why
    
    The package builder should be easy to run during local iteration.
    Requiring callers to provide both a target triple and an output
    directory every time makes the common host-package case more awkward
    than necessary.
    
    This PR keeps explicit overrides available, but makes the default
    invocation useful: build for the current host platform and place the
    package in a fresh temporary directory. Because a temp output path is
    otherwise easy to lose, the builder continues to print the final package
    directory path when it completes.
    
    ## What changed
    
    - Makes `--target` optional and maps the host OS/architecture to
    supported Codex package target triples.
    - Uses GNU Linux target triples for Linux host defaults, while keeping
    the musl targets available for release jobs that pass `--target`
    explicitly.
    - Makes `--package-dir` optional and creates a new `codex-package-*`
    temp directory when omitted.
    - Documents the new defaults in `scripts/codex_package/README.md`.
    
    ## Verification
    
    - Compiled `scripts/build_codex_package.py` and
    `scripts/codex_package/*.py` with `PYTHONDONTWRITEBYTECODE=1`.
    - Ran `scripts/build_codex_package.py --help` from outside the repo.
    - Verified Linux host detection maps `x86_64` and `aarch64` to GNU
    target triples.
    - Ran a fake-Cargo package build while omitting both `--target` and
    `--package-dir`; verified the generated metadata target, expected
    package files, and printed temp package path.
    - Ran a fake-Cargo package build for `x86_64-unknown-linux-gnu` and
    verified `codex`, `bwrap`, and `rg` are assembled into the package.
  • test: fix multi-agent service tier assertion (#23576)
    ## Why
    
    `openai/codex#22169` added a regression test that expects an invalid
    child `service_tier` to be rejected, but the test used
    `Result::expect_err` on `SpawnAgentHandler::handle`. That requires the
    `Ok` type to implement `Debug`, and this handler returns `Box<dyn
    ToolOutput>`, so Bazel failed while compiling `codex-core` tests before
    it could run them.
    
    ## What changed
    
    - Capture the handler result and assert on `result.err()` instead of
    calling `expect_err`.
    - Keep the same `FunctionCallError::RespondToModel` assertion for the
    rejected service tier.
    
    ## Verification
    
    - `cargo test -p codex-core
    spawn_agent_role_service_tier_does_not_hide_invalid_spawn_request`
  • Remove unused ARC monitor path (#23573)
    ## Summary
    - remove the unreachable ARC monitor path from MCP tool approval
    handling
    - delete the unused ARC monitor module/tests and trim the orphaned
    safety-monitor decision plumbing
    - keep `always allow` approvals on the existing auto-approval
    short-circuit without a dead monitor hop
    
    ## Testing
    - `cargo test -p codex-core mcp_tool_call`
    - `just fmt`
    - `just fix -p codex-core`
    - `git diff --check`
    
    ## Additional validation
    - Attempted `cargo test -p codex-core`; the library test target passed,
    then the integration target failed in this local environment.
    - The narrower MCP-focused rerun passed its unit coverage and only hit
    missing local `test_stdio_server` binaries in filtered integration
    cases.
  • build: fetch rg for Codex packages (#23526)
    ## Why
    
    The Codex package builder should produce a complete package without
    requiring callers to pre-populate `rg` under `codex-cli/vendor` or have
    `dotslash` installed on `PATH`. The repo already tracks the
    authoritative DotSlash manifest in `codex-cli/bin/rg`, so the builder
    can read that metadata directly and fetch the correct ripgrep archive
    for the target it is packaging.
    
    ## What changed
    
    - Added `scripts/codex_package/ripgrep.py` to parse `codex-cli/bin/rg`
    after stripping the shebang, select the target platform entry, download
    the configured artifact, and verify the recorded size and SHA-256
    digest.
    - Added a cache under `$TMPDIR/codex-package/<target>-rg` so verified
    archives can be reused without fetching again.
    - Extracted `rg`/`rg.exe` from `tar.gz` and `zip` artifacts into the
    package-builder cache, then copied that into `codex-path` through the
    existing package layout flow.
    - Kept `--rg-bin` as an explicit local override for offline tests and
    unusual local workflows.
    - Documented the default `rg` fetch/cache behavior in
    `scripts/codex_package/README.md`.
    
    ## Verification
    
    - Ran wrapper/module syntax compilation.
    - Ran `scripts/build_codex_package.py --help` from `/private/tmp`.
    - Ran a local manifest fetch test covering shebang-stripped manifest
    parsing, `tar.gz` extraction, `zip` extraction, size/SHA-256
    verification, and cache reuse after deleting the original source
    archives.
    - Ran fake-cargo package/archive builds for macOS, Linux, and Windows
    target layouts with `--rg-bin`, including an assertion that generated
    tar archives contain no duplicate member names.
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23526).
    * #23541
    * __->__ #23526
  • Fix: TUI starting in wrong CWD (#23538)
    This fixes a regression wher codex could start in the wrong directory
    when a live local app-server socket was present. The issue was that
    implicit local socket reuse was being treated like an explicit remote
    workspace session, which dropped the invoking cwd unless --cd was
    passed.
    
    The change separates local socket transport from true remote workspace
    semantics.
    - Plain local startup keeps local cwd, trust, resume, picker, and
    config-refresh behavior.
    - Explicit --remote keeps the existing remote cwd behavior.
    - Added coverage for launch target selection and local-session
    filtering/cwd behavior.
    
    Steps to test:
    - Start a local app-server from a different directory than the repo you
    want to use.
      - Launch codex from a project/worktree without --cd.
    - Confirm the session starts in the invoking directory, not the
    app-server process directory.
    - Confirm explicit codex --remote ... still preserves existing remote
    behavior.
  • Add CUA requirements subsection for locked computer use (#23555)
    Adds a new top-level section for "CUA" requirements that can allow for
    disablement of specific features as needed for enterprises.
  • [codex] Honor role-defined spawn service tiers (#22169)
    ## Why
    Custom agent roles are ordinary config layers, so a role file can
    already express `service_tier` just like other config values. The
    spawned-agent tier path needs to preserve that effective role config and
    follow the same precedence pattern as model/reasoning.
    
    ## What changed
    - Apply an explicit spawn-time `service_tier` onto the child config
    before role application, so a role config layer can override it just
    like role-defined model/reasoning settings do.
    - Validate the final effective child tier after the final child model is
    known, while still falling back to the parent tier when no child tier
    survives.
    - Add focused integration coverage for both v1 and v2 proving role TOML
    loads a service tier, spawned children keep that role-configured tier,
    and a role tier wins over a conflicting spawn-time tier.
    
    ## Validation
    - `just fmt`
    - `git diff --check`
    - Local Rust tests not run, per repo guidance; CI should exercise the
    new coverage.
  • fix: serialize unix app-server startup (#23516)
    # Summary
    
    Unix-socket app-server startup can currently race when multiple launch
    attempts target the same `CODEX_HOME`. Those processes can overlap
    before the control socket exists, which lets them enter SQLite state
    initialization concurrently and reproduce the startup corruption pattern
    seen in SSH mode.
    
    This change makes the app-server own that singleton startup guarantee.
    Unix-socket startup now takes a `CODEX_HOME`-scoped advisory lock before
    SQLite initialization, runs the existing control-socket preparation
    check while holding that lock, returns the established `AddrInUse` error
    when another live listener already owns the socket, and releases the
    lock once the new listener has bound its socket.
    
    # Design decisions
    
    - The singleton rule lives in `app-server --listen unix://`, not in a
    desktop-only caller path, so every Unix-socket launch gets the same race
    protection.
    - A duplicate raw app-server launch returns an error instead of silently
    succeeding. The attach operation remains `app-server proxy`, which
    continues to connect to an already-running listener.
    - The lock is held only across the dangerous startup window: socket
    preparation, SQLite initialization, and socket bind. It is not held for
    the app-server lifetime.
    - Listener detection stays in `prepare_control_socket_path(...)`, so the
    preexisting live-listener and stale-socket behavior remains the single
    source of truth.
    
    # Testing
    
    Tests: targeted Unix-socket transport tests on the branch checkout, full
    `codex-cli` build on `efrazer-db10`, and an SSH-style smoke on
    `efrazer-db10` covering concurrent app-server starts, explicit
    duplicate-start errors, and absence of SQLite startup-error matches in
    launch logs.