## Why
Responses WebSockets are the normal lower-latency transport for
WebSocket-capable providers. They must not bypass an OS-selected proxy
when `features.respect_system_proxy` is enabled, but disabling
WebSockets whenever the feature is enabled would impose a substantial
performance penalty.
Merged PR #31622 introduced the reusable proxy-aware WebSocket
transport. This PR makes the Responses API its first consumer so the
existing fast path uses the same effective proxy and trust policy as
HTTP.
## What changed
- Register `codex-websocket-client` as a workspace dependency and use it
from `codex-api`.
- Feed the shared crate’s route-independent `WebSocketConnection` into
the existing Responses message pump.
- Require a configured `HttpClientFactory` for normal Responses
WebSocket connections and the CLI doctor probe, so neither path can open
a connection without consulting the effective proxy policy.
- Pass the session factory from `core` and the effective configuration
factory from `doctor`.
- Add an end-to-end Responses test that enables `RespectSystemProxy`,
asserts the resolved policy, completes a turn over WebSocket, and
verifies the connection and request counts.
- Keep the existing Responses protocol handling, ping/pong pump, and
session-scoped HTTP fallback unchanged.
The DNS, proxy, TLS, custom-CA, and Happy Eyeballs implementation and
its transport tests live in merged PR #31622. This PR deliberately
contains only the Responses integration and does not duplicate that
transport code.
## Review guide
1. `codex-rs/codex-api/src/endpoint/responses_websocket.rs` constructs
the shared connector and adapts its uniform stream to the existing pump.
2. `codex-rs/core/src/client.rs` supplies the session-scoped factory for
production Responses connections.
3. `codex-rs/cli/src/doctor.rs` supplies the effective configuration
factory to the handshake probe.
4. `codex-rs/core/tests/suite/client_websockets.rs` covers the
enabled-feature path end to end.
## Test plan
- `cargo check --tests -p codex-api -p codex-core -p codex-cli`
- `just test -p codex-api`
- `just test -p codex-core
responses_websocket_streams_with_system_proxy_feature`
- `cargo shear`
- `just bazel-lock-check`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/31441).
* #31637
* #31431
* #31363
* #31362
* #31361
* __->__ #31441
## Description
This PR removes the last path in core that emits `ExecCommandBegin` /
`ExecCommandEnd` directly.
Every command execution now starts and completes through canonical
`ItemStarted` / `ItemCompleted(TurnItem::CommandExecution)`. The
existing `HasLegacyEvent` compatibility layer still fans out Begin/End
afterward, so raw core event consumers and legacy rollout replay keep
seeing the same events.
`UnifiedExecInteraction` is dormant today. Live unified exec uses
`UnifiedExecStartup` for command lifecycle and `TerminalInteraction` for
`write_stdin` and polling, so this is code cleanup rather than a current
product behavior change. The main win is the code-level invariant where
all core flows emit `TurnItem` instead of legacy events.
## What changed
- Removed the `UnifiedExecInteraction` branches that emitted legacy
command events directly.
- Routed every command source through the existing canonical
`CommandExecution` lifecycle and compatibility fanout.
## Why
Amazon Bedrock's GPT-5.6 variants currently appear as only `Sol`,
`Terra`, and `Luna`. Those labels omit the model family and version,
making them ambiguous in model lists and inconsistent with the naming of
other GPT models.
## What changed
- Rename the three Bedrock model display names to `GPT-5.6 Sol`,
`GPT-5.6 Terra`, and `GPT-5.6 Luna`.
- Strengthen the Bedrock model-manager test to verify both model IDs and
their propagated display names.
Model IDs, ordering, priorities, reasoning support, and default
selection are unchanged.
## Description
This PR moves hook prompts onto the canonical `TurnItem` lifecycle in
core.
Stop hooks now record their `ResponseItem` through the existing
lifecycle path, which emits `ItemStarted` and `ItemCompleted`.
App-server consumes those events directly instead of deriving a hook
prompt from `RawResponseItem`.
## Why
Hook prompts were the only `ThreadItem` app-server synthesized from
`RawResponseItem`. This brings them in line with other core-owned turn
items while preserving legacy rollout replay.
## What changed
- Route stop-hook prompts through
`record_response_item_and_emit_turn_item`.
- Materialize canonical hook prompts in `ThreadHistoryBuilder`.
- Remove `RawResponseItem` to `ThreadItem` synthesis while preserving
legacy rollout replay.
- Add focused coverage for lifecycle emission and canonical and legacy
history materialization.
Follow-up to #30226.
## Why
#30226 makes Apps World State inspect the MCP tool list, while
tool-router construction reads the same list again later in the sampling
request. `list_all_tools()` walks the MCP clients and may reconnect or
wait for tools, so doing that work twice adds latency and lets context
and tool construction observe different MCP states for one request.
## What
- Add a lazy MCP tool snapshot to `StepContext`.
- Reuse that snapshot for Apps World State and tool-router construction.
- Let each new `StepContext` refresh naturally for the next sampling
request, without manager-level caching or invalidation.
## Testing
- `just test -p codex-core apps_instructions`
- `just test -p codex-core
apps_guidance_appears_after_background_recovery_within_a_turn`
## Why
Codex Desktop installs its managed primary runtime under
`%USERPROFILE%\.cache\codex-runtimes`. Elevated Windows sandbox commands
run as dedicated sandbox users. The synchronous runtime ACL refresh
repairs read/execute access for the Desktop runtime directories under
`%LOCALAPPDATA%\OpenAI\Codex`, but did not include the managed primary
runtime cache.
As a result, the Desktop app could discover a bundled runtime while a
sandboxed command received `ACCESS_DENIED` when reading or executing it.
## What changed
- Include `%USERPROFILE%\.cache\codex-runtimes` in the managed runtime
paths considered by the Windows sandbox ACL refresh.
- Reuse the existing inherited read/execute ACL repair; no write
permission is added.
- Add Windows-target regression coverage for the runtime path list and
the primary-runtime-only case.
## Impact
Bundled Python, Node, and native tools remain usable from elevated
Windows sandbox sessions without broadening write access or granting
access to the rest of the user profile.
## Validation
- `just fmt`
- `just test -p codex-windows-sandbox` (10/10 host-side tests passed)
- Windows-target path tests included for CI
## Why
The route-aware WebSocket connection setup in #31441 is transport
infrastructure rather than Responses API protocol logic. Landing it
first in a dedicated crate keeps `codex-api` focused on request and
response behavior and makes the transport reusable by future WebSocket
clients.
WebSockets must also apply the same effective outbound proxy and
custom-CA policy as HTTP without disabling the lower-latency WebSocket
path. Requiring an `HttpClientFactory` when constructing the connector
makes proxy-policy resolution part of the API instead of an optional
call-site convention.
This PR is an independent prerequisite based directly on `main`. After
it merges, #31441 can rebase onto it and replace its in-crate connector
with this API.
## What changed
- Add a new `codex-websocket-client` workspace crate with a
`WebSocketConnector` constructed from the effective `HttpClientFactory`.
- Resolve every destination through that factory before connecting, then
support direct connections, transport-default routing, HTTP proxies, and
TLS-encrypted HTTPS proxies.
- Preserve custom-CA trust for proxy and target TLS handshakes and
preserve Happy Eyeballs fallback for explicit direct and proxy routes.
- Expose an established `WebSocketConnection` as a uniform `Stream` and
`Sink`, hiding route-specific transport types from protocol clients.
- Add focused integration-style coverage for the public connector and
message stream, real WSS over direct and CONNECT routes, implicit and
explicit HTTPS proxy ports, and stalled-address-family fallback.
## Review guide
1. `codex-rs/websocket-client/src/lib.rs` defines the small public API
and the factory-required policy invariant.
2. `codex-rs/websocket-client/src/dialer.rs` contains DNS, TCP, proxy
tunneling, TLS, and WebSocket handshake setup.
3. `codex-rs/websocket-client/src/dialer_tests.rs` verifies the public
stream, direct and proxied WSS paths, HTTPS port preservation, and Happy
Eyeballs timing.
4. There is intentionally no consumer migration here; #31441 will become
the first consumer after this prerequisite merges.
## Test plan
- `cargo check -p codex-websocket-client --tests`
- `just test -p codex-websocket-client`
- `cargo shear`
- `just bazel-lock-check`
## Why
Ultra reasoning may proactively use multiple agents. When
`features.multi_agent_v2.max_concurrent_threads_per_session` is
configured at 8 or higher, explicitly selecting Ultra can allow up to `N
- 1` subagents to work concurrently and increase usage quickly. Showing
the configured limits at selection time makes that tradeoff visible and
points users to the setting that controls it.
## What changed
- Show a warning history cell after the user explicitly selects Ultra
reasoning with a concurrent-thread limit of at least 8.
- Include the configured concurrent-thread count and maximum subagent
count in the warning.
- Apply the warning consistently across model and reasoning pickers,
Plan-mode scope selection, and reasoning shortcuts.
- Keep the trigger limited to the selected reasoning effort and
configured thread limit, independent of how multi-agent v2 is activated.
- Add focused threshold coverage and an `insta` snapshot for the
rendered warning.
## User impact
Users with high multi-agent concurrency receive a concrete warning
immediately after selecting Ultra. Other reasoning efforts, limits below
8, and startup behavior are unchanged.
https://github.com/user-attachments/assets/5795bcb9-432e-42bc-bde3-ee363e9aeb69
## Test plan
- `just test -p codex-tui ultra_reasoning_selection`
- Built the full debug CLI with `cargo build -p codex-cli`.
- Manually verified the debug TUI with a 10-thread limit: switching from
Max to Ultra displayed the warning with 10 concurrent threads and up to
9 subagents.
## Summary
- sanitize Claude-derived fallback titles without changing explicit
custom or generated source titles
- recognize known leading control wrappers, including `ide_opened_file`,
`ide_selection`, `local-command-stdout`, and `local-command-stderr`
- skip separate control-only user records and use the next meaningful
user message as the fallback title
- use `Imported session` only when every user record is control-only
- preserve raw imported messages, previews, and transcript provenance
unchanged
## Why
Sessions without an explicit source title fall back to user-message
content. Claude metadata can arrive as separate leading user records, so
stripping only the first record still exposed raw control markup or
produced an unhelpful fallback.
## Impact
Imported chats now receive readable fallback titles such as `Fix auth
flow`, while the original wrapper-bearing messages remain visible in the
imported transcript.
## Validation
- `just test -p codex-external-agent-sessions` — 37 tests passed
- `just test -p codex-app-server
external_agent_config_import_creates_session_rollouts` — focused
integration test passed
- `just fix -p codex-external-agent-sessions`
- `just fix -p codex-app-server`
- `just bazel-lock-check`
- `just fmt`
## Why
An untrusted exec-server can stop reading requests, never answer them,
or send guessed responses before queued requests are written. Without
client-side admission, the orchestrator can retain unbounded RPC call
futures and request payloads.
We need a hard bound without adding a blanket timeout, because
individual operations already own their timeout and cleanup semantics.
## What changed
- hold one of 64 shared admission permits for the full lifetime of each
regular RPC call, so an early response cannot free capacity while the
call remains queued
- allow `process/terminate` and `fs/close` to use one additional cleanup
permit
- close the transport and fail pending calls if the cleanup permit is
also stuck, allowing teardown or recovery to release remote resources
- leave the wire format and existing timeout behavior unchanged
## Tests
- `rpc_client_call_has_no_implicit_deadline` verifies that ordinary
calls remain untimed
- `rpc_client_bounds_in_flight_calls_and_preserves_cleanup` covers the
regular-call cap, guessed early responses, cleanup admission, and the
cleanup circuit breaker
## Summary
- on macOS, resolve the curated plugin sync Git executable without
executing it
- treat Apple’s `/usr/bin/git` shim as unavailable when `xcode-select
-p` reports that developer tools are absent
- skip directly to the existing GitHub HTTP fallback in that case
- preserve the original `git` command lookup on Windows and Linux,
including CCA
## Root cause
Curated plugin startup sync invokes `git ls-remote` before its HTTP
fallback. On a clean Mac, `git` resolves to Apple’s `/usr/bin/git` shim,
and executing the shim opens the Xcode Command Line Tools installer
before the process can fail and reach HTTP.
On macOS, this change resolves Git through `PATH` without executing it.
If the selected binary is Apple’s shim and developer tools are
unavailable, startup sync marks the Git transport unavailable and enters
the existing HTTP fallback immediately.
The new availability detection is macOS-only by construction. Windows
and Linux still execute the literal `git` command as before. If Git is
missing on Windows, the existing spawn-error path falls back to HTTP;
Linux/CCA receives no new lookup or startup behavior.
## Eager Git audit
I also audited production Git process spawns in `codex-rs`.
- This curated catalog sync is the only default projectless app-server
startup path found.
- Configured Git marketplace auto-upgrade runs Git at plugin startup,
but only after a user has explicitly configured a Git marketplace.
- Experimental Memories has background Git metadata/baseline paths when
the feature is enabled.
- The separate cloud-tasks UI probes Git during environment
autodetection.
- Normal thread/turn Git metadata is gated by filesystem discovery of an
existing `.git` entry.
- Marketplace add/install, patch apply, doctor, and TUI `/diff` paths
are explicitly user-invoked.
## Validation
- `just fmt`
- `just bazel-lock-update` — succeeded with no lockfile delta
- `just test -p codex-core-plugins` — 313 passed
- `just fix -p codex-core-plugins` — completed; emitted one pre-existing
unrelated `large_enum_variant` warning in `manifest.rs`
- `git diff --check`
## Description
Use UUIDv7 for item IDs generated locally in `codex-protocol`.
This covers user messages, agent messages, hook prompts, and context
compaction items. These IDs are generated once and carried through item
lifecycle, so this only changes the UUID version. It keeps generated
item IDs consistent with thread and turn IDs.
## Why
A pasted user message can contain a raw CSI sequence that corrupts
terminal scrollback, making the latest response or input appear missing.
The same sequence may already be present in persisted history when an
affected conversation is resumed.
## What changed
- Remove CSI sequences and non-whitespace control characters from
explicit paste input before it enters the composer.
- Apply the same small transform before rich and raw `UserHistoryCell`
rendering, so existing conversations render safely without mutating
stored message content.
## Summary
- Promote code_mode_host to stable and enable it by default.
- Preserve features.code_mode_host = false as an opt-out to the
in-process runtime.
- Run core code-mode tests through the standalone host.
- Keep explicit coverage for missing-host failures.
## Why
The legacy unelevated Windows sandbox allowed tools to create and update
files in workspace-write roots, but it could not delete files that
already existed there. This breaks operations such as `apply_patch` file
deletion and replacement in the workspace, `TEMP`, and `TMP`.
The delete grant must also preserve deny-write carveouts. Granting
`FILE_DELETE_CHILD` on a writable parent would let the sandbox remove
protected children such as `.git` or an explicit read-only subpath even
when those children have direct deny ACEs.
This addresses the delete-failure variant reported in #30009 and #30712.
It does not claim to fix their separate split-root setup,
elevated-helper, or proxy-related failure modes.
## What Changed
- Give writable-root capability ACEs inheritable `DELETE` rights without
granting parent-level `FILE_DELETE_CHILD`, so descendants can be removed
while protected children remain protected.
- Replace stale write ACEs that still contain `FILE_DELETE_CHILD`, and
make elevated setup detect and refresh that unsafe legacy state.
- Keep read-only capability handling unchanged.
- Add Windows regressions covering pre-existing files in the workspace,
`TEMP`, and `TMP`, plus protected `.git` and outside-root controls.
The core ACL behavior is in
[`acl.rs`](https://github.com/openai/codex/blob/767540eec35cc88dafc34d87fcec7e58087413fb/codex-rs/windows-sandbox-rs/src/acl.rs#L303-L438),
stale-ACE detection is in
[`setup_main/win.rs`](https://github.com/openai/codex/blob/767540eec35cc88dafc34d87fcec7e58087413fb/codex-rs/windows-sandbox-rs/src/bin/setup_main/win.rs#L163-L179),
and the end-to-end regression is in
[`unified_exec/tests.rs`](https://github.com/openai/codex/blob/767540eec35cc88dafc34d87fcec7e58087413fb/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs#L458-L568).
## How to Test
On Windows:
1. Start Codex with the legacy unelevated Windows sandbox and a
workspace-write permission profile.
2. Seed pre-existing files in the workspace, `TEMP`, and `TMP`; also
create a sibling file outside the writable roots and a protected `.git`
directory.
3. Delete the three files inside writable roots through a sandboxed
command or `apply_patch`.
4. Confirm the writable-root files are deleted, while the outside-root
file and protected `.git` directory remain intact.
Targeted tests:
- `just test -p codex-windows-sandbox`
- Windows-only
`legacy_workspace_write_delete_is_limited_to_writable_roots`
- Windows-only `write_root_refresh_replaces_stale_delete_child_grant`
The final SHA passed all 31 required checks, including the Windows Bazel
test matrix, in [run
28886245161](https://github.com/openai/codex/actions/runs/28886245161).
## Summary
MCP operation timeout errors currently print the full debug precision of
the remaining timeout budget. That makes a configured 30-second timeout
show up as something like `29.999999875s`.
This PR rounds the displayed duration to a whole unit, so the error is
clean and stable:
```text
timed out awaiting tools/list after 30s
```
The timeout behavior itself is unchanged; this only affects the
human-facing error text. A regression test covers the reported
`tools/list` case.