Commit Graph

7654 Commits

  • 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`
  • [codex] Support marketplace plugin manifest fallback (#28789)
    ## Summary
    
    Support marketplace plugins whose source directory does not include a
    discoverable plugin manifest. Metadata-rich `marketplace.json` entries
    now act as fallback plugin manifests for listing, local detail reads,
    install, and non-curated cache refresh.
    
    The fallback preserves marketplace-entry plugin fields wholesale, then
    adds the small Codex-facing compatibility bridge for presentation
    metadata. A real source `plugin.json` always wins when present.
    
    ## Details
    
    - Capture flattened marketplace-entry fields into
    `MarketplacePluginManifestFallback`, preserving fields such as
    `version`, `description`, `skills`, `mcpServers`, `apps`, `hooks`,
    `agents`, `commands`, `strict`, `author`, and future manifest fields
    without a per-field translation list.
    - Bridge Claude-style top-level `displayName`, `author.name`,
    `homepage`, and marketplace `category` into Codex's nested `interface`
    fields only when the nested values are absent.
    - Treat fallback metadata as installable only when the marketplace entry
    contributes metadata beyond bare `name` and `source`; existing
    missing-manifest behavior remains for metadata-free entries.
    - Read local plugin details from the already parsed fallback manifest,
    including fallback-declared app and MCP paths, instead of rereading only
    an on-disk manifest.
    - Pass fallback contents into `PluginStore`, which validates them and
    injects `.codex-plugin/plugin.json` into Store's existing atomic copy.
    Local marketplace source directories are never mutated, and the fallback
    path no longer needs an additional staging directory.
    - Keep Git source materialization unchanged; Git clones still use the
    existing marketplace source staging area before Store installation.
  • core: load AGENTS.md from foreign environments (#28958)
    ## Why
    
    Make it possible to load AGENTS.md from remote exec-servers whose OS is
    different than app-server.
    
    ## What
    
    - keep `AGENTS.md` discovery and provenance as `PathUri`, with
    root-aware parent and ancestor traversal
    - expose lifecycle instruction sources as legacy app-server path strings
    in events while retaining `PathUri` internally
    - preserve and test mixed POSIX and Windows paths in model context and
    TUI status output
    - cover remote Windows loading end to end by seeding the Wine prefix
    through host filesystem APIs
    - fix bug in `PathUri`'s parent() implementation that would erase
    Windows drive letters
  • [codex] Preserve remote plugin download status errors (#28863)
    ## Summary
    
    - preserve the original HTTP status when a remote plugin bundle download
    returns a non-success response
    - retain at most 8 KiB of the error response body and annotate
    truncation or body-read failures
    - add regression coverage for an oversized error response
    
    ## Root cause
    
    The non-success response path reused the normal size-limited body
    reader. When an error response exceeded 8 KiB, that reader returned
    `DownloadTooLarge` before the code constructed `DownloadStatus`, masking
    the upstream HTTP status and response context.
    
    ## Impact
    
    Remote plugin installation failures now retain the actionable upstream
    HTTP status without allowing unbounded error bodies into logs.
    
    ## Validation
    
    - `just test -p codex-app-server
    plugin_install_preserves_status_when_remote_bundle_error_body_is_too_large`
    - `just fmt`
    - `git diff --check`
  • [connectors] Ignore synthetic links for app accessibility (#28770)
    Summary
    - Stop treating Codex Apps MCP tools with
    `_meta._codex_apps.synthetic_link: true` as evidence that a connector is
    accessible in `app/list`.
    - Preserve synthetic tools in the agent-facing MCP connector set so they
    remain available for install/auth flows.
    - Keep the app-list accessibility cache limited to connectors backed by
    at least one non-synthetic tool.
    - Add focused regression coverage for both sides of the boundary.
    
    Validation
    - `just fmt`
    - `just test -p codex-core
    synthetic_links_are_exposed_to_the_agent_but_not_accessible_in_app_list`
    - `git diff --check`
    - A crate-wide `just test -p codex-core` run completed with 2,699
    passing and 51 unrelated local sandbox/state failures, primarily state
    DB migration races (`UNIQUE constraint failed:
    _sqlx_migrations.version`).
  • feat: opt ChatGPT auth into agent identity (#19049)
    ## Stack
    
    This is PR 2 of the simplified HAI single-run-task stack:
    
    - [#19047](https://github.com/openai/codex/pull/19047) Agent Identity
    assertion and task-registration primitives, including the shared
    run-task helper used by existing Agent Identity JWT auth.
    - [#19049](https://github.com/openai/codex/pull/19049)
    Disabled-by-default ChatGPT auth opt-in that provisions/reuses persisted
    Agent Identity runtime auth and its single run task.
    - [#19051](https://github.com/openai/codex/pull/19051) Run-scoped
    provider auth that uses one backend-owned task id for first-party
    inference and compaction requests.
    
    [#19054](https://github.com/openai/codex/pull/19054) collapsed out of
    the active stack because the simplified design no longer needs a
    separate background/control-plane task helper.
    
    ## Summary
    
    This PR adds the disabled-by-default path for normal ChatGPT-login Codex
    sessions to obtain Agent Identity runtime auth through the Codex
    backend. Existing Agent Identity JWT startup mode remains a separate
    path and does not require the feature flag.
    
    What changed:
    
    - adds the experimental `use_agent_identity` feature flag and config
    schema entry
    - adds an explicit `AgentIdentityAuthPolicy` so call sites choose
    `JwtOnly` or `ChatGptAuth` instead of passing a bare boolean
    - stores standalone Agent Identity JWT credentials separately from
    backend-registered Agent Identity records
    - persists the registered Agent Identity record, private key, and single
    run task id in `auth.json` so process restarts reuse the same identity
    - derives the agent/task registration base URL from ChatGPT/Codex auth
    config while keeping JWT JWKS lookup separate
    - provisions and caches ChatGPT-derived Agent Identity runtime auth when
    `use_agent_identity` is enabled
    - reuses the shared run-task registration helper from PR1 rather than
    adding a second task-registration path
    
    This PR intentionally does not switch model inference over to
    `AgentAssertion` auth. The provider-auth integration lands in the next
    PR.
    
    ## Testing
    
    - `just test -p codex-login`
  • Emit Trusted MCP App Identity on Tool-Call Items (#27132)
    ## Summary
    
    - Add optional `appContext` to app-server MCP tool-call items with
    trusted `connectorId`, `linkId`, and `mcpAppResourceUri` metadata.
    - Preserve that context across tool-call events, persisted history,
    reconnects, and thread resume.
    - Keep the deprecated top-level `mcpAppResourceUri` temporarily for
    client migration.
    
    The consumer contract is `{ appContext: { connectorId, linkId,
    mcpAppResourceUri }, tool }`.
    
    ## Validation
    
    - Full GitHub Actions suite passes, including CLA, Bazel tests, clippy,
    release builds, and argument-comment lint.
    
    ---------
    
    Co-authored-by: martinauyeung-oai <280153141+martinauyeung-oai@users.noreply.github.com>
  • TUI: improve unified mention selection visibility (#28959)
    ## Summary
    
    [@milanglacier reported in
    #28653](https://github.com/openai/codex/issues/28653) that the active
    mention candidate is hard to distinguish. I suspect [@binbjz’s #28500
    report](https://github.com/openai/codex/issues/28500) _(where arrow-key
    navigation appeared not to work)_ may describe the same presentation
    problem: the selection may have been changing, but the UI was not
    showing the active row clearly in their terminal. This PR makes two
    small changes to the selection indication behavior:
    
    - Reserve a two-character gutter and mark the active candidate with `> `
    for color-agnostic indicator coverage.
    - Apply the shared theme-aware accent to the entire selected row for
    extra emphasis.
    - Update the existing popup snapshot.
    
    Reverse-video styling was considered, but avoided it because it is
    overly dependent on the user’s terminal palette.
    
    <img width="2046" height="482" alt="image"
    src="https://github.com/user-attachments/assets/b5eb62c3-fd24-4c09-906e-7bd66913b5c6"
    />
    
    ## Testing
    
    - `just test -p codex-tui default_unified_mention_popup_snapshot`
    - `just clippy -p codex-tui`
    - `just fmt`
    - Compiled `codex-cli` and tested the unified mentions picker in the
    terminal.
  • [codex] Remove hardcoded app ID filters (#28947)
    ## Summary
    
    - remove the duplicated originator-specific connector ID denylists
    - stop filtering connector directory/accessibility results and
    live/cached Codex Apps MCP tools by hardcoded connector ID
    - remove the now-unused `codex-login` dependency from
    `codex-utils-plugins`
    - update regression coverage so formerly blocked connector IDs are
    preserved
    
    ## Why
    
    The client-side policy was duplicated across crates, used opaque IDs
    without ownership or expiry information, and could drift between app
    listing and MCP tool behavior. Server-provided visibility,
    authorization, plugin discoverability, accessibility, enabled-state
    handling, and consequential-tool approval templates remain unchanged.
    
    ## Validation
    
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `git diff --check`
    - confirmed the final diff contains no hardcoded denylist symbols
    
    A targeted `codex-mcp` test build spent an unusually long time in local
    compilation/linking. Its first attempt exposed a test-only `PartialEq`
    assertion issue, which was corrected. A follow-up non-linking `cargo
    check -p codex-mcp --tests` was still running when this draft was
    opened; CI should provide the complete Rust validation.
  • Make auto-review on-request prompt more proactive (#26496)
    ## Why
    
    `on-request` approval policy text is currently tuned for user-reviewed
    approvals. For auto-reviewed productivity runs, likely sandbox blocks
    should be escalated earlier so commands that need remote services,
    authentication, or other out-of-sandbox access do not first fail or hang
    inside the sandbox.
    
    ## What changed
    
    - Adds a separate `on_request_auto_review.md` permissions prompt
    selected for `AskForApproval::OnRequest` with
    `ApprovalsReviewer::AutoReview`.
    - Keeps the normal user-reviewed `on-request` wording unchanged.
    - Makes the `When to request escalation` bullets more explicit about
    likely sandbox blocks, network access, remote
    auth/cluster/cloud/database access, out-of-sandbox environment access,
    git operations that may write lock files, and short-timeout reruns after
    likely sandbox-blocked attempts.
    - Omits approved command prefix and `prefix_rule` guidance for the
    auto-review on-request prompt.
    - Adds prompt tests covering the auto-review path, normal on-request
    wording, and inline permission request behavior.
  • Add app-server current-time impl (varlatency 3/n) (#28835)
    ## What
    
    Server should request:
    
    ```
    {
      "id": 42,
      "method": "currentTime/read",
      "params": {
        "threadId": "11111111-1111-1111-1111-aaaaafdc2c11"
      }
    }
    ```
    
    Client should respond with something like:
    
    ```rust
    {
      "id": 42,
      "result": {
        "currentTimeAt": 1781717655
      }
    }
    ```
    
    ## Why
    
    Sessions configured with `clock_source = "external"` need a
    thread-specific external time source before inference. The system clock
    remains the default production provider.
    
    ## Validation
    
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server --test all
    current_time_read_round_trip_adds_reminder_to_model_input`
    - `cargo test -p codex-app-server
    first_attestation_capable_connection_for_thread_only_uses_thread_subscribers`
    - `cargo test -p codex-analytics`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-app-server`
    
    Stacked on #28824.
  • apply-patch: carry paths as PathUri (#28854)
    ## Why
    
    Allows the model to edit files that are hosted on a different OS than
    where app-server is running.
    
    ## What
    
    * Use `PathUri` for apply_patch-internal data structures
    * Limit `PathUri` -> `AbsolutePathBuf` conversion to cases where the
    inferred path convention matches the host OS, allows requiring valid
    paths to pass to perms check
    * Adds `PathConvention::path_segments()` for iterating over path
    segments regardless of OS
    * Handle cross-platform relative paths in path filename parsing for
    sniffing a shell
    * Ensure we can apply patches in the wine e2e test
  • [codex] Cache plugin metadata for tool suggestions (#27812)
    ## Why
    
    `built_tools` runs for every sampling request, and local plugin
    discovery was repeatedly rereading plugin manifests, skills, MCP
    configuration, and app declarations to build the same tool-suggest
    metadata.
    
    That source-derived metadata is stable until the existing plugin manager
    reloads its cache. Runtime eligibility still needs to reflect the
    current install, disable, policy, app-overlap, and authentication state.
    
    ## What changed
    
    - Add a bounded, in-memory tool-suggest metadata cache owned by
    `PluginsManager`.
    - Key cached metadata by plugin identity and source, while applying
    authentication routing each time the metadata is projected.
    - Invalidate the metadata alongside the existing loaded-plugin cache,
    including its normal configuration, marketplace refresh, and
    remote-installed-plugin invalidation paths.
    - Guard against an in-flight load repopulating stale metadata after
    invalidation.
    - Keep marketplace membership and all runtime eligibility filtering live
    rather than introducing a separate catalog or revision model.
    
    ## Impact
    
    Repeated sampling requests reuse already-loaded plugin capability
    metadata while retaining the existing plugin-manager lifecycle as the
    single freshness boundary.
    
    ## Validation
    
    - `just test -p codex-core-plugins` — 252 passed
    - Added focused coverage for cache invalidation and authentication
    reprojection.
  • current time reminders impl for system clock (varlatency 2/n) (#28824)
    Stacked on #28822.
    
    ## Summary
    
    - add a host-injectable current-time provider with a built-in system
    implementation
    - record UTC developer reminders in history immediately before due model
    requests
    - keep cadence state per session and force a refresh after compaction
    
    This does NOT include the app server client <-> server clock logic. This
    PR is only for the reminder message & system clock that will be used in
    prod.
    
    ## Testing
    
    - `just test -p codex-core varlatency_`
    - `just clippy -p codex-core -p codex-app-server -p codex-mcp-server -p
    codex-thread-manager-sample`
    - `just fmt`
  • [codex] Make thread store turn filter optional (#28949)
    Make `ListItemsParams::turn_id` optional so callers can list persisted
    items across an entire thread or narrow the result to one turn. This
    aligns the thread-store API and documentation with thread-wide item
    listing while preserving the optional turn-filter behavior for
    implementations.
  • Support openai/form extended form elicitations (#27500)
    # Summary
    Allow App Server clients to opt into `openai/form` MCP elicitations.