Commit Graph

6232 Commits

  • [codex] Dedupe fallback model metadata warnings (#21090)
    Fixes #21070.
    
    This is a small cleanup around model metadata handling for
    gateway/provider model names. It follows the report and proposed
    direction from @dkbush by keeping the fallback metadata warning useful
    without repeating it every turn, and by tightening the existing
    provider-prefix lookup path.
    
    - Track fallback metadata warning slugs in session state so each
    unresolved model warns once per session.
    - Keep warning emission outside the session-state lock and preserve the
    existing warning text.
    - Allow one-segment provider prefixes with hyphenated provider IDs,
    while preserving the multi-segment rejection behavior.
    - Add focused coverage for warning dedupe and hyphenated provider-prefix
    metadata matching.
    
    Testing:
    
    - Ran `just fmt`.
    - Ran `git diff --check`.
    - Added tests for the new warning dedupe and provider-prefix lookup
    behavior.
  • Avoid hard-coded environment context shell (#21390)
    ## Summary
    - make resolved turn environment shell metadata optional instead of
    hard-coding bash
    - render environment context shells from explicit environment metadata
    when present, falling back to the existing session shell
    - update environment context tests for inherited PowerShell-style
    fallback and explicit per-environment shell override
    
    ## Testing
    - Not run (not requested; formatted with `just fmt`).
    
    Co-authored-by: Codex <noreply@openai.com>
  • Avoid noisy OTEL diagnostics in codex exec (#21107)
    `codex exec` should not print OpenTelemetry exporter self-diagnostics to
    stderr by default. Suppress the SDK and OTLP exporter targets unless
    callers
    explicitly opt in with `RUST_LOG`.
    
    Also stop defaulting the trace exporter to the log exporter, since OTLP
    HTTP
    endpoints are signal-specific and a logs endpoint is not valid for
    spans.
    
    Co-authored-by: Codex <noreply@openai.com>
    
    Co-authored-by: Codex <noreply@openai.com>
  • Route opted-in MCP elicitations through Guardian (#19431)
    # Motivation
    
    Browser Use origin-access prompts are MCP elicitations, not direct
    tool-call approval prompts, so they were bypassing the Guardian approval
    path. We need a generic opt-in that lets eligible MCP elicitations use
    Guardian when the current turn already routes approvals there.
    
    # Description
    
    Add a generic elicitation reviewer hook in codex-mcp and wire codex-core
    to pass a Guardian reviewer callback when creating the MCP connection
    manager. The reviewer validates explicit mcp_tool_call opt-in metadata,
    builds a Guardian MCP tool-call review request from
    server/tool/connector metadata and tool params, and maps Guardian
    approval, denial, timeout, and cancellation decisions back to MCP
    elicitation responses.
    
    The new option to trigger this in the `_meta` object is:
    ```
    "codex_request_type": "approval_request",
    ```
    
    # Testing
    
    - RUST_MIN_STACK=8388608 NEXTEST_STATUS_LEVEL=leak cargo nextest run
    --no-fail-fast --cargo-profile ci-test --test-threads 2
    - cargo clippy --tests -- -D warnings
    - cargo fmt -- --config imports_granularity=Item --check
    - cargo shear
    - pnpm run format
    - python3 .github/scripts/verify_cargo_workspace_manifests.py
    - python3 .github/scripts/verify_tui_core_boundary.py
    - python3 .github/scripts/verify_bazel_clippy_lints.py
    - git diff --check
  • fix(tui): persist ctrl-c draft via app event (#21397)
    ## Why
    
    The main branch started failing after #21351 merged because the merge
    commit kept calling `AppCommand::add_to_history` from
    `BottomPane::clear_composer_for_ctrl_c`, but main had already removed
    that helper as part of the history persistence refactor. The PR head
    passed because it was based on an older main commit where the helper
    still existed.
    
    This restores the Ctrl+C draft-stashing behavior using the current
    app-event path instead of the removed command helper.
    
    ## What Changed
    
    - Store the active `ThreadId` in `BottomPane` when history metadata is
    provided.
    - Emit `AppEvent::AppendMessageHistoryEntry` for Ctrl+C-cleared drafts.
    - Update the slash-clear regression test to assert the current history
    event shape.
    
    ## How to Test
    
    Targeted tests:
    - `cargo test -p codex-tui
    slash_clear_after_ctrl_c_keeps_stashed_draft_recallable`
    
    Broader local checks:
    - `just fix -p codex-tui`
    - `just argument-comment-lint -p codex-tui`
    - `git diff --check origin/main...HEAD`
    - `cargo test -p codex-tui` reached completion; the fixed test passed,
    and the only local failures were
    `status::tests::status_permissions_full_disk_managed_*`, blocked by this
    machine config rejecting `DangerFullAccess` via
    `/etc/codex/requirements.toml`.
  • [codex] Handle git pagination flags by position (#21381)
    ## Why
    
    This is a follow-up to the Windows Git safe-command bypass fix for
    BUGB-15601. Git's global `--paginate` / `-p` flags can route output
    through a configured pager, so they should not be auto-approved as safe
    before the subcommand. At the same time, `-p` after read-only
    subcommands like `log`, `diff`, and `show` is the common patch-output
    flag, so treating every `-p` as unsafe would make ordinary read-only
    inspection commands prompt unnecessarily.
    
    ## What Changed
    
    - Split Git option safety matching into explicit global-option and
    subcommand-option lists.
    - Treat global `git --paginate ...` and `git -p ...` as unsafe.
    - Keep post-subcommand patch usage such as `git log -p`, `git diff -p`,
    and `git show -p HEAD` safe.
    - Keep the pagination coverage with the shared Git safe-command
    implementation rather than the Windows wrapper tests.
    - Remove the stale `git_global_option_requires_prompt` helper now that
    safe-command Git option matching owns the prompt-required lists.
    
    ## Testing
    
    - `cargo test -p codex-shell-command`
  • Remove core MCP list tools op (#21281)
    ## Why
    
    The core `Op::ListMcpTools` request path is no longer needed. Keeping it
    around left a dead request/response surface alongside the app-server MCP
    inventory APIs that own current server status listing.
    
    ## What Changed
    
    - Removed `Op::ListMcpTools`, `EventMsg::McpListToolsResponse`, and the
    core handler that built the MCP snapshot response.
    - Removed the now-unused `codex-mcp` snapshot wrapper/export and passive
    event handling arms in rollout and MCP-server consumers.
    - Updated tests that used the old op as a synchronization hook to wait
    on existing startup/skills events, and deleted the plugin test that only
    exercised the removed listing op.
    
    ## Validation
    
    - `cargo test -p codex-protocol`
    - `cargo test -p codex-mcp`
    - `cargo test -p codex-rollout -p codex-rollout-trace -p
    codex-mcp-server`
    - `cargo test -p codex-core --test all
    pending_input::queued_inter_agent_mail`
    - `cargo test -p codex-core --test all
    rmcp_client::stdio_mcp_tool_call_includes_sandbox_state_meta`
    - `cargo test -p codex-core --test all
    rmcp_client::stdio_image_responses`
    - `just fix -p codex-core -p codex-protocol -p codex-mcp -p
    codex-rollout -p codex-rollout-trace -p codex-mcp-server`
  • vendor: update bubblewrap to 0.11.2 (#21389)
    ## Why
    
    `codex-rs/vendor/bubblewrap` had fallen behind upstream, and upstream
    `v0.11.2` is the current Bubblewrap release. The release is a security
    update for `CVE-2026-41163`, affecting setuid Bubblewrap builds, and
    deprecates setuid support in favor of the default non-setuid build mode.
    
    ## What changed
    
    - Refreshed the vendored Bubblewrap sources under
    `codex-rs/vendor/bubblewrap` to upstream `v0.11.2`.
    - Brought in the upstream `-Dsupport_setuid` build option, which
    defaults setuid support off.
    - Updated vendored release notes and documentation files included with
    Bubblewrap.
    
    ## Verification
    
    Not run locally; this PR only refreshes the vendored upstream Bubblewrap
    source snapshot.
    
    Upstream release:
    https://github.com/containers/bubblewrap/releases/tag/v0.11.2
  • fix(tui): keep Ctrl-C stashed drafts after /clear (#21351)
    ## Why
    
    When a user stashes a draft with Ctrl+C, then runs `/clear`, the fresh
    chat session loses the in-memory composer history that held the stashed
    draft. Pressing Up after `/clear` can then recall an older submitted
    prompt instead of the draft the user explicitly saved for later.
    
    ## What Changed
    
    - Record Ctrl+C-cleared composer text through the existing message
    history path, so it survives the fresh session created by `/clear`.
    - Keep `/clear` itself out of local slash-command recall so it does not
    sit ahead of the stashed draft.
    - Add regression coverage for the full flow: submit a prompt, stash a
    later draft with Ctrl+C, run `/clear`, then recall the stashed draft
    before the older prompt.
    
    ## How to Test
    
    1. Start Codex with `just c`.
    2. Submit a short prompt such as `ok` and wait for the turn to complete.
    3. Type a new draft, press Ctrl+C, then run `/clear`.
    4. Press Up and confirm the stashed draft is restored.
    5. Press Up again and confirm the older submitted prompt is still
    reachable after the stashed draft.
    
    Targeted tests:
    
    - `cargo test -p codex-tui
    slash_clear_after_ctrl_c_keeps_stashed_draft_recallable`
    
    Manual verification:
    
    - Reproduced the issue in tmux with `RUST_LOG=trace just c -c
    log_dir=...`: before the fix, Up after `/clear` recalled the older
    submitted prompt.
    - Re-tested the same tmux flow after the fix: Up after `/clear` restored
    the Ctrl+C-stashed draft.
  • [codex] Coordinate OpenAI docs sample with API key setup (#21263)
    ## Summary
    - Add the same API key setup coordination guidance to the embedded
    OpenAI Docs sample skill in `codex-rs/skills`.
    - Keep the skill description/frontmatter unchanged; the coordination
    lives only in the body.
    - Preserve direct OpenAI Docs routing for docs-only questions,
    citations, model/API guidance, conceptual explanations, and non-building
    examples.
    
    ## Why
    The Codex repo carries its own OpenAI Docs skill variant under
    `codex-rs/skills/src/assets/samples`. This keeps that embedded sample
    aligned with the other OpenAI Docs variants patched in the related PRs.
    
    ## Validation
    - `cargo test -p codex-skills`
    - `git diff --check`
  • feat: move auto vaccum (#21378)
    The initial vaccum is not needed anymore. We can consider all the DBs
    have been reclaimed by now
  • rollout: coalesce thread updated_at touches (#21367)
    ## Why
    
    Metadata-irrelevant rollout events currently refresh
    `threads.updated_at` on every flush. That keeps thread recency accurate,
    but it also turns high-frequency agent output into unnecessary SQLite
    writes. Recency only needs to advance periodically during an active
    session, while the final suppressed touch still needs to be persisted
    before shutdown.
    
    ## What changed
    
    - coalesce touch-only `updated_at` writes in the rollout writer, with a
    short production interval between persisted touches
    - retain the latest suppressed touch and flush it during shutdown so the
    thread is not left stale
    - extend rollout recorder coverage for coalesced touches, delayed
    refresh, shutdown flushing, and the existing missing-thread fallback
    path
    
    ## Verification
    
    - Added regression coverage in `rollout/src/recorder_tests.rs` for
    coalescing and shutdown flushing behavior.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Add response.processed websocket request (#21284)
    ## Summary
    
    - Add a `response.processed` websocket request payload and sender for
    Responses API websockets.
    - Send `response.processed` from `try_run_sampling_request` after a
    response completes, local turn processing succeeds, and the
    session-owned feature flag is enabled.
    - Add websocket coverage for both enabled and disabled feature-flag
    behavior.
    
    ## Validation
    
    - `just fmt`
    - `cargo test -p codex-core response_processed`
    - `cargo test -p codex-api responses_websocket`
    - `cargo test -p codex-features
    responses_websocket_response_processed_is_under_development`
    - `git diff --check`
    - `just fix -p codex-api -p codex-core -p codex-features`
    - `git diff --check origin/main...HEAD`
  • Move message history out of core (#21278)
    ## Why
    
    Message history was implemented inside `codex-core` and surfaced through
    core protocol ops and `SessionConfiguredEvent` fields even though the
    current consumer is TUI-local prompt recall. That made core own UI
    history persistence and exposed `history_log_id` / `history_entry_count`
    through surfaces that app-server and other clients do not need.
    
    This change moves message history persistence out of core and keeps the
    recall plumbing local to the TUI.
    
    ## What changed
    
    - Added a new `codex-message-history` crate for appending, looking up,
    trimming, and reading metadata from `history.jsonl`.
    - Removed core protocol history ops/events: `AddToHistory`,
    `GetHistoryEntryRequest`, and `GetHistoryEntryResponse`.
    - Removed `history_log_id` and `history_entry_count` from
    `SessionConfiguredEvent` and updated exec/MCP/test fixtures accordingly.
    - Updated the TUI to dispatch local app events for message-history
    append/lookup and keep its persistent-history metadata in TUI session
    state.
    
    ## Validation
    
    - `cargo test -p codex-message-history -p codex-protocol`
    - `cargo test -p codex-exec event_processor_with_json_output`
    - `cargo test -p codex-mcp-server outgoing_message`
    - `cargo test -p codex-tui`
    - `just fix -p codex-message-history -p codex-protocol -p codex-core -p
    codex-tui -p codex-exec -p codex-mcp-server`
  • 2- Use string service tiers in session protocol (#20971)
    ## Summary
    - break service tier session/op/app-server protocol fields from the
    closed enum to string tier ids
    - send the service tier string directly through model requests, prewarm,
    compaction, memories, and TUI/app-server turn starts
    - regenerate app-server protocol JSON/TypeScript schemas, removing the
    standalone ServiceTier TS enum
    
    ## Verification
    - just fmt
    - cargo check -p codex-core -p codex-app-server -p codex-tui
    - just write-app-server-schema
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] fix builtin MCP Windows path test (#21350)
    ## Summary
    - make the builtin MCP config test derive the expected `--codex-home`
    argument from `AbsolutePathBuf`
    
    ## Why
    `AbsolutePathBuf::try_from("/tmp/codex-home")` is rendered as
    `D:\\tmp\\codex-home` on Windows, but the test asserted the Unix literal
    `"/tmp/codex-home"`. That made the Windows Bazel job fail even though
    the production code was behaving correctly.
    
    ## Impact
    This keeps the test cross-platform while preserving the same transport
    assertion on Unix and Windows.
    
    ## Validation
    - `cargo test -p codex-builtin-mcps`
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat(app-server): move v2 sessionId onto Thread (#21336)
    ## Why
    
    `session_id` and `thread_id` are separate identities after #20437, but
    app-server only surfaced `sessionId` on the `thread/start`,
    `thread/resume`, and `thread/fork` response envelopes. Other
    thread-bearing surfaces such as `thread/list`, `thread/read`,
    `thread/started`, `thread/rollback`, `thread/metadata/update`, and
    `thread/unarchive` either lacked the grouping key or forced clients to
    special-case those three responses.
    
    Making `sessionId` part of the reusable `Thread` payload gives every v2
    API surface one place to expose session-tree identity.
    
    ## Mental model
      1. thread.sessionId lives on `Thread`
    2. It is a view/runtime identity for the current live session tree, not
    durable stored lineage metadata
    3. When app-server has a live loaded thread, it copies the real value
    from core’s session_configured.session_id
    4. When it only has stored/unloaded data, it falls back to
    thread.sessionId = thread.id
    
    ## What changed
    
    - Added `sessionId` to the v2
    [`Thread`](https://github.com/openai/codex/blob/8fc9e9b4cf81b6f61d432e71f1eb266f6f104b63/codex-rs/app-server-protocol/src/protocol/v2/thread_data.rs#L105-L109).
    - Removed the duplicate top-level `sessionId` fields from
    `thread/start`, `thread/resume`, and `thread/fork`; clients should now
    read `response.thread.sessionId`.
    - Populated `thread.sessionId` when building live thread responses,
    replaying loaded threads, and returning stored-thread summaries so the
    field is present across start, resume, fork, list, read, rollback,
    metadata-update, unarchive, and `thread/started` paths. See
    [`load_thread_from_resume_source_or_send_internal`](https://github.com/openai/codex/blob/8fc9e9b4cf81b6f61d432e71f1eb266f6f104b63/codex-rs/app-server/src/request_processors/thread_processor.rs#L2824-L2918)
    and
    [`thread_from_stored_thread`](https://github.com/openai/codex/blob/8fc9e9b4cf81b6f61d432e71f1eb266f6f104b63/codex-rs/app-server/src/request_processors/thread_processor.rs#L3671-L3719).
    - Preserved the stored-thread fallback: if a thread has not been loaded
    into a live session tree yet, `thread.sessionId` falls back to
    `thread.id`; once the thread is live again, the field reports the active
    session tree root.
    - Regenerated the JSON/TypeScript schemas and updated the app-server
    README examples to show
    [`thread.sessionId`](https://github.com/openai/codex/blob/8fc9e9b4cf81b6f61d432e71f1eb266f6f104b63/codex-rs/app-server/README.md#L306-L310)
    on the thread object.
  • Move installation ID resolution out of core startup (#21182)
    ## Summary
    
    - resolve or inject the installation ID before core startup and pass it
    through `ThreadManager`, `CodexSpawnArgs`, and `Session` as a plain
    `String`
    - keep child sessions on the parent installation ID instead of
    rediscovering it inside core
    - propagate installation ID startup failures in `mcp-server` instead of
    panicking
    
    ## Why
    
    Core was still touching the filesystem on the session startup path to
    discover `installation_id`. This moves that work to the outer host
    boundary so core no longer depends on `codex_home` reads during session
    construction.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Propagate cache key and service tiers in compact (#21249)
    ## Why
    
    `/responses/compact` should preserve the request-affinity fields that
    apply to the active auth mode. ChatGPT-auth compact requests need the
    effective `service_tier`, and compact requests for every auth mode need
    the stable `prompt_cache_key`, so compaction does not quietly lose
    routing or cache behavior that normal sampling already has.
    
    This follows the request-parity direction from #20719, but keeps the net
    change focused on the compact payload fields needed here.
    
    ## What changed
    
    - Add `service_tier` and `prompt_cache_key` to the compact endpoint
    input payload.
    - Build the remote compact payload from the existing responses request
    builder output so `Fast` still maps to `priority` when compact sends a
    service tier.
    - Pass the turn service tier into remote compaction, but only include it
    in compact payloads for ChatGPT-backed auth.
    - Keep `prompt_cache_key` on compact payloads for all auth modes.
    - Add request-body diff snapshot coverage in
    `core/tests/suite/compact_remote.rs` for:
    - API-key auth reusing `prompt_cache_key` while omitting `service_tier`
    even when `Fast` is configured.
      - ChatGPT auth reusing both `service_tier` and `prompt_cache_key`.
    - Drive the snapshot coverage through five varied turns: plain text,
    multi-part text, tool-call continuation, image+text input, local-shell
    continuation, and final-turn reasoning output.
    
    ## Verification
    
    - Added insta snapshots for compact request-body parity against the last
    normal `/responses` request after five varied turns.
    - Not run locally per repo guidance; relying on GitHub CI for test
    execution.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: return session ID from thread/fork (#21332)
    ## Why
    
    `thread/start` and `thread/resume` already return `sessionId`, but
    `thread/fork` only returned the new thread. That left clients to infer
    the forked thread's session identity from `thread.id`, which kept the
    new `session_id` / `thread_id` split implicit at one lifecycle boundary.
    Follow-up to #20437.
    
    ## What changed
    
    - Add `sessionId` to `ThreadForkResponse`.
    - Populate it from the forked session configuration.
    - Regenerate the v2 JSON/TypeScript schema fixtures and update the
    app-server docs/example.
    - Extend the fork integration test to assert the returned `sessionId`.
    
    ## Verification
    
    - Added coverage in `thread_fork_creates_new_thread_and_emits_started`
    for the new response field.
  • feat: include thread ID in MCP turn metadata (#21329)
    ## Why
    
    MCP tool calls already include `session_id` in `x-codex-turn-metadata`,
    but descendant threads intentionally share that value with the root
    thread. Consumers that need to correlate work at the concrete thread
    level also need the current `thread_id`.
    
    ## What changed
    
    - add `thread_id` to `x-codex-turn-metadata` while preserving
    `session_id` as the shared session identity
    - thread the two identities separately through normal turns and spawned
    review threads
    - add regression coverage for resumed sessions, reserved metadata
    fields, and deferred MCP tool calls
    
    ## Verification
    
    - added focused coverage in `core/src/session/tests.rs`,
    `core/src/turn_metadata_tests.rs`, and `core/tests/suite/search_tool.rs`
  • test: isolate app-server-client in-process test state (#21328)
    ## Why
    
    The in-process `app-server-client` tests were still building their
    configs from the ambient `codex_home` and letting the embedded app
    server create its own state DB when `state_db` was absent. That matters
    because in-process startup falls back to
    `init_state_db_from_config(...)` in that case, so tests can otherwise
    share persisted state instead of getting isolated fixtures:
    [`app-server/src/in_process.rs`](https://github.com/openai/codex/blob/a98623511ba433154ec811fc63091617f5945438/codex-rs/app-server/src/in_process.rs#L368-L373).
    
    ## What changed
    
    - Give each in-process test client its own temporary `codex_home`.
    - Initialize the matching state DB from that per-client config and pass
    it into the client explicitly.
    - Keep the temp directory alive for the lifetime of the test client
    through a small `TestClient` wrapper.
    - Add `tempfile` as a dev dependency for the new harness.
    
    The updated setup lives in
    [`app-server-client/src/lib.rs`](https://github.com/openai/codex/blob/35c1133d45d10931914dbb88a1246a195d025ff6/codex-rs/app-server-client/src/lib.rs#L982-L1055).
    
    ## Testing
    
    - Existing `codex-app-server-client` tests continue to exercise the
    updated in-process client path through the isolated helper.
  • feat: add session_id (#20437)
    ## Summary
    
    Related to
    https://openai.slack.com/archives/C095U48JNL9/p1777537279707449
    TLDR:
    We update the meaning of session ids and thread ids:
    * thread_id stays as now
    * session_id become a shared id between every thread under a /root
    thread (i.e. every sub-agent share the same session id)
    
    This PR introduces an explicit `SessionId` and threads it through the
    protocol/client boundary so `session_id` and `thread_id` can diverge
    when they need to, while preserving compatibility for older serialized
    `session_configured` events.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Support Codex Apps auth elicitations (#19193)
    ## Summary
    
    - request URL-mode MCP elicitations when Codex Apps tool calls fail with
    connector auth metadata
    - route Codex Apps auth URL elicitations into the TUI app-link flow
    
    ## Test plan
    
    - `just fmt`
    - `cargo test -p codex-core mcp_tool_call::tests`
    - `cargo test -p codex-mcp`
    - `cargo test -p codex-tui bottom_pane::app_link_view::tests`
    - `just fix -p codex-core`
    - `just fix -p codex-mcp`
    - `just fix -p codex-tui`
    
    Also attempted broader local runs:
    
    - `cargo test -p codex-core` fails in unrelated
    config/request-permission/proxy-sensitive tests under the current Codex
    Desktop environment.
    - `cargo test -p codex-tui` fails in unrelated status
    snapshots/trust-default tests because the ambient environment renders
    workspace-write/network permission defaults.
  • release: bundle bwrap with Linux codex DotSlash artifact (#21312)
    ## Why
    
    #21255 changed the Linux sandbox fallback so Codex can use a bundled
    `codex-resources/bwrap` executable when no suitable system `bwrap` is
    available. That lookup is relative to the native Codex executable
    returned by
    `std::env::current_exe()`, as implemented in
    [`bundled_bwrap.rs`](https://github.com/openai/codex/blob/9766d3d51cec885114b6d6c53a02e9efbaf87171/codex-rs/linux-sandbox/src/bundled_bwrap.rs#L83-L93).
    
    The release already publishes a separate `bwrap` DotSlash output, but
    the Linux `codex` DotSlash output still pointed at a single-binary
    `.zst` payload. Running the `codex` DotSlash manifest only materializes
    the native `codex` executable; it does not also create sibling files
    from the separate `bwrap` manifest. The fallback path therefore needs
    the Linux `codex` DotSlash artifact itself to include the real `bwrap`
    executable at `codex-resources/bwrap`.
    
    ## What changed
    
    - stage a Linux primary `codex-<target>-bundle.tar.zst` release artifact
    containing `codex` and `codex-resources/bwrap`
    - point the Linux `codex` DotSlash outputs at that bundle tarball
    - leave the standalone `bwrap` DotSlash output in place for consumers
    that want to fetch `bwrap` directly
    
    ## Verification
    
    - `jq . .github/dotslash-config.json`
    - Ruby YAML parse of `.github/workflows/rust-release.yml`
  • fix(bwrap): emit libcap after standalone archive (#21285)
    ## Why
    
    #21255 added the standalone `codex-bwrap` binary. In the Cargo build,
    [`pkg_config::probe("libcap")`](https://github.com/openai/codex/blob/a736cb55a2bce57b4c8e5a4fe56f70c2b2ad892b/codex-rs/bwrap/build.rs#L37-L39)
    emits `-lcap` before
    [`cc::Build::compile("standalone_bwrap")`](https://github.com/openai/codex/blob/a736cb55a2bce57b4c8e5a4fe56f70c2b2ad892b/codex-rs/bwrap/build.rs#L50-L67)
    adds the static bwrap archive. The Linux musl link then sees `-lcap
    -lstandalone_bwrap`; because static archives are resolved left-to-right,
    `cap_from_name` is still undefined once `standalone_bwrap` introduces
    that reference.
    
    The musl setup already builds `libcap.a` and exposes it through
    [`libcap.pc`](https://github.com/openai/codex/blob/a736cb55a2bce57b4c8e5a4fe56f70c2b2ad892b/.github/scripts/install-musl-build-tools.sh#L78-L88),
    so the failure is link ordering rather than a missing dependency.
    
    ## What changed
    
    - probe `libcap` with `cargo_metadata(false)` so `pkg-config` does not
    emit its link flags early
    - emit the discovered `libcap` search paths and libraries after
    `standalone_bwrap` is compiled, preserving the needed static-link order
    
    ## Verification
    
    - `cargo test -p codex-bwrap`
    - `cargo clippy -p codex-bwrap --all-targets`
    
    The affected Linux musl release link is exercised by CI, which is the
    path this fix targets.
  • [mcp] Return Accept early per feedback. (#21277)
    - [x] Return Accept early when auto_deny is enabled per feedback.
  • Preserve session MCP config on refresh (#21055)
    # Overview
    MCP refreshes were rebuilding active threads from fresh disk-backed
    config only, which dropped thread-start session overlays such as
    app-injected MCP servers. This keeps refreshes current with disk config
    while preserving the thread-local config that only the active thread
    knows about.
    
    # Changes
    - Rebuild refreshed config per active thread using that thread's current
    `cwd`, rather than fanning out one app-server config to every thread.
    - Preserve each thread's `SessionFlags` layer while replacing reloadable
    config layers with freshly loaded config, then derive the MCP refresh
    payload from the rebuilt result.
    - Move MCP refresh orchestration into app-server so manual refreshes
    fail loudly while background refreshes remain best-effort, and route
    plugin-triggered refreshes through the same per-thread reload path.
    - Add regression coverage for session overlays, fresh project config,
    plugin-derived MCP config, current requirements, and strict vs
    best-effort refresh behavior.
    
    # Verification
    - Passed focused Rust coverage for the thread-config rebuild behavior
    and deferred MCP refresh flow, plus `cargo test -p codex-app-server
    --lib`.
    - Verified end to end in the Codex dev app against the locally built
    CLI: registered an MCP via thread config, verified that it could be used
    successfully before refresh, manually triggered MCP refresh, and
    verified that it continued to be available afterward.
  • app-server: align dynamic tool identifiers with Responses API (#20724)
    ## Why
    
    Codex currently accepts dynamic tool names and namespaces that the
    upstream Responses function-tool path does not actually support. In
    practice, that means app-server can register a dynamic tool successfully
    and only discover later that the LLM-facing tool contract will reject or
    mishandle it.
    
    This PR tightens the app-server-side dynamic tool contract to match the
    Responses API before we stack dynamic tool hook support on top of it.
    
    ## What changed
    
    - validate dynamic tool `name` against the Responses function-tool
    identifier contract: `^[a-zA-Z0-9_-]+$`, length `1..128`
    - validate dynamic tool `namespace` the same way, with the Responses
    namespace length limit `1..64`
    - reject namespaces that collide with the always-reserved Responses
    runtime namespaces such as `functions`, `multi_tool_use`, `file_search`,
    `web`, `browser`, `image_gen`, `computer`, `container`, `terminal`,
    `python`, `python_user_visible`, `api_tool`, `tool_search`, and
    `submodel_delegator`
    - escape invalid identifiers in error messages so control characters do
    not spill raw into logs or client-visible error text
    - document the tightened dynamic tool identifier contract in
    `codex-rs/app-server/README.md`
    - add both unit coverage for the validator and an app-server integration
    test that rejects a `thread/start` request with Responses-incompatible
    dynamic tool identifiers
    
    ## Verification
    
    - `cargo test -p codex-app-server validate_dynamic_tools_`
    - `cargo test -p codex-app-server --test all
    thread_start_rejects_dynamic_tools_not_supported_by_responses`
  • feat: Add plugin share access controls (#21124)
    Extends `plugin/share/save` to accept optional discoverability and
    shareTargets while uploading plugin contents, and adds
    `plugin/share/updateTargets` for share-only target updates without
    re-uploading.
  • [codex-analytics] rework thread_source for thread analytics (#20949)
    ## Summary
    - make `thread_source` an explicit optional thread-level field on
    `thread/start`, `thread/fork`, and returned thread payloads
    - persist `thread_source` in rollout/session metadata so resumed live
    threads retain the original value
    - replace the old best-effort `session_source` -> `thread_source`
    mapping with an explicit caller-supplied analytics classification
    
    ## Why
    Before this change, analytics `thread_source` was populated by a
    best-effort mapping from `session_source`. `session_source` describes
    the runtime/client surface, not the actual thread-level origin, so that
    projection was not accurate enough to distinguish cases such as `user`,
    `subagent`, `memory_consolidation`, and future thread origins reliably.
    
    Making `thread_source` explicit keeps one thread-level analytics field
    while letting callers provide the real classification directly instead
    of recovering it indirectly from `session_source`.
    
    ## Impact
    For new analytics events, `thread_source` now reflects the explicit
    thread-level classification supplied by the caller rather than an
    inferred value derived from `session_source`. Existing protocol fields
    remain optional; callers that omit `threadSource` now produce `null`
    instead of a best-effort inferred value.
    
    ## Validation
    - `just write-app-server-schema`
    - `cargo test -p codex-analytics -p codex-core -p
    codex-app-server-protocol --no-run`
    - `cargo test -p codex-app-server-protocol
    generated_ts_optional_nullable_fields_only_in_params`
    - `cargo test -p codex-analytics
    thread_initialized_event_serializes_expected_shape`
    - `cargo test -p codex-core
    resume_stopped_thread_from_rollout_preserves_thread_source`
  • Expose plugin manifest keywords in app server (#21271)
    ## Summary
    - Add plugin manifest keywords to core plugin marketplace/detail models
    - Expose keywords on app-server v2 PluginSummary and generated
    schema/types
    - Populate keywords in plugin/list and plugin/read responses for local
    plugins
    
    Depends on https://github.com/openai/openai/pull/891087
    
    ## Validation
    - just fmt
    - just write-app-server-schema
    - cargo test -p codex-app-server-protocol
    - cargo test -p codex-core-plugins
    - cargo test -p codex-app-server
    plugin_list_keeps_valid_marketplaces_when_another_marketplace_fails_to_load
    - cargo test -p codex-app-server
    plugin_read_returns_plugin_details_with_bundle_contents
  • [codex] Remove legacy ListSkills op (#21282)
    ## Why
    
    `skills/list` is already exposed through app-server v2 and covered by
    the app-server test suite. Keeping the separate core `Op::ListSkills`
    path leaves a duplicate legacy protocol surface that no longer needs to
    be maintained.
    
    ## What Changed
    
    - Removed `Op::ListSkills` and `EventMsg::ListSkillsResponse` from the
    core protocol.
    - Deleted the corresponding core session handler and stale core
    integration tests.
    - Removed rollout/MCP ignore branches and protocol v1 docs references
    for the deleted event/op.
    - Left app-server `skills/list` and its existing coverage intact.
    
    ## Validation
    
    - `cargo test -p codex-protocol`
    - `cargo test -p codex-core --test all suite::skills`
    - `cargo check -p codex-mcp-server -p codex-rollout -p
    codex-rollout-trace`
    - `just fix -p codex-core`
  • [codex] Remove unused ListModels op (#21276)
    ## Why
    
    The core protocol still exposed a `ListModels` submission op even though
    no client sends it and the core submission loop treated it as an ignored
    unknown op. Keeping the dead variant made the protocol surface look
    supported while the active model listing API is the app-server
    `model/list` JSON-RPC request.
    
    ## What Changed
    
    - Removed the unused `Op::ListModels` variant from `codex-rs/protocol`.
    - Removed its `Op::kind()` mapping.
    
    The existing app-server `model/list` endpoint is unchanged.
    
    ## Verification
    
    - `cargo test -p codex-protocol`
  • Share Git safe-command logic on Windows (#21275)
    ## Why
    
    BUGB-15601 showed that the Windows safe-command path had drifted from
    the generic Git classifier. The Windows-specific Git parser could
    classify a PowerShell-wrapped `git` command as safe as soon as it found
    a safelisted subcommand, without applying the generic checks for unsafe
    subcommand options such as `--output`, `--ext-diff`, `--textconv`,
    `--paginate`, or `cat-file --filters`.
    
    The generic classifier already models the Git command boundary and the
    read-only argument checks more carefully, so Windows should reuse that
    logic instead of maintaining a smaller parallel parser.
    
    ## What Changed
    
    - Extracted the existing generic Git classification logic into
    `is_safe_git_command`.
    - Updated `windows_safe_commands.rs` to call that shared helper for
    parsed PowerShell `git` commands.
    - Removed the Windows-only Git subcommand safelist, including the
    `cat-file` allowance that was part of the reported bypass.
    - Added a Windows regression test that keeps PowerShell-wrapped Git
    commands with side-effecting options classified unsafe.
    - Made the full-path PowerShell test discover the installed PowerShell
    executable instead of depending on one hard-coded `pwsh.exe` path.
    
    ## Verification
    
    - `cargo test -p codex-shell-command
    rejects_git_subcommand_options_with_side_effects`
    - `cargo test -p codex-shell-command
    git_global_override_flags_are_not_safe`
    - `cargo test -p codex-shell-command
    windows_powershell_full_path_is_safe -- --nocapture`
    
    Co-authored-by: Codex <codex@openai.com>
  • Add model and reasoning effort to MCP turn metadata (#21219)
    ## Why
    - Similar change as https://github.com/openai/codex/pull/19473.
    - Without change: MCP tool calls receive
    `_meta["x-codex-turn-metadata"]` with `session_id`, `turn_id`, and
    `turn_started_at_unix_ms`.
    - Issue: MCP servers may want the model and reasoning effort to better
    understand tool-call behavior and latency relative to turn start.
    
    ## What Changed
    - With change: MCP turn metadata now includes `model` and
    `reasoning_effort`, propagated in `_meta["x-codex-turn-metadata"]`.
    - Normal `/responses` turn metadata headers are unchanged.
    
    ## Verification
    - `codex-rs/core/src/mcp_tool_call_tests.rs`
    - `codex-rs/core/src/turn_metadata_tests.rs`
    - `codex-rs/core/tests/suite/search_tool.rs`
  • [codex] Move thread naming to app server (#21260)
    ## Why
    
    Thread names are app-server metadata now, backed by the thread store and
    sqlite state database. Keeping a core `SetThreadName` op plus a rollout
    `thread_name_updated` event made rename persistence live in the wrong
    layer and required historical replay support for an event that new
    app-server flows should not write.
    
    ## What changed
    
    - Removed `Op::SetThreadName` and `EventMsg::ThreadNameUpdated` from the
    core protocol and deleted the core handler path that appended rename
    events to rollouts.
    - Updated app-server `thread/name/set` so both loaded and unloaded
    threads write through thread-store metadata and app-server emits
    `thread/name/updated` notifications.
    - Updated local thread-store name metadata updates to write sqlite title
    metadata and the legacy thread-name index without appending rollout
    events.
    - Removed state extraction and rollout handling for the deleted
    thread-name event.
    
    ## Validation
    
    - `cargo test -p codex-app-server thread_name_updated_broadcasts`
    - `cargo test -p codex-app-server
    thread_name_set_is_reflected_in_read_list_and_resume`
    - `cargo test -p codex-thread-store
    update_thread_metadata_sets_name_on_active_rollout_and_indexes_name`
    - `cargo test -p codex-state`
    - `cargo check -p codex-mcp-server -p codex-rollout-trace`
    - `just fix -p codex-app-server -p codex-thread-store -p codex-state -p
    codex-mcp-server -p codex-rollout-trace`
    
    ## Docs
    
    No external documentation update is expected for this internal ownership
    change.
  • release: publish standalone bwrap artifacts (#21256)
    **Summary**
    - Build Linux `bwrap` before the main release binaries.
    - Export the release `bwrap` SHA-256 as `CODEX_BWRAP_SHA256` so the
    Codex binary can verify the bundled fallback.
    - Sign, stage, and upload `bwrap` alongside the primary Linux release
    artifacts.
    
    **Verification**
    - YAML parse check for `.github/workflows/rust-release.yml`
    
    
    
    
    
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/21256).
    * #21257
    * __->__ #21256
  • linux-sandbox: use standalone bundled bwrap (#21255)
    **Summary**
    - Add `codex-bwrap`, a standalone `bwrap` binary built from the existing
    vendored bubblewrap sources.
    - Remove the linked vendored bwrap path from `codex-linux-sandbox`;
    runtime now prefers system `bwrap` and falls back to bundled
    `codex-resources/bwrap`.
    - Add bundled SHA-256 verification with missing/all-zero digest as the
    dev-mode skip value, then exec the verified file through
    `/proc/self/fd`.
    - Keep `launcher.rs` focused on choosing and dispatching the preferred
    launcher. Bundled lookup, digest verification, and bundled exec now live
    in `linux-sandbox/src/bundled_bwrap.rs`; Bazel runfiles lookup lives in
    `linux-sandbox/src/bazel_bwrap.rs`; shared argv/fd exec helpers live in
    `linux-sandbox/src/exec_util.rs`.
    - Teach Bazel tests to surface the Bazel-built `//codex-rs/bwrap:bwrap`
    through `CARGO_BIN_EXE_bwrap`; `codex-linux-sandbox` only honors that
    fallback in debug Bazel runfiles environments so release/user runtime
    lookup stays tied to `codex-resources/bwrap`.
    - Allow `codex-exec-server` filesystem helpers to preserve just the
    Bazel bwrap/runfiles variables they need in debug Bazel builds, since
    those helpers intentionally rebuild a small environment before spawning
    `codex-linux-sandbox`.
    - Verify the Bazel bwrap target in Linux release CI with a build-only
    check. Running `bwrap --version` is too strong for GitHub runners
    because bubblewrap still attempts namespace setup there.
    
    **Verification**
    - Latest update: `cargo test -p codex-linux-sandbox`
    - Latest update: `just fix -p codex-linux-sandbox`
    - `cargo check --target x86_64-unknown-linux-gnu -p codex-linux-sandbox`
    could not run locally because this macOS machine does not have
    `x86_64-linux-gnu-gcc`; GitHub Linux Bazel CI is expected to cover the
    Linux-only modules.
    - Earlier in this PR: `cargo test -p codex-bwrap`
    - Earlier in this PR: `cargo test -p codex-exec-server`
    - Earlier in this PR: `cargo check --release -p codex-exec-server`
    - Earlier in this PR: `just fix -p codex-linux-sandbox -p
    codex-exec-server`
    - Earlier in this PR: `bazel test --nobuild
    //codex-rs/linux-sandbox:linux-sandbox-all-test
    //codex-rs/core:core-all-test
    //codex-rs/exec-server:exec-server-file_system-test
    //codex-rs/app-server:app-server-all-test` (analysis completed; Bazel
    then refuses to run tests under `--nobuild`)
    - Earlier in this PR: `bazel build --nobuild //codex-rs/bwrap:bwrap`
    - Prior to this update: `just bazel-lock-update`, `just
    bazel-lock-check`, and YAML parse check for
    `.github/workflows/bazel.yml`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/21255).
    * #21257
    * #21256
    * __->__ #21255
  • ci: trigger rusty-v8 releases from tags (#21259)
    Swap to tag based releasing and allow tags of type `rusty-v8-v*.*.*`
  • chore(app-server-protocol): split v2 API definitions into modules (#21251)
    ## Why
    
    `codex-rs/app-server-protocol/src/protocol/v2.rs` had grown into a
    single ~12k-line definition file for the entire app-server v2 API.
    
    This is purely a mechanical refactor to break up the monolithic `v2.rs`
    file that contains all app-server API v2 types into more modular files,
    grouped by resource (e.g. account, thread, turn, etc.).
    
    `just write-app-server-schema` shows no real changes, so we can be sure
    that this is purely an internal organizational change.
    
    ## What changed
    
    - Replaced the monolithic `protocol/v2.rs` with a `protocol/v2/` module
    tree and a small `mod.rs` that only declares and reexports modules.
    - Grouped v2 API definitions by conceptual owner, including `account`,
    `apps`, `collaboration_mode`, `command_exec`, `config`, `device_key`,
    `experimental_feature`, `feedback`, `fs`, `hook`, `item`, `mcp`,
    `model`, `notification`, `permissions`, `plugin`, `process`, `realtime`,
    `review`, `thread`, `thread_data`, `turn`, and `windows_sandbox`.
    - Moved v2 tests into `protocol/v2/tests.rs` so `mod.rs` stays small.
    - Kept shared protocol helpers in `protocol/v2/shared.rs`, including the
    enum mirroring macro and common cross-resource types.
    - Co-located resource-specific notifications and server-request payloads
    with the modules that own those resources.
    - Regenerated app-server protocol schema fixtures. The schema diffs are
    non-semantic newline-only changes after the refactor.
    
    ## Verification
    
    - `cargo check -p codex-app-server-protocol`
    - `cargo test -p codex-app-server-protocol`
    - `just write-app-server-schema`
  • fix build (#21261)
    I believe a merge race in https://github.com/openai/codex/pull/20689
    broke the build, so this is a quick fix.
    
    `cargo check --tests` passed locally.
  • codex: use ThreadStore history for core review forks (#20577)
    - fork loaded parent threads from `ThreadStore` history in core agent
    control paths
    - migrate guardian review fork history to loaded session history instead
    of rereading rollout files
    
    ## Verification
    - `cargo test -p codex-core spawn_agent_fork`
  • Add cloud executor registration to exec-server (#19575)
    ## Summary
    This PR adds the first `codex-rs` milestone for remote-exec e2e: a local
    `codex exec-server` can now register itself with
    `codex-cloud-environments` and attach to the returned rendezvous
    websocket.
    
    At a high level, `codex exec-server --cloud ...` now:
    - loads ChatGPT auth from normal Codex config
    - registers an executor with `codex-cloud-environments`
    - receives a signed rendezvous websocket URL
    - serves the existing exec-server JSON-RPC protocol over that websocket
    
    ## What Changed
    - Added `--cloud`, `--cloud-base-url`, `--cloud-environment-id`, and
    `--cloud-name` to `codex exec-server`
    - Added a new `exec-server/src/cloud.rs` module that handles:
      - registration requests
      - auth/header setup
      - bounded auth retry on `401/403`
      - reconnect/backoff after websocket disconnects
    - Reused the existing `ConnectionProcessor` / `ExecServerHandler` path
    so cloud mode serves the same exec/filesystem RPC surface as local
    websocket mode
    - Added cloud-specific error variants and minimal docs for the new mode
    
    ## Testing
    Manual e2e test that fully goes through exec server flow with our codex
    cloud agent as orchestrator
  • Inject state DB, agent graph store (#20689)
    ## Why
    
    We want the agent graph store to be passed down the stack as a real
    dependency, the same way we already treat the thread store.
    
    This will let us inject the agent graph store as a real dependency and
    support implementations other than the local SQLite-backed one. Right
    now most code instantiates a state DB and an agent graph store
    just-in-time. Ideally, we would not depend on the state DB directly but
    only read through the higher-level interfaces.
    
    This change makes the dependency boundaries explicit and moves state DB
    initialization to process bootstrap instead of hiding it inside local
    store implementations.
    
    ## What changed
    
    - `ThreadManager` now requires a `StateDbHandle` and an
    `AgentGraphStore` at construction time instead of treating them as
    optional internals.
    - The local store constructors no longer lazily initialize SQLite.
    Callers now initialize the state DB once per process and use that shared
    handle to build:
      - `LocalThreadStore`
      - `LocalAgentGraphStore`
    - App bootstraps (`app-server`, `mcp-server`, `prompt_debug`, and the
    thread-manager sample) now initialize the state DB up front and inject
    the resulting handle down the stack.
    - `app-server` now consistently uses its process-scoped state DB handle
    instead of reopening SQLite or trying to recover it from loaded threads.
    - Device-key storage now reuses the shared state DB handle instead of
    maintaining its own lazy opener.
    - The thread archive / descendant traversal paths now use the injected
    `AgentGraphStore` instead of reaching through local
    thread-store-specific state.
    
    ## Verification
    
    - `cargo check -p codex-core -p codex-thread-store -p codex-app-server
    -p codex-mcp-server -p codex-thread-manager-sample --tests`
    - `cargo test -p codex-thread-store`
    - `cargo test -p codex-core
    thread_manager_accepts_separate_agent_graph_store_and_thread_store --
    --nocapture`
    - `cargo test -p codex-app-server
    thread_archive_archives_spawned_descendants -- --nocapture`
  • Enable V8 sandboxing for source-built builds (#21146)
    ## Summary
    
    This is the first PR in the V8 in-process sandboxing rollout.
    
    It adds the build-system and Rust feature plumbing needed to support
    sandboxed V8 builds, then enables sandboxing by default for the
    source-built Bazel V8 path that we control directly. It deliberately
    keeps the published `rusty_v8` artifact workflows on their current
    non-sandboxed contract so this PR can land and ship independently before
    we change any released artifacts.
    
    ## Rollout plan
    
    - [x] **PR 1: land sandbox plumbing and default source-built Bazel V8 to
    sandboxed mode**
    
    - [ ] **PR 2: publish sandbox-enabled release artifacts and add
    compatibility validation**
    - Produce sandboxed artifact pairs for every released Cargo target that
    does not already use the source-built Bazel path.
    - Add CI coverage that consumes those sandboxed artifacts and verifies:
        - `codex-v8-poc` reports sandbox enabled
        - `codex-code-mode` builds/tests against the sandboxed path
    
    - [ ] **PR 3: switch release consumers to sandboxed artifacts by
    default**
      - Update released artifact selectors/checksums.
    - Enable the Rust `v8_enable_sandbox` feature in the default release
    path.
    - Make the sandboxed artifact family the normal path for published
    builds.
    
    - [ ] **PR 4: remove rollout-only compatibility paths**
    - Remove the temporary non-sandbox release compatibility config once the
    new default has shipped and baked.
      - Keep the invariant tests permanently.
  • [codex] fix TUI turn items view fixtures (#21243)
    ## Summary
    
    Adds the required `items_view` field to the three session picker `Turn`
    test fixtures that populate full turn item lists.
    
    ## Root Cause
    
    `#21063` added `Turn.items_view` to the app-server protocol type. The
    later session picker merge added three test-only
    `codex_app_server_protocol::Turn` literals without the new field, which
    broke Bazel compilation on `main` with `E0063: missing field
    items_view`.
    
    ## Validation
    
    - `just fmt`
    - `cargo test -p codex-tui resume_picker --no-fail-fast`
    - `just argument-comment-lint`
    
    I also ran `cargo test -p codex-tui`; it compiled and ran the suite, but
    this local machine failed two pre-existing status permission-profile
    tests because `/etc/codex/requirements.toml` disallows
    `DangerFullAccess`.