Commit Graph

5659 Commits

Author SHA1 Message Date
starr-openai
6edcdf2bb5 Rename sticky environment state to environments
Introduce a core-owned EnvironmentSelection type so app-server converts API environment params at the core boundary instead of passing protocol operation structs through session/thread state. Rename the internal sticky field from environment_selections to environments to match the v2 API shape.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 16:56:57 -07:00
starr-openai
57182b5653 Polish sticky environment API wiring
Group thread-start options for lint-friendly callsites and update generated v2 schema for sticky environment selections.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 14:51:58 -07:00
starr-openai
7ce7f245c7 Add app-server tests for sticky environments
Cover sticky thread environment selections and turn-level overrides through the app-server v2 thread/start and turn/start JSON-RPC flow. The matrix mirrors the manual smoke cases for omitted, empty, local, remote, and local plus remote selections.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 14:51:22 -07:00
starr-openai
e1d635f7a4 Add sticky thread environment selections
Allow thread/start to configure sticky environment selections that are used by turns when no per-turn override is supplied. Per-turn environments continue to take precedence, while omitted thread selections preserve the existing default behavior.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 14:51:22 -07:00
starr-openai
b49f5c0336 Avoid expect in local environment lookup
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 13:19:38 -07:00
starr-openai
675777cb96 codex: tighten turn environment errors
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 13:13:32 -07:00
starr-openai
3150be409a codex: remove verbose environment docs
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 13:13:32 -07:00
starr-openai
2d5298849f codex: remove dead experimental helper
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 13:13:32 -07:00
starr-openai
bf85976e6e codex: gate empty experimental fields
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 13:13:31 -07:00
starr-openai
8b6f131cea codex: document turn environments API
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 13:13:31 -07:00
starr-openai
2ec1ad9fa5 Add turn-scoped environment selections
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 13:13:31 -07:00
starr-openai
a8f10909db Pass environment manager to app list task
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 13:11:32 -07:00
starr-openai
a2e02d93fe Reuse EnvironmentManager for app-server connectors
Add a connector loading helper that accepts the existing EnvironmentManager and switch app-server paths to use it. Keep the config-only helper as a temporary fallback for callers such as TUI that do not yet pass the manager through.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 13:00:40 -07:00
starr-openai
2c0a752893 Drop unused exec-server env var re-export
Remove the app-server-client re-export now that environment-manager construction owns CODEX_EXEC_SERVER_URL reading directly in exec-server.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 12:47:17 -07:00
starr-openai
fc7a440ec9 Require runtime paths for environments
Make EnvironmentManagerArgs carry ExecServerRuntimePaths for production construction and route test-only unsandboxed setup through explicit _for_tests helpers. Use the manager local environment for MCP and app-server filesystem fallbacks instead of constructing a fresh default environment.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 12:44:35 -07:00
starr-openai
6967e3f100 Remove redundant environment-backed tools test
Drop the networked integration test for CODEX_EXEC_SERVER_URL=none omitting environment-backed tools. Lower-level coverage already verifies disabled environments omit those tools.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 11:47:30 -07:00
starr-openai
f748352bab Hide environment manager env parsing
Make EnvironmentManagerArgs::default() own CODEX_EXEC_SERVER_URL parsing so production entrypoints can keep using EnvironmentManager::new with struct update syntax for runtime paths. Add explicit test defaults so test managers do not depend on the process environment.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 11:23:22 -07:00
starr-openai
0642d36ae4 Share remote environment exec-server client
Create one lazy exec-server client per remote environment and pass clones into the remote process and filesystem backends. This keeps ExecServerClient as the connected-client type while avoiding duplicate websocket clients for one environment.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 11:13:27 -07:00
starr-openai
154be3fc66 codex: remove low-value environment test
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:35:53 -07:00
starr-openai
e354201dd8 Fix environment manager hardening issues
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:35:53 -07:00
starr-openai
a6c81a05fd Fix environment manager follow-up compile errors
Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:35:53 -07:00
starr-openai
9d3188f79b Use optional default environment for disabled mode
Restore CODEX_EXEC_SERVER_URL=none semantics by making EnvironmentManager::default_environment return None when environment access is disabled. Remove the separate disabled-for-agent flag and derive tool availability from the optional default environment.

Add an end-to-end tool exposure test for CODEX_EXEC_SERVER_URL=none.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:35:53 -07:00
starr-openai
64a9a98695 Document shared environment manager handle
Clarify that SessionServices carries an Arc handle to the process-level EnvironmentManager rather than owning a session-specific manager.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:35:53 -07:00
starr-openai
405b9dbe19 Remove local environment convenience method
Drop the unused local_environment helper and keep local lookups on the generic get_environment API.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:35:53 -07:00
starr-openai
6b8fc183a6 Document environment manager behavior
Add high-level EnvironmentManager docs for local/remote initialization, default environment selection, disabled agent access, and lazy remote connection behavior.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:35:52 -07:00
starr-openai
c215ff4e36 Remove path-specific environment factory
Use EnvironmentManager::new with EnvironmentManagerArgs for runtime-path-aware construction and keep from_env only for the no-args env-var factory.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:35:52 -07:00
starr-openai
36eb75bc7a Move lazy exec-server client handle
Keep the lazy remote exec-server client wrapper alongside ExecServerClient and import it from the client module for environment-backed exec and filesystem use.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:35:52 -07:00
starr-openai
e589fddaec Make default environment lookup infallible
Return concrete default and local environments from EnvironmentManager now that the manager always installs local and default entries. Keep arbitrary ID lookup optional.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:32:41 -07:00
starr-openai
194b9d8571 Rename environment manager args constructor
Use EnvironmentManager::new for args-struct construction and keep from_env methods as the env-var factory entrypoints.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:32:17 -07:00
starr-openai
a7fa3679ba Support multiple managed environments
Refactor EnvironmentManager to own a keyed environment registry with explicit default and local lookups. Keep remote exec-server connections lazy at environment use sites and preserve disabled agent environment access separately from internal local environment access.

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 10:31:40 -07:00
Steve Coffey
38ba876ea9 Refresh generated Python app-server SDK types (#18862)
This is the first step in splitting the Python SDK PyPI publish work
into reviewable layers: land the generated SDK refresh by itself before
changing packaging mechanics. The next PRs will make the runtime wheel
publishable, then wire the SDK package/version pinning to that runtime.

## Summary
- Refresh generated Python app-server v2 models and notification
registry from the current schema.
- Update the public API signature expectations for the newly generated
kwargs.

## Stack
- PR 1 of 3 for the Python SDK PyPI publishing split.
- Follow-up PRs will handle runtime wheel publishing mechanics, then
SDK/package version pinning.

## Tests
- `uv run --extra dev pytest` in `sdk/python` -> 51 passed, 37 skipped.
2026-04-21 10:23:27 -07:00
Michael Bolin
f8562bd47b sandboxing: intersect permission profiles semantically (#18275)
## Why

Permission approval responses must not be able to grant more access than
the tool requested. Moving this flow to `PermissionProfile` means the
comparison must be profile-shaped instead of `SandboxPolicy`-shaped, and
cwd-relative special paths such as `:cwd` and `:project_roots` must stay
anchored to the turn that produced the request.

## What changed

This implements semantic `PermissionProfile` intersection in
`codex-sandboxing` for file-system and network permissions. The
intersection accepts narrower path grants, rejects broader grants,
preserves deny-read carve-outs and glob scan depth, and materializes
cwd-dependent special-path grants to absolute paths before they can be
recorded for reuse.

The request-permissions response paths now use that intersection
consistently. App-server captures the request turn cwd before waiting
for the client response, includes that cwd in the v2 approval params,
and core stores the requested profile plus cwd for direct TUI/client
responses and Guardian decisions before recording turn- or
session-scoped grants. The TUI app-server bridge now preserves the
app-server request cwd when converting permission approval params into
core events.

## Verification

- `cargo test -p codex-sandboxing intersect_permission_profiles --
--nocapture`
- `cargo test -p codex-app-server request_permissions_response --
--nocapture`
- `cargo test -p codex-core
request_permissions_response_materializes_session_cwd_grants_before_recording
-- --nocapture`
- `cargo check -p codex-tui --tests`
- `cargo check --tests`
- `cargo test -p codex-tui
app_server_request_permissions_preserves_file_system_permissions`
2026-04-21 10:23:01 -07:00
pakrym-oai
2a226096f6 Split DeveloperInstructions into individual fragments. (#18813)
Split DeveloperInstructions into individual fragments.
2026-04-21 10:22:36 -07:00
pakrym-oai
5fe767e8e1 Refactor app-server config loading into ConfigManager (#18442)
Localize app-server configuration loading in one place.
2026-04-21 10:22:26 -07:00
Eric Traut
4ed722ab8d Move TUI app tests to modules they cover (#18799)
## Summary

The TUI app refactor in #18753 moved the old `app.rs` tests into a
single `app/tests.rs` file. That kept the split mechanically simple, but
it left several focused unit tests far from the modules they exercise.

This PR is a follow-up that moves tests next to the code they cover.

It also adds `tui/src/app/test_support.rs` for shared fixture
construction.

This is just a mechanical refactoring (no functional changes) and does
not affect any production code.
2026-04-21 10:16:51 -07:00
jif-oai
10e1659d4f Stabilize debug clear memories integration test (#18858)
## Why

`debug_clear_memories_resets_state_and_removes_memory_dir` can be flaky
because the test drops its `sqlx::SqlitePool` immediately before
invoking `codex debug clear-memories`. Dropping the pool does not wait
for all SQLite connections to close, so the CLI can race with still-open
test connections.

## What changed

- Await `pool.close()` before spawning `codex debug clear-memories`.
- Close the reopened verification pool before the temp `CODEX_HOME` is
torn down.

## Verification

- `cargo test -p codex-cli --test debug_clear_memories
debug_clear_memories_resets_state_and_removes_memory_dir`
2026-04-21 18:15:37 +01:00
Eric Traut
b7fec54354 Queue follow-up input during user shell commands (#18820)
Fixes #17954.

## Why
When a manual shell command like `!sleep 10` is running, submitting
plain text such as `hi` currently sends that text as a steer for the
active shell turn. User shell turns are not steerable like model turns,
so the TUI can remain stuck in `Working` after the shell command
finishes.

## What Changed
- Detect when the only active work is one or more
`ExecCommandSource::UserShell` commands.
- Queue plain submitted input in that state so it drains after the shell
command and shell turn complete.
- Preserve `!cmd` submissions during running work so explicit shell
commands keep their existing behavior.
- Add regression coverage for the `!sleep 10` plus `hi` flow in
`chatwidget::tests::exec_flow::user_message_during_user_shell_command_is_queued_not_steered`.

## Verification
- Manually confirmed hang before the fix and no hang after the fix
2026-04-21 10:13:13 -07:00
Casey Chow
41652665f5 [codex] Add tmux-aware OSC 9 notifications (#17836)
## Summary
- wrap OSC 9 notifications in tmux's DCS passthrough so terminal
notifications make it through tmux
- use codex-terminal-detection for OSC 9 auto-selection so tmux sessions
inherit the underlying client terminal support
- add focused notification backend tests for plain OSC 9 and
tmux-wrapped output

## Stack
- base PR: #18479
- review order: #18479, then this PR

## Why
Tmux does not forward OSC 9 notifications directly; the sequence has to
be wrapped in tmux's DCS passthrough envelope. Codex also had local
notification heuristics that could miss supported terminals when running
under tmux, even though codex-terminal-detection already knows how to
attribute tmux sessions to the client terminal.

## Validation
- `just fmt`
- `cargo test -p codex-tui` *(currently blocked by an unrelated existing
compile error in `app-server/src/message_processor.rs:754` referencing
`connection_id` out of scope; not caused by this change)*

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 17:10:36 +00:00
Rennie
3a9df58d06 Propagate thread id in MCP tool metadata (#18093)
## Summary
- attach the authoritative Codex thread id to MCP tool request
`_meta.threadId` for model-initiated tool calls
- attach the same thread id for manual `mcpServer/tool/call` requests
before invoking the MCP server
- cover both metadata helper behavior and the manual app-server MCP path
in tests


needed because the Rust app-server is the last place that still has
authoritative knowledge of “this model-generated MCP tool call belongs
to conversation/thread X” before the request leaves Codex and reaches
Hoopa. It adds threadId to MCP request metadata in the model-generated
tool-call path, using sess.conversation_id, and also does the same for
the manual mcpServer/tool/call path.

## Test plan
- `cargo test -p codex-core
mcp_tool_call_thread_id_meta_is_added_to_request_meta --lib`
- `cargo test -p codex-app-server
mcp_server_tool_call_returns_tool_result`

Paired Hoopa consumer PR: https://github.com/openai/openai/pull/833263
2026-04-21 10:09:46 -07:00
Ruslan Nigmatullin
48f82ca7c5 app-server: define device key v2 protocol (#18428)
## Why

Clients need a stable app-server protocol surface for enrolling a local
device key, retrieving its public key, and producing a device-bound
proof.

The protocol reports `protectionClass` explicitly so clients can
distinguish hardware-backed keys from an explicitly allowed OS-protected
fallback. Signing uses a tagged `DeviceKeySignPayload` enum rather than
arbitrary bytes so each signed statement is auditable at the API
boundary.

## What changed

- Added v2 JSON-RPC methods for `device/key/create`,
`device/key/public`, and `device/key/sign`.
- Added request/response types for device-key metadata, SPKI public
keys, protection classes, and ECDSA signatures.
- Added `DeviceKeyProtectionPolicy` with hardware-only default behavior
and an explicit `allow_os_protected_nonextractable` option.
- Added the initial `remoteControlClientConnection` signing payload
variant.
- Regenerated JSON Schema and TypeScript fixtures for app-server
clients.

## Stack

This is PR 1 of 4 in the device-key app-server stack.

## Validation

- `just write-app-server-schema`
- `cargo test -p codex-app-server-protocol`
2026-04-21 10:08:42 -07:00
Michael Bolin
b06fc8bd0d core: make test-log a dev dependency (#18846)
The `test-log` crate is only used by `codex-core` tests, so it does not
need
to be part of the normal `codex-core` dependency graph. Keeping
`test-log` in
`dev-dependencies` removes it from normal `codex-core` builds and keeps
the
production dependency set a little smaller.

Verification:

- `cargo tree -p codex-core --edges normal --invert test-log`
- `cargo check -p codex-core --lib`
- `cargo test -p codex-core --lib`
2026-04-21 09:48:31 -07:00
jif-oai
bf2a34b4b2 feat: baseline lib (#18848)
This add with 2 entry point:
* `reset_git_repository` that takes a directory and set it as a new git
root
* `diff_since_latest_init` this returns the diff for a given directory
since the last `reset_git_repository`
2026-04-21 17:24:30 +01:00
Michael Bolin
53cf12cd52 build: reduce Rust dev debuginfo (#18844)
## What changed

This PR makes the default Cargo dev profile use line-tables-only debug
info:

```toml
[profile.dev]
debug = 1
```

That keeps useful backtraces while avoiding the cost of full variable
debug
info in normal local dev builds.

This also makes the Bazel CI setting explicit with `-Cdebuginfo=0` for
target
and exec-configuration Rust actions. Bazel/rules_rust does not read
Cargo
profiles for this setting, and the current fastbuild action already
emitted
`--codegen=debuginfo=0`; the Bazel part of this PR makes that choice
direct in
our build configuration.

## Why

The slow codex-core rebuilds are dominated by debug-info codegen, not
parsing
or type checking. On a warm-dependency package rebuild, the baseline
codex-core compile was about 39.5s wall / 38.9s rustc total, with
codegen_crate around 14.0s and LLVM_passes around 13.4s. Setting
codex-core
to line-tables-only debug info brought that to about 27.2s wall / 26.7s
rustc
total, with codegen_crate around 3.1s and LLVM_passes around 2.8s.

`debug = 0` was only about another 0.7s faster than `debug = 1` in the
codex-core measurement, so `debug = 1` is the better default dev
tradeoff: it
captures nearly all of the compile-time win while preserving basic
debuggability.

I also sampled other first-party crates instead of keeping a
codex-core-only
package override. codex-app-server showed the same pattern: rustc total
dropped from 15.85s to 10.48s, while codegen_crate plus LLVM_passes
dropped
from about 13.47s to 3.23s. codex-app-server-protocol had a smaller but
still
real improvement, 16.05s to 14.58s total, and smaller crates showed
modest
wins. That points to a workspace dev-profile policy rather than a
hand-maintained list of large crates.

## Relationship to #18612

[#18612](https://github.com/openai/codex/pull/18612) added the
`dev-small`
profile. That remains useful when someone wants a working local build
quickly
and is willing to opt in with `cargo build --profile dev-small`.

This PR is deliberately less aggressive: it changes the common default
dev
profile while preserving line tables/backtraces. `dev-small` remains the
explicit "build quickly, no debuggability concern" path.

## Other investigation

I looked for another structural win comparable to
[#16631](https://github.com/openai/codex/pull/16631) and
[#16630](https://github.com/openai/codex/pull/16630), but did not find
one.
The attempted TOML monomorphization changes were noisy or worse in
measurement, and the async task changes reduced some instantiations but
only
translated to roughly a one-second improvement while being much more
disruptive. The debug-info setting was the one repeatable, material win
that
survived measurement.

## Verification

- `just bazel-lock-update`
- `just bazel-lock-check`
- `cargo check -p codex-core --lib`
- `cargo test -p codex-core --lib`
- Bazel `aquery --config=ci-linux` confirmed `--codegen=debuginfo=0` and
  `-Cdebuginfo=0` for `//codex-rs/core:core`


---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18844).
* #18846
* __->__ #18844
2026-04-21 09:00:40 -07:00
pakrym-oai
833212115e Move external agent config out of core (#18850)
## Summary
- Move external agent config migration logic and tests from `codex-core`
into `app-server/src/config`.
- Keep the migration service crate-private to app-server and update the
API adapter imports.
- Remove stale core re-exports and expose only the needed marketplace
source helper.

## Testing
- `cargo test -p codex-app-server config::external_agent_config`
- `just fmt`
- `just fix -p codex-app-server`
- `just fix -p codex-core`
- `git diff --check`
2026-04-21 08:33:58 -07:00
Felipe Coury
1101dec9ae fix(tui): disable enhanced keys for VS Code WSL (#18741)
Fixes https://github.com/openai/codex/issues/13638

## Why

VS Code's integrated terminal can run a Linux shell through WSL without
exposing `TERM_PROGRAM` to the Linux process, and with crossterm
keyboard enhancement flags enabled that environment can turn dead-key
composition into malformed key events instead of composed Unicode input.
Codex already handles composed Unicode correctly, so the fix is to avoid
enabling the terminal mode that breaks this path for the affected
terminal combination.

## What Changed

- Automatically skip crossterm keyboard enhancement flags when Codex
detects WSL plus VS Code, including a Windows-side `TERM_PROGRAM` probe
through WSL interop.
- Add `CODEX_TUI_DISABLE_KEYBOARD_ENHANCEMENT` so users can
force-disable or force-enable the keyboard enhancement policy for
diagnosis.

## Verification

- Added unit coverage for env parsing, VS Code detection, and the WSL/VS
Code auto-disable policy.
- `cargo check -p codex-tui` passed.
- `./tools/argument-comment-lint/run.py -p codex-tui -- --tests` passed.
- `cargo test -p codex-tui` was attempted locally, but the checkout
failed during linking before tests executed because V8 symbols from
`codex-code-mode` were unresolved for `arm64`.
2026-04-21 09:57:51 -03:00
Abhinav
ef071cf816 show bash mode in the TUI (#18271)
## What

- Explicitly show our "bash mode" by changing the color and adding a
callout similar to how we do for `Plan mode (shift + tab to cycle)`
- Also replace our `›` composer prefix with a bang `!`


![](https://github.com/user-attachments/assets/f5549c75-3a03-433d-aa57-e4c6d0682c49)

## Why

- It was unclear that we had a Bash mode
- This feels more responsive
- It looks cool!

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-21 00:15:49 -07:00
pakrym-oai
a3ed5068c1 [codex] Tighten code review skill wording (#18818)
## Summary

This updates the code review orchestrator skill wording so the
instruction explicitly requires returning every issue from every
subagent.

## Impact

The change is limited to `.codex/skills/code-review/SKILL.md` and
clarifies review aggregation behavior for future Codex-driven reviews.

## Validation

No tests were run because this is a markdown-only skill wording change.
2026-04-21 00:04:04 -07:00
pash-openai
dc1a8f2190 [tool search] support namespaced deferred dynamic tools (#18413)
Deferred dynamic tools need to round-trip a namespace so a tool returned
by `tool_search` can be called through the same registry key that core
uses for dispatch.

This change adds namespace support for dynamic tool specs/calls,
persists it through app-server thread state, and routes dynamic tool
calls by full `ToolName` while still sending the app the leaf tool name.
Deferred dynamic tools must provide a namespace; non-deferred dynamic
tools may remain top-level.

It also introduces `LoadableToolSpec` as the shared
function-or-namespace Responses shape used by both `tool_search` output
and dynamic tool registration, so dynamic tools use the same wrapping
logic in both paths.

Validation:
- `cargo test -p codex-tools`
- `cargo test -p codex-core tool_search`

---------

Co-authored-by: Sayan Sisodiya <sayan@openai.com>
2026-04-21 14:13:08 +08:00
Michael Bolin
1dcea729d3 chore: enable await-holding clippy lints (#18698)
Follow-up to https://github.com/openai/codex/pull/18178, where we said
the await-holding clippy rule would be enabled separately.

Enable `await_holding_lock` and `await_holding_invalid_type` after the
preceding commits fixed or explicitly documented the current offenders.
2026-04-21 06:06:05 +00:00
Michael Bolin
d62421d322 chore: document intentional await-holding cases (#18423)
## Why

This PR prepares the stack to enable Clippy await-holding lints that
were left disabled in #18178. The mechanical lock-scope cleanup is
handled separately; this PR is the documentation/configuration layer for
the remaining await-across-guard sites.

Without explicit annotations, reviewers and future maintainers cannot
tell whether an await-holding warning is a real concurrency smell or an
intentional serialization boundary.

## What changed

- Configures `clippy.toml` so `await_holding_invalid_type` also covers
`tokio::sync::{MutexGuard,RwLockReadGuard,RwLockWriteGuard}`.
- Adds targeted `#[expect(clippy::await_holding_invalid_type, reason =
...)]` annotations for intentional async guard lifetimes.
- Documents the main categories of intentional cases: active-turn state
transitions that must remain atomic, session-owned MCP manager accesses,
remote-control websocket serialization, JS REPL kernel/process
serialization, OAuth persistence, external bearer token refresh
serialization, and tests that intentionally serialize shared global or
session-owned state.
- For external bearer token refresh, documents the existing
serialization boundary: holding `cached_token` across the provider
command prevents concurrent cache misses from starting duplicate refresh
commands, and the current behavior is small enough that an explicit
expectation is easier to maintain than adding another synchronization
primitive.

## Verification

- `cargo clippy -p codex-login --all-targets`
- `cargo clippy -p codex-connectors --all-targets`
- `cargo clippy -p codex-core --all-targets`
- The follow-up PR #18698 enables `await_holding_invalid_type` and
`await_holding_lock` as workspace `deny` lints, so any undocumented
remaining offender will fail Clippy.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18423).
* #18698
* __->__ #18423
2026-04-20 22:41:54 -07:00