Commit Graph

902 Commits

  • Add network environment ID plumbing (#28766)
    ## Why
    
    Prepare network approval scoping to distinguish execution environments
    without changing behavior yet.
    
    ## What changed
    
    - Add optional environment IDs to network policy requests.
    - Add optional network environment IDs to exec and sandbox request
    structs.
    - Thread default None values through existing construction points.
    - Fix stale constructor call sites that caused the CI compile failures.
    
    ## Not included
    
    - Per-environment proxy listeners.
    - Network approval cache or prompt behavior changes.
    - Ambiguous request attribution handling.
    
    Those behavior changes moved to stacked follow-up #28899.
    
    ## Validation
    
    - just fmt
    - CI will run tests and clippy
  • unified-exec: retain PathUri in command events (#28780)
    ## Why
    
    App-server must report command events containing foreign-platform paths
    without changing existing client or rollout path-string formats.
    
    ## What changed
    
    - retain `PathUri` through exec command begin/end events
    - convert cwd values to `LegacyAppPathString` at the app-server
    compatibility boundary
    - drop command actions with foreign paths and log them
    - serialize rollout-trace cwd values using their inferred native path
    representation
    - restore Wine coverage for retained Windows cwd values and successful
    completion
  • Expose selecte namespaces as direct model tools (#28825)
    ## Why
    
    Som tools, such as history and notes, must remain top-level when MCP
    deferral is enabled while staying unavailable through code-mode `exec`.
    
    ## What changed
    
    - Added `features.code_mode.direct_only_tool_namespaces`.
    - Classified matching MCP tools as `DirectModelOnly`.
    - Kept those tools top-level in `code_mode_only`.
    - Excluded them from `tool_search` deferral and the nested `exec`
    surface.
    - Updated the generated config schema.
    
    ## Validation
    
    - `code_mode_only_exposes_direct_model_only_mcp_namespaces`
    - `load_config_resolves_code_mode_config`
  • [codex] Add optional IDs to response items (#28812)
    ## Why
    
    `ResponseItem` variants do not have a consistent internal ID shape: some
    variants carry required IDs, some carry optional IDs, and some cannot
    represent an ID at all. The existing fields also use inconsistent serde,
    TypeScript, and JSON-schema annotations. A single enum-level access path
    is needed before history recording can assign and retain IDs.
    
    This PR establishes that internal model only. It intentionally does not
    generate or serialize IDs; allocation and wire persistence are isolated
    in the stacked follow-up.
    
    ## What changed
    
    - Give every concrete `ResponseItem` variant an `Option<String>` ID
    field.
    - Apply the same internal-only annotations to every ID field:
    `#[serde(default, skip_serializing)]`, `#[ts(skip)]`, and
    `#[schemars(skip)]`.
    - Add `ResponseItem::id()` and `ResponseItem::set_id()` as the shared
    accessors.
    - Preserve IDs when history items are rewritten for truncation.
    - Adapt consumers that previously assumed reasoning and image-generation
    IDs were required.
    - Regenerate app-server schemas so the hidden fields are represented
    consistently.
    
    The serde catch-all `ResponseItem::Other` remains ID-less because it
    must remain a unit variant.
    
    ## Test plan
    
    - `cargo check --tests -p codex-core -p codex-api -p codex-rollout-trace
    -p codex-image-generation-extension`
    - `just test -p codex-protocol`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-api -p codex-rollout-trace -p
    codex-image-generation-extension`
    - `just test -p codex-core event_mapping`
  • [codex] trace tools build latency (#28782)
    Add more tracing spans around tool building.
  • unified-exec: preserve PathUri through exec-server (#28681)
    ## Why
    
    It should be possible for app-server to handle "foreign" OS paths in
    unified_exec working directories, allowing e.g. a Linux app-server to
    run processes on e.g. a Windows exec-server.
    
    ## What
    
    Convert the core unified_exec cwd values to use `PathUri`.
    
    Adds fallible path conversion in several places to try to minimize the
    scope of this change. The only time this change suppresses errors from
    converting `PathUri` to an `AbsolutePathBuf` is when the turn is
    configured with no sandboxing at all to allow us to make progress
    testing without sandboxing.
    
    Future changes to apply_patch and sandboxing will clean up these error
    paths.
    
    A tool's cwd is resolved from joining a model-provided workdir to the
    environment's cwd. When using `AbsolutePathBuf::join()`, an
    absolute-path workdir would overwrite the environment's cwd and we would
    resolve permissions/sandboxing against the model-provided path. This
    change extends `PathUri::join()` to also treat an absolute rhs as an
    override of the base/lhs.
    
    This also removes some coverage from the remove_env_windows tests until
    a follow-up converts foreign paths in command exec events correctly.
    
    ## Breaking Changes
    
    When using `AbsolutePathBuf::join()` for workdir resolution, we ended up
    resolving tilde-prefixed paths against the app-server's `$HOME`, e.g.
    `~/foo/bar` becomes `/home/anp/foo/bar`. It's difficult to do this with
    `PathUri` joining, so after offline discussion this PR no longer
    implements it.
    
    A quick check of some power users' rollouts suggests that models don't
    actually generate home-prefixed absolute working directories for their
    spawns, so this shouldn't have any real blast radius.
  • Scope command approvals by execution environment (#28738)
    ## Why
    
    Command approval cache keys included the command and working directory,
    but not the execution environment. An approval for `/workspace` locally
    could therefore be reused for the same command and path on an executor.
    
    ## What changed
    
    - Include the selected environment ID in shell and unified-exec approval
    cache keys.
    - Carry that ID through the normal command approval request so clients
    can show which environment is being approved.
    - Expose the environment through app-server as a required nullable
    `environmentId` and show it in the inline TUI approval prompt.
    - Keep older recorded approval events compatible when the environment is
    absent.
    
    For example, `echo ok` in local `/workspace` and `echo ok` in executor
    `/workspace` now produce different approval keys and separate prompts.
    
    ## Scope
    
    This PR does not change network approvals, Guardian review actions, MCP
    elicitation, full-screen TUI rendering, or environment-ID validation.
    Remote `shell_command` execution itself remains in #28722; this PR only
    makes its approval key environment-aware.
  • Run fs helper through Windows sandbox wrapper (#28359)
    ## Why
    
    This is the final PR in the Windows fs-helper sandbox stack and contains
    the actual bug fix.
    
    The exec-server filesystem helper is a direct-spawn path: it asks
    `SandboxManager` for a `SandboxExecRequest`, then launches the returned
    argv itself. That works on macOS and Linux because the transformed argv
    is already a self-contained sandbox wrapper. On Windows, the transformed
    request carried `WindowsRestrictedToken` metadata, but the direct-spawn
    fs-helper runner still launched the helper argv directly.
    
    That means Windows filesystem built-ins backed by the fs-helper could
    run with the parent Codex process permissions instead of the configured
    Windows sandbox. This PR makes the direct-spawn transform produce a
    self-contained Windows wrapper argv before fs-helper launches it.
    
    ## What Changed
    
    - Added `SandboxManager::transform_for_direct_spawn()` for callers that
    launch the returned argv themselves.
    - Wrapped Windows restricted-token direct-spawn requests with `codex.exe
    --run-as-windows-sandbox` and then marked the outer request as
    unsandboxed, matching the macOS/Linux wrapper argv shape.
    - Updated `exec-server/src/fs_sandbox.rs` to use the direct-spawn
    transform for fs-helper launches.
    - Materialized the inner `codex.exe --codex-run-as-fs-helper` executable
    into `.sandbox-bin` so the sandboxed user can run it.
    - Carried runtime workspace roots through `FileSystemSandboxContext` as
    `PathUri` values so `:workspace_roots` policies resolve correctly
    without sending native client paths over exec-server JSON.
    - Preserved wrapper setup identity environment needed by Windows sandbox
    setup without changing the serialized inner helper environment.
    
    ## Verification
    
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `just test -p codex-sandboxing transform_for_direct_spawn_windows`
    - `just test -p codex-exec-server fs_sandbox::tests`
    - `just fix -p codex-windows-sandbox -p codex-sandboxing -p
    codex-exec-server -p codex-core -p codex-file-system`
    
    Local note: `just fmt` completed Rust formatting, but this workstation
    still fails the non-Rust formatter phases because uv cannot open its
    cache and the local buildifier/dotslash path is missing.
  • Add join key for MAv2 inter-agent messages (#28561)
    ## Summary
    This keeps inter-agent communication on the existing raw response item
    path and adds a join key for MAv2 tool calls.
    
    MAv2 `spawn_agent`, `send_message`, and `followup_task` now stamp the
    originating tool call id into `ResponseItemMetadata.source_call_id` on
    the raw `ResponseItem::AgentMessage`. App-server clients can join that
    raw item back to the existing tool/activity event by call id, while
    using the raw agent message's existing sender, receiver, and content
    fields.
    
    No new app-server `ThreadItem` or notification type is added.
    
    ## Tests
    - `just fmt`
    - `just write-app-server-schema`
    - `just test -p codex-protocol`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-core
    multi_agent_v2_spawn_returns_path_and_send_message_accepts_relative_path`
    - `just test -p codex-core
    multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn`
    - `just fix -p codex-protocol`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-core`
  • core: remove redundant TurnContext and Prompt fields (#28638)
    ## Why
    
    `TurnContext` had accumulated dead fields and cached projections of
    values already owned by its per-turn `Config` or `ModelInfo`. Keeping
    both copies made ownership unclear and allowed artificial split-brain
    states, such as a compatibility hash differing from the model metadata
    it came from.
    
    `Prompt` similarly carried a write-only personality after personality
    selection had already been materialized into its base instructions.
    
    This makes the canonical owner explicit: configuration-backed values
    come from `config`, model-derived values come from `model_info`, and
    prompts contain only data consumed by request construction.
    
    ## What changed
    
    - Remove the unused `ghost_snapshot`, `codex_self_exe`, and
    `thread_source` fields.
    - Remove duplicate `comp_hash`, `truncation_policy`, `features`,
    `shell_environment_policy`, `codex_linux_sandbox_exe`, `compact_prompt`,
    and `tool_mode` fields.
    - Read those values directly from `TurnContext::config` or
    `TurnContext::model_info` at their consumers.
    - Remove the write-only `Prompt::personality` field and its constructor
    assignments.
    - Preserve review-turn inheritance of the parent turn's shell policy,
    Linux sandbox executable, and compact prompt through the review config.
    
    ## Testing
    
    - `cargo check -p codex-core --tests`
  • [codex] [4/4] Simplify recommended plugin install schema (#28403)
    ## Summary
    - Simplify recommendation-context `request_plugin_install` arguments to
    `plugin_id` and `suggest_reason`.
    - Derive plugin type and install action from the matched candidate while
    preserving Codex-owned elicitation metadata.
    - Keep the legacy list-backed schema unchanged and accept resumed calls
    that still use `tool_id`.
    
    ## Stack
    - #28399
    - #28400
    - #27704
    - This PR
    
    ## Validation
    - `just test -p codex-tools -p codex-core request_plugin_install` (25
    passed)
    - `just fix -p codex-tools -p codex-core`
    - `just fmt`
    - `git diff --check`
  • [codex] [3/4] Activate endpoint plugin recommendations (#27704)
    Summary\n- Await endpoint recommendation selection while constructing
    each authenticated turn, removing the first-turn cache race.\n- Snapshot
    and filter endpoint candidates once per turn, then use that same set for
    the bounded contextual user fragment, tool exposure, and exact install
    validation.\n- Keep recommendation selection ephemeral: do not persist
    recommendation state in or gate resumed threads on prior context.\n-
    Hide the legacy list tool in endpoint mode and preserve legacy discovery
    unchanged when the endpoint is disabled or unavailable.\n- Keep remote
    plugin and connector app identities out of model-visible context and
    attach them only to Codex-owned elicitation metadata.\n\nStack\n- 3/4,
    based on #28400.\n- Endpoint client and cache: #28399.\n- Generalized
    suggestion presentation: #28400.\n- Install-schema follow-up:
    #28403.\n\nValidation\n- \n- \n- \n- \n- Full : 2,649 passed and 88
    environment-dependent tests failed because this sandbox cannot write ,
    nest Seatbelt, or locate auxiliary test binaries.
  • [codex] [2/4] Generalize plugin suggestion presentation (#28400)
    Summary
    - Add list-backed and developer-context presentations for plugin
    suggestion candidates.
    - Let tool planning, install validation, and request-tool copy follow
    the selected presentation.
    - Keep every production caller on the existing list-backed presentation,
    preserving the current list tool, request schema, connector behavior,
    and model-visible copy.
    - Leave developer-context presentation latent until the final PR in the
    stack.
    
    Stack
    - 2/3, based on #28399.
    - Follow-up: #27704 activates endpoint recommendations.
    
    Validation
    - `just test -p codex-core request_plugin_install`
    - `just test -p codex-core spec_plan`
    - `just fix -p codex-core`
    - `just fmt`
    - `git diff --check`
  • app-server: preserve target-native environment cwd (#28146)
    ## Why
    
    app-server may run on a different OS from the selected exec-server
    environment. Parsing that environment’s cwd with the Codex host’s path
    rules prevents thread startup.
    
    ## What
    
    Carry environment cwd values as `LegacyAppPathString` at the app-server
    boundary and `PathUri` internally. Existing tool-call schemas and
    relative-path behavior stay host-native; remaining local-only consumers
    convert explicitly and leave follow-up TODOs.
    
    The Wine integration test verifies app-server can start a thread and
    complete an ordinary turn with a Windows environment cwd from Linux.
    
    ## Validation
    
    - `bazel test //codex-rs/core/tests/remote_env_windows:smoke-test
    --test_output=errors`
    - focused app-server environment-selection and protocol schema tests
    - scoped Clippy for `codex-core` and `codex-app-server-protocol`
  • [codex] Warn clearly when code mode output is truncated (#28467)
    ## Summary
    
    - make `formatted_truncate_text` prepend `Warning: truncated output
    (original token count: N)` above the existing `Total output lines`
    header
    - update direct formatter, unified-exec, user-shell, and code-mode
    expectations
    - add core unit coverage that runs in Bazel without requiring the
    skipped V8-backed code-mode integration suite
    
    ## Validation
    
    - `cargo test -p codex-utils-output-truncation -- --nocapture` (17
    passed)
    - `cargo test -p codex-core --lib
    truncated_text_output_starts_with_warning -- --nocapture`
    - `cargo test -p codex-core --test all
    clamps_model_requested_max_output_tokens_to_policy -- --nocapture` (2
    passed)
    - `cargo test -p codex-core --test all
    unified_exec_formats_large_output_summary -- --nocapture`
    - `cargo test -p codex-core --test all
    user_shell_command_output_is_truncated_in_history -- --nocapture`
    - Bazel CI exercises the shared formatter and downstream integration
    expectations
  • feat: render typed envelopes for multi-agent v2 messages (#28368)
    ## Why
    
    Multi-agent v2 messages need a consistent, model-visible envelope that
    identifies what kind of interaction occurred, who sent it, and which
    agent it targets. Previously, encrypted deliveries exposed only
    `encrypted_content`, while child completion used the legacy
    `<subagent_notification>` shape. That meant the client could not
    consistently present `NEW_TASK`, `MESSAGE`, and `FINAL_ANSWER` using the
    same format.
    
    This change adds the routing envelope as plaintext while keeping task
    and message payloads encrypted. No new Responses API field is required:
    an encrypted delivery is represented as an `input_text` header
    immediately followed by its existing `encrypted_content` item.
    
    Every envelope now follows this shape:
    
    ```text
    Message Type: <NEW_TASK | MESSAGE | FINAL_ANSWER>
    Task name: <recipient agent path>
    Sender: <author agent path>
    Payload:
    <message payload>
    ```
    
    ## Message types
    
    ### `NEW_TASK`
    
    `NEW_TASK` is used when the recipient should begin a new turn, including
    an initial `spawn_agent` task and a later `followup_task`.
    
    For a root agent spawning `/root/worker`, the request contains a
    plaintext envelope followed by the encrypted task:
    
    ```json
    {
      "type": "agent_message",
      "author": "/root",
      "recipient": "/root/worker",
      "content": [
        {
          "type": "input_text",
          "text": "Message Type: NEW_TASK\nTask name: /root/worker\nSender: /root\nPayload:\n"
        },
        {
          "type": "encrypted_content",
          "encrypted_content": "<encrypted task payload>"
        }
      ]
    }
    ```
    
    Conceptually, the model receives:
    
    ```text
    Message Type: NEW_TASK
    Task name: /root/worker
    Sender: /root
    Payload:
    Review the authentication changes and report any regressions.
    ```
    
    ### `MESSAGE`
    
    `MESSAGE` is used for a queued `send_message` delivery. It communicates
    with an existing agent without starting a new turn.
    
    For `/root/worker` reporting progress to the root agent, the request
    contains:
    
    ```json
    {
      "type": "agent_message",
      "author": "/root/worker",
      "recipient": "/root",
      "content": [
        {
          "type": "input_text",
          "text": "Message Type: MESSAGE\nTask name: /root\nSender: /root/worker\nPayload:\n"
        },
        {
          "type": "encrypted_content",
          "encrypted_content": "<encrypted message payload>"
        }
      ]
    }
    ```
    
    Conceptually, the model receives:
    
    ```text
    Message Type: MESSAGE
    Task name: /root
    Sender: /root/worker
    Payload:
    The protocol tests pass; I am checking the resume path now.
    ```
    
    ### `FINAL_ANSWER`
    
    `FINAL_ANSWER` is emitted when a child agent reaches a terminal state
    and reports its result to its parent. Completion payloads are already
    available locally, so the complete envelope is represented as plaintext
    rather than as a plaintext header plus encrypted content.
    
    For `/root/worker` completing work for the root agent, the request
    contains:
    
    ```json
    {
      "type": "agent_message",
      "author": "/root/worker",
      "recipient": "/root",
      "content": [
        {
          "type": "input_text",
          "text": "Message Type: FINAL_ANSWER\nTask name: /root\nSender: /root/worker\nPayload:\nNo regressions found."
        }
      ]
    }
    ```
    
    The model-visible form is:
    
    ```text
    Message Type: FINAL_ANSWER
    Task name: /root
    Sender: /root/worker
    Payload:
    No regressions found.
    ```
    
    Errored, shut down, and missing agents also use `FINAL_ANSWER`, with a
    terminal-status description in the payload.
    
    ## What changed
    
    - Render `NEW_TASK` or `MESSAGE` in
    `InterAgentCommunication::to_model_input_item`, based on whether the
    encrypted delivery starts a turn.
    - Replace the multi-agent v2 `<subagent_notification>` completion
    payload with a model-visible `FINAL_ANSWER` envelope.
    - Document `Task name`, `Sender`, and `Payload` consistently in the
    multi-agent developer instructions.
    - Prevent local-only history projections from treating an encrypted
    message's plaintext header as the complete assistant message.
    - Preserve rollout-trace interaction edges when an agent message
    contains both plaintext and encrypted content.
    
    Legacy multi-agent behavior remains unchanged.
    
    ## Verification
    
    - `just test -p codex-protocol`
    - `just test -p codex-rollout-trace`
    - `just test -p codex-web-search-extension`
    - `just test -p codex-core
    encrypted_multi_agent_v2_spawn_sends_agent_message_to_child`
    - `just test -p codex-core
    plaintext_multi_agent_v2_completion_sends_agent_message`
    - `just test -p codex-core
    multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn`
    - `just test -p codex-core
    multi_agent_v2_completion_queues_message_for_direct_parent`
  • [codex] Use local environment for user shell commands (#28163)
    ## Why
    
    User shell commands still read the legacy turn cwd and session shell
    even though execution context is now owned by selected turn
    environments. App-server also defines `thread/shellCommand` as a
    local-host escape hatch, so it must use an available local environment
    even when a remote environment is primary.
    
    ## What changed
    
    - Add `ResolvedTurnEnvironments::local()` to find the selected local
    environment.
    - Resolve the user shell command cwd and shell from that local
    `TurnEnvironment`.
    - Emit the standard `shell is unavailable in this session` error when no
    selected local environment or resolved local shell is available.
    - Add an integration test covering `/shell` without a local environment.
    
    ## Test plan
    
    - `just test -p codex-core
    user_shell_command_without_local_environment_emits_error`
  • [codex] Add interruptible sleep tool (#28429)
    ## Why
    
    Models sometimes need to pause briefly while waiting for external work,
    but using a shell command for that delay ties the wait to a process and
    does not naturally resume when new turn input arrives.
    
    ## What changed
    
    - add a built-in `sleep` tool behind the under-development `sleep_tool`
    feature
    - accept a bounded `duration_ms` argument, matching the millisecond
    convention used by unified exec
    - end the sleep early when either steered user input or mailbox input
    arrives
    - include elapsed wall-clock time in completed and interrupted outputs
    - emit a dedicated core `SleepItem` through `item/started` and
    `item/completed`
    - expose the sleep item as app-server v2 `ThreadItem::Sleep` and retain
    it in reconstructed thread history
    - regenerate the configuration schema for the new feature flag
    - regenerate app-server JSON and TypeScript schema fixtures
    
    ## Test plan
    
    - `just test -p codex-core sleep_tool_follows_feature_gate`
    - `just test -p codex-core any_new_input_interrupts_sleep`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-app-server
    sleep_emits_started_and_completed_items`
  • [codex] Bind shell snapshots to retained thread environments (#28421)
    ## Why
    
    Shell snapshots are currently session-scoped even though shell and cwd
    are properties of a selected turn environment. That makes snapshot
    refresh depend on separate session-cwd plumbing, prevents retained
    environments from retaining their snapshot work, and can make snapshot
    construction use a different shell than command execution.
    
    This follows #27955 by making the retained thread-environment service
    own environment snapshot lifecycles. Session configuration remains the
    requested selection state, while `ThreadEnvironments` remains the source
    of successfully resolved environments.
    
    ## What changed
    
    - Configure the shell-snapshot builder before initial environment
    resolution.
    - Start each local environment snapshot task when its `TurnEnvironment`
    is built and retain that shared task while environment ID and cwd still
    match.
    - Inherit retained environment snapshots into spawned child threads.
    - Carry the selected `TurnEnvironment` through shell runtimes so
    snapshot construction and command execution use the same
    environment-specific shell and cwd.
    - Load project instructions and warm plugins/skills after initial
    environment resolution.
    - Continue decoding invalid UTF-8 instruction files lossily without
    emitting a startup warning.
    - Keep requested selections in `SessionConfiguration`; failed or
    duplicate resolutions only affect the resolved environment snapshot.
    
    ## Validation
    
    - `cargo check -p codex-core --tests`
    - `just test -p codex-home instructions` (6 passed)
    - Focused environment, instruction, shell-snapshot, and user-shell tests
    (84 passed)
    - Focused shell-snapshot, user-shell, and unified-exec tests (126
    passed; two event-timing tests passed on retry)
  • Use PathUri in filesystem permission paths for exec-server (#28165)
    ## Why
    
    Progress towards letting app-server and exec-server run on different
    platforms, specifically for sandbox configuration.
    
    ## What
    
    - Make the filesystem path containment hierarchy generic, defaulting to
    `AbsolutePathBuf` for now.
    - Have clients specify `AbsolutePathBuf` or `PathUri` directly where
    needed.
    - Use `PathUri` throughout exec-server filesystem protocol and trait
    boundaries.
    - Implement `From` for conversion to path URIs and `TryFrom` for
    fallible conversion to absolute paths through the generic type
    hierarchy.
  • Respect blocking PostToolUse hooks in code mode (#28365)
    ## Summary
    
    Make blocking hook behavior reliable for tools invoked from code mode.
    
    Previously, a `PostToolUse` hook could block a completed tool result,
    but code mode would still return the original typed result to
    JavaScript. The hook appeared blocked in hook telemetry while the
    running script continued with the result.
    
    This change:
    
    - rejects the nested JavaScript tool promise when `PostToolUse` blocks
    - normalizes `decision: "block"` and exit code 2 to the same blocking
    behavior
    - surfaces the hook feedback as the rejected promise's error
    - adds end-to-end coverage for the relevant PreToolUse and PostToolUse
    interactions
    
    ## Hook semantics in code mode
    
    | Hook behavior | Code-mode result |
    |---|---|
    | PreToolUse block | Reject the promise before the tool executes |
    | PreToolUse `updatedInput` | Execute the rewritten invocation and
    return its result |
    | PostToolUse `decision: "block"` | Execute the tool, then reject the
    promise with the hook reason |
    | PostToolUse exit code 2 | Same behavior as `decision: "block"` |
    | PostToolUse `continue: false` | Preserve the existing feedback-only
    behavior; do not reject the promise |
    
    ## Test coverage
    
    Added or strengthened end-to-end coverage proving that:
    
    - a PreToolUse block rejects the JavaScript promise before execution
    - a PreToolUse input rewrite executes only the rewritten command
    - JavaScript receives the rewritten command's result
    - PostToolUse `decision: "block"` rejects after the command executes
    - PostToolUse exit code 2 has the same behavior
    - the hook observes the original completed tool response
    - the blocked original result does not reach JavaScript
    - existing direct-mode replacement behavior remains intact
    - `continue: false` without a reason produces deterministic fallback
    feedback
  • feat(core): add metadata field to ResponseItem (#28355)
    ## Description
    
    This PR adds an optional `metadata` field to `ResponseItem` for
    Responses API calls. Only mechanical plumbing, no actual values
    populated and sent yet. Turns out just adding a new field to
    `ResponseItem` has quite a large blast radius already.
    
    This change is backwards compatible because `metadata` is optional and
    omitted when absent, so existing response items and rollout history
    without it still deserialize and requests that do not set it keep the
    same wire shape. For provider compatibility, we strip out `metadata`
    before non-OpenAI Responses requests so Azure and AWS Bedrock never see
    this field.
    
    My followup PR here will actually make use of it to start storing and
    passing along `turn_id`: https://github.com/openai/codex/pull/28360
    
    ## What changed
    
    - Added `ResponseItemMetadata` with optional `turn_id`, plus optional
    `metadata` on Responses API item variants and inter-agent communication.
    - Preserved item metadata through response-item rewrites such as
    truncation, missing tool-output synthesis, compaction history
    rebuilding, visible-history conversion, rollout/resume, and generated
    app-server schemas/types.
    - Strip item metadata from non-OpenAI Responses requests while
    preserving it for OpenAI-shaped requests.
    - Updated the mechanical fixture/test construction churn required by the
    new optional field.
  • core: cache the tool search handler per session (#27258)
    ## Why
    
    Tool router construction rebuilds the deferred-tool BM25 index during
    session initialization and before each sampling continuation, even when
    the searchable tool metadata is unchanged. Local profiling measured
    `append_tool_search_executor` at roughly 113 ms per continuation, making
    repeated index construction the largest measured router-building cost.
    
    ## What changed
    
    - Add a session-scoped `ToolSearchHandlerCache` so continuations and
    user turns can reuse the existing handler.
    - Key reuse on the complete ordered `Vec<ToolSearchInfo>`, rebuilding
    when searchable text, loadable tool specs, source metadata, or ordering
    changes.
    - Build handlers outside the cache lock and recheck before publishing
    them, avoiding holding the mutex during index construction.
    
    ## Verification
    
    - `cache_reuses_identical_search_infos_and_rebuilds_changed_inputs`
    covers exact cache reuse and invalidation when the ordered search
    metadata changes.
    - Local rollout profiling showed the initial router build populating the
    cache and unchanged later continuations reusing it:
      - uncached: 118 ms median across 14 spans from 3 rollouts
      - cached: 4 ms median across 12 spans from 3 rollouts
  • core: let steer interrupt wait_agent (#28341)
    ## Why
    
    `wait_agent` can block for a long timeout while waiting for sub-agent
    mailbox activity. Although same-turn user steer is accepted during that
    tool call, the input remains pending until the wait returns, so an
    explicit request to change direction can appear unresponsive.
    
    ## What changed
    
    - Notify active `wait_agent` calls when user input is steered into the
    current turn.
    - Check for already-pending steer input when subscribing so input that
    races with tool startup is not missed.
    - Distinguish mailbox activity, steered input, and timeout outcomes,
    returning `Wait interrupted by new input.` for the steer path.
    - Update the `wait_agent` tool description to document the early-return
    behavior.
    
    ## Testing
    
    - `just test -p codex-core input_queue_`
    - `just test -p codex-core wait_agent`
    
    The coverage includes steer notification before and after subscription,
    plus an end-to-end test that verifies the interrupted wait result and
    steered user input are both included exactly once in the follow-up model
    request.
  • [codex] remove stale PathExt import (#28344)
    ## Why
    
    `main` fails dev-profile Cargo and Bazel Clippy builds because
    `core/src/tools/runtimes/mod_tests.rs` imports `PathExt` after its last
    use was removed. With warnings denied, that stale import prevents
    `codex-core` test targets from compiling across platforms.
    
    ## What changed
    
    Remove the unused `PathExt` import. Remaining `.abs()` calls in the
    module operate on `PathBuf` and continue to use `PathBufExt`.
    
    ## Validation
    
    - `just fmt`
    - Focused `codex-core` test compile attempted; blocked locally by disk
    exhaustion before compilation completed. The CI failure itself is the
    unused-import diagnostic this change removes.
  • [codex] simplify memory read metrics (#28164)
    ## Why
    
    Memory read telemetry currently reconstructs the executable shell
    command after a tool call finishes. That duplicates shell, login-policy,
    and cwd resolution owned by the tool handlers, and can diverge from the
    environment-specific command that unified exec actually ran.
    
    ## What changed
    
    - Expose the existing restricted shell-script parser directly for raw
    script text.
    - Parse `shell_command` and `exec_command` input into plain command argv
    before classifying memory reads.
    - Preserve all-or-nothing safe-command validation for multi-command
    scripts.
    - Remove cwd resolution, shell selection, and the unnecessary async
    boundary from memory read metric emission.
    
    ## Testing
    
    - `just test -p codex-shell-command`
    - `cargo check -p codex-core`
  • [codex] simplify shell snapshot ownership (#27756)
    ## Why
    
    Shell snapshot lifecycle state was split between `Shell` and
    `SessionServices`: `Shell` carried the receiver while session code
    exposed and forwarded the raw sender. That coupled shell identity to
    mutable snapshot state and made refresh, inheritance, and file lifetime
    harder to reason about.
    
    ## What changed
    
    - make each `Arc<ShellSnapshot>` represent one cwd-specific snapshot
    generation
    - store the active generation in `SessionServices` with `ArcSwapOption`
    - have construction start the background build and expose only a
    cwd-validated snapshot path
    - use `ShellSnapshotFile` ownership to delete snapshot files
    automatically
    - pass snapshot paths explicitly to shell runtimes instead of storing
    snapshot state on `Shell`
    - preserve inherited and in-flight generations by pinning their `Arc`
    while they are in use
    
    ## Test plan
    
    - `cargo check -p codex-core --lib`
    - `just test -p codex-core 'shell_snapshot::tests'`
    - `just test -p codex-core
    shell_command_snapshot_still_intercepts_apply_patch`
    - `just test -p codex-core
    shell_snapshot_deleted_after_shutdown_with_skills`
  • Represent dynamic tools with explicit namespaces internally (#27365)
    Follow-up to #27356.
    
    ## Stack note
    
    This PR changes Codex's internal dynamic-tool shape while leaving
    `thread/start` unchanged. App-server therefore converts the existing
    per-tool input into explicit functions and namespaces before passing it
    to core.
    
    [#27371](https://github.com/openai/codex/pull/27371) updates
    `thread/start` to use the same explicit shape and removes this temporary
    conversion.
    
    ## Why
    
    Dynamic tools repeat namespace metadata on every function. Core should
    keep one explicit namespace with its member tools so descriptions and
    membership stay consistent across sessions and runtime planning.
    
    ## What changed
    
    - Represent dynamic tools as top-level functions or explicit namespaces
    in protocol and session state.
    - Read old flat rollout metadata and write the canonical hierarchy.
    - Flatten namespace members only when registering callable tools.
    - Keep `thread/start.dynamicTools` flat for now and normalize it at the
    app-server boundary.
    
    New builds can read old rollout metadata. Older builds cannot read newly
    written hierarchical metadata.
    
    ## Test plan
    
    - `just test -p codex-app-server
    thread_start_normalizes_legacy_dynamic_tools_into_model_request`
    - `just test -p codex-protocol
    session_meta_normalizes_legacy_dynamic_tools`
    - `just test -p codex-core
    resume_restores_dynamic_tools_from_rollout_with_sqlite_enabled`
    - `just test -p codex-core
    tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call`
    - `just test -p codex-core code_mode_can_call_hidden_dynamic_tools`
    - `just test -p codex-tools`
  • [codex] update multi-agent v2 prompts (#28283)
    ## Summary
    
    - align the default multi-agent v2 root and subagent hints with the
    evaluated prompt guidance for direct collaboration-tool calls, parallel
    delegation, and shared workspaces
    - keep the current `interrupt_agent` tool name and existing
    concurrency-hint placement, with the explicit no-spawn instruction last
    - document the context tradeoff between `fork_turns="none"` and
    `fork_turns="all"` in the v2 `spawn_agent` description
    - extend the focused prompt and tool-surface tests
    
    ## Why
    
    The evaluated multi-agent prompt includes operational guidance that is
    missing from the current Codex defaults. This applies that guidance to
    the current tool surface without restoring stale `close_agent` or
    duplicated concurrency wording.
    
    ## User impact
    
    Multi-agent v2 receives clearer instructions about when and how to
    parallelize work, how agent workspaces interact, and how `fork_turns`
    affects subagent context. The existing default opt-out behavior remains
    in place.
    
    ## Testing
    
    - `just fmt`
    - `just test -p codex-core
    multi_agent_v2_default_usage_hints_use_configured_thread_cap`
    - `just test -p codex-core
    multi_agent_feature_selects_one_agent_tool_family`
  • [codex] exec-server honors remote environment cwd and shell (#28122)
    ## Why
    
    Next slice needed to make progress on the `remote_env_windows` test is
    to support passing a Windows cwd for the remote environment and using
    that environment's native shell. This lets the test run a real Windows
    process instead of only recording an early path or shell mismatch.
    
    ## What
    
    - change `TurnEnvironmentSelection.cwd` from `AbsolutePathBuf` to
    `PathUri`
    - convert local cwd values to URIs when constructing selections
    - preserve a remote primary cwd instead of replacing it with the local
    legacy fallback
    - prefer the selected environment's discovered shell for unified exec,
    falling back to the session shell when unavailable
    - convert back to a host-native absolute path at current native-only
    consumer boundaries
    - reject or deny unsupported foreign cwd values at the existing
    request-permissions boundary, with TODOs for its future migration
    - extend the hermetic Wine test to execute Windows PowerShell in
    `C:\windows` and verify successful process completion
    - record the current app-server rejection against the same Wine-backed
    remote Windows fixture when its cwd is supplied as a native Windows path
  • [codex] make PathUri::from_abs_path infallible (#27976)
    ## Why
    
    `PathUri::from_abs_path` can fail for absolute paths that do not have a
    normal `file:` URI representation, forcing filesystem call sites to
    handle a conversion error even though the original path can be preserved
    losslessly.
    
    ## What
    
    Make `from_abs_path` infallible and migrate its callers. Unrepresentable
    paths use `file:///%00/bad/path/<base64>`, encoding Unix bytes or
    Windows UTF-16LE; `to_abs_path` validates and decodes that fallback. The
    leading encoded null reserves a namespace that cannot collide with a
    real Unix or Windows path, and fallback URIs remain opaque to lexical
    path operations.
    
    ## Validation
    
    Added path-URI coverage for Unix null and non-UTF-8 paths, Windows
    device/verbatim and non-Unicode paths, serialization, malformed
    fallbacks, opaque lexical operations, invalid native payloads, and
    literal `/bad/path` collision resistance.
  • sandboxing: migrate cwd inputs to PathUri (#27816)
    ## Why
    
    Sandbox cwd values can cross app-server and exec-server host boundaries.
    They should retain URI semantics until the receiving host validates them
    instead of being interpreted early as native paths.
    
    ## What
    
    - Carry `PathUri` through filesystem sandbox contexts, sandbox commands,
    and transform inputs.
    - Convert command and policy cwd once in `SandboxManager::transform`,
    then keep launch requests native.
    - Preserve sandbox cwd over remote filesystem transport and reject
    non-native URIs without fallback.
    - Cache paired native/URI turn-environment cwd values during migration,
    with immutable access to keep them synchronized.
    - Extend existing protocol, forwarding, transform, and core runtime
    tests.
  • chore: prompt MAv2 (#27919)
    Prompt update of MAv2
  • Keep request_user_input direct-model only (#27316)
    ## Why
    
    `request_user_input` has direct blocking semantics when invoked by the
    model. When it is exposed as a nested code-mode tool, the call has to
    flow through code-mode waiting and continuation behavior instead, which
    is not the behavior we want for this user-input request surface.
    
    ## What changed
    
    - Mark `request_user_input` with `ToolExposure::DirectModelOnly` when
    registering the core utility tool.
    - Keep `request_user_input` direct-model visible, including in
    code-mode-only planning.
    - Add focused `spec_plan_tests` coverage that verifies
    `request_user_input` remains visible and registered as
    direct-model-only, while it is omitted from the nested code-mode tool
    description.
    
    No active goal suppression or runtime unavailability behavior is
    included in this PR.
    
    ## Validation
    
    - No new build/test run for this housekeeping pass, per maintainer
    request.
    - Earlier targeted run, confirmed from session context: `just test -p
    codex-core request_user_input` passed.
  • Add request_user_input auto-resolution window contract (#27256)
    ## Why
    
    `request_user_input` is moving beyond its original plan-mode-only
    workflow, and future default/goal-mode usage needs a way for the model
    to ask helpful but non-blocking questions without forcing the turn to
    wait forever. This PR adds an explicit `autoResolutionMs` contract so a
    later client/runtime change can auto-resolve unanswered prompts after a
    bounded window while leaving truly blocking questions unchanged.
    
    This is contract plumbing only; it does not implement the client-side
    timer or auto-selection behavior, and the model-facing description
    treats the field as reserved unless the current runtime explicitly
    supports auto-resolution.
    
    ## What Changed
    
    - Added optional `autoResolutionMs` to the model-facing
    `request_user_input` args and core `RequestUserInputEvent`.
    - Added model-facing schema text for `autoResolutionMs` while marking it
    reserved for runtimes that explicitly support auto-resolution.
    - Bounds `autoResolutionMs` to `60_000..=240_000` ms during argument
    normalization by clamping out-of-range model-provided values.
    - Propagated the field through app-server v2
    `ToolRequestUserInputParams`, app-server request forwarding, generated
    TypeScript, and JSON schema fixtures.
    - Updated app-server, core, protocol, and TUI call sites/tests so
    omitted values preserve existing `None`/`null` behavior and coverage
    verifies a `Some(60_000)` round trip.
    
    ## Verification
    
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-core request_user_input`
    - `just test -p codex-app-server request_user_input_round_trip`
    - `just test -p codex-tui request_user_input`
    - `just test -p codex-protocol`
  • [codex] Remove async_trait from first-party code (#27475)
    ## Why
    
    First-party async traits should expose their `Send` contracts explicitly
    without requiring `async_trait`. This completes the migration pattern
    established in #27303 and #27304.
    
    ## What changed
    
    - Replaced the remaining first-party `async_trait` traits with native
    return-position `impl Future + Send` where statically dispatched and
    explicit boxed `Send` futures where object safety is required.
    - Kept implementations behavior-preserving, outlining existing async
    bodies into inherent methods where that keeps the diff reviewable.
    - Removed all direct first-party `async-trait` dependencies and the
    workspace dependency declaration.
    - Added a cargo-deny policy that permits `async-trait` only through the
    remaining transitive wrapper crates.
    - Updated `rand` from 0.8.5 to 0.8.6 to resolve RUSTSEC-2026-0097 and
    keep the full cargo-deny check passing.
    
    ## Validation
    
    - `just test -p codex-exec-server`: 216 passed, 2 skipped.
    - `just test -p codex-model-provider`: 39 passed.
    - `just test -p codex-core` and `just test`: changed tests passed;
    remaining failures are environment-sensitive suites unrelated to this
    migration.
    - `cargo deny check`
    - `just fix`
    - `just fmt`
    - `cargo shear`
    - `just bazel-lock-check`
  • Route image extension reads through turn environments v2 (#27498)
    ## Why
    
    Image generation used `std::fs::read` for referenced image paths, which
    did not support environment-backed filesystems or their sandbox context.
    
    ## What changed
    
    - Expose optional turn environments to extension tool calls.
    - Include each environment’s ID, working directory, filesystem, and
    sandbox context.
    - Read referenced images through the selected environment filesystem.
    - Keep sandbox usage at the extension call site so extensions can choose
    the appropriate access mode.
    - Consolidate image request construction into one async function.
    - Add coverage for successful environment reads and read failures.
    
    ## Validation
    
    - `cargo check -p codex-image-generation-extension --tests`
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    
    `just test -p codex-image-generation-extension` could not complete
    because the build exhausted available disk space.
  • Include thread id in token budget context (#27663)
    ## Why
    
    The token budget full-context fragment identifies the current context
    window, but not the thread that owns that window. Including the thread
    id makes the initial context-window metadata self-contained, and
    `get_context_remaining` also needs to be usable from Code Mode without
    forcing callers to parse the model-facing fragment string.
    
    ## What changed
    
    - Include the session thread id in the initial `<token_budget>` context
    fragment.
    - Expose `get_context_remaining` as a Code Mode nested tool while
    keeping `new_context` direct-model-only.
    - Keep direct model-facing `get_context_remaining` output as the
    existing `<token_budget>` text fragment.
    - Return only `tokens_left` from the Code Mode structured result for
    `get_context_remaining`.
    - Update token-budget integration tests and add Code Mode coverage for
    the structured result.
    
    ## Verification
    
    - `just test -p codex-core token_budget`
    - `just test -p codex-core
    code_mode_get_context_remaining_returns_structured_result`
    - `just test -p core_test_support redacted_text_mode_normalizes_uuids`
  • [codex] Load user instructions through an injected provider (#27101)
    ## Why
    
    We want to remove implicit use of `$CODEX_HOME` from `codex-core` and
    make embedders responsible for supplying user-level instructions. This
    also ensures user instructions load when no primary environment is
    selected.
    
    ## What changed
    
    Stacked on #27415, which makes `codex exec` surface thread-scoped
    runtime warnings.
    
    - Added `UserInstructionsProvider` to `codex-extension-api`, with
    absolute source attribution and recoverable loading warnings.
    - Added `codex-home` with the filesystem-backed provider for
    `AGENTS.override.md` and `AGENTS.md`, preserving precedence, fallback,
    trimming, lossy UTF-8 handling, and the existing uncapped global
    instruction size.
    - Removed global instruction loading from `Config` and require
    `ThreadManager` callers to inject a provider.
    - Load provider instructions once for each fresh root runtime, including
    runtimes without a primary environment. Running sessions retain their
    snapshot, while child agents inherit the parent snapshot without
    invoking the provider.
    - Keep provider instructions separate while loading project `AGENTS.md`,
    then assemble the model-visible instructions with the existing ordering,
    source attribution, warning, and turn-context behavior.
    - Wired the Codex home provider through the CLI, app server, MCP server,
    core facade, and thread-manager sample.
    
    ## Validation
    
    - `just test -p codex-home -p codex-extension-api`
    - `just test -p codex-core agents_md`
    - `just test -p codex-core guardian`
    - `just test -p codex-app-server
    thread_start_without_selected_environment_includes_only_global_instruction_source`
    - `just test -p codex-exec warning`
    - `just bazel-lock-check`
  • [codex] migrate ExecutorFileSystem paths to PathUri (#27424)
    ## Why
    
    We're moving exec-server to use PathUri for its internal path
    representations.
    
    ## What
    
    Move `ExecutorFileSystem` APIs to use `PathUri` instead of
    `AbsolutePathBuf`. Future changes will convert higher-level parts of
    exec-server.
  • multi-agent: move concurrency guidance into v2 usage hints (#27569)
    ## Why
    
    Native Codex currently teaches multi-agent concurrency through the
    `spawn_agent` tool description, while bridge-driven evals frame the same
    limit as a shared pool of active agent slots. That mismatch makes the
    model-facing story harder to reason about, especially because the
    tool-level wording does not make it explicit that the limit covers the
    whole agent team, including the current agent.
    
    This change gives native Codex the same mental model: tell the root
    agent and subagents how many active slots exist, and remove the separate
    `spawn_agent` limit wording.
    
    ## What changed
    
    - Extend the built-in `multi_agent_v2` root and subagent usage hints
    with shared-slot guidance derived from the resolved
    `max_concurrent_threads_per_session` value.
    - Keep the complete default hints in `MultiAgentV2Config` so initial
    context and forked histories consume the same canonical strings.
    - Drop the redundant `spawn_agent` description text and remove the
    now-unused limit plumbing from the tool spec path.
    
    ## Testing
    
    - `just test -p codex-core usage_hint`
    - `just test -p codex-core
    multi_agent_v2_default_session_thread_cap_counts_root`
    - `just test -p codex-core
    multi_agent_v2_default_usage_hints_use_configured_thread_cap`
    - `just test -p codex-core
    spawn_agent_tool_v2_requires_task_name_and_lists_visible_models`
    - `just test -p codex-core
    multi_agent_feature_selects_one_agent_tool_family`
  • Use generic search metadata for dynamic tools (#27356)
    ## Why
    
    Dynamic tools maintained a separate search-text builder even though the
    shared tool search path already derives the same metadata from
    `ToolSpec`. Using the shared path removes duplicate behavior before
    adding explicit namespaces.
    
    ## What changed
    
    - Build dynamic-tool search entries with
    `ToolSearchInfo::from_tool_spec`.
    - Remove the custom search-text state and its implementation-only unit
    test.
    
    The old search text included the tool name, its space-separated form,
    description, namespace, and top-level parameter names. The shared
    builder preserves all of those terms and also indexes namespace
    descriptions and nested schema metadata.
    
    ## Test plan
    
    - `just test -p codex-core
    tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call`
  • [codex] Add context remaining tool (#27518)
    ## Why
    
    The token budget feature can inject remaining-context notices into
    model-visible context, but the model does not have a direct way to ask
    for that same remaining-token fragment on demand.
    
    This PR adds a small model tool for the token budget feature so the
    model can request the current remaining context window message without
    duplicating the fragment format.
    
    ## What changed
    
    - Adds a `get_context_remaining` direct-model tool behind
    `Feature::TokenBudget`.
    - Renders the tool output through `TokenBudgetRemainingContext`,
    matching the existing budget message shape.
    - Registers the tool alongside `new_context` in the token budget tool
    set.
    - Adds integration coverage that verifies the tool is exposed and
    returns the same `<token_budget>` remaining fragment already present in
    context.
    
    ## Validation
    
    - `just test -p codex-core token_budget`
  • [codex] Add new context window tool (#27488)
    ## Why
    
    The token budget feature tells the model how much room remains in the
    current context window. When the model decides the current window is no
    longer useful, it needs a way to ask Codex to start over with a fresh
    context window without spending tokens on a compaction summary.
    
    This PR adds that model-requestable escape hatch on top of #27438.
    
    ## What changed
    
    - Added a direct-model-only `new_context` tool behind
    `Feature::TokenBudget`.
    - Stores the tool request on `AutoCompactWindow` and consumes it after
    sampling so the next follow-up request in the same turn starts in the
    new window.
    - Starts the new window as a no-summary compaction checkpoint that
    contains only fresh initial context, not preserved conversation history.
    - Keeps the new window aligned with token-budget startup context,
    including the `Current context window Z` message.
    - Added integration coverage and a snapshot showing the same-turn
    `new_context` flow into a fresh full-context follow-up request.
    
    ## Validation
    
    - `just test -p codex-core token_budget`
  • core: resize all history images behind a feature flag (#27247)
    ## Summary
    
    Adds complete client-side image preparation behind the default-off
    `resize_all_images` feature flag.
    
    When enabled, local image producers defer decoding and resizing. Images
    are prepared centrally before insertion into conversation history,
    covering user input, `view_image`, and structured tool-output images.
    
    ## Behavior
    
    - Processes base64 `data:` images in messages and function/custom tool
    outputs.
    - Leaves non-data URLs, including HTTP(S) URLs, unchanged.
    - Applies image-detail budgets:
      - `high` and omitted: 2048px maximum dimension and 2.5K 32px patches.
      - `original`: 6000px maximum dimension and 10K 32px patches.
      - `auto`: uses the same 2048px / 2.5K-patch budget as high.
      - `low`: unsupported and replaced with an actionable placeholder.
    - Preserves original image bytes when no resize or format conversion is
    needed.
    - Enforces the shared 1 GiB encoded and decoded data-URL sanity limits.
    - Replaces only an image that fails preparation, preserving sibling
    content and tool-output metadata.
    - Uses bounded placeholders distinguishing generic processing failures,
    oversized images, and unsupported `low` detail.
    - Prepares resumed and forked history before installing it as live
    history without modifying persisted rollouts.
    
    ## Flag-Off Behavior
    
    When `resize_all_images` is disabled:
    
    - Existing local user-input and `view_image` processing remains
    unchanged.
    - Existing decoding and error behavior remains unchanged.
    - Arbitrary tool-output images are not processed.
    - HTTP(S) image URLs continue to be forwarded unchanged.
    
    
    #### [git stack](https://github.com/magus/git-stack-cli)
    -  `1` https://github.com/openai/codex/pull/27245
    - 👉 `2` https://github.com/openai/codex/pull/27247
    -  `3` https://github.com/openai/codex/pull/27246
    -  `4` https://github.com/openai/codex/pull/27266
  • core: cache turn diff rendering (#27489)
    ## Summary
    
    Turn diff updates repeatedly rendered and serialized the entire
    accumulated diff after every `apply_patch`. The event path also rendered
    once before updating the tracker solely to test whether a diff existed.
    In production feedback CODEX-20PW, 2,589 patches across 72 paths
    produced 401 notifications totaling 441 MB, with the hottest paths
    patched 518 and 495 times.
    
    This change:
    
    - replaces the pre-update render with a cheap cached-state check
    - caches each rendered file diff by path and content revision, so an
    update only invokes Myers for affected paths
    - caches the deterministic aggregate diff so event emission and turn
    completion reuse it without recomputation
    - preserves invalidation and net-zero clear notifications
    - applies a 100 ms per-file `similar` timeout; ordinary files complete
    far below this threshold, while pathological rewrites fall back to a
    coarse unified hunk that still represents the exact final contents
    
    The 100 ms deadline bounds synchronous tool-completion latency while
    leaving substantial headroom for normal diffs. The regression test
    applies the fallback diff through the repository's patch parser and
    verifies byte-for-byte final contents.
    
    ## Validation
    
    - `cargo test -p codex-core turn_diff_tracker::tests` (14 passed)
    - `cargo test -p codex-core tools::events::tests` (4 passed)
    - `just fix -p codex-core`
    - `just fmt`
    
    Focused coverage verifies that 42 updates across two files perform 42
    file renders rather than repeatedly rendering the accumulated set,
    unchanged paths are not re-diffed, clear events remain correct, and a
    48,000-line near-total rewrite returns promptly and applies to the exact
    expected result. The full `codex-core` suite was not used as the final
    gate because an unrelated existing multi-agent test hit a stack overflow
    when run during investigation.
    
    ## Bug context
    
    - Sentry feedback: CODEX-20PW
    - Correlation IDs: `019eb2a9-13d2-74e0-b690-27ee224ffb6d`,
    `019e9ad7-09c3-7cb2-b728-ee3acba103ab`
  • [codex] Skip local curated discovery for remote plugins (#27311)
    ## Summary
    
    - skip the local `openai-curated` marketplace before marketplace loading
    when tool-suggest discovery uses remote plugins
    - preserve existing marketplace listing behavior for all other callers
    and when remote plugins are disabled
    - add regression coverage proving the curated marketplace is excluded
    before its malformed manifest can be read
    
    ## Why
    
    Tool-suggest discovery previously loaded every local `openai-curated`
    plugin manifest and only discarded that marketplace afterward when
    remote plugins were enabled. The remote catalog is used in that mode, so
    the local scan consumed CPU without contributing discoverable plugins.
    
    ## Impact
    
    Remote-plugin tool suggestion discovery no longer reads the local
    curated marketplace and its plugin manifests. `openai-bundled`,
    configured marketplaces, normal `plugin/list` behavior, and local
    curated discovery when remote plugins are disabled are unchanged.
    
    ## Validation
    
    - `just test -p codex-core-plugins
    list_marketplaces_can_skip_openai_curated_before_loading`
    - `just test -p codex-core
    list_tool_suggest_discoverable_plugins_omits_openai_curated_when_remote_enabled`
    - `just fmt`
    - `git diff --check`
  • [plugins] Inject remote_plugin_id into install elicitations (#26409)
    Summary
    - Propagate cached remote plugin IDs through Codex plugin discovery.
    - Inject `remote_plugin_id` and connector IDs into
    `request_plugin_install` elicitation `_meta` from the resolved plugin.
    - Keep the remote plugin ID out of the model-facing tool schema,
    arguments, and result.
    
    Validation
    - `just test -p codex-tools`
    - `just test -p codex-core-plugins`
    - `just test -p codex-core
    list_tool_suggest_discoverable_plugins_includes_cached_remote_global_plugins`
    - `just fix -p codex-tools`
    - `just fix -p codex-core-plugins`
    - `just fix -p codex-core`
    - `git diff --check`
    - `just test -p codex-core` was also attempted: 2,581 passed, 55 failed,
    and 1 timed out across unrelated sandbox/environment-sensitive
    integration tests.
  • [codex] Remove async_trait from ToolExecutor (#27304)
    ## Why
    
    We're now [discouraging use of
    `async_trait`](https://github.com/openai/codex/pull/20242).
    
    Removing use of `async_trait` from `ToolExecutor` yields a `codex_core`
    debug test build speedup of ~78% (from 227.5s to 50.3s) on my machine.
    
    Stacked on #27299, this PR applies the trait change after the handler
    bodies have been outlined.
    
    ## What
    
    Changed `ToolExecutor::handle` to return an explicit boxed
    `ToolExecutorFuture` instead of using `async_trait`.
    
    Updated ToolExecutor implementors to return `Box::pin(...)`, reexported
    the future alias through `codex-tools` and `codex-extension-api`, and
    removed `codex-tools` direct `async-trait` dependency.
  • [codex] Outline ToolExecutor handler bodies (#27299)
    ## Why
    
    We're now [discouraging use of
    `async_trait`](https://github.com/openai/codex/pull/20242).
    
    Removing use of `async_trait` from `ToolExecutor` yields a `codex_core`
    debug test build speedup of ~78% (from 227.5s to 50.3s) on my machine.
    
    For ease of reviewing, this is a prefactor to extract trait method
    implementations to inherent methods. This will prevent changing
    indentation from creating a huge diff.
    
    ## What
    
    Outlined existing `ToolExecutor::handle` bodies into inherent async
    `handle_call` methods across core and extension tool handlers.
    
    The trait methods still use `async_trait` and now delegate to
    `self.handle_call(...).await`; handler behavior is unchanged.