Commit Graph

5801 Commits

  • Add goal persistence foundation (1 / 5) (#18073)
    Adds the persisted goal foundation for the rest of the stack. This PR is
    intentionally limited to feature flag and state-layer behavior;
    app-server APIs, model tools, runtime continuation, and TUI UX are
    layered in later PRs.
    
    ## Why
    
    Goal mode needs durable thread-level state before clients or model tools
    can safely build on it. The state layer needs to know whether a goal
    exists, what objective it tracks, whether it is active, paused,
    budget-limited, or complete, and how much time/token usage has already
    been accounted.
    
    ## What changed
    
    - Added the `goals` feature flag and generated config schema entry.
    - Added the `thread_goals` state table and Rust model for persisted
    thread goals.
    - Added state runtime APIs for creating, replacing, updating, deleting,
    and accounting goal usage.
    - Added `goal_id`-based stale update protection so an old goal update
    cannot overwrite a replacement.
    - Kept this PR scoped to persistence and state runtime behavior, with no
    app-server, model-facing, continuation, or TUI behavior yet.
    
    ## Verification
    
    - Added state runtime coverage for goal creation, replacement, stale
    update protection, status transitions, token-budget behavior, and usage
    accounting.
  • Fix Bazel cargo_bin runfiles paths (#19468)
    ## Summary
    
    Fix a Bazel-only path resolution bug in
    `codex_utils_cargo_bin::cargo_bin`.
    
    Under Bazel runfiles, `rlocation` can return a relative `bazel-out/...`
    path even though `cargo_bin()` documents that it returns an absolute
    path. That can break callers that store the returned binary path and
    later spawn it after changing cwd, because the relative path is resolved
    from the wrong directory.
    
    This patch absolutizes the runfiles-resolved path before returning it.
  • ci: pin codex-action v1.7 (#19472)
    ## Summary
    - update Codex issue automation to pin `openai/codex-action` to
    `5c3f4ccdb2b8790f73d6b21751ac00e602aa0c02`, the commit for `v1.7`
    - keep the release intent visible with `# v1.7` comments beside the hash
    pins
    
    ## Test plan
    - `git diff --check`
    - `yq e '.' .github/workflows/issue-labeler.yml`
    - `yq e '.' .github/workflows/issue-deduplicator.yml`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • permissions: remove legacy read-only access modes (#19449)
    ## Why
    
    `ReadOnlyAccess` was a transitional legacy shape on `SandboxPolicy`:
    `FullAccess` meant the historical read-only/workspace-write modes could
    read the full filesystem, while `Restricted` tried to carry partial
    readable roots. The partial-read model now belongs in
    `FileSystemSandboxPolicy` and `PermissionProfile`, so keeping it on
    `SandboxPolicy` makes every legacy projection reintroduce lossy
    read-root bookkeeping and creates unnecessary noise in the rest of the
    permissions migration.
    
    This PR makes the legacy policy model narrower and explicit:
    `SandboxPolicy::ReadOnly` and `SandboxPolicy::WorkspaceWrite` represent
    the old full-read sandbox modes only. Split readable roots, deny-read
    globs, and platform-default/minimal read behavior stay in the runtime
    permissions model.
    
    ## What changed
    
    - Removes `ReadOnlyAccess` from
    `codex_protocol::protocol::SandboxPolicy`, including the generated
    `access` and `readOnlyAccess` API fields.
    - Updates legacy policy/profile conversions so restricted filesystem
    reads are represented only by `FileSystemSandboxPolicy` /
    `PermissionProfile` entries.
    - Keeps app-server v2 compatible with legacy `fullAccess` read-access
    payloads by accepting and ignoring that no-op shape, while rejecting
    legacy `restricted` read-access payloads instead of silently widening
    them to full-read legacy policies.
    - Carries Windows sandbox platform-default read behavior with an
    explicit override flag instead of depending on
    `ReadOnlyAccess::Restricted`.
    - Refreshes generated app-server schema/types and updates tests/docs for
    the simplified legacy policy shape.
    
    ## Verification
    
    - `cargo check -p codex-app-server-protocol --tests`
    - `cargo check -p codex-windows-sandbox --tests`
    - `cargo test -p codex-app-server-protocol sandbox_policy_`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19449).
    * #19395
    * #19394
    * #19393
    * #19392
    * #19391
    * __->__ #19449
  • fix: Bedrock GPT-5.4 reasoning levels (#19461)
    ## Why
    
    When using the Amazon Bedrock provider with `openai.gpt-5.4-cmb`, the
    model picker allowed `xhigh` because the CMB catalog entry was derived
    from the bundled `gpt-5.4` reasoning metadata. Bedrock rejects that
    effort level, causing the request to fail before the turn can run:
    
    ```text
    {"error":{"code":"validation_error","message":"Failed to deserialize the JSON body into the target type: Invalid 'reasoning': Invalid 'effort': unknown variant `xhigh`, expected one of `high`, `low`, `medium`, `minimal` at line 1 column 77239","param":null,"type":"invalid_request_error"}}
    ```
    
    ## What Changed
    
    - Replace the runtime lookup of bundled `gpt-5.4` metadata for
    `openai.gpt-5.4-cmb` with an explicit Bedrock CMB `ModelInfo` entry.
    - Advertise only the Bedrock-supported CMB reasoning levels: `minimal`,
    `low`, `medium`, and `high`.
    - Keep the existing GPT OSS Bedrock model metadata and reasoning levels
    unchanged.
    - Add catalog coverage for the hardcoded CMB metadata and
    Bedrock-compatible reasoning level list.
  • Refactor log DB into LogWriter interface (#19234)
    ## Why
    
    This prepares feedback log capture for a future remote app-server hook
    sink without changing the current local SQLite upload path. The
    important boundary is now intentionally small: a log sink is a tracing
    `Layer` that can also flush entries it has accepted.
    
    That keeps the existing SQLite implementation simple while giving the
    upcoming gRPC sink a place to fit beside it. SQLite and gRPC have
    different worker/write semantics, so this PR avoids introducing a shared
    buffered-sink abstraction and instead lets each `LogWriter` own the
    buffering mechanics it needs.
    
    ## What Changed
    
    - Added `LogSinkQueueConfig` with the existing local defaults: queue
    capacity `512`, batch size `128`, and flush interval `2s`.
    - Added `LogDbLayer::start_with_config(...)` while preserving
    `LogDbLayer::start(...)` and `log_db::start(...)` defaults.
    - Introduced the `LogWriter` trait as the minimal shared interface:
    `tracing_subscriber::Layer` plus `flush()`.
    - Made `LogDbLayer` implement `LogWriter`.
    - Kept tracing event formatting inside `LogDbLayer`; it still creates
    one `LogEntry` per tracing event before queueing it for SQLite.
    - Kept normal event capture best-effort and non-blocking via bounded
    `try_send`.
    
    ## Behavior Notes
    
    This does not change the SQLite schema, retention behavior,
    `/feedback/upload`, or Sentry upload behavior. Normal log events still
    drop when the queue is full; explicit `flush()` still waits for queue
    capacity and receiver processing before returning.
    
    ## Verification
    
    - `cargo test -p codex-state log_db`
    - `cargo test -p codex-state`
    - `just fix -p codex-state`
    
    The added tests cover configured batch-size flushing, configured
    interval flushing, queue-full drops, and the flush barrier semantics.
  • Serialize legacy Windows PowerShell sandbox tests (#19453)
    ## Why
    
    Recent `main` CI had repeated Windows timeouts in the legacy sandbox
    process tests:
    
    - `codex-windows-sandbox
    session::tests::legacy_capture_powershell_emits_output` failed in runs
    [24909500958](https://github.com/openai/codex/actions/runs/24909500958),
    [24908076251](https://github.com/openai/codex/actions/runs/24908076251),
    [24906197645](https://github.com/openai/codex/actions/runs/24906197645),
    [24905411571](https://github.com/openai/codex/actions/runs/24905411571),
    [24903336028](https://github.com/openai/codex/actions/runs/24903336028),
    and
    [24898949647](https://github.com/openai/codex/actions/runs/24898949647).
    - `legacy_tty_powershell_emits_output_and_accepts_input` failed in the
    same set of runs.
    - `legacy_non_tty_cmd_emits_output` failed in runs
    [24909500958](https://github.com/openai/codex/actions/runs/24909500958),
    [24908076251](https://github.com/openai/codex/actions/runs/24908076251),
    [24906197645](https://github.com/openai/codex/actions/runs/24906197645),
    and
    [24903336028](https://github.com/openai/codex/actions/runs/24903336028).
    - `legacy_non_tty_powershell_emits_output` failed in runs
    [24908076251](https://github.com/openai/codex/actions/runs/24908076251),
    [24906197645](https://github.com/openai/codex/actions/runs/24906197645),
    and
    [24903336028](https://github.com/openai/codex/actions/runs/24903336028).
    
    These failures were 30s timeouts on Windows x64 and/or arm64 rather than
    assertion failures.
    
    ## Root Cause
    
    The active legacy Windows sandbox process tests all exercise host-level
    resources: sandbox setup, ACL/user state, private desktop process
    launch, stdio capture, and PowerShell/cmd child cleanup. Running several
    of these tests concurrently can leave them competing for the same
    Windows sandbox setup path and process/session resources, which makes
    command startup or output collection hang under CI load.
    
    ## What Changed
    
    - Added a shared in-process mutex for the active legacy Windows sandbox
    process tests.
    - Held that guard across each legacy cmd/PowerShell process test so
    those host-resource-heavy cases run one at a time.
    - Kept the skipped legacy cmd TTY tests unchanged.
    
    ## Why This Should Be Reliable
    
    The tests still use unique homes and run the real legacy sandbox process
    path, but they no longer overlap the fragile host-level setup and
    process/session lifecycle. Serializing just this small group removes the
    concurrency race without reducing the behavioral coverage of each test.
    
    ## Verification
    
    - `cargo test -p codex-windows-sandbox`
    - GitHub Windows CI is the primary validation signal for the affected
    tests; on this PR, Windows clippy, Windows release, and Windows local
    Bazel passed after the serialization fix.
  • [codex] Forward Codex Apps tool call IDs to backend metadata (#19207)
    ## Summary
    - include the outer tool `call_id` in Codex Apps MCP request metadata
    under `_meta._codex_apps.call_id`
    - preserve existing Codex Apps metadata like `resource_uri` and
    `contains_mcp_source`
    - add request metadata coverage for both the existing-metadata and
    no-existing-metadata cases
    
    ## Why
    The paired backend change in
    [openai/openai#850796](https://github.com/openai/openai/pull/850796)
    updates MCP compliance logging to prefer `_meta._codex_apps.call_id`
    instead of the JSON-RPC request id. This client change sends that outer
    tool call id so the backend can record the model/tool call identifier
    when it is available.
    
    This is wire-compatible with older backends because `_meta._codex_apps`
    is already reserved backend-only metadata. Backends that do not read
    `call_id` will ignore the extra field.
    
    ## Testing
    - `cargo test -p codex-core request_meta`
    - `just fmt`
    - `just fix -p codex-core`
  • feat: Compress skill paths with root aliases (#19098)
    Add skill root tracking so model-visible skill lists can use short path
    aliases when absolute paths would exceed the metadata budget.
  • [codex] add non-local thread store regression harness (#19266)
    - Add an integration test that guarantees nothing gets written to codex
    home dir or sqlite when running a rollout with a non-local ThreadStore
    - Add an in-memory "spy" ThreadStore for tests like this
    
    Note I could not find a good way to also ensure there were no filesystem
    _reads_ that didn't go through threadstore. I explored a more elaborate
    sandboxed-subprocess approach but it isn't platform portable and felt
    like it wasn't (yet) worth it.
  • Clarify bundled OpenAI Docs upgrade guide wording (#19422)
    ## Summary
    - Mirrors the OpenAI Docs skill cleanup in the bundled Codex skill copy
    - Clarifies reasoning-effort recommendation wording
    - Replaces internal snake_case prompt block names with natural-language
    guidance aligned to the prompting guide
    
    ## Test plan
    - `git diff --check`
    - Verified the old snake_case prompt block names no longer appear in the
    bundled upgrade guide
  • ci: publish codex-app-server release artifacts (#19447)
    ## Why
    The VS Code extension and desktop app do not need the full TUI binary,
    and `codex-app-server` is materially smaller than standalone `codex`. We
    still want to publish it as an official release artifact, but building
    it by tacking another `--bin` onto the existing release `cargo build`
    invocations would lengthen those jobs.
    
    This change keeps `codex-app-server` on its own release bundle so it can
    build in parallel with the existing `codex` and helper bundles.
    
    ## What changed
    - Made `.github/workflows/rust-release.yml` bundle-aware so each macOS
    and Linux MUSL target now builds either the existing `primary` bundle
    (`codex` and `codex-responses-api-proxy`) or a standalone `app-server`
    bundle (`codex-app-server`).
    - Preserved the historical artifact names for the primary macOS/Linux
    bundles so `scripts/stage_npm_packages.py` and
    `codex-cli/scripts/install_native_deps.py` continue to find release
    assets under the paths they already expect, while giving the new
    app-server artifacts distinct names.
    - Added a matching `app-server` bundle to
    `.github/workflows/rust-release-windows.yml`, and updated the final
    Windows packaging job to download, sign, stage, and archive
    `codex-app-server.exe` alongside the existing release binaries.
    - Generalized the shared signing actions in
    `.github/actions/linux-code-sign/action.yml`,
    `.github/actions/macos-code-sign/action.yml`, and
    `.github/actions/windows-code-sign/action.yml` so each workflow row
    declares its binaries once and reuses that list for build, signing, and
    staging.
    - Added `codex-app-server` to `.github/dotslash-config.json` so releases
    also publish a generated DotSlash manifest for the standalone app-server
    binary.
    - Kept the macOS DMG focused on the existing `primary` bundle;
    `codex-app-server` ships as the regular standalone archives and DotSlash
    manifest.
    
    ## Verification
    - Parsed the modified workflow and action YAML files locally with
    `python3` + `yaml.safe_load(...)`.
    - Parsed `.github/dotslash-config.json` locally with `python3` +
    `json.loads(...)`.
    - Reviewed the resulting release matrices, artifact names, and packaging
    paths to confirm that `codex-app-server` is built separately on macOS,
    Linux MUSL, and Windows, while the existing npm staging and Windows
    `codex` zip bundling contracts remain intact.
  • Add gpt-image-2 to bundled OpenAI Docs skill (#19443)
    ## Summary
    - Mirrors openai/skills#374 in the Codex bundled OpenAI Docs skill
    - Adds `gpt-image-2` as the best image generation/edit model
    - Updates `gpt-image-1.5` to less expensive image generation/edit
    quality
    
    ## Test plan
    - `git diff --check`
  • ci: stop publishing GNU Linux release artifacts (#19445)
    ## Why
    We already prefer shipping the MUSL Linux builds, and the in-repo
    release consumers resolve Linux release assets through the MUSL targets.
    Keeping the GNU release jobs around adds release time and extra assets
    without serving the paths we actually publish and consume.
    
    This is also easier to reason about as a standalone change: future work
    can point back to this PR as the intentional decision to stop publishing
    `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu` release
    artifacts.
    
    ## What changed
    - Removed the `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu`
    entries from the `build` matrix in `.github/workflows/rust-release.yml`.
    - Added a short comment in that matrix documenting that Linux release
    artifacts intentionally ship MUSL-linked binaries.
    
    ## Verification
    - Reviewed `.github/workflows/rust-release.yml` to confirm that the
    release workflow now only builds Linux release artifacts for
    `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl`.
  • Migrate fork and resume reads to thread store (#18900)
    - Route cold thread/resume and thread/fork source loading through
    ThreadStore reads instead of direct rollout path operations
    - Keep lookups that explicitly specify a rollout-path using the local
    thread store methods but return an invalid-request error for remote
    ThreadStore configurations
    - Add some additional unit tests for code path coverage
  • permissions: make legacy profile conversion cwd-free (#19414)
    ## Why
    
    The profile conversion path still required a `cwd` even when it was only
    translating a legacy `SandboxPolicy` into a `PermissionProfile`. That
    made profile producers invent an ambient `cwd`, which is exactly the
    anchoring we are trying to remove from permission-profile data. A legacy
    workspace-write policy can be represented symbolically instead: `:cwd =
    write` plus read-only `:project_roots` metadata subpaths.
    
    This PR creates that cwd-free base so the rest of the stack can stop
    threading cwd through profile construction. Callers that actually need a
    concrete runtime filesystem policy for a specific cwd still have an
    explicitly named cwd-bound conversion.
    
    ## What Changed
    
    - `PermissionProfile::from_legacy_sandbox_policy` now takes only
    `&SandboxPolicy`.
    - `FileSystemSandboxPolicy::from_legacy_sandbox_policy` is now the
    symbolic, cwd-free projection for profiles.
    - The old concrete projection is retained as
    `FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd` for
    runtime/boundary code that must materialize legacy cwd behavior.
    - Workspace-write profiles preserve `CurrentWorkingDirectory` and
    `ProjectRoots` special entries instead of materializing cwd into
    absolute paths.
    
    ## Verification
    
    - `cargo check -p codex-protocol -p codex-core -p
    codex-app-server-protocol -p codex-app-server -p codex-exec -p
    codex-exec-server -p codex-tui -p codex-sandboxing -p
    codex-linux-sandbox -p codex-analytics --tests`
    - `just fix -p codex-protocol -p codex-core -p codex-app-server-protocol
    -p codex-app-server -p codex-exec -p codex-exec-server -p codex-tui -p
    codex-sandboxing -p codex-linux-sandbox -p codex-analytics`
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19414).
    * #19395
    * #19394
    * #19393
    * #19392
    * #19391
    * __->__ #19414
  • Skip disabled rows in selection menu numbering and default focus (#19170)
    Selection menus in the TUI currently let disabled rows interfere with
    numbering and default focus. This makes mixed menus harder to read and
    can land selection on rows that are not actionable. This change updates
    the shared selection-menu behavior in list_selection_view so disabled
    rows are not selected when these views open, and prevents them from
    being numbered like selectable rows.
    
    - Disabled rows no longer receive numeric labels
    - Digit shortcuts map to enabled rows only
    - Default selection moves to the first enabled row in mixed menus
    - Updated affected snapshot
    - Added snapshot coverage for a plugin detail error popup
    - Added a focused unit test for shared selection-view behavior
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Update unix socket transport to use WebSocket upgrade (#19244)
    ## Summary
    - Switch Unix socket app-server connections to perform the standard
    WebSocket HTTP Upgrade handshake
    - Update the Unix socket test to exercise a real upgrade over the Unix
    stream
    - Refresh the app-server README to describe the new Unix socket behavior
    
    ## Testing
    - `cargo test -p codex-app-server transport::unix_socket_tests`
    - `just fmt`
    - `git diff --check`
  • [codex] Omit fork turns from thread started notifications (#19093)
    ## Why
    
    `thread/fork` responses intentionally include copied history so the
    caller can render the fork immediately, but `thread/started` is a
    lifecycle notification. The v2 `Thread` contract says notifications
    should return `turns: []`, and the fork path was reusing the response
    thread directly, causing copied turns to be emitted through
    `thread/started` as well.
    
    ## What Changed
    
    - Route app-server `thread/started` notification construction through a
    helper that clears `thread.turns` before sending.
    - Keep `thread/fork` responses unchanged so callers still receive copied
    history.
    - Add persistent and ephemeral fork coverage that asserts
    `thread/started` emits an empty `turns` array while the response retains
    fork history.
    
    ## Testing
    
    - `just fmt`
    - `cargo test -p codex-app-server`
  • Fix: use function apply_patch tool for Bedrock model (#19416)
    ## Why
    
    `openai.gpt-5.4-cmb` is served through the Amazon Bedrock provider,
    whose request validator currently accepts `function` and `mcp` tool
    specs but rejects Responses `custom` tools. The CMB catalog entry reuses
    the bundled `gpt-5.4` metadata, which marks `apply_patch_tool_type` as
    `freeform`. That causes Codex to include an `apply_patch` tool with
    `type: "custom"`, so even heavily disabled sessions can fail before the
    model runs with:
    
    ```text
    Invalid tools: unknown variant `custom`, expected `function` or `mcp`
    ```
    
    This is provider-specific: the model should still expose `apply_patch`,
    but for Bedrock it needs to use the JSON/function tool shape instead of
    the freeform/custom shape.
    
    ## What Changed
    
    - Override the `openai.gpt-5.4-cmb` static catalog entry to set
    `apply_patch_tool_type` to `function` after inheriting the rest of the
    `gpt-5.4` model metadata.
    - Update the catalog test expectation so the CMB entry continues to
    track `gpt-5.4` metadata except for this Bedrock-specific tool shape
    override.
    
    ## Verification
    
    - `cargo test -p codex-model-provider`
    - `just fix -p codex-model-provider`
  • Harden package-manager install policy (#19163)
    ## Summary
    
    This PR hardens package-manager usage across the repo to reduce
    dependency supply-chain risk. It also removes the stale `codex-cli`
    Docker path, which was already broken on `main`, instead of keeping a
    bitrotted container workflow alive.
    
    ## What changed
    
    - Updated pnpm package manager pins and workspace install settings.
    - Removed stale `codex-cli` Docker assets instead of trying to keep a
    broken local container path alive.
    - Added uv settings and lockfiles for the Python SDK packages.
    - Updated Python SDK setup docs to use `uv sync`.
    
    ## Why
    
    This is primarily a security hardening change. It reduces
    package-install and supply-chain risk by ensuring dependency installs go
    through pinned package managers, committed lockfiles, release-age
    settings, and reviewed build-script controls.
    
    For `codex-cli`, the right follow-up was to remove the local Docker path
    rather than keep patching it:
    
    - `codex-cli/Dockerfile` installed `codex.tgz` with `npm install -g`,
    which bypassed the repo lockfile and age-gated pnpm settings.
    - The local `codex-cli/scripts/build_container.sh` helper was already
    broken on `main`: it called `pnpm run build`, but
    `codex-cli/package.json` does not define a `build` script.
    - The container path itself had bitrotted enough that keeping it would
    require extra packaging-specific behavior that was not otherwise needed
    by the repo.
    
    ## Gaps addressed
    
    - Global npm installs bypassed the repo lockfile in Docker and CLI
    reinstall paths, including `codex-cli/Dockerfile` and
    `codex-cli/bin/codex.js`.
    - CI and Docker pnpm installs used `--frozen-lockfile`, but the repo was
    missing stricter pnpm workspace settings for dependency build scripts.
    - Python SDK projects had `pyproject.toml` metadata but no committed
    `uv.lock` coverage or uv age/index settings in `sdk/python` and
    `sdk/python-runtime`.
    - The secure devcontainer install path used npm/global install behavior
    without a local locked package-manager boundary.
    - The local `codex-cli` Docker helper was already broken on `main`, so
    this PR removes that stale Docker path instead of preserving a broken
    surface.
    - pnpm was already pinned, but not to the current repo-wide pnpm version
    target.
    
    ## Verification
    
    - `pnpm install --frozen-lockfile`
    - `.devcontainer/codex-install`: `pnpm install --prod --frozen-lockfile`
    - `.devcontainer/codex-install`: `./node_modules/.bin/codex --version`
    - `sdk/python`: `uv lock --check`, `uv sync --locked --all-extras
    --dry-run`, `uv build`
    - `sdk/python-runtime`: `uv lock --check`, `uv sync --locked --dry-run`,
    `uv build --wheel`
    - `pnpm -r --filter ./sdk/typescript run build`
    - `pnpm -r --filter ./sdk/typescript run lint`
    - `pnpm -r --filter ./sdk/typescript run test`
    - `node --check codex-cli/bin/codex.js`
    - `docker build -f .devcontainer/Dockerfile.secure -t codex-secure-test
    .`
    - `cargo build -p codex-cli`
    - repo-wide package-manager audit
  • Update bundled OpenAI Docs skill for GPT-5.5 (#19407)
    ## Summary
    Updates the bundled OpenAI Docs system skill for GPT-5.5.
    
    ## Changes
    - Updates the bundled latest-model fallback
    - Replaces bundled upgrade guidance with GPT-5.5 migration guidance
    - Replaces bundled prompting guidance with GPT-5.5 prompting guidance
    
    ## Test plan
    - Ran `node scripts/resolve-latest-model-info.js`
    - Verified bundled files match the OpenAI Docs skill fallback content
  • check PID of named pipe consumer (#19283)
    ## Why
    The elevated Windows command runner currently trusts the first process
    that connects to its parent-created named pipes. Tightening the pipe ACL
    already narrows who can reach that boundary, but verifying the connected
    client PID gives the parent one more fail-closed check: it only accepts
    the exact runner process it just spawned.
    
    ## What changed
    - validate `GetNamedPipeClientProcessId` after `ConnectNamedPipe` and
    reject clients whose PID does not match the spawned runner
    - also did some code de-duplication to route the one-shot elevated
    capture flow in `windows-sandbox-rs/src/elevated_impl.rs` through
    `spawn_runner_transport()` so both elevated codepaths use the same pipe
    bootstrap and PID validation
    
    Using the transport unification here also reduces duplication in the
    elevated Windows IPC bootstrap, so future hardening to the runner
    handshake only needs to land in one place.
    
    ## Validation
    - `cargo test -p codex-windows-sandbox`
    - manual testing: one-shot elevated path via `target/debug/codex.exe
    exec` running a randomized shell command and confirming captured output
    - manual testing: elevated session path via `target/debug/codex.exe -c
    'windows.sandbox="elevated"' sandbox windows -- python -u -c ...` with
    stdin/stdout round-trips (`READY`, then `GOT:...` for two input lines)
    
    ---------
    
    Co-authored-by: viyatb-oai <viyatb@openai.com>
  • respect workspace option for disabling plugins (#18907)
    Respects the workspace setting for plugins in Codex
    
    Plugins menu disappears
    Plugins do not load
    Plugins do not load in composer
    
    no plugins loaded
    <img width="809" height="226" alt="Screenshot 2026-04-23 at 3 20 45 PM"
    src="https://github.com/user-attachments/assets/3a4dba8e-69c3-4046-a77e-f13ab77f84b4"
    />
    
    
    no plugins in menu
    <img width="293" height="204" alt="Screenshot 2026-04-23 at 3 20 35 PM"
    src="https://github.com/user-attachments/assets/5cb9bf52-ad72-488f-b90c-5eb457da09a3"
    />
  • Fix hang on turn/interrupt (#18392)
    Fix a bug where the `turn/interrupt` RPC hangs when interrupting a turn
    that has already completed.
    
    Before this change, `turn/interrupt` requests were queued in app-server
    and only answered when a later TurnAborted event arrived. If the target
    turn was already complete, core treated Op::Interrupt as a no-op, so no
    abort event was emitted and the RPC could hang indefinitely.
    
    This change fixes that in two places:
    
    * Reject turn/interrupt immediately with `INVALID_REQUEST` when the
    requested turn is no longer the active turn.
    * Resolve any already-accepted pending interrupt requests when the turn
    reaches TurnComplete, covering the case where a turn finishes naturally
    after the interrupt request is accepted but before it aborts.
    
    I tested this by adding a failing test in
    707487c0634834f6741986b64f61886c2dc10108. You may view the results here:
    https://github.com/openai/codex/actions/runs/24585182419/
    
    <img width="1512" height="310" alt="CleanShot 2026-04-17 at 16 33 30@2x"
    src="https://github.com/user-attachments/assets/f4a88228-b2a4-41f4-9aaa-ec82814096af"
    />
  • Add agents.interrupt_message for interruption markers (#19351)
    ## Why
    
    Agent interruptions currently always persist a model-visible
    interrupted-turn marker before emitting `TurnAborted`. That marker is
    useful by default because it gives the next model turn context about a
    deliberately interrupted task, but some deployments need to suppress
    that history injection entirely while still keeping the client-visible
    interruption event.
    
    ## What changed
    
    - Add `[agents] interrupt_message = false` to disable the model-visible
    interrupted-turn marker.
    - Resolve the setting into `Config::agent_interrupt_message_enabled`,
    defaulting to `true` so existing behavior is unchanged.
    - Apply the setting to both live interrupted turns and interrupted fork
    snapshots.
    - Keep emitting `TurnAborted` even when the history marker is disabled.
    - Regenerate `core/config.schema.json` for the new
    `agents.interrupt_message` field.
    
    ## Testing
    
    - `cargo test -p codex-core load_config_resolves_agent_interrupt_message
    -- --nocapture`
    - `cargo test -p codex-core
    disabled_interrupted_fork_snapshot_appends_only_interrupt_event --
    --nocapture`
    - `cargo test -p codex-core
    multi_agent_v2_interrupted_marker_uses_developer_input_message --
    --nocapture`
    - `cargo test -p codex-core
    multi_agent_v2_followup_task_can_disable_interrupted_marker --
    --nocapture`
    - `cargo test -p codex-core
    multi_agent_v2_followup_task_interrupts_busy_child_without_losing_message
    -- --nocapture`
    - `cargo check -p codex-core`
  • feat: surface multi-agent thread limit in spawn description (#19360)
    ## Summary
    - Thread `agent_max_threads` into `ToolsConfig` and
    `SpawnAgentToolOptions`.
    - Render the configured `max_concurrent_threads_per_session` value in
    the MultiAgentV2 `spawn_agent` description.
    - Cover the description text in `codex-tools` unit tests and
    `codex-core` tool spec tests.
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-tools`
    - `cargo test -p codex-core spawn_agent_description`
    - `git diff --check`
    
    ## Notes
    - `cargo test -p codex-core` was also attempted, but unrelated
    environment-sensitive tests failed with the active local environment.
    Examples: approvals reviewer defaults observed `AutoReview` instead of
    `User`, request-permissions event tests did not emit events, and
    proxy-env tests saw `http://127.0.0.1:50604` from the active proxy
    environment.
    
    Co-authored-by: Codex <noreply@openai.com>
  • Make MultiAgentV2 interruption markers assistant-authored (#19124)
    ## Why
    
    `MultiAgentV2` follow-up messages are delivered to agents as
    assistant-authored `InterAgentCommunication` envelopes. When
    `followup_task` used `interrupt: true`, the interrupted-turn guidance
    was still persisted as a contextual user message, so model-visible
    history made a system-generated interruption boundary look
    user-authored.
    
    This keeps interruption guidance consistent with the rest of the v2
    inter-agent message stream while preserving the legacy marker shape for
    non-v2 sessions.
    
    ## What changed
    
    - Make `interrupted_turn_history_marker` feature-aware.
    - Record the interrupted-turn marker as an assistant `OutputText`
    message when `Feature::MultiAgentV2` is enabled.
    - Keep the existing user contextual fragment for non-v2 sessions.
    - Apply the same feature-aware marker to interrupted fork snapshots.
    - Add coverage for the live `followup_task` interrupt path and the
    helper-level v2 marker shape.
    
    ## Testing
    
    - `cargo test -p codex-core
    multi_agent_v2_followup_task_interrupts_busy_child_without_losing_message
    -- --nocapture`
    - `cargo test -p codex-core
    multi_agent_v2_interrupted_marker_uses_assistant_output_message --
    --nocapture`
    - `cargo test -p codex-core interrupted_fork_snapshot -- --nocapture`
  • Update models.json and related fixtures (#19323)
    Supersedes #18735.
    
    The scheduled rust-release-prepare workflow force-pushed
    `bot/update-models-json` back to the generated models.json-only diff,
    which dropped the test and snapshot updates needed for CI.
    
    This PR keeps the latest generated `models.json` from #18735 and adds
    the corresponding fixture updates:
    - preserve model availability NUX in the app-server model cache fixture
    - update core/TUI expectations for the new `gpt-5.4` `xhigh` default
    reasoning
    - refresh affected TUI chatwidget snapshots for the `gpt-5.5`
    default/model copy changes
    
    Validation run locally while preparing the fix:
    - `just fmt`
    - `cargo test -p codex-app-server model_list`
    - `cargo test -p codex-core includes_no_effort_in_request`
    - `cargo test -p codex-core
    includes_default_reasoning_effort_in_request_when_defined_by_model_info`
    - `cargo test -p codex-tui --lib chatwidget::tests`
    - `cargo insta pending-snapshots`
    
    ---------
    
    Co-authored-by: aibrahim-oai <219906144+aibrahim-oai@users.noreply.github.com>
  • Surface reasoning tokens in exec JSON usage (#19308)
    ## Summary
    
    Fixes #19022.
    
    `codex exec --json` currently emits `turn.completed.usage` with input,
    cached input, and output token counts, but drops the reasoning-token
    split that Codex already receives through thread token usage updates.
    Programmatic consumers that rely on the JSON stream, especially
    ephemeral runs that do not write rollout files, need this field to
    accurately display reasoning-model usage.
    
    This PR adds `reasoning_output_tokens` to the public exec JSON `Usage`
    payload and maps it from the existing `ThreadTokenUsageUpdated` total
    token usage data.
    
    ## Verification
    
    - Added coverage to
    `event_processor_with_json_output::token_usage_update_is_emitted_on_turn_completion`
    so `turn.completed.usage.reasoning_output_tokens` is asserted.
    - Updated SDK expectations for `run()` and `runStreamed()` so TypeScript
    consumers see the new usage field.
    - Ran `cargo test -p codex-exec`.
    - Ran `pnpm --filter ./sdk/typescript run build`.
    - Ran `pnpm --filter ./sdk/typescript run lint`.
    - Ran `pnpm --filter ./sdk/typescript exec jest --runInBand
    --testTimeout=30000`.
  • Hide unsupported MCP bearer_token from config schema (#19294)
    ## Summary
    
    Fixes #19275.
    
    Codex runtime rejects inline MCP `bearer_token` config entries and asks
    users to configure `bearer_token_env_var` instead, but the generated
    config schema still advertised `mcp_servers.<name>.bearer_token` as a
    supported field. That made editor/schema validation disagree with
    runtime validation.
    
    This keeps `bearer_token` in `RawMcpServerConfig` so Codex can continue
    producing the targeted runtime error for recent or existing configs, but
    skips the field during schemars generation. The checked-in
    `core/config.schema.json` fixture now exposes `bearer_token_env_var`
    without exposing unsupported inline `bearer_token`.
    
    ## Verification
    
    - Added `config_schema_hides_unsupported_inline_mcp_bearer_token` to
    assert the generated schema hides `bearer_token` while preserving
    `bearer_token_env_var`.
    - Ran `cargo test -p codex-config`.
    - Ran `cargo test -p codex-core config_schema`.
  • chore: apply truncation policy to unified_exec (#19247)
    we were not respecting turn's `truncation_policy` to clamp output tokens
    for `unified_exec` and `write_stdin`.
    
    this meant truncation was only being applied by `ContextManager` before
    the output was stored in-memory (so it _was_ being truncated from
    model-visible context), but the full output was persisted to rollout on
    disk.
    
    now we respect that `truncation_policy` and `ContextManager`-level
    truncation remains a backup.
    
    ### Tests
    added tests, tested locally.
  • Reject unsupported js_repl image MIME types (#19292)
    ## Summary
    
    `codex.emitImage` accepted arbitrary image MIME types for byte payloads
    and data URLs. That allowed a value like `image/rgba` to be wrapped as
    an `input_image`, even though it is not a supported encoded image
    format, so the invalid image could reach the model-input path and
    trigger output sanitization.
    
    This results in a panic in debug builds because the output sanitization
    is meant as a final safety net, not a primary means of rejecting invalid
    image types. I've hit this case multiple times when executing certain
    long-running tasks.
    
    This PR rejects unsupported image MIME types before they are emitted
    from `js_repl`.
    
    ## Changes
    
    - Validate `codex.emitImage({ bytes, mimeType })` in the JS kernel so
    only encoded PNG, JPEG, WebP, or GIF payloads are accepted.
    - Apply the same MIME allowlist to direct image data URLs, including the
    Rust host-side validation path.
    - Clarify the JS REPL instructions so agents know byte payloads must
    already be encoded as PNG/JPEG/WebP/GIF.
  • ci: reuse Bazel CI startup for target-discovery queries (#19232)
    ## Why
    
    A rerun of the Windows Bazel clippy job after
    [#19161](https://github.com/openai/codex/pull/19161) had exactly the
    cache behavior we wanted in BuildBuddy: zero action-cache misses. Even
    so, the GitHub job still took a little over five minutes.
    
    The problem was that the job was paying for two separate Bazel startup
    paths:
    
    1. a `bazel query` to discover extra lint targets
    2. the real `bazel build --config=clippy ...` invocation
    
    On Windows, that query was bypassing the CI Bazel wrapper, so it did not
    reuse the same `--output_user_root`, CI config, or remote-cache setup as
    the real build. In practice that meant the rerun could still cold-start
    a separate Bazel server before the actual clippy build even began.
    
    ## What
    
    - add `.github/scripts/run-bazel-query-ci.sh` to run CI-side Bazel
    queries with the same startup and cache-related flags as the main Bazel
    command
    - switch `scripts/list-bazel-clippy-targets.sh` to use that helper for
    manual `rust_test` target discovery
    - switch `tools/argument-comment-lint/list-bazel-targets.sh` to use the
    same helper
    - simplify `.github/scripts/run-argument-comment-lint-bazel.sh` so its
    Windows-only query path also goes through the shared helper
    
    This keeps the target-discovery queries aligned with the later
    build/test invocation instead of treating them as a separate cold Bazel
    session.
    
    ## Verification
    
    - `bash -n .github/scripts/run-bazel-query-ci.sh`
    - `bash -n scripts/list-bazel-clippy-targets.sh`
    - `bash -n tools/argument-comment-lint/list-bazel-targets.sh`
    - `bash -n .github/scripts/run-argument-comment-lint-bazel.sh`
    - mocked a Windows invocation of `run-bazel-query-ci.sh` and verified it
    forwards `--output_user_root`, `--config=ci-windows`, the BuildBuddy
    auth header, and the repository cache flags
    
    ## Docs
    
    No documentation updates are needed.
  • Resolve relative agent role config paths from layers (#19261)
    Fixes #19257.
    
    ## Summary
    
    Agent roles declared in config layers can set `config_file` to a
    relative path, but deserializing the layer-local `[agents.*]` table
    happened without an `AbsolutePathBuf` base path. That caused configs
    like `config_file = "agents/my-role.toml"` to fail with `AbsolutePathBuf
    deserialized without a base path`.
    
    This updates agent role layer loading to deserialize `[agents.*]` while
    the layer config folder is active as the path base, matching the
    behavior documented for `AgentRoleToml.config_file`. It also adds
    coverage for a user config layer with a relative agent role
    `config_file`.
  • permissions: make profiles represent enforcement (#19231)
    ## Why
    
    `PermissionProfile` is becoming the canonical permissions abstraction,
    but the old shape only carried optional filesystem and network fields.
    It could describe allowed access, but not who is responsible for
    enforcing it. That made `DangerFullAccess` and `ExternalSandbox` lossy
    when profiles were exported, cached, or round-tripped through app-server
    APIs.
    
    The important model change is that active permissions are now a disjoint
    union over the enforcement mode. Conceptually:
    
    ```rust
    pub enum PermissionProfile {
        Managed {
            file_system: FileSystemSandboxPolicy,
            network: NetworkSandboxPolicy,
        },
        Disabled,
        External {
            network: NetworkSandboxPolicy,
        },
    }
    ```
    
    This distinction matters because `Disabled` means Codex should apply no
    outer sandbox at all, while `External` means filesystem isolation is
    owned by an outside caller. Those are not equivalent to a broad managed
    sandbox. For example, macOS cannot nest Seatbelt inside Seatbelt, so an
    inner sandbox may require the outer Codex layer to use no sandbox rather
    than a permissive one.
    
    ## How Existing Modeling Maps
    
    Legacy `SandboxPolicy` remains a boundary projection, but it now maps
    into the higher-fidelity profile model:
    
    - `ReadOnly` and `WorkspaceWrite` map to `PermissionProfile::Managed`
    with restricted filesystem entries plus the corresponding network
    policy.
    - `DangerFullAccess` maps to `PermissionProfile::Disabled`, preserving
    the “no outer sandbox” intent instead of treating it as a lax managed
    sandbox.
    - `ExternalSandbox { network_access }` maps to
    `PermissionProfile::External { network }`, preserving external
    filesystem enforcement while still carrying the active network policy.
    - Split runtime policies that legacy `SandboxPolicy` cannot faithfully
    express, such as managed unrestricted filesystem plus restricted
    network, stay `Managed` instead of being collapsed into
    `ExternalSandbox`.
    - Per-command/session/turn grants remain partial overlays via
    `AdditionalPermissionProfile`; full `PermissionProfile` is reserved for
    complete active runtime permissions.
    
    ## What Changed
    
    - Change active `PermissionProfile` into a tagged union: `managed`,
    `disabled`, and `external`.
    - Keep partial permission grants separate with
    `AdditionalPermissionProfile` for command/session/turn overlays.
    - Represent managed filesystem permissions as either `restricted`
    entries or `unrestricted`; `glob_scan_max_depth` is non-zero when
    present.
    - Preserve old rollout compatibility by accepting the pre-tagged `{
    network, file_system }` profile shape during deserialization.
    - Preserve fidelity for important edge cases: `DangerFullAccess`
    round-trips as `disabled`, `ExternalSandbox` round-trips as `external`,
    and managed unrestricted filesystem + restricted network stays managed
    instead of being mistaken for external enforcement.
    - Preserve configured deny-read entries and bounded glob scan depth when
    full profiles are projected back into runtime policies, including
    unrestricted replacements that now become `:root = write` plus deny
    entries.
    - Regenerate the experimental app-server v2 JSON/TypeScript schema and
    update the `command/exec` README example for the tagged
    `permissionProfile` shape.
    
    ## Compatibility
    
    Legacy `SandboxPolicy` remains available at config/API boundaries as the
    compatibility projection. Existing rollout lines with the old
    `PermissionProfile` shape continue to load. The app-server
    `permissionProfile` field is experimental, so its v2 wire shape is
    intentionally updated to match the higher-fidelity model.
    
    ## Verification
    
    - `just write-app-server-schema`
    - `cargo check --tests`
    - `cargo test -p codex-protocol permission_profile`
    - `cargo test -p codex-protocol
    preserving_deny_entries_keeps_unrestricted_policy_enforceable`
    - `cargo test -p codex-app-server-protocol
    permission_profile_file_system_permissions`
    - `cargo test -p codex-app-server-protocol serialize_client_response`
    - `cargo test -p codex-core
    session_configured_reports_permission_profile_for_external_sandbox`
    - `just fix`
    - `just fix -p codex-protocol`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-core`
    - `just fix -p codex-app-server`
  • [codex] Support remote plugin install writes (#18917)
    ## Summary
    - Add a remote plugin install write call that POSTs the selected remote
    plugin to the ChatGPT cloud plugin API.
    - Align remote install with the latest remote read contract:
    `pluginName` carries the backend remote plugin id directly, for example
    `plugins~Plugin_linear`, and install no longer synthesizes
    `<name>@<marketplace>` ids.
    - Validate remote install ids with the same character rules as remote
    read, return the same install response shape as local installs, and
    include mocked app-server coverage for the write path.
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-app-server --test all plugin_install`
    - `cargo test -p codex-core-plugins`
    - `just fix -p codex-app-server`
    - `just fix -p codex-core-plugins`
  • app-server: persist device key bindings in sqlite (#19206)
    ## Why
    
    Device-key providers should only own platform key material. The
    account/client binding used to authorize a signing payload is app-server
    state, and keeping that state in provider-specific metadata makes the
    same check harder to audit and harder to share across platform
    implementations.
    
    Persisting the binding in the shared state database gives the device-key
    crate a platform-neutral source of truth before it asks a provider to
    sign. It also lets app-server move potentially blocking key operations
    off the main message processor path, which matters once providers may
    wait for OS authentication prompts.
    
    ## What changed
    
    - Add a `device_key_bindings` state migration plus `StateRuntime`
    helpers keyed by `key_id`.
    - Add an async `DeviceKeyBindingStore` abstraction to `codex-device-key`
    and use it from `DeviceKeyStore::create` and `DeviceKeyStore::sign`.
    - Keep provider calls behind async store methods and run the synchronous
    provider work through `spawn_blocking`.
    - Wire app-server device-key RPC handling to the SQLite-backed binding
    store and spawn response/error delivery tasks for device-key requests.
    - Run the turn-start tracing test on the existing larger current-thread
    test harness after the larger async surface made the default test stack
    too small locally.
    
    ## Validation
    
    - `cargo test -p codex-device-key`
    - `cargo test -p codex-state device_key`
    - `cargo test -p codex-state`
    - `cargo test -p codex-app-server device_key`
    - `cargo test -p codex-app-server
    message_processor::tracing_tests::turn_start_jsonrpc_span_parents_core_turn_spans`
    - `cargo test -p codex-app-server`
    - `just fix -p codex-device-key`
    - `just fix -p codex-state`
    - `just fix -p codex-app-server`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `git diff --check`
  • feat: let model providers own model discovery (#18950)
    ## Why
    
    `codex-models-manager` had grown to own provider-specific concerns:
    constructing OpenAI-compatible `/models` requests, resolving provider
    auth, emitting request telemetry, and deciding how provider catalogs
    should be sourced. That made the manager harder to reuse for providers
    whose model catalog is not fetched from the OpenAI `/models` endpoint,
    such as Amazon Bedrock.
    
    This change moves provider-specific model discovery behind
    provider-owned implementations, so the models manager can focus on
    refresh policy, cache behavior, picker ordering, and model metadata
    merging.
    
    ## What Changed
    
    - Introduced a `ModelsManager` trait with separate `OpenAiModelsManager`
    and `StaticModelsManager` implementations.
    - Added `ModelsEndpointClient` so OpenAI-compatible HTTP fetching lives
    outside `codex-models-manager`.
    - Moved `/models` request construction, provider auth resolution,
    timeout handling, and request telemetry into `codex-model-provider` via
    `OpenAiModelsEndpoint`.
    - Added provider-owned `models_manager(...)` construction so configured
    OpenAI-compatible providers use `OpenAiModelsManager`, while
    static/catalog-backed providers can return `StaticModelsManager`.
    - Added an Amazon Bedrock static model catalog for the GPT OSS Bedrock
    model IDs.
    - Updated core/session/thread manager code and tests to depend on
    `Arc<dyn ModelsManager>`.
    - Moved offline model test helpers into
    `codex_models_manager::test_support`.
    ## Metadata References
    
    The Bedrock catalog metadata is based on the official Amazon Bedrock
    OpenAI model documentation:
    
    - [Amazon Bedrock OpenAI
    models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-openai.html)
    lists the Bedrock model IDs, text input/output modalities, and `128,000`
    token context window for `gpt-oss-20b` and `gpt-oss-120b`.
    - [Amazon Bedrock `gpt-oss-120b` model
    card](https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html)
    lists the `bedrock-runtime` model ID `openai.gpt-oss-120b-1:0`, the
    `bedrock-mantle` model ID `openai.gpt-oss-120b`, text-only modalities,
    and `128K` context window.
    - [OpenAI `gpt-oss-120b` model
    docs](https://developers.openai.com/api/docs/models/gpt-oss-120b)
    document configurable reasoning effort with `low`, `medium`, and `high`,
    plus text input/output modality.
    
    The display names, default reasoning effort, and priority ordering are
    Codex-local catalog choices.
    
    ## Test Plan
    - Manually verified app-server model listing with an AWS profile:
    
    ```shell
    CODEX_HOME="$(mktemp -d)" cargo run -p codex-app-server-test-client -- \
      --codex-bin ./target/debug/codex \
      -c 'model_provider="amazon-bedrock"' \
      -c 'model_providers.amazon-bedrock.aws.profile="codex-bedrock"' \
      -c 'model_providers.amazon-bedrock.aws.region="us-west-2"' \
      model-list
    ```
    
    The response returned the Bedrock catalog with `openai.gpt-oss-120b-1:0`
    as the default model and `openai.gpt-oss-20b-1:0` as the second listed
    model, both text-only and supporting low/medium/high reasoning effort.
  • feat: Use short SHA versions for curated plugin cache entries (#19095)
    Curated plugin cache entries now use an 8-character SHA prefix, instead
    of the full SHA, as the cache folder version number.
  • [rollout_trace] Trace sessions and multi-agent edges (#18879)
    ## Summary
    
    Adds the remaining session and multi-agent edge wiring needed to
    reconstruct rollout relationships across spawned agents, resumed
    sessions, and parent/child message delivery.
    
    ## Stack
    
    This is PR 4/5 in the rollout trace stack.
    
    - [#18876](https://github.com/openai/codex/pull/18876): Add rollout
    trace crate
    - [#18877](https://github.com/openai/codex/pull/18877): Record core
    session rollout traces
    - [#18878](https://github.com/openai/codex/pull/18878): Trace tool and
    code-mode boundaries
    - [#18879](https://github.com/openai/codex/pull/18879): Trace sessions
    and multi-agent edges
    - [#18880](https://github.com/openai/codex/pull/18880): Add debug trace
    reduction command
    
    ## Review Notes
    
    This is the stack layer that makes traces useful for multi-threaded
    agent workflows. The main invariant is that reconstructed relationships
    should come from durable rollout data rather than transient in-memory
    manager state wherever possible.
    
    The PR is intentionally small relative to the preceding layers: it uses
    the recorder and reducer contracts already established by the stack and
    only adds the session/agent relationship events needed by later debug
    reduction.
  • Add sticky environment API and thread state (#18897)
    ## Summary
    - add sticky environment selections to app-server v2 thread/start and
    turn/start request flow
    - carry thread-level selections through core session/thread state
    - add app-server coverage for sticky selections and turn overrides
    
    ## Stack
    1. This PR: API and thread persistence
    2. #18898: config.toml named environment loading
    3. #18899: downstream tool/runtime consumers
    
    ## Validation
    - Not run locally; split only.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [rollout_trace] Add debug trace reduction command (#18880)
    ## Summary
    
    Adds the debug CLI entry point for reducing recorded rollout traces.
    This gives developers a direct way to inspect whether the emitted trace
    stream reduces into the expected conversation/runtime model.
    
    ## Stack
    
    This is PR 5/5 in the rollout trace stack.
    
    - [#18876](https://github.com/openai/codex/pull/18876): Add rollout
    trace crate
    - [#18877](https://github.com/openai/codex/pull/18877): Record core
    session rollout traces
    - [#18878](https://github.com/openai/codex/pull/18878): Trace tool and
    code-mode boundaries
    - [#18879](https://github.com/openai/codex/pull/18879): Trace sessions
    and multi-agent edges
    - [#18880](https://github.com/openai/codex/pull/18880): Add debug trace
    reduction command
    
    ## Review Notes
    
    This PR is intentionally last: it depends on the trace crate, core
    recorder, runtime/tool events, and session/agent edge data all existing.
    The command should remain a debug/developer tool and avoid adding new
    runtime behavior.
    
    The useful review question is whether the CLI exposes the reducer in the
    smallest practical way for local inspection without turning the debug
    command into a supported user-facing workflow.
  • feat: expose AWS account state from account/read (#19048)
    ## Why
    
    AWS/Bedrock mode currently reports `account: null` with
    `requiresOpenaiAuth: false` from `account/read`. That suppresses the
    OpenAI-auth requirement, but it does not let app clients distinguish AWS
    auth from any other non-OpenAI custom provider. For the prototype AWS
    provider UX, clients need a simple provider-derived signal so they can
    suppress ChatGPT/API-key login and token-refresh paths without
    hardcoding Bedrock checks.
    
    ## What changed
    
    - Adds an `aws` variant to the v2 `Account` protocol union.
    - Adds `ProviderAccountKind` to `codex-model-provider` so the runtime
    provider owns the app-visible account classification.
    - Makes Amazon Bedrock return `ProviderAccountKind::Aws` from the
    model-provider layer.
    - Updates app-server `account/read` to map `ProviderAccountKind` to the
    existing `GetAccountResponse` wire shape.
    - Preserves the existing `account: null, requiresOpenaiAuth: false`
    behavior for other non-OpenAI providers.
    - Regenerates the app-server protocol schema fixtures.
    - Adds coverage for provider account classification and for the Amazon
    Bedrock `account/read` response.
    
    ## Testing
    
    - `cargo test -p codex-model-provider`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server get_account_with_aws_provider`
    
    ## Notes
    
    I attempted `just bazel-lock-update` and `just bazel-lock-check`, but
    both are blocked in my local environment because `bazel` is not
    installed.
  • Increase app-server WebSocket outbound buffer (#19246)
    Fixes #18203.
    
    ## Why
    
    Remote TUI clients connected through `codex app-server --listen
    ws://...` can receive short bursts of outbound turn and tool-output
    notifications. The WebSocket transport previously used the shared
    128-message channel capacity for its outbound writer queue, so a healthy
    client that briefly lagged during normal output streaming could fill the
    queue and be disconnected immediately.
    
    This is a smaller mitigation than #18265: instead of adding a new
    overflow/backpressure pipeline, keep the existing non-blocking router
    behavior and give WebSocket clients enough bounded headroom for
    realistic bursts.
    
    ## What Changed
    
    - Added a WebSocket-only outbound writer capacity of `64 * 1024`
    messages.
    - Used that larger capacity only for the WebSocket data writer queue in
    `codex-rs/app-server/src/transport/websocket.rs`.
    - Left the shared `CHANNEL_CAPACITY` and the existing disconnect-on-full
    behavior unchanged for internal/control channels and genuinely stuck
    clients.
    
    ## Verification
    
    - `cargo test -p codex-app-server
    transport::tests::broadcast_does_not_block_on_slow_connection`
    - Manually retried the #18203 repro prompt against the remote TUI and
    confirmed it stayed connected.