Commit Graph

7740 Commits

  • chore(core) rm AskForApproval::OnFailure (#28418)
    ## Summary
    Deletes the OnFailure variant of the `AskForApproval` enum. This option
    has been deprecated since #11631.
    
    ## Testing
    - [x] Tests pass
  • Prepare managed network sandbox context (#29456)
    ## Why
    
    Managed network configures commands to use local HTTP and SOCKS proxies.
    For commands delegated to the exec server, the proxy environment and the
    sandbox policy were prepared separately. On macOS, that meant a command
    could receive `HTTPS_PROXY=http://127.0.0.1:43123` while Seatbelt still
    denied access to port `43123`.
    
    ## What changed
    
    `NetworkProxy` now prepares the command environment and sandbox context
    together from the same runtime snapshot:
    
    ```text
    Prepared managed network
    ├── command environment: HTTPS_PROXY=http://127.0.0.1:43123
    └── sandbox context: allow outbound to 127.0.0.1:43123
    ```
    
    That context travels with remote exec requests. The exec server
    preserves the managed proxy and CA environment, and macOS Seatbelt
    allows only the prepared loopback proxy ports without enabling broad
    network access or local binding.
    
    The protocol field is optional and the existing enforcement flag remains
    in place, preserving compatibility with callers that do not send the new
    context.
  • app-server: document thread and turn IDs are UUID7 (#27714)
    It's actually a very nice property that these are UUID7s, so documenting
    them so we think twice before changing it away from UUID7s in the
    future.
  • Handle additional tools in rollout persistence metrics (#29669)
    ## Why
    
    The rollout persistence metrics added on current `main` exhaustively
    match `ResponseItem`, but omit `ResponseItem::AdditionalTools`. That
    prevents `codex-rollout` and downstream targets from compiling across
    Cargo and Bazel builds.
    
    ## What
    
    Map `ResponseItem::AdditionalTools` to the `response.additional_tools`
    metric label, consistent with the existing exact-variant labels.
    
    ## Validation
    
    - `just test -p codex-rollout` (76 passed)
    - `just fix -p codex-rollout`
  • [codex] Handle additional tools in rollout persistence metrics (#29672)
    ## Summary
    
    Handle `ResponseItem::AdditionalTools` in rollout persistence metrics.
    
    The persistence metrics match was added after the `AdditionalTools`
    variant and omitted it, causing release builds to fail with a
    non-exhaustive pattern error. This assigns the item the
    `response.additional_tools` metrics label.
    
    Release failure:
    https://github.com/openai/codex/actions/runs/28043786727/job/83016608475
    
    ## Validation
    
    - `just fmt`
    - `just test -p codex-rollout` (76 passed)
  • core: use turn-owned world state for inline compaction (#29527)
    ## Why
    
    Follow-up to #29249 and its [compaction review
    thread](https://github.com/openai/codex/pull/29249#discussion_r3455055101).
    
    During a turn, environment readiness can change between sampling
    requests. Inline compaction must render the same model-visible
    `WorldState` used by the request it follows. Rebuilding that state
    during compaction can observe a newer environment, make replacement
    history disagree with what the model saw, and suppress the next
    environment update.
    
    ## What changed
    
    - Make `run_turn` own the current `Arc<WorldState>` and replace it only
    between sampling requests.
    - Build each state from an explicitly chosen environment snapshot, diff
    deferred-executor steps against the turn-owned state, and retain the
    latest state in `ContextManager` only for cross-turn and resume
    tracking.
    - Pass the exact turn-owned state into inline compaction and explicit
    new-context-window replacement.
    - Carry that state with
    `InitialContextInjection::BeforeLastUserMessage`, so replacement context
    and its stored baseline cannot come from different snapshots.
    - Remove obsolete state-recapture helpers and ambiguous TurnContext-only
    WorldState builders.
    - Add an integration test that moves an environment from starting to
    ready during a paused turn, triggers compaction, and verifies the next
    request receives the readiness update exactly once.
    
    ## Test plan
    
    - `just test -p codex-core
    deferred_executor_compaction_preserves_then_updates_environment_once`
    - `just test -p codex-core process_compacted_history`
    - `just test -p codex-core mid_turn_continuation_compaction`
    - `just test -p codex-core build_initial_context`
    - `just test -p codex-core
    ignores_session_prefix_messages_when_truncating`
  • Shut down superseded MCP managers on refresh (#29608)
    ## Summary
    
    MCP refresh replaced the published connection manager without shutting
    down the manager it superseded. If another task retained that old
    manager, its stdio MCP processes stayed alive and accumulated across
    refreshes.
    
    Atomically swap in the refreshed manager, then explicitly shut down the
    exact manager returned by the swap. Add a process-level regression test
    that retains the old manager during refresh and verifies its stdio
    process exits while the replacement remains available.
    
    ## Context
    
    Explicit cleanup was lost when manager publication moved to `ArcSwap`.
    Dropping the old manager is not a reliable shutdown boundary because
    active callers can retain its `Arc` and underlying client process
    handles.
  • [core] debounce current-time reminders by elapsed time (#29659)
    ## Summary
    - rename `reminder_interval_model_requests` to
    `reminder_interval_seconds`
    - read the configured time provider before every model request and
    inject a reminder only after the configured number of seconds has
    elapsed
    - preserve immediate first delivery and forced delivery after compaction
    changes the context window
    
    ## Tests
    - `just test -p codex-core current_time_reminder`
  • [codex] Instrument rollout persistence bytes (#29498)
    - Add 1%-sampled rollout persistence metrics that report per-item and
    per-thread JSON byte totals before and after filtering when metrics
    export is enabled.
    - Tag each item with its exact response or event variant, including
    nested turn-item kinds for conditionally persisted completion events, so
    aggregate cloud-storage impact can be estimated by policy choice.
  • Update vulnerable Hono and fast-uri dependencies (#29650)
    ## Summary
    
    - Pin `hono` to 4.12.25, the first patched release for the recent Hono
    security advisories.
    - Pin `fast-uri` to 3.1.1 to fix the percent-encoded path traversal
    vulnerability.
    - Refresh `pnpm-lock.yaml` with only those dependency updates.
    
    `hono` 4.12.25 is used instead of the newer 4.12.27 because the
    repository requires dependencies to be at least seven days old.
  • Update rmcp to 1.8.0 (#29634)
    ## Summary
    
    - Update `rmcp` and `rmcp-macros` from 1.7.0 to 1.8.0.
    - Adapt to the new shared `peer_info` return type.
    - Box OAuth status discovery at the MCP boundary to keep the expanded
    future type from overflowing Rust's trait recursion limit.
    
    This brings in custom OAuth HTTP client support from
    [modelcontextprotocol/rust-sdk#908](https://github.com/modelcontextprotocol/rust-sdk/pull/908).
  • Share resumed rollout history (#28426)
    ## Summary
    
    Resuming a persisted thread currently deep-clones its complete rollout
    history several times. `InitialHistory` is retained for the app-server
    response, copied into thread persistence, and copied again by read-only
    accessors. These copies scale with the complete rollout rather than the
    bounded model context and add measurable latency for large sessions.
    
    This change stores resumed rollout history in `Arc<Vec<RolloutItem>>`.
    Rollout loading wraps the parsed vector once, while app-server response
    construction, session initialization, and thread persistence share it
    through inexpensive `Arc` clones. Read-only history access now returns a
    borrowed slice, and fork paths use `Arc::unwrap_or_clone` where they
    genuinely need mutable ownership. Rollout reconstruction also consumes
    its temporary context instead of cloning the reconstructed model
    history.
    
    The serialized representation remains unchanged. In an artificial 123 MB
    rollout benchmark, sharing resumed history reduced cold resume latency
    by roughly 9–10%. The affected crates compile with their test targets,
    all 80 thread-store tests pass, and the Bazel dependency lock remains
    valid.
  • path-uri: add lexical containment (#29614)
    ## Why
    
    Executor-owned paths must stay portable while the orchestrator reasons
    about them. Converting a Windows or remote path to the orchestrator
    host's native path just to check containment breaks that boundary.
    
    ## What changed
    
    - Add lexical containment to `PathUri`.
    - Compare URI authorities and complete path segments, so `plugin-other`
    is not treated as a child of `plugin`.
    - Fail closed for encoded path separators and opaque fallback URIs.
    
    For example:
    
    ```text
    file:///C:/plugins/foo/assets/icon.svg
      is below file:///C:/plugins/foo
    
    file:///C:/plugins/foo2/icon.svg
      is not below file:///C:/plugins/foo
    ```
    
    This is the shared foundation for keeping executor-owned plugin
    resources URI-native without consulting the orchestrator filesystem.
  • Namespace multi-agent v2 tools under collaboration (#29067)
    ## Summary
    
    Multi-agent v2 tools now use the fixed `collaboration` namespace when
    namespace tools are available. This keeps the model-visible hint and the
    actual tool surface aligned around `functions.collaboration.*`, without
    exposing an unshipped namespace knob to users.
    
    The PR also removes the old `features.multi_agent_v2.tool_namespace`
    config/schema surface, updates the MAv2 test fixtures for namespaced
    calls, and fixes stale `TurnContext.features` references that were
    breaking `codex-core` builds.
    
    ## Changes
    
    - Expose MAv2 tools under `collaboration` instead of relying on a
    configurable namespace.
    - Remove `tool_namespace` from MAv2 TOML config, resolved config,
    validation, schema, and tests.
    - Update tool-planning and integration fixtures to assert or emit
    namespaced MAv2 tool calls.
    - Read feature state through `TurnContext.config.features` in the
    multi-agent mode context paths.
    
    ## Testing
    
    - `just write-config-schema`
    - `just test -p codex-features`
  • Fix Codex Apps auth elicitation hang (#29615)
    ## Summary
    - Require the reserved Codex Apps MCP server name to be present in the
    connection manager before treating it as host-owned.
    - Update auth elicitation tests to model an installed host-owned Codex
    Apps server without sending startup events to the test session.
    
    ## Why
    PR #29518 replaced the old host-owned flag with a name-only check. That
    made non-host-owned tests with the reserved codex_apps name enter auth
    elicitation and wait forever for a response.
  • Stop persisting bridged log events (#29599)
    ## Why
    
    The 0.142.0 persistent-log filter disables target=log, but bridged log
    records are filtered using their original dependency target before
    tracing-log emits them as target=log. This allowed high-volume
    dependency TRACE events to keep reaching SQLite.
    
    This is a follow-up to #28224.
    
    ## What changed
    
    - Reject bridged target=log events inside the SQLite sink before
    formatting or queueing them.
  • Allow codex sandbox to consume MCP sandbox state (#29358)
    ## Summary
    
    - let `codex sandbox` accept the JSON value from
    `codex/sandbox-state-meta`
    - require the payload `permissionProfile` instead of falling back to
    ambient permissions
    - reuse the existing macOS, Linux, and Windows launch paths, treating
    external sandbox state conservatively as read-only
    - let opaque forwarders add runtime read roots and disable direct
    network access without decoding the payload
    
    Builds on #29113, which is now on `main`.
    
    ## Tests
    
    - `just test -p codex-cli debug_sandbox::tests`
    - `cargo build -p codex-rmcp-client --bin test_stdio_server`
    - `just test -p codex-core
    stdio_mcp_tool_call_includes_sandbox_state_meta`
    - `just test -p codex-mcp`
    - `just fmt`
  • Group Codex Apps client setup (#29583)
    ## Why
    
    `McpConnectionManager::new` classified the Codex Apps server twice: once
    to create its tools cache context and again to select its runtime
    authentication provider. Keeping those decisions separate makes it
    harder to see that they belong to the same server-specific setup path.
    
    ## What changed
    
    - Group Codex Apps cache and authentication setup under one explicit
    branch.
    - Keep regular MCP server setup in the corresponding `else` branch.
    - Limit environment bearer-token inspection to the Codex Apps path where
    it affects runtime authentication.
  • Remove redundant Codex Apps cache guard (#29575)
    ## Why
    
    Codex Apps cache writes are already restricted to Codex Apps call paths:
    startup invokes the helper only from the Codex Apps branch, and hard
    refresh operates on the reserved Codex Apps server directly. Rechecking
    the server name inside the cache helper duplicates that classification
    and leaves the helper with an argument that cannot change valid
    behavior.
    
    ## What changed
    
    - Remove the redundant server-name check and parameter from the cache
    writer.
    - Rename the helper to `write_codex_apps_tools_cache` to reflect its
    narrower contract.
    - Update production and test callsites to use the simplified API.
  • Handle additional tools in image URL validation (#29577)
    ## Why
    
    `ResponseItem::AdditionalTools` was added without updating app-server
    image URL validation. The exhaustive match therefore prevents app-server
    and downstream targets from compiling on `main`.
    
    ## What changed
    
    Treat `AdditionalTools` like the other response items that cannot
    contain input-image URLs.
  • Centralize Codex Apps client handling (#29528)
    ## Why
    
    Codex Apps-specific behavior is currently distributed across cache
    helpers, startup, tool conversion, and model-visible annotation. Each
    layer independently checks the reserved server name, which obscures the
    boundary between trusted host-owned connector metadata and regular MCP
    server data.
    
    Classifying the server once when `AsyncManagedClient` is created gives
    the client a single source of truth and makes the two processing paths
    explicit.
    
    ## What changed
    
    - Record whether an `AsyncManagedClient` represents the Codex Apps
    server at construction time.
    - Route startup cache loading, cache persistence, and cache telemetry
    through the Codex Apps branch.
    - Split uncached tool conversion between Codex Apps normalization and
    regular MCP metadata sanitization.
    - Split model-visible schema and plugin provenance handling along the
    same boundary.
    - Remove redundant server-name guards from helpers that are now called
    only from the Codex Apps branch.
    
    ## Verification
    
    - Preserve behavioral coverage that verifies Codex Apps connector
    metadata and the complete converted `ToolInfo` shape.
    
    ## Stack
    
    Depends on #29518.
  • [codex] Use input items for Responses Lite tools (#27946)
    When using Responses Lite, we should all use `additional_tools` and a
    developer item instead of the top level tools array & instructions
    field. This keeps things 1-to-1.
    
    Forced namespacing for _all_ tools will land in a following PR after
    some coordination & fixes in Responses API (around collisions & return
    items).
    
    The goal is to eventually expand the scope of this to _all_ requests
    from codex, but that will require larger coordination across providers &
    slower rollout.
  • Remove redundant Codex Apps manager flag (#29518)
    ## Why
    
    Codex Apps server admission is already decided before
    `McpConnectionManager` is constructed. `effective_mcp_servers` and
    `effective_mcp_servers_from_configured` remove the server when the apps
    feature or required authentication is unavailable, so storing the same
    decision on the manager duplicates state that can drift from the
    effective server map.
    
    ## What changed
    
    - Remove `host_owned_codex_apps_enabled` from `McpConnectionManager` and
    its constructor.
    - Identify the host-owned Codex Apps server by its reserved server name
    once it is present in the effective server map.
    - Remove the now-unused flag calculations and constructor arguments from
    production and test callsites.
  • [codex] stylistic changes (#29068)
    ## Summary
    
    - express remote compaction result handling as an exhaustive match
    - preserve the special `TurnAborted` path without emitting a generic
    compaction error
    - rely on the standard `test_codex` provider setup in the compaction
    budget test
    
    Follow-up to review feedback on #28707.
    
    ## Testing
    
    - `just test -p codex-core
    compaction_budget_exhaustion_aborts_without_error_or_retry`
    - `just fmt`
  • [codex] Expose service tier and reasoning effort in OTEL (#29155)
    ## Summary
    
    NVIDIA asked to measure Fast mode usage and reasoning effort from Codex
    CLI OTEL logs. Add the finalized `service_tier` and
    `model_reasoning_effort` to the existing `codex.sse_event`
    `response.completed` record.
    
    This intentionally reuses the existing completion event and leaves
    transport APIs and shared telemetry plumbing unchanged.
    
    ## Testing
    
    - `cargo build -p codex-cli --bin codex`
    - `just test -p codex-core responses_api_emits_api_request_event`
    - End-to-end with the built CLI and a local OTLP/HTTP collector:
    - Fast/high emitted `service_tier=priority` and
    `model_reasoning_effort=high` with token usage.
    - Standard/low omitted `service_tier` and emitted
    `model_reasoning_effort=low` with token usage.
  • [codex] Reject unlowered PowerShell AST regions (#24092)
    ## Why
    
    On Windows, Codex uses a PowerShell safe-command classifier to decide
    whether a command is read-only enough to run without additional
    approval. The classifier lowers `EndBlock.Statements` into argv-like
    command words and checks those words against a safelist.
    
    PowerShell can execute code stored elsewhere in the AST. Parameter
    defaults, named blocks, `using` preambles, and top-level `trap` handlers
    are not represented in the lowered statement list. Ignoring those
    regions can make a side-effecting script look like a read-only command.
    
    ## What
    
    Fail closed whenever a PowerShell script contains executable AST content
    that the current lowering does not represent.
    
    ## How
    
    - Return `unsupported` for parameter, dynamic-parameter, begin, process,
    and clean blocks.
    - Return `unsupported` for `using module` and `using assembly`
    preambles.
    - Return `unsupported` for non-empty `EndBlock.Traps` collections.
    - Preserve compatibility with Windows PowerShell 5.1 by looking up
    `CleanBlock` dynamically.
    - Treat `unsupported` as a failure to prove that the command is safe,
    routing it through the normal approval path.
    - Add parser-level and end-to-end regressions for parameter blocks,
    named blocks, using statements, and trap handlers.
    
    This does not make these PowerShell forms invalid or prevent them from
    running. It prevents automatic safe-command approval when the classifier
    cannot account for all executable behavior.
    
    ## Testing
    
    - `just test -p codex-shell-command`
    - Windows CI exercises the parser and end-to-end safe-command
    regressions against a real PowerShell installation.
    
    ---------
    
    Co-authored-by: viyatb-oai <viyatb@openai.com>
  • Propagate safety buffering treatment metadata (#29473)
    ## Summary
    
    - read the request-scoped safety-buffering treatment from HTTP response
    headers and per-turn WebSocket metadata through one shared header parser
    - combine that treatment with Responses API safety-buffering signals
    - propagate `showBufferingUi` and nullable `fasterModel` through the
    existing `model/safetyBuffering/updated` app-server notification
    - update the app-server documentation and generated JSON and TypeScript
    schemas
    
    The public implementation contains no model mapping or real model
    identifier. Tests and protocol examples use generic `current-model` and
    `faster-model` placeholders only.
    
    ## Dependencies
    
    - server-side treatment evaluation:
    https://github.com/openai/openai/pull/1060247
    - initial Responses API safety-buffering propagation:
    https://github.com/openai/codex/pull/29371
    - Codex App UI: https://github.com/openai/openai/pull/1057789
    
    ## Validation
    
    - Codex API tests: 129 passed
    - focused Codex core safety-buffering integration test passed
    - app-server protocol tests passed after regenerating schema fixtures
    - Clippy fix and repository formatting completed successfully
    
    The broader app-server run compiled all changed crates and completed
    with 1,269 passing tests. Its remaining failures were unrelated
    environment limitations: macOS sandbox application was denied, one
    expected test binary was unavailable, and several existing subprocess
    tests timed out as a result.
  • mcp: accept foreign absolute cwd for remote stdio (#29493)
    ## Why
    
    Remote stdio MCP servers can run in an environment whose path convention
    differs from the Codex host. A Windows cwd such as
    `C:\Users\openai\share` is absolute for the executor but was rejected by
    a POSIX orchestrator.
    
    Built on #29501, now merged, which only clarifies the host-native
    `PathUri` constructor name.
    
    ## What changed
    
    - Deserialize MCP cwd values as `LegacyAppPathString` so config does not
    apply host path rules.
    - Interpret that spelling as host-native for local launches and convert
    it to `PathUri` at executor launch.
    - Skip host filesystem and command resolution checks for remote stdio in
    `codex doctor`.
    - Add host-independent config and executor-boundary coverage using the
    foreign path convention for each test platform.
    
    ## Validation
    
    - `just test -p codex-utils-path-uri -p codex-config -p codex-mcp -p
    codex-rmcp-client` (408 passed)
    - `just test -p codex-cli -p codex-rmcp-client` (372 passed)
    - `cargo check --workspace --tests`
    - `just test` (11,311 passed; 43 unrelated environment/timing failures)
    - `just fix -p codex-cli -p codex-config -p codex-core -p codex-mcp -p
    codex-mcp-extension -p codex-rmcp-client -p codex-tui`
  • PAC 4 - Add macOS system proxy resolver (#26709)
    ## Summary
    
    Stacked on #26708.
    
    Adds the macOS implementation of the shared system-proxy contract. This
    allows Codex-owned auth clients to use the route macOS selects for each
    auth URL through SystemConfiguration and CFNetwork, including PAC and
    WPAD results.
    
    The `respect_system_proxy` feature is disabled by default, so existing
    client behavior remains unchanged unless explicitly enabled.
    
    ## Implementation
    
    - Adds the macOS-only `system-configuration` dependency to
    `codex-client`.
    - Dispatches system-proxy resolution to `outbound_proxy/macos.rs` on
    macOS.
    - Reads system proxy settings from `SCDynamicStore` and resolves the
    target URL with `CFNetworkCopyProxiesForURL`.
    - Executes PAC URLs and inline PAC JavaScript through a bounded run loop
    with a five-second timeout.
    - Handles `DIRECT`, HTTP proxies, and CFNetwork HTTPS entries using HTTP
    CONNECT; unsupported SOCKS entries map to `UnsupportedProxyScheme`.
    - Builds concrete proxy URLs from host and port entries, including IPv6
    host bracketing.
    - Maps results into the shared `SystemProxyDecision::{Direct, Proxy,
    Unavailable}` contract.
    - Hashes URL-specific cache keys so PAC decisions remain distinct
    without retaining raw request URLs or query strings.
    
    ## End-user behavior
    
    - Disabled/default: existing client behavior is unchanged.
    - Enabled with `[features.respect_system_proxy]`:
      - macOS auth clients honor system proxy configuration, PAC, and WPAD;
      - valid OS/PAC `DIRECT` decisions use a direct connection;
    - unavailable system resolution falls back to explicit environment proxy
    variables, then `DIRECT`, through the shared contract from #26707.
    - Unsupported proxy schemes are not silently translated into another
    route.
    - Custom CA handling remains separate from proxy selection.
    - Known limitation: only the first supported system/PAC candidate is
    used. Subsequent proxy or `DIRECT` candidates are not attempted after a
    connection failure. This matches the current Windows behavior and leaves
    room for future ordered-fallback support.
    
    ## Tests
    
    - `just test -p codex-client` — 34 tests passed.
    - `just clippy -p codex-client`
    - `just fmt`
    - `just bazel-lock-check`
  • Make formatter output quiet on success (#29467)
    ## Why
    
    `just fmt` is quite noisy even on successful runs.
    
    ## What
    
    Only print output when a formatter fails.
    
    - Buffer output from each formatter and print only a failed command and
    its diagnostics.
    - Prefix the `justfile` driver invocations with `@` so Just does not
    echo the command itself.
    - Retain rustfmt stderr on failure and cover silent-success and
    failure-reporting behavior.
    
    ## Validation
    
    - Confirmed `just fmt` and `just fmt-check` both exit successfully with
    empty stdout and stderr.
  • chore: improve expired Bedrock credential errors (#28992)
    ## Why
    
    Amazon Bedrock returns a `401 Unauthorized` response containing
    `Signature expired:` when an AWS credential, including a short-lived
    `AWS_BEARER_TOKEN_BEDROCK`, has expired. Codex currently surfaces that
    response as a generic `unexpected status` error, which does not explain
    how to recover.
    
    Environment-provided bearer tokens cannot be refreshed automatically, so
    the error should direct users to refresh their AWS credentials or
    replace or remove the environment token and restart Codex. This
    classification belongs to the Amazon Bedrock provider so similar
    responses from other providers retain their existing behavior.
    
    ## What changed
    
    - Add a synchronous `ModelProvider::map_api_error` hook that defaults to
    the existing provider-neutral API error mapping, and route model
    request, stream, WebSocket, and terminal unauthorized errors through the
    active provider.
    - Override the hook for Amazon Bedrock. After preserving the structured
    status, body, URL, and request metadata, recognize `401` responses
    containing `Signature expired:` and attach actionable credential
    guidance.
    - Keep `codex-protocol` provider-neutral by representing the guidance as
    an optional `user_message`. Error rendering prefers this message while
    continuing to append the URL, request ID, Cloudflare ray, and
    authorization diagnostics.
    - Add model-provider coverage for expired signatures and negative cases,
    core coverage for provider dispatch after unauthorized recovery, and a
    TUI snapshot for the rendered error.
    
    ## Testing
    Tested with a real request with expired bedrock key:
    <img width="962" height="126" alt="Screenshot 2026-06-22 at 3 56 51 PM"
    src="https://github.com/user-attachments/assets/7e21cc7c-798e-4662-8467-7f304a2f2b59"
    />
  • [codex] reject remote images at app-server ingress (#29419)
    ## Stack
    
    Stacked on #29417. Review and land that PR first.
    
    ## Summary
    
    - reject HTTP(S) image URLs in the handlers for `turn/start` and
    `turn/steer`
    - validate `thread/inject_items` after its existing
    JSON-to-`ResponseItem` conversion, so each item is deserialized once
    - turn invalid dynamic-tool image responses into the existing
    unsuccessful text fallback; the model receives the validation message as
    the function output
    - leave `thread/resume.history` compatible with legacy history; #29417
    replaces remote images before model input
    - continue accepting inline data URLs and `localImage` inputs
    - keep this policy in app-server; this PR does not add a shared protocol
    API or change core image preparation
    
    ## Test plan
    
    - `just test -p codex-app-server -E
    'test(/request_handlers_reject_remote_image_urls|dynamic_tool_remote_image_response_becomes_model_visible_error|dynamic_tool_call_round_trip_sends_content_items_to_model|turn_start_tracks_turn_event_analytics|standalone_image_edit_uses_recent_pathless_image/)'`
    (5 passed)
    - `just fix -p codex-app-server`
    - `just fmt`
  • TUI Plugin Sharing 4 - cover remote plugin catalog flows (#26704)
    Remote plugin catalogs now span workspace, shared, and local sources, so
    their TUI behavior needs focused regression coverage across loading,
    navigation, actions, and refreshes.
    
    This PR:
    
    - Covers product labels and rendered loading/error states for Workspace,
    Shared with me, Shared with me (link), and Local tabs, including tab
    persistence across refresh and detail navigation.
    - Covers remote/local deduplication, Installed-tab remote detail
    routing, marketplace load-error handling, and disabled install/uninstall
    navigation.
    - Adds a full detail snapshot for local shared-plugin metadata plus
    focused snapshots for marketplace labels and admin-disabled status.
    - Verifies shared plugins remain eligible for mentions and successful
    uninstalls trigger a catalog refresh.
  • fix: world state response item test (#29504)
    seems to be a merge conflict on main:
    
    > pakrym-oai introduced the stale initializer in commit 3b32d861c5, PR
    #29249.
    > Context: Owen Lin renamed metadata to
    internal_chat_message_metadata_passthrough in PR #28968. PR #29249 then
    landed afterward with the old field name, causing the compile/Clippy
    failure.
  • path-uri: clarify host-native path conversion (#29501)
    ## Why
    
    Downstream refactors are producing confusing code with this
    functionality having a very generic name. Encoding the specific
    conversion approach in the method name makes it clearer.
    
    ## What
    
    Rename `PathUri::from_path` to `PathUri::from_host_native_path` and
    update its Rust call sites.
  • [codex] Use tool search for MCP tools by default (#29486)
    ## Why
    
    MCP tools were only placed behind `tool_search` when a feature flag was
    enabled or when there were at least 100 tools. That made the model's
    tool flow depend on both rollout configuration and the number of
    installed tools.
    
    The searched-tool flow is now the intended behavior. Making it
    unconditional when the model and provider support it gives every
    supported setup the same behavior and lets us retire the feature flag
    safely.
    
    ## What changed
    
    - Defer all effective MCP tools when `tool_search` and namespaced tools
    are supported.
    - Keep exposing MCP tools directly when search cannot be used, so older
    or unsupported model/provider combinations still work.
    - Mark `tool_search_always_defer_mcp_tools` as removed and ignore old
    configured values.
    - Keep plugin filtering, app-only filtering, file handling, and MCP
    calls working through the searched-tool flow.
    
    ## Why many tests changed
    
    Many tests used to act as if the model could see MCP tools in its first
    request and call them immediately. That is no longer the real flow: the
    model first receives `tool_search`, searches for a tool, receives the
    matching MCP tool, and then calls it in the next request.
    
    The tests therefore needed an extra search step, and checks for tool
    names, descriptions, and input fields had to move from the first request
    to the search result. These are not separate product changes; they make
    the tests follow what the model will actually see after this change. The
    plugin tests still check which tools are allowed and where they came
    from, the file tests still check upload fields and behavior, and the MCP
    round-trip test still checks a successful call from start to finish.
    
    ## Tests
    
    - `just test -p codex-features`
    - Focused `codex-core` tests for MCP exposure and tool planning
    - `just test -p codex-core explicit_plugin_mentions`
    - `just test -p codex-core stdio_server_round_trip`
    - Focused `codex-core` tests for tool search, app-only tools, and MCP
    file uploads
  • feat(core): store turn_id on ResponseItem metadata (#28360)
    ## Description
    
    This PR is a followup to https://github.com/openai/codex/pull/28355 and
    starts assigning `internal_chat_message_metadata_passthrough.turn_id` to
    durable Responses API items created during a turn.
    
    The goal is that those items keep the `turn_id` that introduced them
    when Codex resends stateless HTTP context, reconstructs history for
    resume/fork paths, or reuses websocket response state.
    
    ## What changed
    
    - Set `internal_chat_message_metadata_passthrough.turn_id` when missing
    as response items enter durable history, initial/replacement history,
    inter-agent communication history, and local compaction summaries.
    - Preserve existing item turn IDs instead of overwriting them during
    persistence, resume reconstruction, compaction, forked history, and
    websocket incremental reuse.
    - Keep `compaction_trigger` fieldless because it is a request control,
    not a durable response item.
    - Update focused history/request assertions and fixtures for stateless
    requests, websocket incrementals, compaction, thread injection, prompt
    debug, and related CI coverage.
  • [codex] replace remote images with model-visible error text (#29417)
    ## What
    
    This PR will extend the existing centralized image-preparation path to
    replace HTTP(S) image inputs with a model visible error message. It
    won't "ruin" and break existing rollouts, but it will deprecate support
    for the pathway. App server clients should no longer use HTTP image urls
    if they'd like to upgrade.
    
    The HTTP image url pathway is currently resolved in the responsesapi. It
    is slow and not reccomended.
    
    ## Behavior
    
    - HTTP(S) image URL: replace with `input_text`
    - data URL: use the existing decode and resize path
    - other image URL schemes: leave unchanged
    
    This intentionally does not change app-server ingress. That validation
    remains a follow-up.
    
    ## Test plan
    
    - `just test -p codex-core -E
    'test(/image_preparation|prepares_image_failures_before_history_insertion|prepares_resumed_history_before_installing_it|responses_lite_prepares_images/)'`
    — 7 passed
    - `just fix -p codex-core`
    - `just fmt`
  • core: wrap token budget window context (#29494)
    Token-budget initial context carries thread and context-window lineage
    that the model should treat as one structured context-window block.
    Wrapping it in `<context_window>` makes that boundary explicit while
    preserving the existing window id content.
    
    Before this change, the window identifiers were injected as an untagged
    developer text fragment:
    
    ```text
    Thread id <THREAD_ID>.
    First context window id: <FIRST_WINDOW_ID>
    Current context window id: <WINDOW_ID>
    Previous context window id: <PREVIOUS_WINDOW_ID>
    ```
    
    After this change, the same payload is wrapped as a context-window
    block:
    
    ```text
    <context_window>
    Thread id: <THREAD_ID>
    First context window id: <FIRST_WINDOW_ID>
    Current context window id: <WINDOW_ID>
    Previous context window id: <PREVIOUS_WINDOW_ID>
    </context_window>
    ```
    
    This adds shared `CONTEXT_WINDOW_*_TAG` protocol constants, updates
    `TokenBudgetContext` to render with those markers, treats the new
    wrapper as contextual developer content when mapping history, and
    refreshes the token-budget request-shape assertions and snapshot.
    
    Verification:
    - `just test -p codex-core token_budget`
    - `just test -p codex-core
    recognizes_context_window_as_contextual_developer_content`
  • [codex] migrate environment context to model world state (#29249)
    ## Why
    
    Environment context is model-visible state, but it is currently
    assembled from transient turn values and diffed through
    environment-specific paths. That makes initial injection, turn-to-turn
    updates, and changes that happen within a turn use different baselines.
    
    This PR introduces the smallest useful model world-state slice:
    environments only, with one in-memory baseline and one renderer for full
    state and diffs.
    
    ## What changed
    
    - Add a typed `WorldState` container whose sections render fragments
    relative to an optional previous value. Full rendering uses the same
    diff path with no previous state.
    - Replace the parallel `EnvironmentContext` representation with an
    `EnvironmentsState` section keyed by environment ID and rendered in
    deterministic order.
    - Preserve the legacy single-environment output while supporting
    multiple environments, starting environments, unavailable tombstones,
    and changes to persisted turn-context values.
    - Store the latest complete `WorldState` on `ContextManager` and use it
    for both turn-boundary and mid-turn environment diffs.
    - Build initial and post-compaction context from the same world-state
    builder, then retain the rendered state as the next baseline.
    - Seed the in-memory baseline from the latest `TurnContextItem` when
    resuming an existing rollout; the world state itself is not serialized.
    - Keep non-world settings updates on their existing path and merge
    rendered world-state fragments at the session consumer.
    
    ## Known limitation
    
    A legacy `TurnContextItem` only reconstructs the primary environment as
    `local`; it cannot faithfully recover a remote-primary environment ID
    after resume. Live state uses the exact environment IDs once a complete
    baseline is established.
    
    ## Test plan
    
    - `just test -p codex-core world_state`
    - `just test -p codex-core record_context_updates`
    - `just test -p codex-core deferred_executor_`
    - `just test -p codex-core build_initial_context`
    - `just test -p codex-core rollout_reconstruction`
    - `just test -p codex-core
    process_compacted_history_reinjects_full_initial_context`
  • [plugins] Add dark-mode logo metadata (#29488)
    Adds additive dark-mode plugin logo metadata across manifests, remote
    catalogs, and the app-server protocol while keeping uninstalled Git
    listings free of synthetic local paths.
    
    Supersedes #28945. This replacement uses an upstream branch so trusted
    CI can use the repository-provided remote Bazel configuration.
    
    ## Current state
    
    Plugin interfaces expose only the default logo asset. Clients therefore
    cannot select a dedicated dark-mode logo even when a plugin provides
    one.
    
    ## What this PR changes
    
    - Adds nullable `logoDark` and `logoUrlDark` fields to
    `PluginInterface`.
    - Resolves local `interface.logoDark` assets and maps remote
    `logo_url_dark` values.
    - Removes path-backed interface assets, including `logoDark`, from
    uninstalled Git fallback listings until the plugin has a real local
    root.
    - Updates the bundled plugin validator and manifest reference.
    - Regenerates the app-server JSON schemas and TypeScript types.
    
    Local manifests expose `interface.logoDark` as a package-relative asset
    path. Remote catalog responses expose `logo_url_dark`. These values map
    into separate app-server fields so clients can preserve local-path and
    remote-URL handling.
    
    ## Risk
    
    The fields are additive and nullable, so existing clients retain their
    current logo behavior. The main risks are an incomplete mapping path or
    exposing a synthetic local path for an uninstalled Git plugin.
    Local-manifest, remote-catalog, fallback-listing, protocol
    serialization, and app-server integration tests cover those paths.
    
    Spiciness: 2/5
    
    ## Testing
    
    - `just write-app-server-schema`
    - `just fmt`
    - Regression test first failed with `logo_dark` resolved to
    `/assets/logo-dark.png`, then passed after the fallback-listing fix.
    - `just test -p codex-core-plugins` (267 tests passed)
    - `just test -p codex-app-server 'suite::v2::plugin'` (114 tests passed)
    - `just test -p codex-app-server-protocol -p codex-core-plugins -p
    codex-plugin -p codex-skills` (517 tests passed before the follow-up)
    - `just test -p codex-tui plugin` (47 tests passed)
    - Validated a local plugin manifest containing `interface.logoDark` with
    the bundled validator.
    
    ## Manual verification
    
    Create a local plugin with both `interface.logo` and
    `interface.logoDark`, then call `plugin/list` or `plugin/read`. Confirm
    the response contains separate `logo` and `logoDark` paths. For a remote
    catalog entry, confirm `logoUrlDark` is populated from `logo_url_dark`.
    For an uninstalled Git marketplace entry, confirm path-backed interface
    assets remain absent until installation.
    
    Issue: N/A - coordinated maintainer change.
  • [codex] Update esbuild to 0.28.1 (#29489)
    ## Why
    
    The TypeScript workspace resolved `esbuild` 0.25.10 transitively through
    the SDK toolchain. `esbuild` 0.28.1 adds integrity verification to the
    Deno binary download path addressed by
    [GHSA-gv7w-rqvm-qjhr](https://github.com/evanw/esbuild/security/advisories/GHSA-gv7w-rqvm-qjhr),
    preventing an attacker-controlled npm registry from supplying an
    executable without a content check.
    
    ## What changed
    
    - Add a root workspace resolution for `esbuild` 0.28.1.
    - Regenerate `pnpm-lock.yaml` so `tsup`, `bundle-require`, and `ts-jest`
    all resolve the patched version.
    
    ## Validation
    
    - Frozen pnpm install, including the SDK's `tsup` build
    - `pnpm --filter @openai/codex-sdk exec jest tests/exec.test.ts
    --runInBand`
    - Confirmed the installed dependency graph contains only `esbuild`
    0.28.1
  • Upgrade bundled OpenSSL to 3.6.3 (#29487)
    ## Summary
    
    - upgrade the bundled OpenSSL source from 3.5.5 to 3.6.3
    - update the Bazel `openssl-sys` build dependency to use the upgraded
    source crate
    - refresh the Bazel module lockfile
    
    ## Why
    
    OpenSSL 3.5.5 is within the affected ranges for security issues fixed in
    later releases. The Rust `openssl-src` wrapper does not currently
    publish OpenSSL 3.5.7, so this moves the vendored Linux musl build to
    the available patched 3.6.3 release.
  • [codex] fetch featured IDs for remote plugins (#29485)
    ## Summary
    
    - fetch featured plugin IDs when the loaded catalog includes
    `openai-curated-remote`
    - extend the existing remote marketplace regression test to cover the
    featured IDs response
    
    ## Why
    
    When the remote plugin catalog was enabled, app-server loaded
    `openai-curated-remote` but skipped `/plugins/featured` because the
    request processor only fetched featured IDs for the local
    `openai-curated` marketplace. As a result, the desktop app could not
    render the backend-curated remote featured set.
    
    This keeps the existing local behavior and also returns the curated
    ranking for remote plugins.
    
    ## Test plan
    
    - `just fmt`
    - `git diff --check`
    - `just test -p codex-app-server
    plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled`
  • Register full CDP requirements feature (#28769)
    register cdp requirements feature flag
  • PAC 3 - Add Windows system proxy resolver (#26708)
    ## Summary
    
    Stacked on #26707.
    
    Adds the Windows implementation of the shared system-proxy contract.
    This allows Codex-owned auth clients to use the route Windows selects
    for each auth URL, including explicit PAC configuration, WPAD
    auto-detection, static proxies, and bypass rules.
    
    The `respect_system_proxy` feature is disabled by default, so existing
    client behavior remains unchanged unless explicitly enabled.
    
    ## Implementation
    
    - Adds Windows-only `codex-client` dependencies:
    - `windows-sys` with `Win32_Foundation` and `Win32_Networking_WinHttp`;
      - `sha2` for redacted cache keys.
    - Dispatches system-proxy resolution to `outbound_proxy/windows.rs` on
    Windows.
    - Reads the current-user WinHTTP/IE proxy configuration via
    `WinHttpGetIEProxyConfigForCurrentUser`.
    - Resolves explicit PAC URLs first, then OS-enabled WPAD auto-detection,
    then static proxy and bypass settings.
    - Uses `WinHttpGetProxyForUrl` for PAC/WPAD and maps results into the
    shared `SystemProxyDecision::{Direct, Proxy, Unavailable}` contract.
    - Parses `DIRECT`, `PROXY`, `HTTPS`, and keyed static proxy entries.
    - Treats unsupported schemes such as SOCKS as unavailable so the shared
    resolver can apply its environment-proxy fallback.
    - Handles Windows bypass entries, including `<local>` and host, suffix,
    wildcard, and port matching.
    - Releases WinHTTP-owned strings with `GlobalFree` and closes sessions
    with `WinHttpCloseHandle`.
    - Hashes URL-specific cache keys with SHA-256 so PAC decisions remain
    URL-specific without retaining raw request URLs or query strings.
    
    ## End-user behavior
    
    - Disabled/default: existing client behavior is unchanged.
    - Enabled with `[features.respect_system_proxy]`:
    - Windows auth clients honor explicit PAC configuration, OS-enabled
    WPAD, static proxies, and bypass rules;
      - valid OS/PAC `DIRECT` decisions use a direct connection;
    - unavailable system resolution falls back to explicit environment proxy
    variables, then `DIRECT`, through the shared contract from #26707.
    - Unsupported proxy schemes are not silently translated into a different
    route.
    - Custom CA handling remains separate from proxy selection.
    
    ## Tests
    
    Adds coverage for:
    
    - PAC-style proxy tokens such as `PROXY proxy.internal:8080` and `HTTPS
    proxy.internal:8443`;
    - static WinHTTP proxy entries keyed by target scheme;
    - `DIRECT` and unsupported proxy-token behavior;
    - Windows bypass matching, including `<local>`, wildcard, suffix, and
    port-qualified entries;
    - preserving URL-specific PAC cache decisions without retaining the raw
    URL on Windows.
  • [codex-core-plugins] Remote Plugin ID Persisted to File (#27669)
    ## This PR
    
    Remote plugin analytics cannot rely only on the in-memory
    installed-plugin snapshot because that snapshot is refreshed
    asynchronously after startup. This PR persists the authoritative backend
    identity alongside each cached remote plugin bundle so later consumers
    can resolve it without a network request.
    
    ### Behavior
    
    - Store Codex-owned remote installation metadata in an atomic
    `.codex-remote-plugin-install.json` sidecar under the plugin cache root.
    - Use a versioned, snake_case schema:
    
      ```json
      {
        "schema_version": 1,
        "remote_plugin_id": "plugins~Plugin_..."
      }
      ```
    
    - Write the metadata during remote bundle installation.
    - Backfill it when bundle sync finds an already-current cached bundle.
    - Clear it when a generic/local install replaces the cache.
    - Let existing uninstall and stale-cache removal delete it with the
    plugin cache root.
    - Reject unsupported schema versions rather than silently misreading
    future formats.
    
    This PR does not change analytics serialization or event behavior.
    
    ### Review surface
    
    The implementation is limited to four `codex-core-plugins` files:
    
    - `store.rs`: owns the versioned sidecar read/write/remove lifecycle.
    - `remote_bundle.rs`: persists the backend ID after a remote bundle
    install.
    - `remote/remote_installed_plugin_sync.rs`: backfills metadata for an
    already-current cached bundle.
    - Tests cover the storage lifecycle and both remote write paths.
    
    ## Testing / Validation
    
    ### Automated
    
    - `just test -p codex-core-plugins` (268 tests passed)
    - `just fix -p codex-core-plugins` passes with one pre-existing
    `large_enum_variant` warning in `manifest.rs`.
    - Coverage verifies the exact filename and JSON schema, identity
    replacement, local reinstall clearing, uninstall cleanup, remote bundle
    installation, unsupported schema rejection, and installed-plugin sync
    backfill.
    
    ### Live manual validation
    
    Validated the production app-server RPC path with an isolated temporary
    `CODEX_HOME` and the PR-built Codex binary. The app-server communicated
    over stdio and did not bind a port.
    
    Test plugin: `plugins~Plugin_b80dd84519148191a409cde181c9b3d6`
    (`build-macos-apps@openai-curated-remote`).
    
    1. Confirmed `plugin/read` initially reported the plugin uninstalled.
    2. Installed it through `plugin/install` and confirmed version `0.1.4`
    was cached.
    3. Verified
    `$CODEX_HOME/plugins/cache/openai-curated-remote/build-macos-apps/.codex-remote-plugin-install.json`
    was created beside the `0.1.4/` bundle directory with mode `0600` and
    the expected contents:
    
       ```json
       {
         "schema_version": 1,
    "remote_plugin_id": "plugins~Plugin_b80dd84519148191a409cde181c9b3d6"
       }
       ```
    
    4. Deleted only the sidecar, restarted the app-server, and confirmed
    installed-plugin startup sync recreated it with the same contents.
    5. Uninstalled through `plugin/uninstall`, confirmed `plugin/read`
    returned `installed: false`, and verified the local plugin cache root
    was removed.
    6. Restored the account's original uninstalled state and removed the
    isolated home and copied credentials.
    
    ## Split Overview
    
    ```text
    main
    ├── #27093  Debug analytics capture                     merged
    │   └── #27099  Non-mutating plugin smoke               merged
    │       └── #27100  Remote install/uninstall smoke      merged
    └── #27102  Plugin telemetry metadata refactor          merged
        └── #27669  Persist remote plugin identity           ← this PR
    
    Next:
    └── Final PR: add explicit local and remote IDs to plugin analytics
    ```
    
    This PR is based directly on `main`; prerequisite
    [#27102](https://github.com/openai/codex/pull/27102) has merged. The
    original combined [#26281](https://github.com/openai/codex/pull/26281)
    remains the aggregate reference until the final replacement PR is
    published.
  • chore: advance tungstenite fork pins (#29480)
    ## Why
    
    `openai-oss-forks/tokio-tungstenite` now includes the updated
    `tungstenite` fork revision from
    [openai-oss-forks/tokio-tungstenite#3](https://github.com/openai-oss-forks/tokio-tungstenite/pull/3).
    Codex should consume the merged fork commit and resolve its direct and
    transitive `tungstenite` dependencies to the same revision instead of
    retaining the older pins.
    
    ## What Changed
    
    - Advanced the `tokio-tungstenite` git pin to
    `0e5b2d73aa18dd9f0a50ee9ff199d5aef7594186`.
    - Advanced the `tungstenite` fork pin to
    `4fffad30fe373adbdcffab9545e9e9bf4f2fc19f` and adjusted the patch source
    so the transitive dependency resolves to that revision.
    - Updated `Cargo.lock` and `MODULE.bazel.lock` to match the dependency
    graph.