7919 Commits

  • Add workspace messages app-server API (#29001)
    ## Summary
    
    - Add backend-client types and fetch support for active workspace
    messages.
    - Add the app-server v2 `account/workspaceMessages/read` method,
    generated schemas, and README documentation.
    - Delegate workspace-message eligibility to the Codex backend feature
    gate; map a backend 404 to `featureEnabled: false`.
    
    ## Testing
    
    - `just write-app-server-schema`
    - `just test -p codex-backend-client`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-app-server workspace_messages`
    - `just fix -p codex-backend-client -p codex-app-server-protocol -p
    codex-app-server`
    - `just fmt`
    
    ## Stack
    
    - Base PR for #28232, which adds the TUI status-line integration.
  • Apply sandbox intent inside remote exec servers (#29113)
    ## Why
    
    PR #29108 lets the orchestrator send sandbox intent with `process/start`
    without wrapping the command for its own operating system.
    
    This PR completes that boundary by making the executor interpret and
    enforce the intent using its own filesystem paths and sandbox
    implementation.
    
    For example, a macOS TUI targeting a Linux devbox sends `/bin/bash -lc
    pwd`. The Linux executor turns that into its own `codex-linux-sandbox
    ... /bin/bash -lc pwd` launch.
    
    ## What changes
    
    - Keep `process/start` unchanged when no sandbox intent is present.
    - Convert sandbox `PathUri` values into native paths on the executor.
    - Bind symbolic `:workspace_roots` permissions to the executor's native
    sandbox cwd.
    - Select the sandbox implementation on the executor and wrap the
    original command immediately before spawning it.
    - Reject sandbox-required execution before spawning when the executor
    cannot enforce the intent.
    - Pass exec-server runtime paths into process creation so Linux can
    locate `codex-linux-sandbox`.
    
    The boundary is therefore:
    
    ```text
    orchestrator                         executor
    original argv + sandbox intent  ->  select and enforce local sandbox
    ```
    
    This PR intentionally treats a denied remote command as an ordinary
    command failure. Draft follow-up #29424 carries a semantic
    `sandboxDenied` result back to unified exec for the existing approval
    and retry flow.
    
    ## Platform scope
    
    Linux and macOS use their existing direct-spawn sandbox transforms.
    
    Windows sandboxed remote process launch is intentionally unsupported in
    this PR. The current Windows direct-spawn wrapper does not correctly
    preserve arbitrary argv, TTY behavior, or pass the full child
    environment out of band. The executor rejects the request instead of
    running it incorrectly or unsandboxed.
    
    ## Known follow-ups
    
    - The transported permission profile can still contain
    orchestrator-materialized helper or explicit paths. A `TODO(jif)` marks
    where the executor boundary should receive pre-host-materialization
    permission intent.
    - The sandbox wrapper currently replaces a requested custom inner
    `arg0`. A `TODO(jif)` marks where this must be preserved or rejected
    explicitly.
    - Draft PR #29424 contains the deferred sandbox-denial classification
    and approval/retry behavior.
    
    ## Rollout assumption
    
    This executor-sandbox stack is unreleased and its client and executor
    are expected to move together. This PR does not add mixed-version
    negotiation with older exec servers.
  • Simplify multi-agent mode controls (#29324)
    ## Why
    
    Multi-agent delegation policy was split across `multiAgentMode`,
    `features.multi_agent_mode`, and `usage_hint_enabled`. These controls
    could disagree: a requested mode could be downgraded by the feature
    flag, and disabling usage hints also disabled mode instructions.
    
    Some clients also need multi-agent tools without adding
    delegation-policy text to model context. The previous two-mode API could
    not express that directly.
    
    ## What changed
    
    `multiAgentMode` is now the only live delegation-policy control:
    
    | Mode | Behavior |
    | --- | --- |
    | `none` | Keep multi-agent tools available without adding mode
    instructions. |
    | `explicitRequestOnly` | Only delegate after an explicit user request.
    |
    | `proactive` | Delegate when parallel work materially improves speed or
    quality. |
    
    - new threads default to `explicitRequestOnly`; omitting the mode on
    later turns keeps the current value
    - thread start, resume, fork, and settings responses always report the
    concrete current mode instead of `null`
    - mode selection remains sticky across turns and resume
    - usage-hint text no longer controls whether mode instructions apply
    - `features.multi_agent_mode` and `usage_hint_enabled` remain accepted
    as ignored compatibility settings so existing configs continue to load
    - app-server documentation and generated schemas describe the three-mode
    API
    
    ## Tests
    
    - `just test -p codex-core multi_agent_mode`
    - `just test -p codex-core multi_agent_v2_config_from_feature_table`
    - `just test -p codex-core spawn_agent_description`
    - `just test -p codex-features`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-app-server multi_agent_mode`
  • Persist session IDs across thread resume (#29327)
    ## Summary
    
    A cold-resumed subagent kept its durable thread ID but could receive a
    new session ID, splitting one agent tree across multiple sessions after
    a restart.
    
    Persist the root session ID in every rollout `SessionMeta`, carry it
    through thread creation, and restore it before initializing the resumed
    `Session` and `AgentControl`.
    
    ## Behavior
    
    For a nested agent tree:
    
    ```text
    root session R
      parent thread P
        child thread C
    ```
    
    The child rollout stores:
    
    ```text
    session_id:       R
    parent_thread_id: P
    id:               C
    ```
    
    After a cold resume, the child still belongs to root session `R` while
    its immediate parent remains `P`. The integration coverage uses distinct
    values for all three IDs so it catches restoring the session from
    `parent_thread_id`.
    
    ## Legacy rollouts
    
    Previous rollouts have `id` but no `session_id`. `SessionMetaLine`
    deserialization treats a missing `session_id` as `id`, keeping those
    files readable, listable, and resumable. When a legacy subagent is
    resumed through its root, that synthesized child ID no longer overrides
    the inherited root-scoped `AgentControl`. New rollouts always persist
    the explicit root session ID.
  • chore: fix merge race (auto-compaction feature access) (#29393)
    ## Summary
    
    - read the `AutoCompaction` feature flag through `TurnContext::config`
    - fix both the mid-turn and pre-sampling compaction checks
    
    ## Why
    
    #28260 was validated against an older base where `TurnContext` exposed a
    direct `features` field. It was then merged after that field had moved
    under `config`, leaving the merge result unable to compile with `E0609`
    on `turn_context.features`.
    
    This restores compilation for Bazel, SDK, and argument-comment-lint jobs
    that build `codex-core`. Behavior is unchanged: disabling
    `auto_compaction` still skips automatic compaction.
    
    ## Validation
    
    - `just fmt`
    - `CODEX_HOME=/private/tmp/codex-fix-auto-compaction-test-home just test
    -p codex-core auto_compaction_feature_disabled` — 4 passed
    - `just test -p codex-core` — `codex-core` compiled; 2,722 passed and 89
    unrelated local-environment failures remained because the sandbox could
    not write the default Codex SQLite/proxy paths and some first-party test
    binaries were unavailable
  • Propagate safety buffering events to app-server clients (#29371)
    Responses API safety buffering metadata currently stops at the transport
    boundary, so app-server clients cannot render the in-progress safety
    review state.
    
    This change:
    - decodes and deduplicates `safety_buffering` metadata from Responses
    API SSE and WebSocket events without suppressing the original response
    event
    - emits a typed core event containing the requested model plus backend
    use cases and reasons
    - forwards that event as `turn/safetyBuffering/updated` through
    app-server v2 and updates generated protocol schemas
    - keeps the side-channel event out of persisted rollouts and turn timing
    
    This supports the Codex Apps buffering UX and depends on the Responses
    API backend work in https://github.com/openai/openai/pull/1044569 and
    https://github.com/openai/openai/pull/1044571.
    
    Validation:
    - focused `codex-core` safety-buffering integration test passes
    - `cargo check -p codex-core -p codex-app-server -p
    codex-app-server-protocol`
    - `just fix -p codex-api -p codex-protocol -p codex-core -p
    codex-app-server-protocol -p codex-app-server -p codex-rollout -p
    codex-rollout-trace -p codex-otel`
    - `just fmt`
    - broad package test run: 4,430/4,492 passed; 62 unrelated
    local-environment/concurrency failures involved unavailable test
    binaries, MCP subprocess setup, and app-server timeouts
  • [codex] Add internal auto-compaction opt-out (#28260)
    ## Summary
    
    - add a default-on `auto_compaction` feature flag as an internal escape
    hatch
    - skip pre-turn, model-switch/hash, and mid-turn automatic compaction
    when the flag is disabled
    - preserve manual `/compact` behavior and surface the existing
    context-window error when the provider runs out of room
    - add integration coverage for disabled pre-turn and mid-turn compaction
    
    ## Motivation
    
    Long-running SPO optimization rollouts need the option to preserve their
    full context and fail on context exhaustion instead of entering another
    compaction window. This deliberately uses the existing feature-flag
    mechanism rather than adding a dedicated public config or app-server
    API.
    
    Disable it with:
    
    ```sh
    codex --disable auto_compaction
    ```
    
    ## Testing
    
    - `just test -p codex-features` — 51 passed
    - `just test -p codex-core auto_compaction_feature_disabled` — 2 passed
    - `just fix -p codex-core -p codex-features`
    - `just write-config-schema`
    - `just test -p codex-core` — the new compaction tests passed; the
    overall local run had 54 unrelated environment failures, primarily
    missing first-party test binaries and shell-snapshot timeouts
  • code-mode: preserve initial yield at completion (#29289)
    ## Summary
    
    - Retain the first pre-observation `yield_control()` boundary when a
    cell completes before observation.
    - Deliver the preserved yield before the buffered completion.
    - Keep later unattached yields as no-ops.
    
    ## Why
    
    Create followed by the initial wait must preserve the former execute
    response boundary even when the script runs to completion first.
    
    ## Impact
    
    The first wait observes the same initial yield boundary as before create
    and observe were decoupled.
    
    ## Validation
    
    - Focused initial-yield signature regression passed.
    - Stack-tip validation: `just test -p codex-code-mode -p
    codex-code-mode-protocol` (70 passed).
    - Parent branch:
    `cconger/code-mode-runtime-compact-03e2-observation-delivery`.
  • code-mode: preserve dropped observation output (#29288)
    ## Summary
    
    - Restore yielded output when an observation receiver disappears before
    delivery.
    - Preserve pending-frontier output and tool IDs across failed delivery.
    - Add dropped-observer coverage for yield and pending observations.
    
    ## Why
    
    Canceling a wait must not consume output or a pending frontier that the
    caller never received.
    
    ## Impact
    
    A later observation can recover undelivered incremental output without
    duplication.
    
    ## Validation
    
    - Stack-tip validation: `just test -p codex-code-mode -p
    codex-code-mode-protocol` (70 passed).
    - Parent branch:
    `cconger/code-mode-runtime-compact-03e-shutdown-hierarchy`.
  • [prompting] updated plan mode prompt (#29301)
    Update plan mode prompt to render the implementation plan to the user on
    relevant follow-ups, such that the user can exit out of plan mode to
    implement rather than manually switch of plan mode.
  • code-mode: make session shutdown authoritative (#29287)
    ## Summary
    
    - Give each session and cell a hierarchical cancellation token.
    - Track cell tasks so shutdown waits for admitted actors without polling
    the registry.
    - Make shutdown authoritative across concurrent admission and
    non-cooperative callbacks.
    
    ## Why
    
    A best-effort registry scan can miss cells admitted concurrently or
    blocked behind the registry lock.
    
    ## Impact
    
    Session shutdown reliably stops every admitted cell and rejects new work
    once shutdown begins.
    
    ## Validation
    
    - Stack-tip validation: `just test -p codex-code-mode -p
    codex-code-mode-protocol` (70 passed).
    - Parent branch: `cconger/code-mode-runtime-compact-03c-terminal-state`.
  • code-mode: linearize cell terminal state (#29286)
    ## Summary
    
    - Introduce a single cell terminal-state machine for completion and
    termination.
    - Make stored-value commits atomic with the winning terminal outcome.
    - Buffer terminal results for later observation and cover
    termination-before-commit behavior.
    
    ## Why
    
    Completion, termination, observation, and stored-value updates must
    agree on one linearized outcome under cancellation races.
    
    ## Impact
    
    Terminal delivery becomes deterministic and terminated cells cannot
    commit state after termination wins.
    
    ## Validation
    
    - Focused terminal-state regression passed.
    - Stack-tip validation: `just test -p codex-code-mode -p
    codex-code-mode-protocol` (70 passed).
    - Parent branch:
    `cconger/code-mode-runtime-compact-03b-session-runtime`.
  • code-mode: move session ownership into runtime (#29285)
    ## Summary
    
    - Move code-mode cell ownership and shared stored values from
    `CodeModeService` into `SessionRuntime`.
    - Keep the protocol-facing execute/wait behavior behind the existing
    service adapter.
    - Add runtime-level ownership and isolation coverage.
    
    ## Why
    
    This establishes a transport-neutral session boundary before later
    lifecycle and create/observe changes.
    
    ## Impact
    
    No intended model-facing behavior change. This is an ownership and
    layering refactor.
    
    ## Validation
    
    - Stack-tip validation: `just test -p codex-code-mode -p
    codex-code-mode-protocol` (70 passed).
    - Parent branch: `cconger/code-mode-runtime-compact-03a-runtime-types`.
  • code-mode: define transport-neutral runtime types (#29170)
    ## Summary
    
    - introduce a private `session_runtime` boundary for cell creation
    requests, observation modes, lifecycle events, output items, and tool
    metadata
    - update the cell actor and in-process service to use those
    transport-neutral types
    - keep cell ID allocation on the owning session side
    
    ## Motivation
    
    Cell lifecycle vocabulary currently lives inside the cell actor
    implementation. That makes the service adapter and future session
    runtime depend on actor-specific types, increasing the size and
    complexity of the runtime ownership change.
    
    This is the first reviewable slice of the session-runtime stack. It
    separates the transport-neutral data model without moving lifecycle
    ownership or changing behavior.
    
    Later slices will move session state behind this boundary, harden
    terminal and shutdown behavior, and split cell creation from
    observation.
    
    ## Behavior
    
    There are no public API or user-visible behavior changes in this PR.
    
    In particular:
    
    - `CodeModeSession::execute` and `wait` are unchanged
    - cell IDs remain allocated by the owning session
    - cell admission, observation, termination, and shutdown behavior are
    unchanged
  • Use controlled time for remote initialization timeout test (#29329)
    ## Summary
    
    The remote-control initialization timeout test used a 50 ms wall-clock
    deadline around a 10 ms transport timeout. A busy CI runner could miss
    that outer deadline even when the rollback behavior was correct.
    
    Pause Tokio time and advance it explicitly through the transport timeout
    instead. The test still verifies that initialization fails and emits the
    matching connection-closed event, without depending on scheduler speed.
  • Parallelize skill metadata stats (#29326)
    ## Summary
    
    This switches skill discovery to the simpler same-connection scalar
    request shape.
    
    After reading a skills directory, discovery now starts the existing
    `fs/getMetadata` calls for all visible entries in that directory before
    awaiting the results. There is no JSON-RPC batch frame and no new
    filesystem API; remote filesystems use the existing request-id
    multiplexing on the same exec-server connection.
    
    This is the scoped alternative to the batch-frame approach in #29074 /
    #29075.
    
    ## What changed
    
    - Collect visible directory entries before processing them.
    - Run their existing `fs.get_metadata(...)` calls with `join_all`.
    - Process the results in the original directory order, so skill
    discovery behavior stays the same.
    
    ## Benchmarks
    
    Fresh local benchmark against generated skill trees over a real
    exec-server remote filesystem. The benchmark calls the actual
    `load_skills_from_roots` path, so this includes directory reads,
    metadata stats, `SKILL.md` reads, and parsing.
    
    Times are p50 milliseconds from 5 samples after 1 warmup, using warmed
    runs.
    
    | Scenario | Legacy `main` | Batch frame stack (#29074 / #29075) |
    Same-connection scalar stack |
    | --- | ---: | ---: | ---: |
    | 100 flat skills | 377.4 | 389.0 | 378.6 |
    | 500 flat skills | 1983.2 | 1856.6 | 1757.5 |
    
    Takeaway: for the actual skill discovery path, same-connection scalar is
    tied with legacy at 100 skills and best at 500 skills. The batch-frame
    stack does not show enough win here to justify the extra protocol/API
    surface.
    
    Benchmark command:
    
    - `just test -p codex-exec-server benchmark_remote_skill_discovery
    --run-ignored ignored-only --no-capture`
    
    Checked locally with:
    
    - `just test -p codex-core-skills`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
  • Test pipelined scalar exec-server requests (#29325)
    ## Summary
    
    This adds focused coverage for the simpler same-connection scalar
    request path.
    
    The exec-server connection already supports multiple in-flight JSON-RPC
    scalar requests on one connection. This test locks in that behavior by
    sending two normal requests before reading either response, without
    adding a batch frame or any new API surface.
    
    ## What changed
    
    - Added a processor-level test that initializes an exec-server
    connection.
    - Sends two scalar `environment/info` requests back-to-back on the same
    connection.
    - Verifies both responses come back on the same connection by request
    id.
    
    Checked locally with:
    
    - `just test -p codex-exec-server
    connection_accepts_pipelined_scalar_requests`
  • Carry sandbox intent to remote exec servers (#29108)
    ## What changed
    
    PR #29099 stopped sending the orchestrator's concrete sandbox wrapper to
    a remote exec-server. Remote commands now arrive as plain native argv.
    
    This PR adds the next piece: Codex also sends portable sandbox intent
    next to that plain argv.
    
    For a remote unified-exec command, the request can now include:
    
    - the canonical permission profile before local workspace-root
    materialization
    - the sandbox cwd and workspace roots as `PathUri` values
    - Windows sandbox settings
    - the legacy Landlock setting
    - whether managed networking must be enforced
    
    The important part is that symbolic entries such as `:workspace_roots`
    stay symbolic while crossing the boundary. The executor can then bind
    them to its own workspace-root paths instead of receiving
    orchestrator-local absolute paths.
    
    The data travels through `ExecRequest` into `ExecParams`. Older
    exec-servers can still deserialize requests because the new fields have
    defaults.
    
    ## Why
    
    The orchestrator should not decide how another machine implements
    sandboxing.
    
    For example:
    
    - a local macOS Codex would normally build a Seatbelt command
    - a remote Linux executor needs a Linux sandbox command instead
    
    The orchestrator now sends the plain command plus the policy it intended
    to enforce. A later PR can let the exec-server choose and build the
    correct sandbox for its own operating system.
    
    ## Important detail
    
    This keeps the portable intent separate from the local `SandboxType`.
    
    `SandboxType::None` is ambiguous:
    
    - it can mean the command was explicitly approved to run without a
    sandbox
    - it can also mean the orchestrator host has no concrete sandbox
    implementation available
    
    Those cases are different for remote execution. This PR adds
    `sandbox_requested` so an executor can still receive sandbox intent when
    the orchestrator cannot build a local wrapper. Explicit unsandboxed
    retries still send no sandbox context.
    
    ## Behavior today
    
    This PR only transports the intent. The exec-server accepts the new
    fields but does not apply them yet.
    
    Remote commands therefore remain unsandboxed after this PR, just as they
    are after PR #29099.
    
    ## Follow-up
    
    The next PR will make exec-server read this portable intent, bind
    symbolic workspace permissions to executor-native roots, choose the
    sandbox for its own operating system, build the wrapper locally, and
    then spawn the command.
  • [codex] simplify token budget context (#29295)
    ## Why
    
    The token-budget feature currently adds remaining-token messages
    whenever usage crosses the 25%, 50%, and 75% thresholds. Those periodic
    inserts create prompt churn without requiring action, while the
    near-compaction reminder and explicit `get_context_remaining` tool
    already cover actionable and on-demand budget information.
    
    The context-window lineage block is also easier to scan as plain labeled
    text than as a `<token_budget>`-wrapped fragment.
    
    ## What changed
    
    - Stop recording automatic remaining-token messages at percentage
    thresholds.
    - Render context-window lineage in `First`, `Current`, `Previous` order
    with colon-separated labels.
    - Omit the `Previous` line for the first context window.
    - Remove `<token_budget>` wrappers from newly rendered lineage,
    near-compaction reminders, and `get_context_remaining` output.
    - Keep recognizing legacy wrapped fragments so existing rollouts remain
    compatible.
    - Remove the post-sampling token snapshot that was only needed by the
    periodic threshold path.
    
    ## Testing
    
    - `just test -p codex-core token_budget` (11 tests passed)
  • [codex] add configurable token budget compaction reminder (#29255)
    ## Why
    
    The token-budget feature reports coarse remaining-context milestones,
    but it does not give the model a configurable wrap-up prompt before
    automatic compaction. A strict threshold-crossing check can also miss
    resumed or reconfigured windows that are already inside the threshold.
    
    ## What changed
    
    - Add structured `[features.token_budget]` configuration for an absolute
    `reminder_threshold_tokens` and bounded `reminder_message_template`;
    `{n_remaining}` is expanded when the reminder is delivered.
    - Compute remaining tokens against the next effective auto-compaction
    boundary, including scoped `body_after_prefix` accounting and the full
    context-window limit.
    - Make reminder delivery level-triggered before and after sampling, with
    one-shot state owned by `AutoCompactWindow` and re-armed on compaction,
    `new_context`, restore, or history replacement.
    - Leave the existing initial full-window token-budget context, 25/50/75%
    notices, and token-budget tools unchanged.
    - Persist the resolved feature configuration in the session config lock
    and regenerate the config schema.
    
    ## Validation
    
    - `just test -p codex-core token_budget`
    - `just test -p codex-core
    token_budget_reminder_emits_after_crossing_compaction_threshold`
    - `just test -p codex-core auto_compact_window`
    - `just test -p codex-core
    lock_contains_prompts_and_materializes_features`
    - `just test -p codex-features`
    - `just test -p codex-config`
  • [codex] prototype mcp_history thread hint injection (#29259)
    ## Why
    
    Prototype whether the harness can invoke the `mcp_history` MCP while
    constructing full initial context and expose its thread hint to the
    model without requiring a model-issued tool call.
    
    The prototype builds on the context-window lineage added by #29256 and
    is now based directly on `main`.
    
    ## What changed
    
    - Call `mcp_history/thread_hint` with no arguments while building the
    full `<token_budget>` context.
    - Pass the current `threadId` through MCP request metadata, matching the
    normal MCP tool-call path.
    - Serialize only the unstructured `content` result and append it inside
    `<token_budget>` when the call succeeds.
    - Omit the additional context when the MCP call or content serialization
    fails.
    
    ## Prototype limitations
    
    - The direct call bypasses the normal model-initiated MCP approval,
    lifecycle-event, telemetry, and result-sanitization path.
    - The call has no prototype-specific timeout, result-size cap, or
    per-window cache.
    - MCP latency is added to full-context construction, including
    applicable compaction paths.
    
    ## Validation
    
    - `just test -p codex-core token_budget`
  • core: add context window lineage IDs (#29256)
    ## Why
    
    The rendered `<token_budget>` fragment identifies the thread and current
    context window, but it does not expose enough lineage to identify the
    first window in the thread or the immediately preceding window. Those
    IDs also need to remain stable across compaction, resume, and rollback.
    
    ## What changed
    
    - Track first, previous, and current UUIDv7 context-window IDs in
    auto-compaction state.
    - Render `thread_id`, `first_window_id`, `previous_window_id`, and the
    current window ID in the full `<token_budget>` fragment.
    - Persist the first and previous window IDs in compacted rollout
    checkpoints and restore them during rollout reconstruction.
    - Preserve compatibility with older compacted records that do not
    contain the new optional fields.
    - Update focused state, rendering, reconstruction, rollback, and
    serialization coverage.
    
    ## Validation
    
    - `just test -p codex-core token_budget`
    - `just test -p codex-protocol compacted_item::tests`
    - `just test -p codex-core tracks_prefill_and_window_boundaries`
    - `just test -p codex-core
    reconstruct_history_uses_replacement_history_verbatim`
    - `just test -p codex-core
    thread_rollback_restores_cleared_reference_context_item_after_compaction`
  • Allow resume and settings commands during tasks and MCP startup (#29154)
    ## Why
    
    The TUI treats both an active turn and MCP startup as a running task.
    That currently blocks `/resume` and several settings commands even
    though they do not compete with turn execution, which is especially
    frustrating when MCP startup is slow.
    
    Model, permissions, personality, and service-tier selections already
    update thread settings independently of the running turn. Other clients
    can send those updates mid-turn, while the current turn continues with
    its captured settings. Allowing the same updates from local slash
    commands makes the TUI consistent with that existing behavior.
    
    ## What changed
    
    - Allow `/resume` while a task is running.
    - Allow `/model`, `/permissions`, `/personality`, and service-tier
    commands such as `/fast` while a task is running.
    - Keep the existing behavior where the active turn uses its captured
    settings and updates apply to subsequent turns.
    - Exercise the commands under the busy state in the existing TUI tests
    and retain coverage for commands that should remain blocked.
    
    ## Behavior note
    
    Turn settings such as model selection and reasoning effort are captured
    when a turn starts. Changing them during an active turn affects the next
    turn, not the turn already in progress. The status bar updates
    immediately, so it may temporarily display the newly selected setting
    before that setting is actually in effect.
    
    ## Verification
    
    - Focused `codex-tui` tests for resume dispatch, settings popups,
    `/fast`, and disabled-command behavior.
    
    ## Related issues
    
    Closes #19015.
    
    Addresses the next-turn-safe model/reasoning switching portion of
    #14356; dedicated shortcuts and the proposed depth meter remain out of
    scope.
  • [codex] Preserve skill descriptions outside model context (#29006)
    ## Why
    
    Skill descriptions are used in model-visible lists: the default
    available-skills catalog that supports implicit selection, and the
    on-demand `skills.list` tool response used to discover orchestrator
    skills. A single overlong description should not consume a
    disproportionate share of either list.
    
    Enforcing the 1024-character limit while loading or migrating skills is
    the wrong boundary: it rejects otherwise-valid skills and discards
    metadata that non-model consumers and full skill reads may need. Skill
    metadata and `SKILL.md` content should remain intact; the cap belongs at
    model-visible list rendering boundaries.
    
    ## What changed
    
    - Preserve full `description` and `metadata.short-description` values
    when loading skills.
    - Preserve full external-agent command descriptions during
    `source-command-*` migration instead of skipping commands solely because
    their descriptions exceed 1024 characters.
    - Preserve full normalized orchestrator descriptions in the underlying
    skills catalog.
    - Cap each description at 1024 Unicode characters when rendering the
    default available-skills context in `codex-core-skills` and
    `codex-skills-extension`.
    - Apply the same cap when serializing descriptions in the model-visible
    `skills.list` response.
    - Render truncated descriptions as 1021 original characters plus `...`.
    - Leave explicit `$skill` injection, `skills.read`, underlying metadata,
    and on-disk `SKILL.md` files unchanged and full-fidelity.
    
    ## Implicit skill selection
    
    Codex injects a bounded catalog containing each implicitly allowed
    skill's name, description, and source locator, together with
    instructions to use a skill when the task clearly matches its
    description. The model makes that semantic choice; after selecting a
    skill, it reads the full `SKILL.md` from its filesystem or provider
    resource. Explicit `$skill` mentions remain a separate path that injects
    the full skill instructions. For orchestrator skills, `skills.list`
    provides bounded discovery metadata before `skills.read` returns the
    full selected resource.
    
    ## Test plan
    
    - `just test -p codex-core-skills`
    - `just test -p codex-skills-extension`
    - `just test -p codex-external-agent-migration`
    
    The focused regressions verify that overlong metadata is preserved at
    load and migration boundaries while default available-skills rendering
    and `skills.list` output produce the 1021-character prefix plus `...`.
  • chore(deps): advance tokio-tungstenite (#29132)
    ## Why
    
    Responses websocket connections use `tokio-tungstenite`. When DNS
    returns an unusable native IPv6 address before a working IPv4 address,
    sequential dialing can consume Codex's outer websocket timeout before
    reaching IPv4. The merged fork change adds Happy Eyeballs-style
    alternate-family racing so websocket dialing matches the recovery
    behavior already present in the HTTP path.
    
    ## What Changed
    
    Advance the workspace `tokio-tungstenite` patch from `132f5b39` to
    merged commit `e5e64b86`, and update the matching lockfile source. The
    new revision comes from
    [openai-oss-forks/tokio-tungstenite#1](https://github.com/openai-oss-forks/tokio-tungstenite/pull/1).
  • Use cached and live web access terminology (#29095)
    ## Summary
    
    - Rename the string-valued external web access enum variants from
    `Offline` / `Online` to `Cached` / `Live`.
    - Align the transport names with the existing `web_search = "cached"` /
    `"live"` configuration vocabulary.
    
    Existing behavior is unchanged: `WebSearchMode::Cached` and
    `WebSearchMode::Live` continue to send the backward-compatible boolean
    values `false` and `true`; `Indexed` remains the only mode currently
    sent as a string.
    
    ## Validation
    
    - `just fmt`
    - `just test -p codex-api` (127 passed)
  • Keep remote exec commands native to the executor (#29099)
    ## Summary
    
    - Remote unified-exec now sends the original command argv to exec-server
    instead of materializing the orchestrator's sandbox wrapper first.
    - Local unified-exec keeps the existing sandbox path unchanged.
    - Add a focused regression test for a macOS-selected sandbox producing
    plain remote argv.
    
    Before:
    
        macOS orchestrator -> /usr/bin/sandbox-exec ... -> Linux exec-server
    
    After:
    
        macOS orchestrator -> /bin/bash -lc pwd -> Linux exec-server
    
    This is intentionally only the first cleanup step. Remote unified-exec
    commands are sent without a process sandbox until the targeted
    follow-ups below land. For the macOS-to-Linux path this is not a
    practical regression: the old sandboxed attempt failed before process
    launch because the Linux executor could not spawn macOS sandbox paths.
    
    ## Targeted follow-ups
    
    1. Carry sandbox intent separately from argv.
       - Add an optional sandbox field to exec-server process params.
    - Reuse FileSystemSandboxContext rather than introducing a new sandbox
    model.
       - Carry managed-network enforcement as one explicit bit.
       - Keep argv plain.
    
    2. Apply that intent inside exec-server.
       - Add a small process-start adapter before LocalProcess::exec.
    - Reuse the existing codex-sandboxing SandboxManager and exec-server
    runtime paths.
    - Follow the same shape already used by exec-server filesystem
    sandboxing.
       - Do not duplicate or move the sandbox implementations.
    
    3. Report the sandbox actually used.
       - Return the executor-selected sandbox type from process/start.
    - Use that value in core for sandbox-denial detection and retry
    behavior.
    
    ## End state
    
    The orchestrator sends plain commands plus portable sandbox intent. The
    executor chooses and applies its own native sandbox: Linux executors use
    Linux sandboxing, macOS executors use Seatbelt, and Windows executors
    use Windows sandboxing. Concrete wrapper argv, helper paths, and sandbox
    env markers never cross the executor boundary.
  • Add config toggles for orchestrator skills and MCP (#28942)
    ## Why
    
    Orchestrator-provided skills and Codex Apps MCP tools add model-visible
    instructions, resources, and tools beyond the local workspace. Hosts
    need config-level switches to disable those orchestrator-owned surfaces
    independently, without disabling regular skills or regular MCP servers.
    
    ## What changed
    
    - Adds `[orchestrator.skills].enabled` and `[orchestrator.mcp].enabled`
    config entries, both defaulting to `true`.
    - Includes the new settings in `config.schema.json` and in the config
    lock so resolved thread configuration preserves the same orchestrator
    exposure decisions.
    - Threads `orchestrator.skills.enabled` through the app-server skills
    extension so disabled orchestrator skills do not expose the `skills`
    namespace or inject orchestrator skill context.
    - Gates Codex Apps MCP exposure, app instructions, and app auth
    eligibility on `orchestrator.mcp.enabled` while leaving non-Codex-Apps
    MCP tools available.
    - Updates the thread-manager sample config to disable both
    orchestrator-owned surfaces.
    
    ## Verification
    
    - Added config parsing, loading, defaulting, and schema coverage for the
    new settings.
    - Added MCP exposure coverage that `orchestrator.mcp.enabled = false`
    removes Codex Apps tools while preserving regular MCP tools.
    - Added app-server coverage that `orchestrator.skills.enabled = false`
    prevents orchestrator skill tools, prompts, and resource reads from
    reaching the model turn.
  • Add indexed web search mode (#28489)
    ## Summary
    
    - Add `web_search = "indexed"` alongside `disabled`, `cached`, and
    `live`.
    - Use that same resolved mode for both hosted and standalone web search.
    - For hosted search, send `index_gated_web_access: true` with external
    web access enabled only when `indexed` is selected.
    - For standalone search, preserve the existing boolean wire values for
    existing modes (`cached` maps to `false` and `live` to `true`) and send
    `"indexed"` only for `indexed`; `disabled` keeps the tool unavailable.
    - Carry the mode through managed configuration requirements and
    generated schemas.
    
    ## Why
    
    Indexed search provides a middle ground between cached-only search and
    unrestricted live page fetching. Search queries can remain live while
    direct page fetches are limited to URLs admitted by the server.
    
    The existing `web_search` setting remains the single source of truth, so
    hosted and standalone executors cannot drift into different access
    modes. Without an explicit `indexed` selection, the existing
    model-visible tool and request shapes are unchanged.
    
    ```toml
    web_search = "indexed"
    
    [features]
    standalone_web_search = true
    ```
    
    ## Validation
    
    - `just fmt`
    - `just test -p codex-api` (`126 passed`)
    - `just test -p codex-web-search-extension` (`7 passed`)
    - `just test -p codex-core
    code_mode_can_call_indexed_standalone_web_search` (`1 passed`)
    - Focused configuration, hosted request, standalone request, and
    managed-requirement coverage is included in the PR; remaining suites run
    in CI.
    
    The full workspace test suite was not run locally.
  • Document raw response item compatibility (#29086)
    Adds a short AGENTS.md note asking reviewers to treat raw response item
    events as compatibility-sensitive, even while they are experimental.
    
    This keeps future app-server changes from accidentally breaking Codex
    Cloud consumers of raw response item events.
  • Scope network approvals by environment (#28899)
    Stacked on #28766.
    
    ## Why
    
    Network approvals are environment-scoped: allowing a host in one
    execution environment should not allow the same host in another
    environment.
    
    #28766 adds the inert IDs and constructor plumbing. This PR applies the
    behavior on top.
    
    ## What changed
    
    - Route managed network traffic through per-environment HTTP and SOCKS
    proxy listeners.
    - Stamp HTTP, HTTPS CONNECT, SOCKS TCP, and SOCKS UDP policy requests
    with the source environment at the proxy boundary.
    - Carry the selected execution environment through shell, unified exec,
    zsh-fork, and sandbox transform paths.
    - Include the environment in pending, approved-for-session, and
    denied-for-session network approval cache keys.
    - Include the environment in approval IDs and approval prompts.
    - Preserve legacy fallback for unattributed requests, but deny when
    active-call attribution is ambiguous.
    - Fail closed if an environment-specific proxy endpoint cannot be
    prepared.
    
    ## Validation
    
    - just fmt
    - CI will run tests and clippy
  • [codex] abort turns when rollout budgets expire (token budget 3/3) (#28707)
    ## Stack
    
    Depends on #28494.
    
    ## Description
    
    This PR propagates shared rollout-budget exhaustion through the existing
    `CodexErr::TurnAborted` task result.
    
    Each thread records its model usage against the same ledger. Once the
    ledger is exhausted, that usage update and all later usage updates
    return `TurnAborted`. The task wrapper emits the normal aborted-turn
    event and lifecycle instead of completing the turn.
    
    This is intentionally a soft boundary: there is no cross-thread
    `Op::Interrupt` fanout. An in-flight thread can finish its current
    response before it observes the exhausted ledger, but every thread
    aborts at its next usage-accounting boundary.
    
    ## Tests
    
    The integration coverage verifies that:
    
    - the response that exhausts the budget aborts its turn;
    - a later response also aborts because the shared ledger remains
    exhausted; and
    - sub-agent usage draws from the same shared ledger; and
    - local and remote-v2 compaction abort without retrying or emitting a
    generic error.
    
    Local checks:
    
    - `just test -p codex-core
    exhausted_budget_aborts_current_and_later_turns`
    - `just test -p codex-core subagent_usage_draws_from_the_shared_budget`
    - `just test -p codex-core
    abort_regular_task_emits_marker_before_turn_aborted`
    - `just test -p codex-core
    compaction_budget_exhaustion_aborts_without_error_or_retry`
    - `just fix -p codex-core`
    - `just fmt`
    - `git diff --check`
    
    The full workspace test suite was not run locally.
  • Expose thread-level multi-agent mode (#28792)
    ## Why
    
    Once multi-agent mode can be selected per turn, clients also need to
    choose the initial selection when creating a thread and observe that
    selection through lifecycle and settings APIs.
    
    The selected value is intentionally distinct from the effective
    model-visible value: no client selection is represented as `null`, even
    though an eligible multi-agent v2 turn derives `explicitRequestOnly` as
    its effective default.
    
    ## What changed
    
    - Add the optional experimental `thread/start.multiAgentMode` parameter
    and pass it through thread creation.
    - Preserve an omitted initial value as an unset selection rather than
    eagerly storing `explicitRequestOnly`.
    - Apply an explicit `thread/start` selection to the first turn through
    the session configuration established at thread creation.
    - Restore the latest persisted effective mode as the selected baseline
    on cold resume when rollout history contains one.
    - Inherit the optional selected mode from a loaded parent when creating
    related runtime threads.
    - Return the current selected `multiAgentMode` from `thread/start`,
    `thread/resume`, `thread/fork`, and thread settings, using `null` when
    no mode is selected.
    - Keep lifecycle reporting independent from model capability and feature
    eligibility; core turn construction remains responsible for calculating
    and persisting the effective mode.
    
    ## Not covered
    
    - Clearing an existing loaded-session selection back to unset through
    `turn/start`; omitted or `null` currently retains the session's
    selection.
    - A TUI control, slash command, or `config.toml` preference.
    
    ## Verification
    
    - `CARGO_INCREMENTAL=0 just test -p codex-app-server-protocol`
    - `CARGO_INCREMENTAL=0 just test -p codex-app-server multi_agent_mode`
    
    The focused app-server coverage verifies explicit `thread/start`
    initialization, first-turn prompting, nullable reporting for an omitted
    selection, and retention of selections that are not currently
    runtime-eligible.
    
    ## Stack
    
    Stacked on #28685. This PR contains only the thread initialization and
    lifecycle/settings API layer.
  • Add per-turn multi-agent mode (#28685)
    ## Why
    
    Multi-agent v2 currently carries an explicit-request-only delegation
    rule in its static usage hint. That provides a safe default, but it
    prevents clients from selecting proactive delegation per turn without
    changing static guidance or rewriting prior model context.
    
    This change makes delegation mode a session selection that can be
    updated through `turn/start`, while deriving the effective model-visible
    mode separately for each turn. Eligible multi-agent v2 turns remain
    explicit-request-only unless proactive mode is both selected and
    enabled.
    
    ## What changed
    
    - Add the experimental `turn/start.multiAgentMode` parameter with
    `explicitRequestOnly` and `proactive` values. Omission retains the
    loaded session's current optional selection.
    - Add the default-off `features.multi_agent_mode` feature gate. Eligible
    multi-agent v2 turns use the selected mode when enabled; an unset
    selection or disabled gate resolves to `explicitRequestOnly`.
    - Treat mode prompting as inapplicable for multi-agent v1 and other
    unsupported session configurations, producing no multi-agent mode
    developer message rather than rejecting the turn.
    - Move the explicit-request-only rule out of the static v2 usage hint
    and into a bounded, tagged developer context fragment.
    - Emit the effective mode in initial context and only when that
    effective mode changes on later turns.
    - Persist the effective mode in `TurnContextItem` as the durable
    baseline for resume and context-update comparisons.
    
    Historical rollout items are not rewritten. Later mode developer
    messages establish the current rule incrementally.
    
    ## Not covered
    
    - Initial selection through `thread/start` and selected-mode reporting
    from thread lifecycle/settings APIs; those are isolated in the stacked
    #28792.
    - A TUI control or slash command for selecting the mode.
    - Persisting a preferred mode to `config.toml`; selection remains
    session/turn scoped.
    - Changes to multi-agent concurrency limits, tool availability, or model
    catalog capability declarations.
    - Rewriting historical rollout prompt items. Cold resume restores the
    latest persisted effective mode when available while leaving historical
    developer messages intact.
    
    ## Verification
    
    - `CARGO_INCREMENTAL=0 just test -p codex-core multi_agent_mode`
    - Focused app-server coverage verifies that `turn/start.multiAgentMode`
    produces proactive developer instructions for an eligible v2 turn.
    
    ## Stack
    
    Followed by #28792, which adds `thread/start` initialization and
    lifecycle/settings observability.
  • [3/3] app-server: configure environment connection timeout (#29025)
    ## Why
    
    Remote environments registered through `environment/add` currently use
    the fixed 10-second WebSocket connection timeout. Slow-starting
    executors need a caller-selected connection window, but this should not
    add retry policy or couple exec-server behavior to Core’s
    `deferred_executor` feature.
    
    Make the timeout an optional part of the existing experimental request.
    Existing clients continue using the current default, while callers that
    know an executor may take longer can request a larger window explicitly.
    
    Depends on #28683.
    
    ## What changed
    
    - Add optional `connectTimeoutMs` to `EnvironmentAddParams` and document
    it in the app-server README.
    - Pass the optional timeout through `EnvironmentRequestProcessor` into
    one `EnvironmentManager::upsert_environment()` path; the manager applies
    the existing default when it is omitted.
    - Preserve the existing single-attempt lifecycle. The configured value
    controls WebSocket connection and handshake time for both initial
    connection and later reconnects; initialization retains its separate
    timeout.
    - Add an app-server integration test that sends the real JSON-RPC
    request and verifies a stalled handshake observes the requested timeout.
    
    ## Test plan
    
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-exec-server`
    - `just test -p codex-app-server
    environment_add_applies_connect_timeout`
    
    ## Rollout
    
    This is additive and does not enable `deferred_executor`. Callers should
    send a non-default timeout only after a compatible app-server is
    deployed; omitted or `null` values retain the existing 10-second
    default.
  • [2/3] core: track starting environments in snapshots (#28683)
    ## Why
    
    Remote environments may still be resolving when Codex creates a session
    or turn. Waiting for the existing all-or-nothing environment snapshot
    can hold startup until the selected environment is usable.
    
    Behind the default-off `deferred_executor` feature, let callers take a
    useful snapshot immediately: completed environments remain available
    normally, while unfinished environments are reported without blocking
    startup. With the feature disabled, snapshots preserve the existing
    blocking behavior.
    
    Depends on #28674.
    
    ## What changed
    
    - Store one ordered list of selected environments in
    `ThreadEnvironments`. Each selection owns one shared resolution that
    produces its complete `TurnEnvironment`.
    - Start new resolutions in the background with `remote_handle()`,
    allowing snapshots and the future wait tool to share the same result
    while cancellation follows the retained handles.
    - Make `snapshot()` a read-only operation: nonblocking snapshots collect
    completed resolutions and retain handles for unfinished ones, while
    blocking snapshots await every resolution.
    - Replace completed failed resolutions from the current manager entry
    and log when failed environments are omitted.
    - Return attached and starting environments as a point-in-time view, and
    count starting environments when deciding whether a snapshot is
    local-only.
    - Keep existing consumers attached-only. `to_selections()` derives from
    attached environments, so child threads do not inherit an environment
    that is still starting.
    
    ## Test plan
    
    - `just test -p codex-core environment_selection`
    - `just test -p codex-core
    deferred_executor_reaches_model_before_remote_environment_is_ready`
    
    ## Landing note
    
    Keep `deferred_executor` disabled for slow-starting executors until
    configurable `environment/add` connection timeouts and caller support
    land. When enabled, an environment that attaches after session startup
    may remain absent from environment-derived model context, tools,
    instructions, skills, and related state until follow-up refresh work
    lands.
  • [1/3] core: add remote environment connection lifecycle (#28674)
    ## Why
    
    Remote environments can be registered before their exec-server is first
    used. Starting the connection at registration time uses that startup
    window, while sharing one startup result prevents background work and
    capability calls from opening competing connections.
    
    Keep initial startup simple: each environment makes one connection
    attempt using its configured transport timeout. A failed initial attempt
    is final for that environment, while an environment that disconnects
    after connecting can still recover on a later operation.
    
    ## What changed
    
    - Start URL and Noise environments in the background when they are added
    to `EnvironmentManager`. Provider snapshots are fully validated before
    connection work begins.
    - Share one initial connection attempt and its saved result across
    metadata, process, filesystem, and HTTP callers.
    - Keep configured stdio environments lazy until first use so
    registration does not launch a process.
    - Tie background startup work to the environment lifetime so replacing
    or dropping an environment cancels unfinished work.
    - After an established client disconnects, share one fresh connection
    attempt across concurrent callers. A failed attempt fails the current
    operation without permanently preventing a later attempt.
    - Store the shared lazy client directly on `Environment` and expose
    small methods for starting, observing, and awaiting startup.
    
    ## Test plan
    
    - `just test -p codex-exec-server`
    - `just test -p codex-app-server
    turn_start_resolves_sticky_thread_local_environment_and_turn_overrides`
  • [codex] Support protected resource OAuth discovery (#29022)
    ## Why
    
    Plugin-install preflight and the actual OAuth login flow used different
    discovery implementations. Preflight had a Codex-specific implementation
    that only queried authorization-server metadata on the MCP host, while
    login already used the upstream `rmcp` Rust MCP SDK. As a result,
    servers that advertise a separate authorization server through RFC 9728
    Protected Resource Metadata were classified as OAuth-unsupported during
    plugin installation, so login was skipped.
    
    ## What changed
    
    - delegate plugin-install OAuth discovery to
    `rmcp::transport::AuthorizationManager`, the same implementation used by
    the login flow
    - let `rmcp` follow Protected Resource Metadata first and perform direct
    RFC 8414 authorization-server discovery when protected-resource
    discovery does not yield usable metadata
    - retain Codex's existing HTTP headers, timeout, `no_proxy` behavior,
    and scope normalization around that discovery
    - add unit coverage and a pure-MCP plugin-install integration test that
    proves the protected-resource path reaches OAuth client registration
    
    This only changes shared MCP OAuth discovery. App declarations and
    `appsNeedingAuth` behavior are unchanged.
    
    ## Verification
    
    - `just test -p codex-rmcp-client auth_status`
    - `just test -p codex-app-server plugin_install_starts_mcp_oauth`
    - real plugin-install smoke test with an isolated `CODEX_HOME`: both
    DigitalOcean MCP servers started OAuth callback listeners, while Linear
    continued to start its existing direct-discovery OAuth flow
  • core: assign item IDs to compacted replacement history (#29012)
    ## Why
    
    Remote v2 compaction can return replacement-history items without IDs.
    Because replacement history is installed directly, those items bypass
    normal history preparation and remain ID-less in later Responses
    requests even when the `item_ids` feature is enabled.
    
    ## What changed
    
    - Pass the active `TurnContext` into `replace_compacted_history`.
    - When `item_ids` is enabled, assign missing IDs before installing and
    persisting replacement history.
    - Rebuild `CompactedItem` from the prepared history so live and
    persisted replacement histories match.
    - Add integration coverage requiring IDs on every ID-capable input item
    in the initial, remote v2 compaction, and post-compaction requests.
    
    ## Test plan
    
    - `just test -p codex-core response_item_ids`
    - `just test -p codex-core websocket_v2_test_codex_shell_chain`
    - `just test -p codex-core remote_compaction_parity_pre_turn_auto`
    - `just test -p codex-app-server
    thread_inject_items_adds_raw_response_items_to_thread_history`
  • [codex] add clock current-time tool (#29011)
    ## Summary
    - expose `clock.curr_time` when current-time reminders are enabled
    - query the session's configured time provider with the calling thread
    id
    - return the existing UTC reminder text for direct model calls
    - return `{ "current_time": "YYYY-MM-DD HH:MM:SS UTC" }` in Code Mode
    
    Clock lookup failures remain fatal, matching pre-inference reminder
    behavior.
    
    ## Testing
    - `just test -p codex-core current_time_tool_returns_the_latest_time`
    - `just test -p codex-core
    code_mode_current_time_returns_structured_result`
    - `just fix -p codex-core`
  • [codex] Skip curated repo sync for remote plugins (#29005)
    ## Summary
    
    - skip the legacy `openai-curated` startup repository sync when remote
    plugins are enabled and the current auth uses the Codex backend
    - keep the curated sync for API-key, Bedrock, and unauthenticated
    sessions that fall back to the local marketplace
    - preserve configured marketplace upgrades and all remote plugin startup
    warmups
    
    ## Why
    
    The remote catalog owns plugin discovery and materialization only when
    it is usable for the current auth mode. Starting the legacy curated
    repository sync in that case performs an unnecessary Git/HTTP/archive
    download and cache refresh. API-key and Bedrock sessions still require
    the local curated marketplace, so they must continue syncing it.
    
    ## User impact
    
    Codex startup no longer downloads or refreshes the local
    `openai-curated` snapshot when the remote catalog is active. Behavior is
    unchanged for auth modes that use the local curated marketplace.
    
    ## Validation
    
    - `just fmt`
    - `git diff --check`
    
    Rust tests were not run per the repository's local verification policy
    for this narrow conditional change.
  • [codex] Assign response item IDs when recording history (#28814)
    ## Why
    
    Client-created response items enter history without IDs, so their
    identity is lost across rollout persistence and resume. IDs should be
    assigned once at the history-recording boundary, while IDs returned by
    the server must remain unchanged.
    
    The Responses API validates item IDs using type-specific prefixes.
    Locally generated IDs therefore use the matching prefix plus a
    hyphenated UUIDv7, keeping them valid while distinguishable from
    server-generated IDs. Because this changes persisted history and
    provider request shapes, the behavior is opt-in behind the
    under-development `item_ids` feature. Compaction triggers remain request
    controls whose API shape does not accept an ID.
    
    ## What changed
    
    - Register the disabled-by-default `item_ids` feature and expose it in
    `config.schema.json`.
    - Make supported optional `ResponseItem` IDs serializable and expose
    them in the generated app-server schemas.
    - When `item_ids` is enabled, assign an ID during conversation-history
    preparation if an item has no ID.
    - Generate type-prefixed, hyphenated UUIDv7 IDs using the Responses API
    item conventions.
    - Preserve existing server IDs without rewriting them.
    - Persist assigned IDs in rollouts and include them in subsequent
    Responses requests.
    - Remove the unsupported ID field from `CompactionTrigger` and document
    why it has no ID.
    - Add integration coverage for enabled ID persistence, preservation of
    server IDs, and omission of generated IDs while the feature is disabled.
    
    `prepare_conversation_items_for_history` is the single response-item ID
    allocation boundary.
    
    ## Test plan
    
    - `just test -p codex-features`
    - `just test -p codex-core
    response_item_ids_persist_across_resume_and_preserve_server_ids`
    - `just test -p codex-core
    non_openai_responses_requests_omit_item_turn_metadata`
    - `just test -p codex-core
    resize_all_images_prepares_failures_before_history_insertion`
    - `just test -p codex-protocol`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-api azure_default_store_attaches_ids_and_headers`
  • Always use AVAS for realtime WebRTC calls (#28856)
    ## Summary
    
    - Remove the realtime `architecture` selector from core protocol,
    app-server protocol, config parsing, generated schemas, and callers.
    - Always create WebRTC realtime calls with the AVAS query params:
    `intent=quicksilver&architecture=avas`.
    - Keep direct websocket realtime behavior on the existing config/default
    path, while WebRTC starts without an explicit version now default to
    realtime v1 because AVAS requires v1.
    
    ## Notes
    
    - WebRTC realtime now means AVAS. If a caller explicitly asks to start
    WebRTC with realtime v2, Codex rejects that request because the AVAS
    WebRTC path only supports realtime v1. Websocket realtime is separate
    and can still use realtime v2.
    - The old `[realtime] architecture = "realtimeapi" | "avas"` config knob
    is removed. Local configs that still set it will need to delete that
    line.
    - Some app-server tests that were only trying to exercise realtime v2
    protocol behavior now use websocket transport, because WebRTC is
    intentionally locked to AVAS/v1. Separate WebRTC tests cover the AVAS
    query params, v1 startup, SDP flow, and sideband join.
    
    ## Validation
    
    - Merged fresh `origin/main` at `83e6a786a2`.
    - `just fmt`
    - `just write-config-schema`
    - `just write-app-server-schema`
    - `git diff --check`
    - `just test -p codex-api -p codex-core -p codex-app-server-protocol -p
    codex-app-server realtime` (176 passed)
    - `just test -p codex-protocol -p codex-config` (413 passed)
  • [plugins] Refresh plugin and tool caches after remote install (#28951)
    Summary
    - Refresh the installed remote-plugin snapshot and Codex Apps tools
    after completing a remote JIT install.
    - Gate `completed: true` on every expected `app_connector_id` appearing
    after the uncached `tools/list` refresh, while continuing to skip local
    bundle verification for server-side installs.
    - Keep the cached recommendations response and filter refreshed
    installed remote IDs locally, so this does not add another
    recommendations fetch.
    - Add regression coverage for tools appearing after the hard refresh and
    remaining absent after the refresh. The resumed model request sees the
    refreshed tool router when installation completes.
    
    Root Cause
    - Remote suggestions from `openai-curated-remote` returned `true` before
    taking the existing connector refresh path, leaving the resumed turn
    with the pre-install Apps tool catalog.
    
    Validation
    - `just test -p codex-core request_plugin_install`
    - `just test -p codex-core-plugins
    recommended_plugin_candidates_filter_installed_and_disabled_plugins`
    - `just test -p codex-core-plugins`
    - `just fix -p codex-core-plugins`
    - `just fix -p codex-core`
    - `just fmt`
    - `just test -p codex-core` was not fully clean locally: 2,729 passed,
    26 failed, and 16 skipped. The failures were dominated by local
    Seatbelt/network/timing issues, including plugin-install timeouts under
    full-suite contention; the focused plugin-install runs pass.
  • core: add UUIDv7 context window IDs (#28953)
    ## Why
    
    The token-budget context currently identifies a context window by its
    thread-local sequence number. A UUIDv7 gives the model a stable opaque
    identity that remains fixed for a window and rotates when compaction or
    `new_context` starts the next one.
    
    ## What changed
    
    - Preserve the existing monotonic value as `window_number` and add a
    UUIDv7 `window_id` to `CompactedItem`.
    - Generate and rotate the UUID with auto-compaction window state,
    persist it alongside the number, and reconstruct it on resume and
    rollback.
    - Accept legacy compacted rollout records where the numeric `window_id`
    represented the window number.
    - Use the UUID only in token-budget context; existing request headers
    and metadata continue using `thread_id:window_number`.
    
    ## Testing
    
    - `just test -p codex-protocol compacted_item::tests`
    - `just test -p codex-core token_budget`
  • [codex] Reuse parsed plugin skills during session startup (#28844)
    ## Summary
    
    - Preserve raw plugin skill-root snapshots in the matching loaded-plugin
    cache entry, keyed by the effective plugin root identity including
    namespace.
    - Pass those snapshots through `SkillsLoadInput` as an optional preload,
    so session startup reuses plugin parsing while ordinary skill loads pass
    `None`.
    - Keep plugin skill loading cohesive: the existing loaders accept the
    optional snapshots directly, and uncached or marketplace-detail paths do
    not create a cache.
    
    ## Why
    
    Plugin discovery already parses plugin skills to determine available
    capabilities. Cold session startup then scanned and parsed the same
    roots again while building the skills snapshot.
    
    This solves the same duplicate-work problem as #28623 while keeping
    ownership narrow: `PluginsManager` creates and owns
    `PluginSkillSnapshots` only for its loaded-plugin cache entry;
    `SkillsService` consumes an optional clone. Entry replacement or
    clearing naturally drops the snapshots, with no separate generation,
    capacity policy, or watcher coupling.
    
    ## Validation
    
    - `cargo clippy -p codex-core-skills --all-targets -- -D warnings`
    - `just test -p codex-core-plugins
    skills_service_reuses_skills_parsed_during_plugin_load`
    - `just test -p codex-core-skills
    namespaces_plugin_skills_using_provided_namespace`
    - `just fmt`
  • core: keep remote exec on reported shell (#28983)
    ## Why
    
    We need to avoid resolving shells on the app-server's host for remote
    environments. We might make it possible to do fancier shell resolution
    from remote envs but for now just require the model to produce a shell
    that matches the environment's default.
    
    This gets my e2e demo working for shell commands after #28854 moved
    shell resolution to PathUri and caused remote envs to hit the fallback
    shell when the shell wasn't available on the host.
    
    ## What
    
    Remote `exec_command` calls now accept only the environment's reported
    default shell name or exact path, and execute with that reported path.
    Other explicit shells return a concise error. A Wine-backed integration
    test covers explicit PowerShell execution in the Windows cwd.
  • core: log AGENTS.md paths as URIs (#28989)
    ## Why
    
    No need to do path contortions when it's for our own logs.
    
    ## What
    
    Follow up on a previous PR's nit and update the path-types skill for
    future reference.
  • [codex] Remove child AGENTS.md prompt experiment (#28993)
    ## Why
    
    `child_agents_md` is a disabled, under-development experiment that adds
    a second model-visible explanation of hierarchical `AGENTS.md` behavior.
    Keeping it leaves unused prompt, configuration, documentation, and test
    surface.
    
    ## What changed
    
    - remove the `ChildAgentsMd` feature and `child_agents_md` config schema
    entry
    - remove the hierarchical prompt asset, export, and instruction
    injection
    - remove feature-specific tests and documentation
    - keep the generic unstable-feature warning coverage using
    `apply_patch_streaming_events`
    
    Normal project `AGENTS.md` discovery and composition are unchanged.
    
    ## Testing
    
    - `just test -p codex-features`
    - `just test -p codex-prompts`
    - `just test -p codex-core agents_md`
    - `just test -p codex-core unstable_features_warning`