Commit Graph

7379 Commits

  • [codex] parallelize release code generation (#27702)
    The release profile still uses one codegen unit, which serializes LLVM
    code generation within each crate. That setting was selected alongside
    fat LTO for optimization quality and binary size, but releases now use
    ThinLTO and code generation dominates the critical-path build.
    
    Use four codegen units. On an Apple M4 Max with 16 cores and 128 GiB
    RAM, using rustc 1.96.0, four and eight units took 507.486 and 505.325
    seconds respectively. Four therefore keeps the build-time gain while
    limiting the stripped `codex` increase to 14.7%, compared with 21.5% at
    eight units. The gzip-compressed binary grows 7.8% at four units.
    
    The one-unit build from an empty target directory took 981.150 seconds.
    That comparison also populated dependency and native build caches, so it
    is directional rather than controlled. It agrees with the earlier clean
    matrix where eight units reduced 671 seconds to 303 seconds:
    https://gist.github.com/anp/4b88393a0acd35783d9f42156f3243d5
    
    At the local 48% reduction, the current release's 55m22s critical-path
    macOS Cargo step would save about 26 minutes from the 71m28s workflow:
    https://github.com/openai/codex/actions/runs/27367405663
    
    The prompt-image medians ranged from 3.9% faster to 0.9% slower. CLI
    startup shifted by 1-2 ms while user and system CPU time were unchanged.
    
    This is a draft because the release-latency improvement may not justify
    the binary-size increase.
  • ci(v8): gate Windows source builds on relevant changes (#27715)
    Avoid rebuilding sandboxed Windows MSVC V8 artifacts for unrelated
    changes to `codex-rs/Cargo.toml`.
    
    The V8 canary now compares the resolved V8 version between the base and
    head commits and only runs the Windows source-build matrix when:
    
    - the resolved V8 crate version changes;
    - Windows artifact-production scripts or workflows change; or
    - the workflow is manually dispatched.
    
    The existing Bazel V8 matrix is unchanged.
    
    ## Why
    
    The Windows MSVC source builds take roughly two to three hours and
    currently run whenever any entry in the broad `v8-canary` path filter
    changes.
  • fix: Recover from sqlite directory being a file (#27719)
    Missed this file in the last PR -- this ensures that if you're in the
    really-weird edge case of your sqlite directory being a file, that it
    will fix it and recover properly.
  • [codex] Remove async_trait from first-party code (#27475)
    ## Why
    
    First-party async traits should expose their `Send` contracts explicitly
    without requiring `async_trait`. This completes the migration pattern
    established in #27303 and #27304.
    
    ## What changed
    
    - Replaced the remaining first-party `async_trait` traits with native
    return-position `impl Future + Send` where statically dispatched and
    explicit boxed `Send` futures where object safety is required.
    - Kept implementations behavior-preserving, outlining existing async
    bodies into inherent methods where that keeps the diff reviewable.
    - Removed all direct first-party `async-trait` dependencies and the
    workspace dependency declaration.
    - Added a cargo-deny policy that permits `async-trait` only through the
    remaining transitive wrapper crates.
    - Updated `rand` from 0.8.5 to 0.8.6 to resolve RUSTSEC-2026-0097 and
    keep the full cargo-deny check passing.
    
    ## Validation
    
    - `just test -p codex-exec-server`: 216 passed, 2 skipped.
    - `just test -p codex-model-provider`: 39 passed.
    - `just test -p codex-core` and `just test`: changed tests passed;
    remaining failures are environment-sensitive suites unrelated to this
    migration.
    - `cargo deny check`
    - `just fix`
    - `just fmt`
    - `cargo shear`
    - `just bazel-lock-check`
  • Fix image extension PathUri conversion (#27711)
    ## Why
    
    `main` stopped compiling when #27498 passed an `AbsolutePathBuf` to the
    `ExecutorFileSystem` API migrated to `PathUri` by #27653.
    
    ## What
    
    Convert referenced image paths to `PathUri` before filesystem reads,
    declare the internal path-URI dependency, and refresh `Cargo.lock`.
  • tui: clear stale hook row after turn completion (#27619)
    Fixes #27210.
    
    ## Why
    When the app server reports a visible `HookStarted` event for a
    `PostToolUse` hook but the turn reaches `TurnCompleted` before a
    matching hook completion event arrives, the TUI can leave the transient
    `Running PostToolUse hook` row visible after the agent is done.
    Interrupted and failed turn cleanup already drops transient live hook
    rows; the normal completion path did not.
    
    ## What Changed
    - Added `ChatWidget::clear_active_hook_cell()` for dropping transient
    live hook status without writing it to history.
    - Call that cleanup from normal task completion, while reusing it for
    the existing start/finalize cleanup paths.
    - Added `completed_turn_clears_visible_running_hook` snapshot coverage
    for the reported `PostToolUse` case.
    
    ## Tests
    - `just test -p codex-tui completed_turn_clears_visible_running_hook`
    - `just test -p codex-tui` (fails on current `main` in unrelated
    guardian tests:
    `update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
    and
    `update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`)
  • Add spans to turn lifecycle gaps (#27623)
    ## Why
    Codex app-server latency traces do not granularly cover turn task
    startup and inter-request handoffs. These spans help attribute time
    across task execution, startup prewarm, in-flight tool completion, and
    rollout persistence.
    
    ## What changed
    - Add `session_task.run` spans around task execution and
    `session_task.flush_rollout` around flushing pending conversation
    transcript writes to durable storage
    - Add `regular_task.prepare_run_turn` around regular-turn startup (Send
    the `TurnStarted` event, reset turn-specific reasoning state, and
    resolve any startup prewarm)
    - Add `startup_prewarm.resolve` around waiting for background session
    prewarming to finish, fail, time out, or be cancelled
    - Add a function-level trace span around draining in-flight tool calls
    (Wait for tool calls to complete, record tool result in conversation
    history, and other bookkeeping)
    
    ## Verification
    Trigger Codex rollout and observe new spans are included
  • Route image extension reads through turn environments v2 (#27498)
    ## Why
    
    Image generation used `std::fs::read` for referenced image paths, which
    did not support environment-backed filesystems or their sandbox context.
    
    ## What changed
    
    - Expose optional turn environments to extension tool calls.
    - Include each environment’s ID, working directory, filesystem, and
    sandbox context.
    - Read referenced images through the selected environment filesystem.
    - Keep sandbox usage at the extension call site so extensions can choose
    the appropriate access mode.
    - Consolidate image request construction into one async function.
    - Add coverage for successful environment reads and read failures.
    
    ## Validation
    
    - `cargo check -p codex-image-generation-extension --tests`
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    
    `just test -p codex-image-generation-extension` could not complete
    because the build exhausted available disk space.
  • [codex] Move persistence policy application into ThreadStore (#27318)
    Move the application of the persistence policy into the thread store, so
    thread stores can get raw append items rather than canonical append
    items. This will enable store-specific projections over the raw input
    items.
  • Warn when hooks.json has unsupported top-level fields (#26426)
    Addresses #25875.
    
    ## Summary
    
    `hooks.json` accepted unknown top-level fields. A file with
    `SessionStart` at the root parsed as an empty hook configuration without
    warning.
    
    ## Repro
    
    ```json
    { "SessionStart": [...] }
    ```
    
    Previously: zero hooks, zero warnings.
    
    Now:
    
    ```text
    unknown field `SessionStart`, expected `hooks`
    ```
    
    The supported shape remains:
    
    ```json
    { "hooks": { "SessionStart": [...] } }
    ```
    
    ## Fix
    
    Reject unknown top-level fields and surface the parse warning in human
    and JSONL `codex exec` output.
  • Remove fs/join and fs/parent from exec-server protocol (#27700)
    ## Summary
    
    Path composition is already handled by `PathUri`, leaving `fs/join` and
    `fs/parent` as redundant exec-server protocol surface. Because
    app-server and exec-server are deployed atomically, these obsolete
    methods can be removed without a compatibility shim.
    
    This removes the protocol constants and payloads, public client APIs,
    server registrations and handlers, and endpoint-only tests. Existing
    in-process `PathUri` join/parent coverage remains.
    
    ## Validation
    
    - `just test -p codex-exec-server` (215 passed, 2 skipped)
  • feat: prefer managed Bedrock auth in model provider (#27689)
    ## Why
    
    The Amazon Bedrock model provider currently discards the shared
    `AuthManager`, so a Codex-managed Bedrock API key cannot reach
    request-time provider auth. Bedrock instead falls through to AWS
    environment or SDK credentials, and the request endpoint can be resolved
    from a different region than the managed credential.
    
    Managed Bedrock login should control both the bearer credential and
    Mantle region. Unrelated OpenAI or ChatGPT credentials must remain
    isolated from Bedrock.
    
    ## What changed
    
    - Pass the shared `AuthManager` into `AmazonBedrockModelProvider`.
    - Select `CodexAuth::BedrockApiKey` before the existing
    `AWS_BEARER_TOKEN_BEDROCK` and AWS SDK/SigV4 paths.
    - Use the managed Bedrock auth region when resolving the Mantle
    endpoint.
    - Filter other `CodexAuth` variants so OpenAI and ChatGPT auth are not
    exposed to Bedrock request auth or unauthorized recovery.
    - Add focused coverage for provider construction, managed-auth
    precedence, bearer headers, endpoint selection, and OpenAI-auth
    isolation.
  • [codex] Avoid duplicate hooks.json discovery with profiles (#26418)
    ## Summary
    
    V2 profiles add both `config.toml` and `<profile>.config.toml` to the
    config stack. Because both user layers resolve hook discovery to the
    same Codex home, Codex loaded the same `hooks.json` twice. This
    duplicated hook rows and caused each matching command to run twice.
    
    Deduplicate JSON hook discovery by absolute config folder within each
    effective config stack. TOML hooks remain layer-specific, and multi-cwd
    `hooks/list` results remain independently resolved per cwd.
    
    ## Reproduction
    
    1. Add `config.toml` and `work.config.toml` under `$CODEX_HOME`.
    2. Add one command hook to `$CODEX_HOME/hooks.json`.
    3. Run Codex with `--profile work`.
    4. Trigger the hook.
    
    Before this change, one declaration creates two handlers. Afterward, it
    creates one.
    
    Fixes #25645 and addresses the single-cwd duplication in #25437.
    
    ## Validation
    
    - `cargo nextest run -p codex-hooks`
    - `just fix -p codex-hooks`
    - `just fmt`
    - `just argument-comment-lint -p codex-hooks`
  • Include thread id in token budget context (#27663)
    ## Why
    
    The token budget full-context fragment identifies the current context
    window, but not the thread that owns that window. Including the thread
    id makes the initial context-window metadata self-contained, and
    `get_context_remaining` also needs to be usable from Code Mode without
    forcing callers to parse the model-facing fragment string.
    
    ## What changed
    
    - Include the session thread id in the initial `<token_budget>` context
    fragment.
    - Expose `get_context_remaining` as a Code Mode nested tool while
    keeping `new_context` direct-model-only.
    - Keep direct model-facing `get_context_remaining` output as the
    existing `<token_budget>` text fragment.
    - Return only `tokens_left` from the Code Mode structured result for
    `get_context_remaining`.
    - Update token-budget integration tests and add Code Mode coverage for
    the structured result.
    
    ## Verification
    
    - `just test -p codex-core token_budget`
    - `just test -p codex-core
    code_mode_get_context_remaining_returns_structured_result`
    - `just test -p core_test_support redacted_text_mode_normalizes_uuids`
  • [codex] migrate exec-server filesystem protocol to PathUri (#27653)
    Exec-server filesystem calls should preserve cross-platform `file:` URIs
    across the remote boundary instead of converting them through paths
    native to the client host.
    
    This changes the exec-server filesystem protocol DTOs to use `PathUri`,
    carries those values directly through remote and sandbox-helper
    transports, and keeps legacy native absolute-path request strings
    readable for compatibility. It also updates protocol documentation and
    coverage for URI serialization and non-native URI forwarding.
  • [codex-rs] enforce PAT workspace restrictions (#27450)
    ## Summary
    
    - validate a hydrated personal access token's workspace against
    `forced_chatgpt_workspace_id` before persisting `codex login
    --with-access-token`
    - apply the same PAT-only check when restricted auth managers load
    environment, ephemeral, or persisted credentials
    - enforce PAT workspace restrictions in the existing central
    login-restriction path
    - leave Agent Identity and cloud bootstrap behavior unchanged
    
    ## Scope
    
    This is intentionally the small PAT-only change. It does not attempt the
    broader auth-manager/bootstrap unification; that needs separate design
    work.
    
    ## Validation
    
    - `CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=/tmp/codex-pat-target just test
    -p codex-login -p codex-cli` (410 passed)
    - `CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=/tmp/codex-pat-target just fix
    -p codex-login -p codex-cli`
    - `just fmt`
    - `git diff --check`
    
    Context: https://openai.slack.com/archives/D0AUPLV03RQ/p1781138331548269
  • core: Consolidate Responses API Codex metadata (#27122)
    ## What
    Introduce a `CodexResponsesMetadata` struct that defines all the core
    metadata we send to Responses API. Example fields are `thread_id`,
    `turn_id`, `window_id`, etc.
    
    Going forward, `client_metadata["x-codex-turn-metadata"]` will be the
    canonical way Codex sends metadata to Responses API across both HTTP and
    websocket transports.
    
    For now, we continue to emit the existing top-level HTTP headers and
    top-level `client_metadata` fields from the same
    `CodexResponsesMetadata` struct for compatibility reasons.
    
    Also, app-server clients who specify additional
    `responsesapi_client_metadata` via `turn/start` and `turn/steer` will
    have those fields merged into
    `client_metadata["x-codex-turn-metadata"]`, but cannot override the
    reserved fields that core uses (i.e. the fields in
    `CodexResponsesMetadata`).
    
    ## Why
    
    Responses API request instrumentation is the source of truth for
    downstream Codex analytics that join requests by Codex IDs such as
    session, thread, turn, and context window. Before this change, those
    values were assembled through several request-specific paths: HTTP
    request bodies, websocket handshake headers, websocket `response.create`
    payloads, compaction requests, and the rich `x-codex-turn-metadata`
    envelope all had their own wiring.
    
    That made metadata propagation easy to drift across API-key/direct
    Responses API requests, ChatGPT-auth/proxied requests, websocket
    requests, and compaction requests. It also made additions like
    `window_id` error-prone because a field could be added to one transport
    projection but missed in another.
    
    ## What changed
    
    - Added `CodexResponsesMetadata` as the core-owned snapshot for Codex
    metadata sent to ResponsesAPI.
    - Render `client_metadata["x-codex-turn-metadata"]`, flat
    `client_metadata` projections, and direct compatibility headers from
    that same snapshot.
    - Include the known Codex-owned fields in the turn metadata blob,
    including installation/session/thread/turn/window IDs, request kind,
    lineage, sandbox/workspace metadata, timing, and compaction details.
    - Treat app-server `responsesapi_client_metadata` as enrichment for the
    Codex turn metadata blob while preventing those extras from overriding
    Codex-owned fields.
    - Use the same metadata path for normal turns, websocket prewarm, local
    compaction, remote v1 compaction, and remote v2 compaction.
    - Keep websocket connection-only preconnect metadata separate so
    handshakes carry compatibility identity headers without inventing a fake
    turn metadata blob.
    
    ## Verification
    
    - `cargo check -p codex-core`
    - `just fix -p codex-core`
  • Resolve MCP server registrations through a catalog (#27634)
    ## Why
    
    MCP servers currently come from user config, local plugins,
    compatibility Apps synthesis, and host extensions. Those sources were
    composed by mutating a shared map, leaving registration identity,
    precedence, removal, and provenance implicit in assembly order.
    
    Before adding executor-owned MCPs, Codex needs one durable resolution
    boundary above `McpConnectionManager`. This PR introduces that boundary
    while preserving current server configuration, policy, and runtime
    behavior. Executor-scoped registrations and explicit policy layers
    remain follow-ups.
    
    ## What changed
    
    - Add typed `McpServerRegistration` inputs and an immutable
    `ResolvedMcpCatalog` in `codex-mcp`.
    - Retain each registration's complete `McpServerConfig`, including its
    environment binding, while recording its source and provenance.
    - Preserve the existing structural precedence between plugin, config,
    compatibility, and ordered extension sources.
    - Resolve equal-precedence actions by contribution order; provenance IDs
    are used only for diagnostics and cannot affect the winner.
    - Preserve extension removals and the existing name-scoped `enabled =
    false` veto.
    - Report same-tier conflicts with every contender and the final catalog
    outcome, including whether the winning action registers or removes the
    server.
    - Require MCP contributors to provide a stable diagnostic identity.
    - Derive materialized server maps and plugin ownership from the resolved
    catalog.
    
    `McpConnectionManager`, transport startup, tool calls, and resource
    routing continue to consume the same effective `McpServerConfig` values.
    
    ## Scope
    
    This PR does not add new MCP capabilities or change user-visible
    behavior. It does not add executor plugin discovery, thread-scoped
    registrations, dynamic refresh generations, or new user/managed policy
    semantics.
    
    ## Verification
    
    - Added focused catalog coverage for source precedence, complete
    configuration preservation, disabled vetoes, plugin ownership,
    contribution-order tie breaking, removal outcomes, and conflict
    diagnostics.
    - Extended hosted Apps coverage for ordered extension removal and
    Apps-disabled hosts with and without the hosted extension installed.
    - `cargo check -p codex-mcp --tests -p codex-extension-api -p
    codex-core`
  • [codex] Load user instructions through an injected provider (#27101)
    ## Why
    
    We want to remove implicit use of `$CODEX_HOME` from `codex-core` and
    make embedders responsible for supplying user-level instructions. This
    also ensures user instructions load when no primary environment is
    selected.
    
    ## What changed
    
    Stacked on #27415, which makes `codex exec` surface thread-scoped
    runtime warnings.
    
    - Added `UserInstructionsProvider` to `codex-extension-api`, with
    absolute source attribution and recoverable loading warnings.
    - Added `codex-home` with the filesystem-backed provider for
    `AGENTS.override.md` and `AGENTS.md`, preserving precedence, fallback,
    trimming, lossy UTF-8 handling, and the existing uncapped global
    instruction size.
    - Removed global instruction loading from `Config` and require
    `ThreadManager` callers to inject a provider.
    - Load provider instructions once for each fresh root runtime, including
    runtimes without a primary environment. Running sessions retain their
    snapshot, while child agents inherit the parent snapshot without
    invoking the provider.
    - Keep provider instructions separate while loading project `AGENTS.md`,
    then assemble the model-visible instructions with the existing ordering,
    source attribution, warning, and turn-context behavior.
    - Wired the Codex home provider through the CLI, app server, MCP server,
    core facade, and thread-manager sample.
    
    ## Validation
    
    - `just test -p codex-home -p codex-extension-api`
    - `just test -p codex-core agents_md`
    - `just test -p codex-core guardian`
    - `just test -p codex-app-server
    thread_start_without_selected_environment_includes_only_global_instruction_source`
    - `just test -p codex-exec warning`
    - `just bazel-lock-check`
  • [codex] migrate ExecutorFileSystem paths to PathUri (#27424)
    ## Why
    
    We're moving exec-server to use PathUri for its internal path
    representations.
    
    ## What
    
    Move `ExecutorFileSystem` APIs to use `PathUri` instead of
    `AbsolutePathBuf`. Future changes will convert higher-level parts of
    exec-server.
  • [codex] remove EnvironmentPathRef (#27433)
    We're switching to using a static encoding of the host path in
    `PathUri`. We may need a type like this again but we can add it when
    it's more compelling.
    
    Stacked on #27454.
  • [codex] Provide ARM64 MinGW powl compatibility support (#27323)
    ## Why
    
    Windows ARM64 uses 64-bit `long double`, but the LLVM MinGW Bazel
    configuration omits the upstream `powl` compatibility source and does
    not link the `mingwex` archive that owns it. Cross-linking the release
    binary therefore fails with an unresolved `powl` symbol.
    
    ## What changed
    
    Patch the LLVM module to compile `math/arm-common/powl.c` into the ARM64
    MinGW extension sources and add `-lmingwex` to the Windows toolchain
    defaults.
    
    ## Validation
    
    - `just bazel-lock-check`
    
    Stack: 3 of 6. Depends on #27322.
  • feat: disable orchestrator skills for now (#27646)
    Temp disable orchestrator-only skills while waiting for the endpoint to
    be fixed
  • [codex] revert concurrent npm publishing (#27639)
    In https://github.com/openai/codex/actions/runs/27354608310, the
    concurrency introduced by
    
    https://github.com/openai/codex/commit/5e50e7e639c9284ceac24a5498b73a5602fb6615
    caused the npm publish job to fail.
    
    The six platform tarballs contain different versions of the same
    `@openai/codex` package. Every publish updates the same packument, so
    only two concurrent updates succeeded while four failed with HTTP 409.
    
    Serializing that group would leave only the responses API proxy running
    in parallel. Saving one publish does not justify the nested `xargs`
    machinery needed to express those groups.
    
    Restore the serial publish loop and document why the platform variants
    must not publish concurrently. Platform packages remain ahead of the
    root CLI wrapper, and the SDK remains after its exact root dependency.
  • [codex] Surface runtime warnings in codex exec (#27415)
    ## Why
    
    `codex exec` drops thread-scoped warning notifications. Warnings
    discovered while a thread starts, including unreadable or invalid UTF-8
    project `AGENTS.md` files, therefore become silent.
    
    ## What changed
    
    - Process global and primary-thread warning notifications while
    continuing to ignore warnings from unrelated threads.
    - Render runtime warnings in human output and expose them through the
    existing non-fatal error item in JSONL output.
    - Add focused routing, rendering, and malformed project-instruction
    coverage.
  • [codex] add cross-platform filesystem adapter coverage (#27454)
    ## Why
    
    The exec-server's existing filesystem tests only run on `#[cfg(unix)]`.
    We should be running the applicable ones on Windows, and also include
    the basic filesystem operations that will be modified by migrating to
    `PathUri`.
    
    ## What
    
    Split platform-neutral local/remote tests into a shared Unix/Windows
    suite while keeping the existing `AbsolutePathBuf` API, and add Windows
    junction canonicalization coverage.
  • [codex] Propagate plugin app categories (#27420)
    ## What
    - Parse optional `.app.json` `category` overrides for plugin apps.
    - Add nullable `category` to `AppSummary` and `AppTemplateSummary` in
    the app-server protocol.
    - Fall back from `branding.category` to the first non-empty
    `app_metadata.categories` value when building app/template summaries.
    - Regenerate schema/type fixtures and update plugin read/install tests.
    
    ## Why
    The plugin details UI needs a normalized per-app category. Some apps
    only provide their default category in metadata, while others need a
    local `.app.json` override.
  • lint: allow self-documenting builder arguments (#27507)
    Builder-style setters often repeat the setting name in both the method
    and its sole argument. Calls such as `.enabled(false)` are already
    self-documenting, so requiring `/*enabled*/` adds noise without
    clarifying the call.
    
    ## What changed
    
    - Exempt a method's sole non-self argument when its resolved parameter
    name matches the method name.
    - Continue validating any explicit argument comment against the resolved
    parameter name.
    - Continue requiring comments when method and parameter names differ or
    when a method has multiple non-self arguments.
    - Document the exception in `AGENTS.md` and the lint's own behavior
    documentation.
    
    ## Examples
    
    Before this change we'd need redundant comments like this:
    
    ```rust
    builder.enabled(/*false*/ false);
    builder.retry_count(/*retry_count*/ 3);
    builder.base_url(/*base_url*/ None);
    ```
    
    Now can be written like this:
    
    ```rust
    builder.enabled(false);
    builder.retry_count(3);
    builder.base_url(None);
    ```
    
    Still disallowed:
    
    ```rust
    client.set_flag(true); // Method name does not match parameter `enabled`.
    options.enabled(false, /*retry_count*/ 3); // More than one non-self argument.
    options.enabled(/*value*/ false); // Explicit comment does not match `enabled`.
    ```
    
    ## Validation
    
    Added UI coverage for boolean, numeric, and `None` builder arguments,
    multi-argument methods, and explicit comment mismatches. Ran `rustup run
    nightly-2025-09-18 cargo test` in `tools/argument-comment-lint`.
  • Print TUI session info on fatal exits (#27417)
    ## Summary
    
    TUI exits printed the resume/session summary only after checking the
    exit reason. On fatal exits, both CLI wrappers wrote the error and
    called `process::exit(1)` immediately, so an active session that ended
    on a fatal error could skip the session information entirely.
    
    This change prints the normal exit summary before returning the fatal
    nonzero exit code. If a fatal exit has a known thread id but no
    resumable rollout hint, it prints `Session ID: <id>` instead of staying
    silent. It also flushes stdout before `process::exit(1)` so the summary
    line is not lost during process teardown.
    
    ## Implementation
    
    - Apply the fatal-exit ordering fix in both `codex` and standalone
    `codex-tui`.
    - Keep normal user-requested exit behavior unchanged.
    - Preserve the existing resume hint when a rollout is resumable, and use
    the raw thread id only as a fatal-exit fallback.
  • Emit plugin ID on MCP tool call analytics events (#27483)
    MCP tool-call items already carry the runtime-resolved plugin owner, but
    the analytics reducer dropped that field. Forwarding the existing value
    provides direct attribution without downstream server-name inference.
    
    ## Summary
    
    - emit `plugin_id` on `codex_mcp_tool_call_event` payloads
    - preserve `null` for MCP calls without a plugin owner
    - verify the serialized field through the MCP item lifecycle test
    
    ## Test
    
    - `cd codex-rs && just test -p codex-analytics`
    - `cd codex-rs && just fix -p codex-analytics`
    - `cd codex-rs && just fmt`
  • Remove TUI legacy Windows sandbox dependency (#27490)
    ## Why
    
    This is part of an ongoing attempt to eliminate the TUI's direct
    dependency on core features. When we moved the TUI to the app server, we
    left a `legacy_core` shim that re-exported some remaining core symbols
    for the TUI. The intent was to eventually remove all of these.
    
    In this PR, we remove the symbols related to the Windows sandbox.
    
    The change should be behavior-neutral and low risk because it's just
    refactoring and removal of code that is now effectively dead.
    
    When working on this PR, I noticed a big existing problem that affects
    mixed-platform remoting. For example, if you run the TUI on a Linux box
    and remote into a Windows box, the TUI logic doesn't properly handle
    Windows sandbox setup properly. Fixing this is beyond the scope of this
    PR, but I've left a TODO comment in place so we don't forget.
    
    ## What changed
    
    - Move the remaining TUI-specific sandbox level, setup, telemetry, and
    read-root helpers into `codex-tui`, calling `codex-windows-sandbox`
    directly.
    - Remove the Windows sandbox namespace and read-root grant re-exports
    from the client-side `legacy_core` facade.
    - Remove the dormant pre-elevation prompt fallback guarded by the
    permanently enabled `ELEVATED_SANDBOX_NUX_ENABLED` switch. The reachable
    elevated and non-elevated setup flows remain unchanged.
  • [codex] download only release artifacts (#27529)
    In https://github.com/openai/codex/actions/runs/27308011621, the
    release job downloaded 10.0 GiB of workflow artifacts in 87 seconds,
    then discarded 42 artifacts accounting for 3.3 GiB.
    
    Select target and supplemental release artifact patterns at download
    time. This also excludes duplicate Cargo timing files without a cleanup
    pass and should reduce total release time by about 30 seconds.
  • [codex] publish DotSlash alongside npm (#27528)
    In https://github.com/openai/codex/actions/runs/27308011621,
    preparing and publishing the three DotSlash configurations took 72
    seconds after creating the GitHub release. npm publication could not
    start until those independent steps finished.
    
    Move DotSlash publication to a sibling job that starts after the GitHub
    release. npm and DotSlash can then proceed concurrently, reducing total
    release time by about one minute.
  • [codex] publish npm packages concurrently (#27527)
    In https://github.com/openai/codex/actions/runs/27308011621,
    publishing the npm tarballs serially took 147 seconds. Six platform
    packages and the responses API proxy are independent.
    
    Publish those packages concurrently, then publish the root CLI wrapper
    and SDK in dependency order. Individual platform publishes took 19 to
    23 seconds, so this should reduce total release time by nearly two
    minutes.
  • skills: decouple the skills extension from core (#27413)
    ## Why
    
    `ext/skills` currently depends on `codex-core` for two host concerns:
    reading the concrete `Config` type and borrowing core-owned
    model-context fragment types. That coupling prevents the extension from
    being assembled independently above core and leaves context that belongs
    to the skills feature owned by core.
    
    This stacked PR introduces the host boundary needed for the broader
    extension migration while intentionally preserving existing skills
    behavior. It is stacked on #27404.
    
    ## What changed
    
    - Adds a small public `SkillsExtensionConfig` view and makes skills
    installation generic over the host config type.
    - Requires the host to map its config into that view; app-server
    supplies the current `Config` values.
    - Moves the available-skills and selected-skill context fragment
    implementations into `ext/skills`, preserving their roles, markers, and
    rendered bytes.
    - Removes the direct `codex-core` dependency from
    `codex-skills-extension`.
    - Keeps local discovery, invocation, side effects, and the
    `codex-core-skills` compatibility types unchanged for later staged PRs.
    
    ## Behavior
    
    This adds no capability and is intended to have no user-visible or
    model-visible behavior change. The install API and ownership boundary
    change internally; emitted skills context remains byte-for-byte
    compatible.
    
    ## Validation
    
    - Updates the skills extension integration coverage to use a host-owned
    test config.
    - Asserts the complete rendered catalog and selected-skill fragments,
    including their roles and markers.
    - `just bazel-lock-check`
    - Rust tests and Clippy were not run locally per request; CI will run
    them.
  • skills: render catalog locators by authority (#27591)
    ## Why
    
    Hosted skills introduced by #27388 use opaque `skill://` resource
    identifiers, but the skills catalog rendered every locator as a `file`
    and told the model that every skill body lived on disk. That can send
    the model toward filesystem tools for a resource that must instead be
    read through its owning authority.
    
    The catalog should describe how each source is accessed without changing
    the underlying discovery or invocation behavior.
    
    ## What changed
    
    - Render host skills as `file`, executor-owned skills as `environment
    resource`, orchestrator-owned skills as `orchestrator resource`, and
    custom-provider skills as `custom resource`.
    - Update the shared no-alias guidance to describe source locators rather
    than assuming every skill is stored on the host filesystem.
    - Direct orchestrator resources through `skills.list` and `skills.read`,
    and explicitly tell the model not to treat `skill://` identifiers as
    filesystem paths.
    - Preserve the existing filesystem and alias behavior for local skills.
    
    ## Scope
    
    This PR changes only model-visible catalog rendering and guidance. It
    does not change skill discovery, selection, prompt injection, provider
    routing, catalog caching or refresh behavior, resource validation, or
    the `skills.*` tool contract.
    
    ## Verification
    
    - Extended skills-extension coverage for host-file and executor-resource
    labels.
    - Extended the no-executor app-server flow to assert
    orchestrator-resource wording and non-filesystem guidance.
  • test: cover referenced backend skill reads without an executor (#27404)
    ## Why
    
    PR #27388 lets models read child resources referenced by backend plugin
    skills without an executor. The integration fixture should prove that
    real flow: the injected `SKILL.md` advertises a child `skill://`
    resource, and `skills.read` resolves that exact resource through the
    backend provider.
    
    This is stacked on #27388.
    
    ## What changed
    
    - Adds a child-resource link to the backend skill fixture and asserts
    that it reaches model context.
    - Tightens the end-to-end skills test around `skills.list` followed by
    `skills.read` for the referenced resource.
    - Splits the existing app-server `mcpResource/read` coverage into a
    focused test so the generic RPC path remains covered independently.
    
    ## Validation
    
    - Adds app-server integration coverage for both the referenced backend
    skill resource and the generic MCP resource read path.
  • nit: cap error (#27585)
    Just cap an error that could end up in the model context
  • multi-agent: move concurrency guidance into v2 usage hints (#27569)
    ## Why
    
    Native Codex currently teaches multi-agent concurrency through the
    `spawn_agent` tool description, while bridge-driven evals frame the same
    limit as a shared pool of active agent slots. That mismatch makes the
    model-facing story harder to reason about, especially because the
    tool-level wording does not make it explicit that the limit covers the
    whole agent team, including the current agent.
    
    This change gives native Codex the same mental model: tell the root
    agent and subagents how many active slots exist, and remove the separate
    `spawn_agent` limit wording.
    
    ## What changed
    
    - Extend the built-in `multi_agent_v2` root and subagent usage hints
    with shared-slot guidance derived from the resolved
    `max_concurrent_threads_per_session` value.
    - Keep the complete default hints in `MultiAgentV2Config` so initial
    context and forked histories consume the same canonical strings.
    - Drop the redundant `spawn_agent` description text and remove the
    now-unused limit plumbing from the tool spec path.
    
    ## Testing
    
    - `just test -p codex-core usage_hint`
    - `just test -p codex-core
    multi_agent_v2_default_session_thread_cap_counts_root`
    - `just test -p codex-core
    multi_agent_v2_default_usage_hints_use_configured_thread_cap`
    - `just test -p codex-core
    spawn_agent_tool_v2_requires_task_name_and_lists_visible_models`
    - `just test -p codex-core
    multi_agent_feature_selects_one_agent_tool_family`
  • skills: expose remote skill resource tools (#27388)
    ## Why
    
    PR #27387 makes backend plugin skills discoverable and invocable without
    an executor, but resources referenced by those skills still sit behind
    the generic MCP resource surface. The model needs a skills-owned API
    that preserves the provider authority and package boundary instead of
    treating remote resources like local files.
    
    This is stacked on #27387.
    
    ## What
    
    - Adds one `skills` namespace with bounded `list` and `read` tools for
    remote skill providers.
    - Revalidates `authority + package` against the live remote catalog on
    every read, then routes the opaque resource ID back through that
    provider.
    - Allows the backend provider to read canonical child `skill://`
    resources while rejecting cross-package, non-canonical, query, fragment,
    and traversal-shaped URIs.
    - Caps each serialized tool result at 8 KB. Lists are paginated; reads
    return an opaque continuation cursor.
    - Marks the JSON output as external context so memory generation can
    apply its normal suppression policy.
    - Deliberately does not add `skills.search`; that waits for a bounded
    plugin-service search contract.
    
    ## Tool contract
    
    Pseudo-Python matching the wire shape:
    
    ```python
    from typing import Literal, NotRequired, TypedDict
    
    
    class RemoteSkillAuthority(TypedDict):
        kind: Literal["remote"]
        id: str  # e.g. "codex_apps"
    
    
    class RemoteSkill(TypedDict):
        authority: RemoteSkillAuthority
        package: str  # opaque provider-owned package ID
        name: str
        description: str
        main_resource: str  # opaque provider-owned SKILL.md ID
    
    
    class SkillsListParams(TypedDict):
        cursor: NotRequired[str]
    
    
    class SkillsListResult(TypedDict):
        skills: list[RemoteSkill]
        next_cursor: str | None
        warnings: list[str]
        truncated: bool
    
    
    class SkillsReadParams(TypedDict):
        authority: RemoteSkillAuthority  # copied from skills.list
        package: str  # copied from skills.list
        resource: str  # provider-owned child resource ID
        cursor: NotRequired[str]  # copy next_cursor to continue
    
    
    class SkillsReadResult(TypedDict):
        resource: str
        contents: str
        next_cursor: str | None
        truncated: bool
    
    
    class Skills:
        def list(self, params: SkillsListParams) -> SkillsListResult: ...
        def read(self, params: SkillsReadParams) -> SkillsReadResult: ...
    ```
    
    There is one namespace for all remote skills, not one tool or MCP server
    per skill. No resource ID is converted into a filesystem path.
    
    ## Backend dependency
    
    `/ps/mcp` must support direct reads of child resources such as
    `skill://plugin_demo/deploy/references/deploy.md`. This PR implements
    and tests the Codex side of that contract; production child reads remain
    dependent on the corresponding plugin-service support. Search remains
    out of scope until that service exposes a bounded search/resource API.
    
    ## Validation
    
    - Added an app-server integration test covering `skills.list` followed
    by `skills.read` with no executor.
    - Ran `just fmt`.
    - Ran `just bazel-lock-update` and `just bazel-lock-check`.
    - Did not run Rust tests or Clippy locally, per request; CI will run
    them.
  • core: enable remote compaction v2 by default (#27573)
    ## Why
    
    Remote compaction v2 is ready to become the default for providers that
    already support remote compaction. Leaving it behind an
    under-development opt-in keeps eligible sessions on the legacy
    remote-compaction path.
    
    This does not broaden provider eligibility: OpenAI and Azure move to v2,
    while Bedrock and OSS providers retain their existing local-compaction
    behavior.
    
    ## What changed
    
    - Mark `remote_compaction_v2` stable and enable it by default.
    - Make tests that intentionally cover legacy remote compaction
    explicitly disable v2.
    - Update parity coverage so v2 exercises the production default and only
    legacy mode opts out.
    
    ## Verification
    
    - `just test -p codex-core
    auto_compact_runs_after_resume_when_token_usage_is_over_limit
    auto_compact_counts_encrypted_reasoning_before_last_user
    auto_compact_runs_when_reasoning_header_clears_between_turns
    responses_lite_compact_request_uses_lite_transport_contract`
  • skills: cache remote catalog failures per thread (#27403)
    ## Summary
    
    - cache the first remote skill catalog outcome per thread, including
    failures
    - preserve discovery errors as catalog warnings
    - update the existing cache regression test to verify failed discovery
    is attempted once
    
    ## Why
    
    A failed or hanging `codex_apps` `resources/list` call could run once
    while building initial context and immediately again while contributing
    first-turn input. With the discovery timeout, an ordinary Apps turn
    could wait up to 20 seconds before inference and retry again on later
    turns even when no remote skill was mentioned.
    
    Caching a warning-only empty catalog preserves graceful degradation
    while preventing repeated synchronous discovery attempts.
    
    ## Testing
    
    - `just fmt`
    - Tests and Clippy not run per request; CI will validate the change.
  • skills: make backend plugin skills invocable without an executor (#27387)
    ## Why
    
    #27198 made the extension-owned `codex_apps` MCP connection the hosted
    plugin runtime, but its `mcp/skill` resources still bypassed the skills
    extension. App-server could list and read those resources through
    generic MCP APIs, but a thread with no selected environment did not
    expose them in the model's skills catalog or load their `SKILL.md`
    through `$skill`.
    
    Hosted skills should stay remote while using the same typed catalog,
    source authority, deduplication, bounded contextual catalog, and
    selected-skill prompt injection as host and executor skills. They should
    not be downloaded or exposed as ambient filesystem paths.
    
    ## What changed
    
    - Add a session-scoped `McpResourceClient` over the replaceable MCP
    connection manager so resource list/read calls follow startup and
    refresh replacements.
    - Add a `BackendSkillProvider` that pages `codex_apps` resources,
    accepts bounded and validated `mcp/skill` entries, and reads a selected
    skill's `SKILL.md` through the same MCP connection.
    - Register the remote provider in app-server and include it in the
    skills catalog even when a thread has no selected capability roots or
    executor.
    - Contribute hosted skill metadata through the bounded
    `AvailableSkillsInstructions` developer-context path, exclude remote
    entries from per-turn catalog injection, and classify `<skills>`
    messages as contextual developer content so rollback can trim and
    rebuild them correctly.
    
    ## Testing
    
    - Extend the app-server MCP resource integration test with
    `environments: []` to exercise two-page discovery, filter a
    non-`mcp/skill` resource, verify the escaped developer catalog entry and
    user-role `<skill>` fragment containing the fetched `SKILL.md`, and
    preserve generic MCP resource reads.
    - Add core event-mapping coverage that classifies `<skills>` developer
    messages as contextual history.
  • [codex] Tune cloud config cache intervals (#26513)
    ## Summary
    - Increase the cloud config bundle background refresh interval from 5
    minutes to 15 minutes.
    - Increase the local cloud config bundle cache TTL from 30 minutes to 1
    hour.
    
    ## Why
    - Reduce background cloud config fetch frequency while keeping cached
    workspace-managed policies available longer between refreshes.
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-cloud-config`
  • [codex-analytics] Emit structured compaction codex errors (#27082)
    ## Summary
    - replace raw compaction `error` analytics with `codex_error_kind` and
    `codex_error_http_status_code`
    - derive compaction error telemetry from `CodexErr` using the same
    `CodexErrKind` mapping and HTTP status helper used by turn events
    - remove the pre-compact hook stop reason from the internal compaction
    outcome now that it is no longer emitted as raw analytics text
    
    ## Why
    Compaction `error` was a raw `CodexErr::to_string()` value, which can
    carry free-form provider or user-derived text. Structured Codex error
    fields preserve useful low-cardinality telemetry without sending the raw
    string.
    
    ## Validation
    - `just fmt`
    - `just test -p codex-analytics`
    - `just test -p codex-core
    compact::tests::build_token_limited_compacted_history_appends_summary_message`
    
    Attempted `just test -p codex-core`; the changed crate compiled, but the
    full target failed in unrelated environment-dependent tests such as
    missing helper binaries and shell snapshot timeouts.
  • Use generic search metadata for dynamic tools (#27356)
    ## Why
    
    Dynamic tools maintained a separate search-text builder even though the
    shared tool search path already derives the same metadata from
    `ToolSpec`. Using the shared path removes duplicate behavior before
    adding explicit namespaces.
    
    ## What changed
    
    - Build dynamic-tool search entries with
    `ToolSearchInfo::from_tool_spec`.
    - Remove the custom search-text state and its implementation-only unit
    test.
    
    The old search text included the tool name, its space-separated form,
    description, namespace, and top-level parameter names. The shared
    builder preserves all of those terms and also indexes namespace
    descriptions and nested schema metadata.
    
    ## Test plan
    
    - `just test -p codex-core
    tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call`
  • [codex-analytics] report cached input tokens for v2 compaction (#27103)
    ## Summary
    
    - add nullable `cached_input_tokens` to the compaction analytics event
    - populate it from response usage for compaction v2
    - leave it `null` for other compaction implementations
    
    This adds visibility into prompt-cache usage for v2 compaction without
    changing compaction behavior.
    
    ## Testing
    
    - `just test -p codex-analytics`
    - `just test -p codex-core
    collect_compaction_output_accepts_additional_output_items`
  • image: preserve metadata when resizing prompt images (#27266)
    ## Summary
    
    - Preserve ICC profiles and EXIF metadata when resizing and re-encoding
    prompt images.
    - Retain EXIF orientation metadata without rotating or otherwise
    modifying the pixel data locally.
    - Support metadata preservation for PNG, JPEG, and WebP outputs.
    - Continue returning the original bytes when an image does not require
    re-encoding.
    
    This intentionally preserves the metadata most important for rendering
    prompt images faithfully. Other format-specific metadata is not copied.
    
    ## Motivation
    
    Client-side resizing previously discarded image metadata during
    re-encoding. This could lose color-profile information and EXIF
    orientation needed by downstream image consumers.
    
    
    #### [git stack](https://github.com/magus/git-stack-cli)
    -  `1` https://github.com/openai/codex/pull/27245
    -  `2` https://github.com/openai/codex/pull/27247
    -  `3` https://github.com/openai/codex/pull/27246
    - 👉 `4` https://github.com/openai/codex/pull/27266
  • [codex] Add context remaining tool (#27518)
    ## Why
    
    The token budget feature can inject remaining-context notices into
    model-visible context, but the model does not have a direct way to ask
    for that same remaining-token fragment on demand.
    
    This PR adds a small model tool for the token budget feature so the
    model can request the current remaining context window message without
    duplicating the fragment format.
    
    ## What changed
    
    - Adds a `get_context_remaining` direct-model tool behind
    `Feature::TokenBudget`.
    - Renders the tool output through `TokenBudgetRemainingContext`,
    matching the existing budget message shape.
    - Registers the tool alongside `new_context` in the token budget tool
    set.
    - Adds integration coverage that verifies the tool is exposed and
    returns the same `<token_budget>` remaining fragment already present in
    context.
    
    ## Validation
    
    - `just test -p codex-core token_budget`
  • [codex] Compact when comp_hash changes (#27520)
    ## Summary
    - snapshot `comp_hash` into `TurnContext` when the turn is created and
    use that snapshot as the downstream source of truth
    - persist the turn hash in rollout context and recover it into
    previous-turn settings during resume and fork replay
    - compact existing history with the previous model only when both
    adjacent turns provide hashes and the values differ
    - record `comp_hash_changed` as the compaction reason
    - cover ordinary transitions, resume, and missing-hash compatibility
    with end-to-end tests
    
    ## Why
    History produced under one compaction-compatible model configuration may
    not be safe to carry directly into another. Compacting at the turn
    boundary converts that history before context updates and the new user
    message are added. Persisting the turn snapshot in `TurnContextItem`
    makes the same protection work after resuming a rollout.
    
    A missing hash is not treated as evidence of incompatibility. `None →
    Some`, `Some → None`, and `None → None` do not trigger compaction; only
    `Some(previous) → Some(current)` with unequal values does.
    
    ## Stack
    - depends on #27532
    - #27532 is based directly on `main`
    
    ## Testing
    - `just test -p codex-core pre_sampling_compact_` — 6 passed
    - `just test -p codex-core
    turn_context_item_uses_turn_context_comp_hash_snapshot` — passed
    - `just fix -p codex-core -p codex-protocol -p codex-analytics -p
    codex-models-manager`