Commit Graph

7631 Commits

  • [codex] Support protected resource OAuth discovery (#29022)
    ## Why
    
    Plugin-install preflight and the actual OAuth login flow used different
    discovery implementations. Preflight had a Codex-specific implementation
    that only queried authorization-server metadata on the MCP host, while
    login already used the upstream `rmcp` Rust MCP SDK. As a result,
    servers that advertise a separate authorization server through RFC 9728
    Protected Resource Metadata were classified as OAuth-unsupported during
    plugin installation, so login was skipped.
    
    ## What changed
    
    - delegate plugin-install OAuth discovery to
    `rmcp::transport::AuthorizationManager`, the same implementation used by
    the login flow
    - let `rmcp` follow Protected Resource Metadata first and perform direct
    RFC 8414 authorization-server discovery when protected-resource
    discovery does not yield usable metadata
    - retain Codex's existing HTTP headers, timeout, `no_proxy` behavior,
    and scope normalization around that discovery
    - add unit coverage and a pure-MCP plugin-install integration test that
    proves the protected-resource path reaches OAuth client registration
    
    This only changes shared MCP OAuth discovery. App declarations and
    `appsNeedingAuth` behavior are unchanged.
    
    ## Verification
    
    - `just test -p codex-rmcp-client auth_status`
    - `just test -p codex-app-server plugin_install_starts_mcp_oauth`
    - real plugin-install smoke test with an isolated `CODEX_HOME`: both
    DigitalOcean MCP servers started OAuth callback listeners, while Linear
    continued to start its existing direct-discovery OAuth flow
  • core: assign item IDs to compacted replacement history (#29012)
    ## Why
    
    Remote v2 compaction can return replacement-history items without IDs.
    Because replacement history is installed directly, those items bypass
    normal history preparation and remain ID-less in later Responses
    requests even when the `item_ids` feature is enabled.
    
    ## What changed
    
    - Pass the active `TurnContext` into `replace_compacted_history`.
    - When `item_ids` is enabled, assign missing IDs before installing and
    persisting replacement history.
    - Rebuild `CompactedItem` from the prepared history so live and
    persisted replacement histories match.
    - Add integration coverage requiring IDs on every ID-capable input item
    in the initial, remote v2 compaction, and post-compaction requests.
    
    ## Test plan
    
    - `just test -p codex-core response_item_ids`
    - `just test -p codex-core websocket_v2_test_codex_shell_chain`
    - `just test -p codex-core remote_compaction_parity_pre_turn_auto`
    - `just test -p codex-app-server
    thread_inject_items_adds_raw_response_items_to_thread_history`
  • [codex] add clock current-time tool (#29011)
    ## Summary
    - expose `clock.curr_time` when current-time reminders are enabled
    - query the session's configured time provider with the calling thread
    id
    - return the existing UTC reminder text for direct model calls
    - return `{ "current_time": "YYYY-MM-DD HH:MM:SS UTC" }` in Code Mode
    
    Clock lookup failures remain fatal, matching pre-inference reminder
    behavior.
    
    ## Testing
    - `just test -p codex-core current_time_tool_returns_the_latest_time`
    - `just test -p codex-core
    code_mode_current_time_returns_structured_result`
    - `just fix -p codex-core`
  • [codex] Skip curated repo sync for remote plugins (#29005)
    ## Summary
    
    - skip the legacy `openai-curated` startup repository sync when remote
    plugins are enabled and the current auth uses the Codex backend
    - keep the curated sync for API-key, Bedrock, and unauthenticated
    sessions that fall back to the local marketplace
    - preserve configured marketplace upgrades and all remote plugin startup
    warmups
    
    ## Why
    
    The remote catalog owns plugin discovery and materialization only when
    it is usable for the current auth mode. Starting the legacy curated
    repository sync in that case performs an unnecessary Git/HTTP/archive
    download and cache refresh. API-key and Bedrock sessions still require
    the local curated marketplace, so they must continue syncing it.
    
    ## User impact
    
    Codex startup no longer downloads or refreshes the local
    `openai-curated` snapshot when the remote catalog is active. Behavior is
    unchanged for auth modes that use the local curated marketplace.
    
    ## Validation
    
    - `just fmt`
    - `git diff --check`
    
    Rust tests were not run per the repository's local verification policy
    for this narrow conditional change.
  • [codex] Assign response item IDs when recording history (#28814)
    ## Why
    
    Client-created response items enter history without IDs, so their
    identity is lost across rollout persistence and resume. IDs should be
    assigned once at the history-recording boundary, while IDs returned by
    the server must remain unchanged.
    
    The Responses API validates item IDs using type-specific prefixes.
    Locally generated IDs therefore use the matching prefix plus a
    hyphenated UUIDv7, keeping them valid while distinguishable from
    server-generated IDs. Because this changes persisted history and
    provider request shapes, the behavior is opt-in behind the
    under-development `item_ids` feature. Compaction triggers remain request
    controls whose API shape does not accept an ID.
    
    ## What changed
    
    - Register the disabled-by-default `item_ids` feature and expose it in
    `config.schema.json`.
    - Make supported optional `ResponseItem` IDs serializable and expose
    them in the generated app-server schemas.
    - When `item_ids` is enabled, assign an ID during conversation-history
    preparation if an item has no ID.
    - Generate type-prefixed, hyphenated UUIDv7 IDs using the Responses API
    item conventions.
    - Preserve existing server IDs without rewriting them.
    - Persist assigned IDs in rollouts and include them in subsequent
    Responses requests.
    - Remove the unsupported ID field from `CompactionTrigger` and document
    why it has no ID.
    - Add integration coverage for enabled ID persistence, preservation of
    server IDs, and omission of generated IDs while the feature is disabled.
    
    `prepare_conversation_items_for_history` is the single response-item ID
    allocation boundary.
    
    ## Test plan
    
    - `just test -p codex-features`
    - `just test -p codex-core
    response_item_ids_persist_across_resume_and_preserve_server_ids`
    - `just test -p codex-core
    non_openai_responses_requests_omit_item_turn_metadata`
    - `just test -p codex-core
    resize_all_images_prepares_failures_before_history_insertion`
    - `just test -p codex-protocol`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-api azure_default_store_attaches_ids_and_headers`
  • Always use AVAS for realtime WebRTC calls (#28856)
    ## Summary
    
    - Remove the realtime `architecture` selector from core protocol,
    app-server protocol, config parsing, generated schemas, and callers.
    - Always create WebRTC realtime calls with the AVAS query params:
    `intent=quicksilver&architecture=avas`.
    - Keep direct websocket realtime behavior on the existing config/default
    path, while WebRTC starts without an explicit version now default to
    realtime v1 because AVAS requires v1.
    
    ## Notes
    
    - WebRTC realtime now means AVAS. If a caller explicitly asks to start
    WebRTC with realtime v2, Codex rejects that request because the AVAS
    WebRTC path only supports realtime v1. Websocket realtime is separate
    and can still use realtime v2.
    - The old `[realtime] architecture = "realtimeapi" | "avas"` config knob
    is removed. Local configs that still set it will need to delete that
    line.
    - Some app-server tests that were only trying to exercise realtime v2
    protocol behavior now use websocket transport, because WebRTC is
    intentionally locked to AVAS/v1. Separate WebRTC tests cover the AVAS
    query params, v1 startup, SDP flow, and sideband join.
    
    ## Validation
    
    - Merged fresh `origin/main` at `83e6a786a2`.
    - `just fmt`
    - `just write-config-schema`
    - `just write-app-server-schema`
    - `git diff --check`
    - `just test -p codex-api -p codex-core -p codex-app-server-protocol -p
    codex-app-server realtime` (176 passed)
    - `just test -p codex-protocol -p codex-config` (413 passed)
  • [plugins] Refresh plugin and tool caches after remote install (#28951)
    Summary
    - Refresh the installed remote-plugin snapshot and Codex Apps tools
    after completing a remote JIT install.
    - Gate `completed: true` on every expected `app_connector_id` appearing
    after the uncached `tools/list` refresh, while continuing to skip local
    bundle verification for server-side installs.
    - Keep the cached recommendations response and filter refreshed
    installed remote IDs locally, so this does not add another
    recommendations fetch.
    - Add regression coverage for tools appearing after the hard refresh and
    remaining absent after the refresh. The resumed model request sees the
    refreshed tool router when installation completes.
    
    Root Cause
    - Remote suggestions from `openai-curated-remote` returned `true` before
    taking the existing connector refresh path, leaving the resumed turn
    with the pre-install Apps tool catalog.
    
    Validation
    - `just test -p codex-core request_plugin_install`
    - `just test -p codex-core-plugins
    recommended_plugin_candidates_filter_installed_and_disabled_plugins`
    - `just test -p codex-core-plugins`
    - `just fix -p codex-core-plugins`
    - `just fix -p codex-core`
    - `just fmt`
    - `just test -p codex-core` was not fully clean locally: 2,729 passed,
    26 failed, and 16 skipped. The failures were dominated by local
    Seatbelt/network/timing issues, including plugin-install timeouts under
    full-suite contention; the focused plugin-install runs pass.
  • core: add UUIDv7 context window IDs (#28953)
    ## Why
    
    The token-budget context currently identifies a context window by its
    thread-local sequence number. A UUIDv7 gives the model a stable opaque
    identity that remains fixed for a window and rotates when compaction or
    `new_context` starts the next one.
    
    ## What changed
    
    - Preserve the existing monotonic value as `window_number` and add a
    UUIDv7 `window_id` to `CompactedItem`.
    - Generate and rotate the UUID with auto-compaction window state,
    persist it alongside the number, and reconstruct it on resume and
    rollback.
    - Accept legacy compacted rollout records where the numeric `window_id`
    represented the window number.
    - Use the UUID only in token-budget context; existing request headers
    and metadata continue using `thread_id:window_number`.
    
    ## Testing
    
    - `just test -p codex-protocol compacted_item::tests`
    - `just test -p codex-core token_budget`
  • [codex] Reuse parsed plugin skills during session startup (#28844)
    ## Summary
    
    - Preserve raw plugin skill-root snapshots in the matching loaded-plugin
    cache entry, keyed by the effective plugin root identity including
    namespace.
    - Pass those snapshots through `SkillsLoadInput` as an optional preload,
    so session startup reuses plugin parsing while ordinary skill loads pass
    `None`.
    - Keep plugin skill loading cohesive: the existing loaders accept the
    optional snapshots directly, and uncached or marketplace-detail paths do
    not create a cache.
    
    ## Why
    
    Plugin discovery already parses plugin skills to determine available
    capabilities. Cold session startup then scanned and parsed the same
    roots again while building the skills snapshot.
    
    This solves the same duplicate-work problem as #28623 while keeping
    ownership narrow: `PluginsManager` creates and owns
    `PluginSkillSnapshots` only for its loaded-plugin cache entry;
    `SkillsService` consumes an optional clone. Entry replacement or
    clearing naturally drops the snapshots, with no separate generation,
    capacity policy, or watcher coupling.
    
    ## Validation
    
    - `cargo clippy -p codex-core-skills --all-targets -- -D warnings`
    - `just test -p codex-core-plugins
    skills_service_reuses_skills_parsed_during_plugin_load`
    - `just test -p codex-core-skills
    namespaces_plugin_skills_using_provided_namespace`
    - `just fmt`
  • core: keep remote exec on reported shell (#28983)
    ## Why
    
    We need to avoid resolving shells on the app-server's host for remote
    environments. We might make it possible to do fancier shell resolution
    from remote envs but for now just require the model to produce a shell
    that matches the environment's default.
    
    This gets my e2e demo working for shell commands after #28854 moved
    shell resolution to PathUri and caused remote envs to hit the fallback
    shell when the shell wasn't available on the host.
    
    ## What
    
    Remote `exec_command` calls now accept only the environment's reported
    default shell name or exact path, and execute with that reported path.
    Other explicit shells return a concise error. A Wine-backed integration
    test covers explicit PowerShell execution in the Windows cwd.
  • core: log AGENTS.md paths as URIs (#28989)
    ## Why
    
    No need to do path contortions when it's for our own logs.
    
    ## What
    
    Follow up on a previous PR's nit and update the path-types skill for
    future reference.
  • [codex] Remove child AGENTS.md prompt experiment (#28993)
    ## Why
    
    `child_agents_md` is a disabled, under-development experiment that adds
    a second model-visible explanation of hierarchical `AGENTS.md` behavior.
    Keeping it leaves unused prompt, configuration, documentation, and test
    surface.
    
    ## What changed
    
    - remove the `ChildAgentsMd` feature and `child_agents_md` config schema
    entry
    - remove the hierarchical prompt asset, export, and instruction
    injection
    - remove feature-specific tests and documentation
    - keep the generic unstable-feature warning coverage using
    `apply_patch_streaming_events`
    
    Normal project `AGENTS.md` discovery and composition are unchanged.
    
    ## Testing
    
    - `just test -p codex-features`
    - `just test -p codex-prompts`
    - `just test -p codex-core agents_md`
    - `just test -p codex-core unstable_features_warning`
  • [codex] Support marketplace plugin manifest fallback (#28789)
    ## Summary
    
    Support marketplace plugins whose source directory does not include a
    discoverable plugin manifest. Metadata-rich `marketplace.json` entries
    now act as fallback plugin manifests for listing, local detail reads,
    install, and non-curated cache refresh.
    
    The fallback preserves marketplace-entry plugin fields wholesale, then
    adds the small Codex-facing compatibility bridge for presentation
    metadata. A real source `plugin.json` always wins when present.
    
    ## Details
    
    - Capture flattened marketplace-entry fields into
    `MarketplacePluginManifestFallback`, preserving fields such as
    `version`, `description`, `skills`, `mcpServers`, `apps`, `hooks`,
    `agents`, `commands`, `strict`, `author`, and future manifest fields
    without a per-field translation list.
    - Bridge Claude-style top-level `displayName`, `author.name`,
    `homepage`, and marketplace `category` into Codex's nested `interface`
    fields only when the nested values are absent.
    - Treat fallback metadata as installable only when the marketplace entry
    contributes metadata beyond bare `name` and `source`; existing
    missing-manifest behavior remains for metadata-free entries.
    - Read local plugin details from the already parsed fallback manifest,
    including fallback-declared app and MCP paths, instead of rereading only
    an on-disk manifest.
    - Pass fallback contents into `PluginStore`, which validates them and
    injects `.codex-plugin/plugin.json` into Store's existing atomic copy.
    Local marketplace source directories are never mutated, and the fallback
    path no longer needs an additional staging directory.
    - Keep Git source materialization unchanged; Git clones still use the
    existing marketplace source staging area before Store installation.
  • core: load AGENTS.md from foreign environments (#28958)
    ## Why
    
    Make it possible to load AGENTS.md from remote exec-servers whose OS is
    different than app-server.
    
    ## What
    
    - keep `AGENTS.md` discovery and provenance as `PathUri`, with
    root-aware parent and ancestor traversal
    - expose lifecycle instruction sources as legacy app-server path strings
    in events while retaining `PathUri` internally
    - preserve and test mixed POSIX and Windows paths in model context and
    TUI status output
    - cover remote Windows loading end to end by seeding the Wine prefix
    through host filesystem APIs
    - fix bug in `PathUri`'s parent() implementation that would erase
    Windows drive letters
  • [codex] Preserve remote plugin download status errors (#28863)
    ## Summary
    
    - preserve the original HTTP status when a remote plugin bundle download
    returns a non-success response
    - retain at most 8 KiB of the error response body and annotate
    truncation or body-read failures
    - add regression coverage for an oversized error response
    
    ## Root cause
    
    The non-success response path reused the normal size-limited body
    reader. When an error response exceeded 8 KiB, that reader returned
    `DownloadTooLarge` before the code constructed `DownloadStatus`, masking
    the upstream HTTP status and response context.
    
    ## Impact
    
    Remote plugin installation failures now retain the actionable upstream
    HTTP status without allowing unbounded error bodies into logs.
    
    ## Validation
    
    - `just test -p codex-app-server
    plugin_install_preserves_status_when_remote_bundle_error_body_is_too_large`
    - `just fmt`
    - `git diff --check`
  • [connectors] Ignore synthetic links for app accessibility (#28770)
    Summary
    - Stop treating Codex Apps MCP tools with
    `_meta._codex_apps.synthetic_link: true` as evidence that a connector is
    accessible in `app/list`.
    - Preserve synthetic tools in the agent-facing MCP connector set so they
    remain available for install/auth flows.
    - Keep the app-list accessibility cache limited to connectors backed by
    at least one non-synthetic tool.
    - Add focused regression coverage for both sides of the boundary.
    
    Validation
    - `just fmt`
    - `just test -p codex-core
    synthetic_links_are_exposed_to_the_agent_but_not_accessible_in_app_list`
    - `git diff --check`
    - A crate-wide `just test -p codex-core` run completed with 2,699
    passing and 51 unrelated local sandbox/state failures, primarily state
    DB migration races (`UNIQUE constraint failed:
    _sqlx_migrations.version`).
  • feat: opt ChatGPT auth into agent identity (#19049)
    ## Stack
    
    This is PR 2 of the simplified HAI single-run-task stack:
    
    - [#19047](https://github.com/openai/codex/pull/19047) Agent Identity
    assertion and task-registration primitives, including the shared
    run-task helper used by existing Agent Identity JWT auth.
    - [#19049](https://github.com/openai/codex/pull/19049)
    Disabled-by-default ChatGPT auth opt-in that provisions/reuses persisted
    Agent Identity runtime auth and its single run task.
    - [#19051](https://github.com/openai/codex/pull/19051) Run-scoped
    provider auth that uses one backend-owned task id for first-party
    inference and compaction requests.
    
    [#19054](https://github.com/openai/codex/pull/19054) collapsed out of
    the active stack because the simplified design no longer needs a
    separate background/control-plane task helper.
    
    ## Summary
    
    This PR adds the disabled-by-default path for normal ChatGPT-login Codex
    sessions to obtain Agent Identity runtime auth through the Codex
    backend. Existing Agent Identity JWT startup mode remains a separate
    path and does not require the feature flag.
    
    What changed:
    
    - adds the experimental `use_agent_identity` feature flag and config
    schema entry
    - adds an explicit `AgentIdentityAuthPolicy` so call sites choose
    `JwtOnly` or `ChatGptAuth` instead of passing a bare boolean
    - stores standalone Agent Identity JWT credentials separately from
    backend-registered Agent Identity records
    - persists the registered Agent Identity record, private key, and single
    run task id in `auth.json` so process restarts reuse the same identity
    - derives the agent/task registration base URL from ChatGPT/Codex auth
    config while keeping JWT JWKS lookup separate
    - provisions and caches ChatGPT-derived Agent Identity runtime auth when
    `use_agent_identity` is enabled
    - reuses the shared run-task registration helper from PR1 rather than
    adding a second task-registration path
    
    This PR intentionally does not switch model inference over to
    `AgentAssertion` auth. The provider-auth integration lands in the next
    PR.
    
    ## Testing
    
    - `just test -p codex-login`
  • Emit Trusted MCP App Identity on Tool-Call Items (#27132)
    ## Summary
    
    - Add optional `appContext` to app-server MCP tool-call items with
    trusted `connectorId`, `linkId`, and `mcpAppResourceUri` metadata.
    - Preserve that context across tool-call events, persisted history,
    reconnects, and thread resume.
    - Keep the deprecated top-level `mcpAppResourceUri` temporarily for
    client migration.
    
    The consumer contract is `{ appContext: { connectorId, linkId,
    mcpAppResourceUri }, tool }`.
    
    ## Validation
    
    - Full GitHub Actions suite passes, including CLA, Bazel tests, clippy,
    release builds, and argument-comment lint.
    
    ---------
    
    Co-authored-by: martinauyeung-oai <280153141+martinauyeung-oai@users.noreply.github.com>
  • TUI: improve unified mention selection visibility (#28959)
    ## Summary
    
    [@milanglacier reported in
    #28653](https://github.com/openai/codex/issues/28653) that the active
    mention candidate is hard to distinguish. I suspect [@binbjz’s #28500
    report](https://github.com/openai/codex/issues/28500) _(where arrow-key
    navigation appeared not to work)_ may describe the same presentation
    problem: the selection may have been changing, but the UI was not
    showing the active row clearly in their terminal. This PR makes two
    small changes to the selection indication behavior:
    
    - Reserve a two-character gutter and mark the active candidate with `> `
    for color-agnostic indicator coverage.
    - Apply the shared theme-aware accent to the entire selected row for
    extra emphasis.
    - Update the existing popup snapshot.
    
    Reverse-video styling was considered, but avoided it because it is
    overly dependent on the user’s terminal palette.
    
    <img width="2046" height="482" alt="image"
    src="https://github.com/user-attachments/assets/b5eb62c3-fd24-4c09-906e-7bd66913b5c6"
    />
    
    ## Testing
    
    - `just test -p codex-tui default_unified_mention_popup_snapshot`
    - `just clippy -p codex-tui`
    - `just fmt`
    - Compiled `codex-cli` and tested the unified mentions picker in the
    terminal.
  • [codex] Remove hardcoded app ID filters (#28947)
    ## Summary
    
    - remove the duplicated originator-specific connector ID denylists
    - stop filtering connector directory/accessibility results and
    live/cached Codex Apps MCP tools by hardcoded connector ID
    - remove the now-unused `codex-login` dependency from
    `codex-utils-plugins`
    - update regression coverage so formerly blocked connector IDs are
    preserved
    
    ## Why
    
    The client-side policy was duplicated across crates, used opaque IDs
    without ownership or expiry information, and could drift between app
    listing and MCP tool behavior. Server-provided visibility,
    authorization, plugin discoverability, accessibility, enabled-state
    handling, and consequential-tool approval templates remain unchanged.
    
    ## Validation
    
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `git diff --check`
    - confirmed the final diff contains no hardcoded denylist symbols
    
    A targeted `codex-mcp` test build spent an unusually long time in local
    compilation/linking. Its first attempt exposed a test-only `PartialEq`
    assertion issue, which was corrected. A follow-up non-linking `cargo
    check -p codex-mcp --tests` was still running when this draft was
    opened; CI should provide the complete Rust validation.
  • Make auto-review on-request prompt more proactive (#26496)
    ## Why
    
    `on-request` approval policy text is currently tuned for user-reviewed
    approvals. For auto-reviewed productivity runs, likely sandbox blocks
    should be escalated earlier so commands that need remote services,
    authentication, or other out-of-sandbox access do not first fail or hang
    inside the sandbox.
    
    ## What changed
    
    - Adds a separate `on_request_auto_review.md` permissions prompt
    selected for `AskForApproval::OnRequest` with
    `ApprovalsReviewer::AutoReview`.
    - Keeps the normal user-reviewed `on-request` wording unchanged.
    - Makes the `When to request escalation` bullets more explicit about
    likely sandbox blocks, network access, remote
    auth/cluster/cloud/database access, out-of-sandbox environment access,
    git operations that may write lock files, and short-timeout reruns after
    likely sandbox-blocked attempts.
    - Omits approved command prefix and `prefix_rule` guidance for the
    auto-review on-request prompt.
    - Adds prompt tests covering the auto-review path, normal on-request
    wording, and inline permission request behavior.
  • Add app-server current-time impl (varlatency 3/n) (#28835)
    ## What
    
    Server should request:
    
    ```
    {
      "id": 42,
      "method": "currentTime/read",
      "params": {
        "threadId": "11111111-1111-1111-1111-aaaaafdc2c11"
      }
    }
    ```
    
    Client should respond with something like:
    
    ```rust
    {
      "id": 42,
      "result": {
        "currentTimeAt": 1781717655
      }
    }
    ```
    
    ## Why
    
    Sessions configured with `clock_source = "external"` need a
    thread-specific external time source before inference. The system clock
    remains the default production provider.
    
    ## Validation
    
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server --test all
    current_time_read_round_trip_adds_reminder_to_model_input`
    - `cargo test -p codex-app-server
    first_attestation_capable_connection_for_thread_only_uses_thread_subscribers`
    - `cargo test -p codex-analytics`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-app-server`
    
    Stacked on #28824.
  • apply-patch: carry paths as PathUri (#28854)
    ## Why
    
    Allows the model to edit files that are hosted on a different OS than
    where app-server is running.
    
    ## What
    
    * Use `PathUri` for apply_patch-internal data structures
    * Limit `PathUri` -> `AbsolutePathBuf` conversion to cases where the
    inferred path convention matches the host OS, allows requiring valid
    paths to pass to perms check
    * Adds `PathConvention::path_segments()` for iterating over path
    segments regardless of OS
    * Handle cross-platform relative paths in path filename parsing for
    sniffing a shell
    * Ensure we can apply patches in the wine e2e test
  • [codex] Cache plugin metadata for tool suggestions (#27812)
    ## Why
    
    `built_tools` runs for every sampling request, and local plugin
    discovery was repeatedly rereading plugin manifests, skills, MCP
    configuration, and app declarations to build the same tool-suggest
    metadata.
    
    That source-derived metadata is stable until the existing plugin manager
    reloads its cache. Runtime eligibility still needs to reflect the
    current install, disable, policy, app-overlap, and authentication state.
    
    ## What changed
    
    - Add a bounded, in-memory tool-suggest metadata cache owned by
    `PluginsManager`.
    - Key cached metadata by plugin identity and source, while applying
    authentication routing each time the metadata is projected.
    - Invalidate the metadata alongside the existing loaded-plugin cache,
    including its normal configuration, marketplace refresh, and
    remote-installed-plugin invalidation paths.
    - Guard against an in-flight load repopulating stale metadata after
    invalidation.
    - Keep marketplace membership and all runtime eligibility filtering live
    rather than introducing a separate catalog or revision model.
    
    ## Impact
    
    Repeated sampling requests reuse already-loaded plugin capability
    metadata while retaining the existing plugin-manager lifecycle as the
    single freshness boundary.
    
    ## Validation
    
    - `just test -p codex-core-plugins` — 252 passed
    - Added focused coverage for cache invalidation and authentication
    reprojection.
  • current time reminders impl for system clock (varlatency 2/n) (#28824)
    Stacked on #28822.
    
    ## Summary
    
    - add a host-injectable current-time provider with a built-in system
    implementation
    - record UTC developer reminders in history immediately before due model
    requests
    - keep cadence state per session and force a refresh after compaction
    
    This does NOT include the app server client <-> server clock logic. This
    PR is only for the reminder message & system clock that will be used in
    prod.
    
    ## Testing
    
    - `just test -p codex-core varlatency_`
    - `just clippy -p codex-core -p codex-app-server -p codex-mcp-server -p
    codex-thread-manager-sample`
    - `just fmt`
  • [codex] Make thread store turn filter optional (#28949)
    Make `ListItemsParams::turn_id` optional so callers can list persisted
    items across an entire thread or narrow the result to one turn. This
    aligns the thread-store API and documentation with thread-wide item
    listing while preserving the optional turn-filter behavior for
    implementations.
  • Support openai/form extended form elicitations (#27500)
    # Summary
    Allow App Server clients to opt into `openai/form` MCP elicitations.
  • [codex] rollout budget implementation (varlength 2/N) (#28494)
    ## Stack
    
    Depends on #28746. This PR implements shared rollout-budget accounting
    and model-visible reminders using the configuration defined in #28746.
    
    # Description / Main changes to Core:
    
    `AgentControl` will now be the area where "rollout level" features &
    accounting will have to live. It is incorrectly named for this
    responsibility, but I think it can hold all the necessary shared state &
    features (rollout token budget, mutliple thread interruption
    responsibilitym etc)
    
    In this PR, we have one "token ledger" that each thread will subtract
    from when sampling. The "charge" will occur when response.completed() is
    done and the calculation will be done on the responses api usage
    carrier. The calculation will weigh sampling and pre-fill tokens as
    specified.
    
    Every time the budget crosses the configured reminder threshold, a
    developer message is appended before the thread's next request
    
    This remaining budget will _always_ be restated/reminded after a
    compaction event.
    
    Expiration and fan-out interruption will be in the stacked follow-up
    (and also live in Agent Control).
    
    ## Reminders
    
    "You have weighted {session_tokens_left} tokens left in the shared
    session token budget."
    
    The first request in each thread context receives the current remainder.
    Later reminders are emitted after aggregate weighted usage crosses a
    configured interval. If several intervals are crossed before a thread
    sends another request, Core inserts one reminder with the latest
    remainder.
    
    Compaction response usage is charged before the next context starts. The
    next reminder is appended after the compaction summary, leaving the
    initial context content stable.
    
    ## Tests
    
    Integration coverage verifies:
    
    - weighted output and non-cached input accounting
    - initial and periodic reminders
    - shared accounting between a root and sub-agent
    - post-compaction remainder and message placement
    
    Local checks:
    
    - `just fmt`
    - `just test -p codex-core rollout_budget`
    - `git diff --check`
    
    The full workspace test suite was not run locally.
  • Add Config for Time Reminders (varlatency 1/n) (#28822)
    ## Summary
    
    Example:
    
    > [features.current_time_reminder]
    enabled = true
    reminder_interval_model_requests = 1
    clock_source = "system"
    
    ## Testing
    
    - `just test -p codex-core varlatency`
    - `just test -p codex-core
    lock_contains_prompts_and_materializes_features`
    - `just fix -p codex-core -p codex-config -p codex-features`
  • Synchronize realtime notification test requests (#28946)
    ## What
    
    Deliver the scripted realtime notification batch after the assistant
    text append request instead of after the preceding developer text append
    request.
    
    ## Why
    
    The batch ends with an upstream error that closes the realtime
    conversation. When it is emitted after the developer append, it races
    the subsequent assistant append: the app-server RPC can acknowledge the
    append before its downstream WebSocket send completes, and the test
    intermittently observes three requests instead of four.
    
    Making the fake server wait for the assistant append before emitting the
    terminal batch establishes the ordering the test asserts without sleeps
    or production-code changes.
    
    ## Validation
    
    - `git diff --check`
    - CI (the failure is timing-dependent and most reproducible in the
    Windows Bazel shard)
  • [codex] Fix Windows sandbox runtime ACL refresh (#28943)
    ## Why
    
    Codex Desktop repairs sandbox-user read/execute access for binaries
    copied to `%LOCALAPPDATA%\OpenAI\Codex\bin`, but Computer Use launches
    its bundled Node runtime from `%LOCALAPPDATA%\OpenAI\Codex\runtimes`.
    
    On fresh Windows installations, `CodexSandboxUsers` may therefore be
    unable to execute the bundled Node binary. The command runner starts,
    but `CreateProcessAsUserW` fails with error 5 (`ACCESS_DENIED`), causing
    the Node REPL to exit before Computer Use can discover applications.
    
    This is a follow-up to #21564, which added the original runtime `bin`
    ACL repair.
    
    ## What changed
    
    - Expand the Codex Desktop runtime ACL roots from only `bin` to both
    `bin` and `runtimes`.
    - Apply the existing inherited read/execute ACL repair to each runtime
    directory when it exists.
    - Rename the setup helper to reflect that it now handles multiple
    runtime paths.
    
    ## Validation
    
    - `cargo fmt -- --check`
    - `just test -p codex-windows-sandbox` was run: 113 tests passed and
    five environment-dependent legacy execution tests failed because
    `CreateRestrictedToken` returned error 87.
  • [codex] Initialize exec-server OpenTelemetry at startup (#25019)
    ## Summary
    
    - Initialize stderr tracing and the configured OpenTelemetry provider
    for local and remote `codex exec-server` startup.
    - Instrument the local and remote server entrypoints with a root runtime
    span.
    - Keep raw Noise environment, registration, and stream identifiers out
    of exported spans while preserving them in local debug events.
    - Keep telemetry setup in a focused CLI module instead of growing the
    top-level command entrypoint.
    
    ## Stack
    
    - Previous: none (`#27058` has merged)
    - Next: #27466
    
    ## Validation
    
    - `just test -p codex-exec-server --lib` (139 passed)
    - `just test -p codex-cli --test exec_server` (3 passed)
    - `just bazel-lock-check`
    - `just fix -p codex-exec-server -p codex-cli`
    - `just fmt`
    
    ---------
    
    Co-authored-by: Richard Lee <richardlee@openai.com>
  • Fix goal-first live threads missing from thread/list (#28808)
    Fixes #28263.
    
    ## Why
    
    When a thread starts with `/goal`, the goal extension can update SQLite
    goal state before the thread has any user-turn rollout items.
    `thread/list` and `thread/search` rely on persisted listing metadata, so
    a goal-first live thread could be absent from app-server listings after
    restart even though the goal itself existed.
    
    This regressed when goal handling moved out of core: the core path wrote
    the goal update through the live thread rollout path, while the
    extension-backed app-server path only updated goal state and emitted the
    live notification.
    
    ## What
    
    - Add `GoalSetOutcome::thread_goal_updated_item()` so the goal extension
    owns the canonical `ThreadGoalUpdated` rollout item shape.
    - Expose a narrow `CodexThread::append_rollout_items()` helper that
    appends through the live thread and keeps derived SQLite metadata in
    sync.
    - When app-server sets a goal on an active live thread, persist the goal
    update through that live-thread path.
    - Add an app-server regression test that starts a live thread with
    `thread/goal/set` and verifies it appears in state-DB-only
    `thread/list`.
    
    ## Verification
    
    - `env -u CODEX_SQLITE_HOME just test -p codex-app-server
    goal_first_live_thread_appears_in_state_db_thread_list`
  • Add turn-scoped context contributions (#28911)
    ## Summary
    - keep context injection on a single ContextContributor trait
    - split context injection into thread-scoped and turn-scoped
    contribution methods
    - wire turn-scoped fragments into initial context assembly so extensions
    can contribute context from turn-local state
  • Scope MCP sandbox metadata to server environment (#28914)
    Scope MCP sandbox metadata to the MCP server's owning environment.
    
    Previously, `codex/sandbox-state-meta` always used the turn's primary
    cwd and rebuilt a legacy sandbox policy from that cwd. That can be wrong
    for MCP servers owned by a different execution environment.
    
    This now sends the owning environment cwd as a `file:` URI in
    `sandboxCwd`, keeps `permissionProfile` as the permission source of
    truth, and omits sandbox-state metadata when a non-default server
    environment is not selected for the turn. Local/default MCP servers keep
    the existing fallback cwd behavior.
    
    Tests:
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `just test -p codex-mcp`
    - `just test -p codex-core mcp_sandbox_cwd`
    - `cargo build -p codex-rmcp-client --bin test_stdio_server`
    - `just test -p codex-core
    stdio_mcp_tool_call_includes_sandbox_state_meta`
  • Pin Windows argument lint to Windows 2022 (#28940)
    ## What
    
    Run the Windows argument-comment-lint job on the `windows-2022` hosted
    runner instead of the custom Windows runner pool.
    
    ## Why
    
    The custom pool recently moved from the Visual Studio 2022 Windows image
    to `windows-2025-vs2026`. Since that migration, the job fails while
    Bazel materializes LLVM external repository sources, before the argument
    lint itself runs. The same failure appears across unrelated PRs.
    
    This narrow change tests GitHub’s recommended mitigation for workloads
    that still require the Visual Studio 2022 image:
    https://github.com/actions/runner-images/issues/14017
    
    ## How
    
    Use the standard `windows-2022` runner for only the Windows
    argument-comment-lint matrix entry. No product code or lint behavior
    changes.
  • Recover exec process stdin writes (#28895)
    ## Summary
    
    Remote stdio MCP servers send tool calls by writing JSON-RPC bytes
    through `process/write`.
    
    When the exec-server websocket drops at the wrong time, the remote
    process can survive session recovery, but the stdin write can still fail
    back to RMCP as a transport send error. RMCP then closes the stdio MCP
    transport, so tools like `node_repl` are lost even though the
    process/session recovery path is working.
    
    This changes `process/write` to be safe to retry across exec-server
    recovery:
    
    - adds a required `writeId` to `process/write`
    - retries remote `Session::write` with the same `writeId` after
    reconnect
    - remembers accepted write ids per process so duplicate retries return
    `Accepted` without writing the same bytes to child stdin again
    - covers both the client retry path and server-side write id dedupe with
    tests
    
    In simple terms:
    
    ```text
    before:
    write to MCP stdin -> websocket closes -> write errors -> RMCP closes node_repl
    
    after:
    write to MCP stdin -> websocket closes -> reconnect -> retry same writeId
    server either writes once or recognizes it already did
    ```
  • Pause active goals before TUI interrupts (#28813)
    Fixes #28104.
    
    ## Summary
    Active `/goal` turns should leave the persisted goal paused whenever the
    TUI interrupts the running turn. The bug in #28104 showed this most
    visibly through `Esc`: some interrupt paths aborted the turn without
    updating the goal status, so the goal could remain active and continue
    automatically.
    
    This change makes `ChatWidget` pause an active goal before the TUI sends
    an interrupt from the status-row path, the pending-steer path, `Ctrl+C`,
    or a request-user-input overlay. The modal overlay now reports whether a
    key will interrupt the turn, which keeps modal `Esc` and `Ctrl+C`
    behavior aligned with the normal interrupt paths.
    
    ## Manual Testing
    Built the local CLI with `just codex --help`, then launched the local
    TUI with goals enabled. Started an active `/goal` turn and interrupted
    it with `Esc`, then resumed and repeated with `Ctrl+C`; both paths
    showed `Goal paused`, the interrupted-conversation message, and the
    `Goal paused (/goal resume)` footer. I also stopped the background
    terminal and exited the TUI cleanly after the run.
    
    I did not find a reliable standalone manual path to force the
    request-user-input overlay case, so that path is covered by the focused
    automated test.
  • Avoid sandbox helper in apply_patch approval tests (#28915)
    ## Summary
    This keeps the apply_patch approval tests focused on approval behavior
    instead of macOS sandboxed filesystem helper startup.
    
    The changed cases still force patch approval with `UnlessTrusted`, but
    use `DangerFullAccess` after approval so the patch write is direct and
    cheap. Workspace-write and sandbox-helper behavior remain covered by the
    filesystem and apply_patch sandbox tests.
  • 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
  • [codex] add rollout token budget configuration (varlength 1/N) (#28746)
    ## What
    
    This PR defines the structured configuration contract for shared rollout
    token budgets (across ALL agent threads under 1 rollout).
    
    ```toml
    [features.rollout_budget]
    enabled = true
    limit_tokens = 100000
    reminder_interval_tokens = 10000
    sampling_token_weight = 1.0
    prefill_token_weight = 0.1
    ```
    
    The reminder interval defaults to 10% of the rollout limit. Sampling and
    prefill weights default to `1.0`.
    
    ## Scope
    
    This PR only defines and validates configuration. It does not track
    usage, inject reminders, or stop a rollout. Accounting and reminders are
    implemented in the stacked follow-up #28494.
    
    The existing `token_budget` feature remains unchanged. `rollout_budget`
    has its own feature key and configuration type.
    
    ## Tests
    
    The config test verifies that the structured fields resolve into
    `RolloutBudgetConfig` and do not enable the existing `token_budget`
    feature.
    
    Local checks:
    
    - `just write-config-schema`
    - `just test -p codex-core load_config_resolves_rollout_budget`
    - `cargo check -p codex-thread-manager-sample`
    - `git diff --check`
    
    The full workspace test suite was not run locally.
  • [codex] Pass plugin namespace into skill loading (#28608)
    ## What changed
    
    - retain the parsed plugin manifest namespace on loaded plugins
    - carry that namespace through `PluginSkillRoot` and `SkillRoot`
    - use the provided namespace when qualifying plugin skill names
    - include the namespace in the skills cache key
    
    ## Why
    
    Plugin loading has already parsed `plugin.json`, but skill parsing
    currently walks every `SKILL.md` ancestor and probes/reads the manifest
    again to reconstruct the same namespace. Passing the parsed namespace
    removes those repeated filesystem calls, which are particularly costly
    on remote filesystems.
    
    Context:
    https://openai.slack.com/archives/C0ARA9GF5D4/p1781639496496439?thread_ts=1781202444.891669&cid=C0ARA9GF5D4
    
    ## Impact
    
    Plugin skill names remain unchanged. A regression test uses a
    deliberately different on-disk manifest name to verify that plugin roots
    use the provided parsed namespace.
    
    ## Validation
    
    - `just test -p codex-core-skills -p codex-core-plugins -p codex-plugin
    -p codex-utils-plugins` (352 passed)
    - `just fix -p codex-core-skills -p codex-core-plugins -p codex-plugin
    -p codex-utils-plugins`
    - `just fmt`
  • [codex] Split plugin and skill warmup tracing (#28605)
    ## What changed
    
    - promote plugin config loading to an info-level `plugins_for_config`
    span
    - promote skill config loading to an info-level `skills_for_config` span
    - attach stable OpenTelemetry names to both spans
    
    ## Why
    
    `session_init.plugin_skill_warmup` currently combines plugin loading and
    skill loading, which makes cold-start traces unable to identify which
    phase dominates. These child spans preserve the existing aggregate while
    making the two costs independently visible.
    
    Context:
    https://openai.slack.com/archives/C0ARA9GF5D4/p1781639496496439?thread_ts=1781202444.891669&cid=C0ARA9GF5D4
    
    ## Impact
    
    This is observability-only. It does not change plugin or skill loading
    behavior.
    
    ## Validation
    
    - `just test -p codex-core-skills -p codex-core-plugins` (347 passed)
    - `just fmt`
  • 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
  • Record more path migration guidance for codex. (#28851)
    Some common themes pulled out of both human and automated reviews from
    the last couple of days' migrations to `PathUri` and
    `LegacyAppPathString`.
  • [codex] Support plugin manifest path lists (#28790)
    ## Summary
    
    Allow plugin manifests to declare `skills` as either a single path
    string or an array of path strings in the core plugin loader.
    
    ## Why
    
    Some plugin packages need to expose skills from more than one directory.
    Before this change, `plugin.json` only accepted a single string for
    `skills`, so manifests like this were ignored as an invalid `skills`
    shape:
    
    ```json
    {
      "skills": ["./skills/abc", "./skills/edk"]
    }
    ```
    
    This keeps the existing single-string form working while adding support
    for the list form. The final scope is intentionally limited to the core
    plugin manifest/load path for `skills`; `apps`, file-backed
    `mcpServers`, and the bundled plugin-creator assets are unchanged in
    this PR.
    
    ## What changed
    
    - Parse `skills` as either a string or an array of strings in
    `plugin.json`.
    - Store resolved skill paths as a list in `PluginManifestPaths`.
    - Load manifest-declared skill roots in addition to the default
    `./skills` root.
    - Deduplicate exact duplicate skill roots before loading.
    - Rely on existing skill-loader dedupe by canonical `SKILL.md` path for
    overlapping roots such as `./skills` plus `./skills/abc`.
    - Update plugin manifest tests to cover:
      - single string `skills`
      - list of string `skills`
      - duplicate skill roots
      - `./skills` as a manifest path
      - explicit child roots like `./skills/abc` and `./skills/edk`
      - overlapping-root dedupe
    
    ## Validation
    
    - `just test -p codex-plugin`
    - `just test -p codex-core-plugins`
    - `just test -p codex-mcp-extension`
    - `git diff --check`
  • 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`
  • Refresh signed exec-server URLs on reconnect (#28374)
    ## Summary
    
    - add a provider API that supplies a fresh signed WebSocket URL for each
    remote exec-server connection
    - refresh the signed URL after disconnects and retry once when a
    handshake returns `401 Unauthorized`
    - allow `EnvironmentManager` consumers to register remote environments
    backed by the URL provider
    
    ## Tests
    
    - `just test -p codex-exec-server -E
    'test(remote_websocket_client_refreshes_url_after_unauthorized_handshake)
    | test(remote_websocket_client_refreshes_url_after_disconnect)'` — 2
    passed
    - `cargo check -p codex-core-api` — passed
    - `just fix -p codex-exec-server` — passed
    - `just fix -p codex-core-api` — no test targets; no-op
    - `just fmt` — passed
    - `just test -p codex-exec-server` — 187 passed; 32 unrelated macOS
    sandbox tests could not invoke nested `sandbox-exec` (`Operation not
    permitted`)
  • [codex] Support assistant realtime append text (#28836)
    ## Why
    
    Frontend realtime voice continuity needs to replay a tiny
    previous-session overlap as actual conversation items, including
    assistant text. The app-server `thread/realtime/appendText` API already
    carries a role through to the Rust realtime websocket layer, but the
    shared role enum only accepted `user` and `developer`.
    
    ## What Changed
    
    - Added `assistant` to `ConversationTextRole` and regenerated the
    app-server schema/type fixtures.
    - Added `output_text` as a realtime conversation content type.
    - Updated realtime websocket item creation so assistant appendText emits
    `content: [{ type: "output_text", text }]`, while user and developer
    continue to emit `input_text`.
    - Updated app-server docs and tests to cover assistant appendText
    alongside the existing developer role behavior.
    
    ## Validation
    
    - `just write-app-server-schema`
    - `just fmt` (first sandboxed attempt failed because `uv` could not
    access `~/.cache/uv`; reran with filesystem access and passed)
    - `just test -p codex-api` passed: 126/126
    - `just test -p codex-app-server-protocol` passed: 239/239, including
    generated JSON/TypeScript fixture checks
    - `just test -p codex-app-server` was started locally but stopped per
    request after unrelated local sandbox/Seatbelt failures (`sandbox-exec:
    sandbox_apply: Operation not permitted`) and one missing local `codex`
    binary failure; CI should be faster and more authoritative for the full
    suite.
  • [codex] control automatic realtime handoff delivery (#27986)
    ## What
    
    Built on the realtime speech-control plumbing merged in #27917.
    
    - Add optional `codexResponseHandoffPrefix` to `thread/realtime/start`.
    - Apply that prefix only to automatic V1 commentary sent through
    `conversation.handoff.append`; final answers remain unprefixed.
    - Add opt-in `clientManagedHandoffs`. When true, core suppresses
    automatic response handoffs and completion output so delivery is
    controlled by explicit client append APIs.
    - Preserve existing automatic behavior by default.
    `codexResponsesAsItems: true` continues to select item routing when
    client-managed mode is disabled.
    
    ## Why
    
    Voice clients need two delivery policies: automatic background context
    with silent commentary instructions and fully client-owned handoffs.
    Phase-aware prefixing keeps routine commentary silent without
    suppressing the final answer, while client-managed mode lets an app
    decide exactly which updates to append.
    
    ## Validation
    
    - `just fmt`
    - `cargo test -p codex-app-server-protocol
    serialize_thread_realtime_start`
    - `RUST_MIN_STACK=16777216 cargo test -p codex-core --test all
    conversation_handoff_persists_across_item_done_until_turn_complete`
    - `RUST_MIN_STACK=16777216 cargo test -p codex-app-server --test all
    webrtc_v1_client_managed_handoffs_disable_automatic_output`
    - `RUST_MIN_STACK=16777216 cargo test -p codex-app-server --test all
    webrtc_v1_final_automatic_handoff_omits_silent_prefix`
    - `cargo build -p codex-cli --bin codex`
    - Local Codex Apps compatibility check: 43 focused webview tests passed,
    and a live voice session routed through the source-built app-server.
    
    The explicit `RUST_MIN_STACK` avoids a macOS Tokio test-worker stack
    overflow seen with the default test environment.