This test was flaking on Windows.
Problem: The Windows CI test for turn metadata compared git remote URLs
byte-for-byte even though equivalent remotes can be formatted
differently across Git code paths.
Solution: Normalize the expected and actual origin URLs in the test by
trimming whitespace, removing a trailing slash, and stripping a trailing
.git suffix before comparing.
## Why
`provider_auth_command_supplies_bearer_token` and
`provider_auth_command_refreshes_after_401` were still flaky under
Windows Bazel because the generated fixture used `powershell.exe`, whose
startup can be slow enough to trip the provider-auth timeout in CI.
## What
Replace the generated Windows auth fixture script in
`codex-rs/core/tests/suite/client.rs` with a small `.cmd` script
executed by `cmd.exe /D /Q /C`, and advance `tokens.txt` one line at a
time so the refresh-after-401 test still gets the second token on the
second invocation.
Also align the fixture timeout with the provider-auth default (`5_000`
ms) to avoid introducing a test-only timing budget that is stricter than
production behavior.
## Testing
Left to CI, specifically the Windows Bazel
`//codex-rs/core:core-all-test` coverage for the two provider-auth
command tests.
Send pending mailbox mail after completed reasoning or commentary items
so follow-up requests can pick it up mid-turn.
---------
Co-authored-by: Codex <noreply@openai.com>
This adds an `include_environment_context` config/profile flag that
defaults on, and guards both initial injection and later environment
updates to allow skipping injection of `<environment_context>`.
This PR adds root and profile config switches to omit the generated
`<permissions instructions>` and `<apps_instructions>` prompt blocks
while keeping both enabled by default, and it gates both the initial
developer-context injection and later permissions diff injection so
turning the permissions block off stays effective across turn-context
overrides.
Also added a prompt debug tool that can be used as `codex debug
prompt-input "hello"` and dumps the constructed items list.
# Why this PR exists
This PR is trying to fix a coverage gap in the Windows Bazel Rust test
lane.
Before this change, the Windows `bazel test //...` job was nominally
part of PR CI, but a non-trivial set of `//codex-rs/...` Rust test
targets did not actually contribute test signal on Windows. In
particular, targets such as `//codex-rs/core:core-unit-tests`,
`//codex-rs/core:core-all-test`, and `//codex-rs/login:login-unit-tests`
were incompatible during Bazel analysis on the Windows gnullvm platform,
so they never reached test execution there. That is why the
Cargo-powered Windows CI job could surface Windows-only failures that
the Bazel-powered job did not report: Cargo was executing those tests,
while Bazel was silently dropping them from the runnable target set.
The main goal of this PR is to make the Windows Bazel test lane execute
those Rust test targets instead of skipping them during analysis, while
still preserving `windows-gnullvm` as the target configuration for the
code under test. In other words: use an MSVC host/exec toolchain where
Bazel helper binaries and build scripts need it, but continue compiling
the actual crate targets with the Windows gnullvm cfgs that our current
Bazel matrix is supposed to exercise.
# Important scope note
This branch intentionally removes the non-resource-loading `.rs` test
and production-code changes from the earlier
`codex/windows-bazel-rust-test-coverage` branch. The only Rust source
changes kept here are runfiles/resource-loading fixes in TUI tests:
- `codex-rs/tui/src/chatwidget/tests.rs`
- `codex-rs/tui/tests/manager_dependency_regression.rs`
That is deliberate. Since the corresponding tests already pass under
Cargo, this PR is meant to test whether Bazel infrastructure/toolchain
fixes alone are enough to get a healthy Windows Bazel test signal,
without changing test behavior for Windows timing, shell output, or
SQLite file-locking.
# How this PR changes the Windows Bazel setup
## 1. Split Windows host/exec and target concerns in the Bazel test lane
The core change is that the Windows Bazel test job now opts into an MSVC
host platform for Bazel execution-time tools, but only for `bazel test`,
not for the Bazel clippy build.
Files:
- `.github/workflows/bazel.yml`
- `.github/scripts/run-bazel-ci.sh`
- `MODULE.bazel`
What changed:
- `run-bazel-ci.sh` now accepts `--windows-msvc-host-platform`.
- When that flag is present on Windows, the wrapper appends
`--host_platform=//:local_windows_msvc` unless the caller already
provided an explicit `--host_platform`.
- `bazel.yml` passes that wrapper flag only for the Windows `bazel test
//...` job.
- The Bazel clippy job intentionally does **not** pass that flag, so
clippy stays on the default Windows gnullvm host/exec path and continues
linting against the target cfgs we care about.
- `run-bazel-ci.sh` also now forwards `CODEX_JS_REPL_NODE_PATH` on
Windows and normalizes the `node` executable path with `cygpath -w`, so
tests that need Node resolve the runner's Node installation correctly
under the Windows Bazel test environment.
Why this helps:
- The original incompatibility chain was mostly on the **exec/tool**
side of the graph, not in the Rust test code itself. Moving host tools
to MSVC lets Bazel resolve helper binaries and generators that were not
viable on the gnullvm exec platform.
- Keeping the target platform on gnullvm preserves cfg coverage for the
crates under test, which is important because some Windows behavior
differs between `msvc` and `gnullvm`.
## 2. Teach the repo's Bazel Rust macro about Windows link flags and
integration-test knobs
Files:
- `defs.bzl`
- `codex-rs/core/BUILD.bazel`
- `codex-rs/otel/BUILD.bazel`
- `codex-rs/tui/BUILD.bazel`
What changed:
- Replaced the old gnullvm-only linker flag block with
`WINDOWS_RUSTC_LINK_FLAGS`, which now handles both Windows ABIs:
- gnullvm gets `-C link-arg=-Wl,--stack,8388608`
- MSVC gets `-C link-arg=/STACK:8388608`, `-C
link-arg=/NODEFAULTLIB:libucrt.lib`, and `-C link-arg=ucrt.lib`
- Threaded those Windows link flags into generated `rust_binary`,
unit-test binaries, and integration-test binaries.
- Extended `codex_rust_crate(...)` with:
- `integration_test_args`
- `integration_test_timeout`
- Used those new knobs to:
- mark `//codex-rs/core:core-all-test` as a long-running integration
test
- serialize `//codex-rs/otel:otel-all-test` with `--test-threads=1`
- Added `src/**/*.rs` to `codex-rs/tui` test runfiles, because one
regression test scans source files at runtime and Bazel does not expose
source-tree directories unless they are declared as data.
Why this helps:
- Once host-side MSVC tools are available, we still need the generated
Rust test binaries to link correctly on Windows. The MSVC-side
stack/UCRT flags make those binaries behave more like their Cargo-built
equivalents.
- The integration-test macro knobs avoid hardcoding one-off test
behavior in ad hoc BUILD rules and make the generated test targets more
expressive where Bazel and Cargo have different runtime defaults.
## 3. Patch `rules_rs` / `rules_rust` so Windows MSVC exec-side Rust and
build scripts are actually usable
Files:
- `MODULE.bazel`
- `patches/rules_rs_windows_exec_linker.patch`
- `patches/rules_rust_windows_bootstrap_process_wrapper_linker.patch`
- `patches/rules_rust_windows_build_script_runner_paths.patch`
- `patches/rules_rust_windows_exec_msvc_build_script_env.patch`
- `patches/rules_rust_windows_msvc_direct_link_args.patch`
- `patches/rules_rust_windows_process_wrapper_skip_temp_outputs.patch`
- `patches/BUILD.bazel`
What these patches do:
- `rules_rs_windows_exec_linker.patch`
- Adds a `rust-lld` filegroup for Windows Rust toolchain repos,
symlinked to `lld-link.exe` from `PATH`.
- Marks Windows toolchains as using a direct linker driver.
- Supplies Windows stdlib link flags for both gnullvm and MSVC.
- `rules_rust_windows_bootstrap_process_wrapper_linker.patch`
- For Windows MSVC Rust targets, prefers the Rust toolchain linker over
an inherited C++ linker path like `clang++`.
- This specifically avoids the broken mixed-mode command line where
rustc emits MSVC-style `/NOLOGO` / `/LIBPATH:` / `/OUT:` arguments but
Bazel still invokes `clang++.exe`.
- `rules_rust_windows_build_script_runner_paths.patch`
- Normalizes forward-slash execroot-relative paths into Windows path
separators before joining them on Windows.
- Uses short Windows paths for `RUSTC`, `OUT_DIR`, and the build-script
working directory to avoid path-length and quoting issues in third-party
build scripts.
- Exposes `RULES_RUST_BAZEL_BUILD_SCRIPT_RUNNER=1` to build scripts so
crate-local patches can detect "this is running under Bazel's
build-script runner".
- Fixes the Windows runfiles cleanup filter so generated files with
retained suffixes are actually retained.
- `rules_rust_windows_exec_msvc_build_script_env.patch`
- For exec-side Windows MSVC build scripts, stops force-injecting
Bazel's `CC`, `CXX`, `LD`, `CFLAGS`, and `CXXFLAGS` when that would send
GNU-flavored tool paths/flags into MSVC-oriented Cargo build scripts.
- Rewrites or strips GNU-only `--sysroot`, MinGW include/library paths,
stack-protector, and `_FORTIFY_SOURCE` flags on the MSVC exec path.
- The practical effect is that build scripts can fall back to the Visual
Studio toolchain environment already exported by CI instead of crashing
inside Bazel's hermetic `clang.exe` setup.
- `rules_rust_windows_msvc_direct_link_args.patch`
- When using a direct linker on Windows, stops forwarding GNU driver
flags such as `-L...` and `--sysroot=...` that `lld-link.exe` does not
understand.
- Passes non-`.lib` native artifacts as explicit `-Clink-arg=<path>`
entries when needed.
- Filters C++ runtime libraries to `.lib` artifacts on the Windows
direct-driver path.
- `rules_rust_windows_process_wrapper_skip_temp_outputs.patch`
- Excludes transient `*.tmp*` and `*.rcgu.o` files from process-wrapper
dependency search-path consolidation, so unstable compiler outputs do
not get treated as real link search-path inputs.
Why this helps:
- The host-platform split alone was not enough. Once Bazel started
analyzing/running previously incompatible Rust tests on Windows, the
next failures were in toolchain plumbing:
- MSVC-targeted Rust tests were being linked through `clang++` with
MSVC-style arguments.
- Cargo build scripts running under Bazel's Windows MSVC exec platform
were handed Unix/GNU-flavored path and flag shapes.
- Some generated paths were too long or had path-separator forms that
third-party Windows build scripts did not tolerate.
- These patches make that mixed Bazel/Cargo/Rust/MSVC path workable
enough for the test lane to actually build and run the affected crates.
## 4. Patch third-party crate build scripts that were not robust under
Bazel's Windows MSVC build-script path
Files:
- `MODULE.bazel`
- `patches/aws-lc-sys_windows_msvc_prebuilt_nasm.patch`
- `patches/ring_windows_msvc_include_dirs.patch`
- `patches/zstd-sys_windows_msvc_include_dirs.patch`
What changed:
- `aws-lc-sys`
- Detects Bazel's Windows MSVC build-script runner via
`RULES_RUST_BAZEL_BUILD_SCRIPT_RUNNER` or a `bazel-out` manifest-dir
path.
- Uses `clang-cl` for Bazel Windows MSVC builds when no explicit
`CC`/`CXX` is set.
- Allows prebuilt NASM on the Bazel Windows MSVC path even when `nasm`
is not available directly in the runner environment.
- Avoids canonicalizing `CARGO_MANIFEST_DIR` in the Bazel Windows MSVC
case, because that path may point into Bazel output/runfiles state where
preserving the given path is more reliable than forcing a local
filesystem canonicalization.
- `ring`
- Under the Bazel Windows MSVC build-script runner, copies the
pregenerated source tree into `OUT_DIR` and uses that as the
generated-source root.
- Adds include paths needed by MSVC compilation for
Fiat/curve25519/P-256 generated headers.
- Rewrites a few relative includes in C sources so the added include
directories are sufficient.
- `zstd-sys`
- Adds MSVC-only include directories for `compress`, `decompress`, and
feature-gated dictionary/legacy/seekable sources.
- Skips `-fvisibility=hidden` on MSVC targets, where that
GCC/Clang-style flag is not the right mechanism.
Why this helps:
- After the `rules_rust` plumbing started running build scripts on the
Windows MSVC exec path, some third-party crates still failed for
crate-local reasons: wrong compiler choice, missing include directories,
build-script assumptions about manifest paths, or Unix-only C compiler
flags.
- These crate patches address those crate-local assumptions so the
larger toolchain change can actually reach first-party Rust test
execution.
## 5. Keep the only `.rs` test changes to Bazel/Cargo runfiles parity
Files:
- `codex-rs/tui/src/chatwidget/tests.rs`
- `codex-rs/tui/tests/manager_dependency_regression.rs`
What changed:
- Instead of asking `find_resource!` for a directory runfile like
`src/chatwidget/snapshots` or `src`, these tests now resolve one known
file runfile first and then walk to its parent directory.
Why this helps:
- Bazel runfiles are more reliable for explicitly declared files than
for source-tree directories that happen to exist in a Cargo checkout.
- This keeps the tests working under both Cargo and Bazel without
changing their actual assertions.
# What we tried before landing on this shape, and why those attempts did
not work
## Attempt 1: Force `--host_platform=//:local_windows_msvc` for all
Windows Bazel jobs
This did make the previously incompatible test targets show up during
analysis, but it also pushed the Bazel clippy job and some unrelated
build actions onto the MSVC exec path.
Why that was bad:
- Windows clippy started running third-party Cargo build scripts with
Bazel's MSVC exec settings and crashed in crates such as `tree-sitter`
and `libsqlite3-sys`.
- That was a regression in a job that was previously giving useful
gnullvm-targeted lint signal.
What this PR does instead:
- The wrapper flag is opt-in, and `bazel.yml` uses it only for the
Windows `bazel test` lane.
- The clippy lane stays on the default Windows gnullvm host/exec
configuration.
## Attempt 2: Broaden the `rules_rust` linker override to all Windows
Rust actions
This fixed the MSVC test-lane failure where normal `rust_test` targets
were linked through `clang++` with MSVC-style arguments, but it broke
the default gnullvm path.
Why that was bad:
-
`@@rules_rs++rules_rust+rules_rust//util/process_wrapper:process_wrapper`
on the gnullvm exec platform started linking with `lld-link.exe` and
then failed to resolve MinGW-style libraries such as `-lkernel32`,
`-luser32`, and `-lmingw32`.
What this PR does instead:
- The linker override is restricted to Windows MSVC targets only.
- The gnullvm path keeps its original linker behavior, while MSVC uses
the direct Windows linker.
## Attempt 3: Keep everything on pure Windows gnullvm and patch the V8 /
Python incompatibility chain instead
This would have preserved a single Windows ABI everywhere, but it is a
much larger project than this PR.
Why that was not the practical first step:
- The original incompatibility chain ran through exec-side generators
and helper tools, not only through crate code.
- `third_party/v8` is already special-cased on Windows gnullvm because
`rusty_v8` only publishes Windows prebuilts under MSVC names.
- Fixing that path likely means deeper changes in
V8/rules_python/rules_rust toolchain resolution and generator execution,
not just one local CI flag.
What this PR does instead:
- Keep gnullvm for the target cfgs we want to exercise.
- Move only the Windows test lane's host/exec platform to MSVC, then
patch the build-script/linker boundary enough for that split
configuration to work.
## Attempt 4: Validate compatibility with `bazel test --nobuild ...`
This turned out to be a misleading local validation command.
Why:
- `bazel test --nobuild ...` can successfully analyze targets and then
still exit 1 with "Couldn't start the build. Unable to run tests"
because there are no runnable test actions after `--nobuild`.
Better local check:
```powershell
bazel build --nobuild --keep_going --host_platform=//:local_windows_msvc //codex-rs/login:login-unit-tests //codex-rs/core:core-unit-tests //codex-rs/core:core-all-test
```
# Which patches probably deserve upstream follow-up
My rough take is that the `rules_rs` / `rules_rust` patches are the
highest-value upstream candidates, because they are fixing generic
Windows host/exec + MSVC direct-linker behavior rather than
Codex-specific test logic.
Strong upstream candidates:
- `patches/rules_rs_windows_exec_linker.patch`
- `patches/rules_rust_windows_bootstrap_process_wrapper_linker.patch`
- `patches/rules_rust_windows_build_script_runner_paths.patch`
- `patches/rules_rust_windows_exec_msvc_build_script_env.patch`
- `patches/rules_rust_windows_msvc_direct_link_args.patch`
- `patches/rules_rust_windows_process_wrapper_skip_temp_outputs.patch`
Why these seem upstreamable:
- They address general-purpose problems in the Windows MSVC exec path:
- missing direct-linker exposure for Rust toolchains
- wrong linker selection when rustc emits MSVC-style args
- Windows path normalization/short-path issues in the build-script
runner
- forwarding GNU-flavored CC/link flags into MSVC Cargo build scripts
- unstable temp outputs polluting process-wrapper search-path state
Potentially upstreamable crate patches, but likely with more care:
- `patches/zstd-sys_windows_msvc_include_dirs.patch`
- `patches/ring_windows_msvc_include_dirs.patch`
- `patches/aws-lc-sys_windows_msvc_prebuilt_nasm.patch`
Notes on those:
- The `zstd-sys` and `ring` include-path fixes look fairly generic for
MSVC/Bazel build-script environments and may be straightforward to
propose upstream after we confirm CI stability.
- The `aws-lc-sys` patch is useful, but it includes a Bazel-specific
environment probe and CI-specific compiler fallback behavior. That
probably needs a cleaner upstream-facing shape before sending it out, so
upstream maintainers are not forced to adopt Codex's exact CI
assumptions.
Probably not worth upstreaming as-is:
- The repo-local Starlark/test target changes in `defs.bzl`,
`codex-rs/*/BUILD.bazel`, and `.github/scripts/run-bazel-ci.sh` are
mostly Codex-specific policy and CI wiring, not generic rules changes.
# Validation notes for reviewers
On this branch, I ran the following local checks after dropping the
non-resource-loading Rust edits:
```powershell
cargo test -p codex-tui
just --shell 'C:\Program Files\Git\bin\bash.exe' --shell-arg -lc -- fix -p codex-tui
python .\tools\argument-comment-lint\run-prebuilt-linter.py -p codex-tui
just --shell 'C:\Program Files\Git\bin\bash.exe' --shell-arg -lc fmt
```
One local caveat:
- `just argument-comment-lint` still fails on this Windows machine for
an unrelated Bazel toolchain-resolution issue in
`//codex-rs/exec:exec-all-test`, so I used the direct prebuilt linter
for `codex-tui` as the local fallback.
# Expected reviewer takeaway
If this PR goes green, the important conclusion is that the Windows
Bazel test coverage gap was primarily a Bazel host/exec toolchain
problem, not a need to make the Rust tests themselves Windows-specific.
That would be a strong signal that the deleted non-resource-loading Rust
test edits from the earlier branch should stay out, and that future work
should focus on upstreaming the generic `rules_rs` / `rules_rust`
Windows fixes and reducing the crate-local patch surface.
The `OPENAI_BASE_URL` environment variable has been a significant
support issue, so we decided to deprecate it in favor of an
`openai_base_url` config key. We've had the deprecation warning in place
for about a month, so users have had time to migrate to the new
mechanism. This PR removes support for `OPENAI_BASE_URL` entirely.
## Why
Extracted from [#16528](https://github.com/openai/codex/pull/16528) so
the Windows Bazel app-server test failures can be reviewed independently
from the rest of that PR.
This PR targets:
-
`suite::v2::thread_shell_command::thread_shell_command_runs_as_standalone_turn_and_persists_history`
-
`suite::v2::thread_start::thread_start_with_elevated_sandbox_trusts_project_and_followup_loads_project_config`
-
`suite::v2::thread_start::thread_start_with_nested_git_cwd_trusts_repo_root`
There were two Windows-specific assumptions baked into those tests and
the underlying trust lookup:
- project trust keys were persisted and looked up using raw path
strings, but Bazel's Windows test environment can surface canonicalized
paths with `\\?\` / UNC prefixes or normalized symlink/junction targets,
so follow-up `thread/start` requests no longer matched the project entry
that had just been written
- `item/commandExecution/outputDelta` assertions compared exact trailing
line endings even though shell output chunk boundaries and CRLF handling
can differ on Windows, and Bazel made that timing-sensitive mismatch
visible
There was also one behavior bug separate from the assertion cleanup:
`thread/start` decided whether to persist trust from the final resolved
sandbox policy, but on Windows an explicit `workspace-write` request may
be downgraded to `read-only`. That incorrectly skipped writing trust
even though the request had asked to elevate the project, so the new
logic also keys off the requested sandbox mode.
## What
- Canonicalize project trust keys when persisting/loading `[projects]`
entries, while still accepting legacy raw keys for existing configs.
- Persist project trust when `thread/start` explicitly requests
`workspace-write` or `danger-full-access`, even if the resolved policy
is later downgraded on Windows.
- Make the Windows app-server tests compare persisted trust paths and
command output deltas in a path/newline-normalized way.
## Verification
- Existing app-server v2 tests cover the three failing Windows Bazel
cases above.
- Keep only parent system/developer/user messages plus assistant
final-answer messages in forked child history.
- Strip parent tool/reasoning items and remove the unmatched synthetic
spawn output.
## Why
We were seeing failures in the following tests as part of trying to get
all the tests running under Bazel on Windows in CI
(https://github.com/openai/codex/pull/16528):
```
suite::shell_command::unicode_output::with_login
suite::shell_command::unicode_output::without_login
```
Certainly `PATHEXT` should have been included in the extra `CORE_VARS`
list, so we fix that up here, but also take things a step further for
now by forcibly ensuring it is set on Windows in the return value of
`create_env()`. Once we get the Windows Bazel build working reliably
(i.e., after #16528 is merged), we should come back to this and confirm
we can remove the special case in `create_env()`.
## What
- Split core env inheritance into `COMMON_CORE_VARS` plus
platform-specific allowlists for Windows and Unix in
[`exec_env.rs`](1b55c88fbf/codex-rs/core/src/exec_env.rs (L45-L81)).
- Preserve `PATHEXT`, `USERNAME`, and `USERPROFILE` on Windows, and
`HOME` / locale vars on Unix.
- Backfill a default `PATHEXT` in `create_env()` on Windows if the
parent env does not provide one, so child process launch still works in
stripped-down Bazel environments.
- Extend the Windows exec-env test to assert mixed-case `PathExt`
survives case-insensitive core filtering, and document why the
shell-command Unicode test goes through a child process.
## Verification
- `cargo test -p codex-core exec_env::tests`
Stacked on #16508.
This removes the temporary `codex-core` / `codex-login` re-export shims
from the ownership split and rewrites callsites to import directly from
`codex-model-provider-info`, `codex-models-manager`, `codex-api`,
`codex-protocol`, `codex-feedback`, and `codex-response-debug-context`.
No behavior change intended; this is the mechanical import cleanup layer
split out from the ownership move.
---------
Co-authored-by: Codex <noreply@openai.com>
## Why
This is a follow-up to #16665. The Windows `unicode_output` test should
still exercise a child process so it verifies PowerShell's UTF-8 output
configuration, but `$env:COMSPEC` depends on that environment variable
surviving the curated Bazel test environment.
Using `cmd.exe` keeps the child-process coverage while avoiding both
bare `cmd` + `PATHEXT` lookup and `$env:COMSPEC` env passthrough
assumptions.
## What
- Run `cmd.exe /c echo naïve_café` in the Windows branch of
`unicode_output`.
## Verification
- `cargo test -p codex-core unicode_output`
## Why
Windows Bazel shell tests launch PowerShell with a curated environment,
so `PATHEXT` may be absent. The existing `unicode_output` test invokes
bare `cmd`, which can fail before the test exercises UTF-8 child-process
output.
## What
- Use `$env:COMSPEC /c echo naïve_café` in the Windows branch of
`unicode_output`.
- Preserve the external child-process path instead of switching the test
to a PowerShell builtin.
## Verification
- `cargo test -p codex-core unicode_output`
https://github.com/openai/codex/pull/16460 was a large PR created by
Codex to try to get the tests to pass under Bazel on Windows. Indeed, it
successfully ran all of the tests under `//codex-rs/core:` with its
changes to `codex-rs/core/`, though the full set of changes seems to be
too broad.
This PR tries to port the key changes, which are:
- Under Bazel, the `USERNAME` environment variable is not guaranteed to
be set on Windows, so for tests that need a non-empty env var as a
convenient substitute for an env var containing an API key, just use
`PATH`. Note that `PATH` is unlikely to contain characters that are not
allowed in an HTTP header value.
- Specify `"powershell.exe"` instead of just `"powershell"` in case the
`PATHEXT` env var gets lost in the shuffle.
## Summary
- split `models-manager` out of `core` and add `ModelsManagerConfig`
plus `Config::to_models_manager_config()` so model metadata paths stop
depending on `core::Config`
- move login-owned/auth-owned code out of `core` into `codex-login`,
move model provider config into `codex-model-provider-info`, move API
bridge mapping into `codex-api`, move protocol-owned types/impls into
`codex-protocol`, and move response debug helpers into a dedicated
`response-debug-context` crate
- move feedback tag emission into `codex-feedback`, relocate tests to
the crates that now own the code, and keep broad temporary re-exports so
this PR avoids a giant import-only rewrite
## Major moves and decisions
- created `codex-models-manager` as the owner for model
cache/catalog/config/model info logic, including the new
`ModelsManagerConfig` struct
- created `codex-model-provider-info` as the owner for provider config
parsing/defaults and kept temporary `codex-login`/`codex-core`
re-exports for old import paths
- moved `api_bridge` error mapping + `CoreAuthProvider` into
`codex-api`, while `codex-login::api_bridge` temporarily re-exports
those symbols and keeps the `auth_provider_from_auth` wrapper
- moved `auth_env_telemetry` and `provider_auth` ownership to
`codex-login`
- moved `CodexErr` ownership to `codex-protocol::error`, plus
`StreamOutput`, `bytes_to_string_smart`, and network policy helpers to
protocol-owned modules
- created `codex-response-debug-context` for
`extract_response_debug_context`, `telemetry_transport_error_message`,
and related response-debug plumbing instead of leaving that behavior in
`core`
- moved `FeedbackRequestTags`, `emit_feedback_request_tags`, and
`emit_feedback_request_tags_with_auth_env` to `codex-feedback`
- deferred removal of temporary re-exports and the mechanical import
rewrites to a stacked follow-up PR so this PR stays reviewable
## Test moves
- moved auth refresh coverage from `core/tests/suite/auth_refresh.rs` to
`login/tests/suite/auth_refresh.rs`
- moved text encoding coverage from
`core/tests/suite/text_encoding_fix.rs` to
`protocol/src/exec_output_tests.rs`
- moved model info override coverage from
`core/tests/suite/model_info_overrides.rs` to
`models-manager/src/model_info_overrides_tests.rs`
---------
Co-authored-by: Codex <noreply@openai.com>
## Why
The Windows `ProviderAuthScript` test helpers do not need PowerShell.
Running them through `cmd.exe` is enough to emit the next fixture token
and rotate `tokens.txt`, and it avoids a PowerShell-specific dependency
in these tests.
## What changed
- Replaced the Windows `print-token.ps1` fixtures with `print-token.cmd`
in `codex-rs/core/src/models_manager/manager_tests.rs` and
`codex-rs/login/src/auth/auth_tests.rs`.
- Switched the failing external-auth helper in
`codex-rs/login/src/auth/auth_tests.rs` from `powershell.exe -Command
'exit 1'` to `cmd.exe /d /s /c 'exit /b 1'`.
- Updated Windows timeout comments so they no longer call out PowerShell
specifically.
## Verification
- `cargo test -p codex-login`
- `cargo test -p codex-core` (fails in unrelated
`core/src/config/config_tests.rs` assertions in this checkout)
## Why
This continues the compile-time cleanup from #16630. `SessionTask`
implementations are monomorphized, but `Session` stores the task behind
a `dyn` boundary so it can drive and abort heterogenous turn tasks
uniformly. That means we can move the `#[async_trait]` expansion off the
implementation trait, keep a small boxed adapter only at the storage
boundary, and preserve the existing task lifecycle semantics while
reducing the amount of generated async-trait glue in `codex-core`.
One measurement caveat showed up while exploring this: a warm
incremental benchmark based on `touch core/src/tasks/mod.rs && cargo
check -p codex-core --lib` was basically flat, but that was the wrong
benchmark for this change. Using package-clean `codex-core` rebuilds,
like #16630, shows the real win.
Relevant pre-change code:
- [`SessionTask` with
`#[async_trait]`](3c7f013f97/codex-rs/core/src/tasks/mod.rs (L129-L182))
- [`RunningTask` storing `Arc<dyn
SessionTask>`](3c7f013f97/codex-rs/core/src/state/turn.rs (L69-L77))
## What changed
- Switched `SessionTask::{run, abort}` to native RPITIT futures with
explicit `Send` bounds.
- Added a private `AnySessionTask` adapter that boxes those futures only
at the `Arc<dyn ...>` storage boundary.
- Updated `RunningTask` to store `Arc<dyn AnySessionTask>` and removed
`#[async_trait]` from the concrete task impls plus test-only
`SessionTask` impls.
## Timing
Benchmarked package-clean `codex-core` rebuilds with dependencies left
warm:
```shell
cargo check -p codex-core --lib >/dev/null
cargo clean -p codex-core >/dev/null
/usr/bin/time -p cargo +nightly rustc -p codex-core --lib -- \
-Z time-passes \
-Z time-passes-format=json >/dev/null
```
| revision | rustc `total` | process `real` | `generate_crate_metadata`
| `MIR_borrow_checking` | `monomorphization_collector_graph_walk` |
| --- | ---: | ---: | ---: | ---: | ---: |
| parent `3c7f013f9735` | 67.21s | 67.71s | 24.61s | 23.43s | 22.43s |
| this PR `2cafd783ac22` | 35.08s | 35.60s | 8.01s | 7.25s | 7.15s |
| delta | -47.8% | -47.4% | -67.5% | -69.1% | -68.1% |
For completeness, the warm touched-file benchmark stayed flat (`1.96s`
parent vs `1.97s` this PR), which is why that benchmark should not be
used to evaluate this refactor.
## Verification
- Ran `cargo test -p codex-core`; this change compiled and task-related
tests passed before hitting the same unrelated 5
`config::tests::*guardian*` failures already present on the parent
stack.
Recently, I merged a number of PRs to increase startup timeouts for
scripts that ran under PowerShell, but in the failure for
`suite::codex_tool::test_shell_command_approval_triggers_elicitation`, I
found this in the error logs when running on Bazel with BuildBuddy:
```
[mcp stderr] 2026-04-02T19:54:10.758951Z ERROR codex_core::tools::router: error=Exit code: 1
[mcp stderr] Wall time: 0.2 seconds
[mcp stderr] Output:
[mcp stderr] 'New-Item' is not recognized as an internal or external command,
[mcp stderr] operable program or batch file.
[mcp stderr]
```
This error implies that the command was run under `cmd.exe` instead of
`pwsh.exe`. Under GitHub Actions, I suspect that the `%PATH%` that is
passed to our Bazel builder is scrubbed such that our tests cannot find
PowerShell where GitHub installs it. Having these explicit fallback
paths should help.
While we could enable these only for tests, I don't see any harm in
keeping them in production, as well.
In https://github.com/openai/codex/pull/16528, I am trying to get tests
running under Bazel on Windows, but currently I see:
```
thread 'suite::user_shell_cmd::user_shell_command_does_not_set_network_sandbox_env_var' (10220) panicked at core/tests\suite\user_shell_cmd.rs:358:5:
assertion failed: `(left == right)`
Diff < left / right > :
<1
>0
```
This PR updates the `assert_eq!()` to provide more information to help
diagnose the failure.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16606).
* #16608
* __->__ #16606
## Why
This finishes the config-type move out of `codex-core` by removing the
temporary compatibility shim in `codex_core::config::types`. Callers now
depend on `codex-config` directly, which keeps these config model types
owned by the config crate instead of re-expanding `codex-core` as a
transitive API surface.
## What Changed
- Removed the `codex-rs/core/src/config/types.rs` re-export shim and the
`core::config::ApprovalsReviewer` re-export.
- Updated `codex-core`, `codex-cli`, `codex-tui`, `codex-app-server`,
`codex-mcp-server`, and `codex-linux-sandbox` call sites to import
`codex_config::types` directly.
- Added explicit `codex-config` dependencies to downstream crates that
previously relied on the `codex-core` re-export.
- Regenerated `codex-rs/core/config.schema.json` after updating the
config docs path reference.
## Why
`codex-rs/core/src/config/types.rs` is a plain config-type module with
no dependency on `codex-core`. Moving it into `codex-config` shrinks the
core crate and gives config-only consumers a more natural dependency
boundary.
## What Changed
- Added `codex_config::types` with the moved structs, enums, constants,
and unit tests.
- Kept `codex_core::config::types` as a compatibility re-export to avoid
a broad call-site migration in this PR.
- Switched notice-table writes in `core/src/config/edit.rs` to a local
`NOTICE_TABLE_KEY` constant.
- Added the `wildmatch` runtime dependency and `tempfile` test
dependency to `codex-config`.
## Why
#16513 moved pure tool-registry planning into `codex-tools`, but much of
the corresponding spec/feature-gating coverage still lived in
`codex-core`. That leaves the tests for planner behavior in the crate
that no longer owns that logic and makes the next extraction steps
harder to review.
## What
Move the planner-only `spec_tests.rs` coverage into
`codex-rs/tools/src/tool_registry_plan_tests.rs` and wire it up from
`codex-rs/tools/src/tool_registry_plan.rs` using the crate-local `#[path
= "tool_registry_plan_tests.rs"] mod tests;` pattern.
The `codex-core` test file now keeps the core-side integration checks:
router-visible model tool lists, namespaced handler alias registration,
shell adapter behavior, and MCP schema edge cases that still exercise
the `core` binding layer.
## Verification
- `cargo test -p codex-tools`
- `cargo test -p codex-core tools::spec::tests`
## Why
This is a larger step in the `codex-core` -> `codex-tools` migration
called out in `AGENTS.md`.
`codex-rs/core/src/tools/spec.rs` had become mostly pure tool-spec
assembly plus handler registration. That made it hard to move more of
the tool-definition layer into `codex-tools`, because the runtime
binding and the crate-independent planning logic were still interleaved
in one function.
Splitting those concerns gives `codex-tools` ownership of the
declarative registry plan while keeping `codex-core` responsible for
instantiating concrete handlers.
## What Changed
- Add a `codex-tools` registry-plan layer in
`codex-rs/tools/src/tool_registry_plan.rs` and
`codex-rs/tools/src/tool_registry_plan_types.rs`.
- Move feature-gated tool-spec assembly, MCP/dynamic tool conversion,
tool-search aliases, and code-mode nested-plan expansion into
`codex-tools`.
- Keep `codex-rs/core/src/tools/spec.rs` as the core-side adapter that
maps each planned handler kind to concrete runtime handler instances.
- Update `spec_tests.rs` to import the moved `codex_tools` symbols
directly instead of relying on top-level `spec.rs` re-exports.
This is intended to be a straight refactor with no behavior change and
no new test surface.
## Verification
- `cargo test -p codex-tools`
- `cargo test -p codex-core tools::spec::tests`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16513).
* #16521
* __->__ #16513
## Why
`codex-core` was re-exporting APIs owned by sibling `codex-*` crates,
which made downstream crates depend on `codex-core` as a proxy module
instead of the actual owner crate.
Removing those forwards makes crate boundaries explicit and lets leaf
crates drop unnecessary `codex-core` dependencies. In this PR, this
reduces the dependency on `codex-core` to `codex-login` in the following
files:
```
codex-rs/backend-client/Cargo.toml
codex-rs/mcp-server/tests/common/Cargo.toml
```
## What
- Remove `codex-rs/core/src/lib.rs` re-exports for symbols owned by
`codex-login`, `codex-mcp`, `codex-rollout`, `codex-analytics`,
`codex-protocol`, `codex-shell-command`, `codex-sandboxing`,
`codex-tools`, and `codex-utils-path`.
- Delete the `default_client` forwarding shim in `codex-rs/core`.
- Update in-crate and downstream callsites to import directly from the
owning `codex-*` crate.
- Add direct Cargo dependencies where callsites now target the owner
crate, and remove `codex-core` from `codex-rs/backend-client`.
## Why
This is another small step in the `codex-core` -> `codex-tools`
migration described in `AGENTS.md`.
`core/src/tools/spec.rs` and `core/src/tools/code_mode/mod.rs` were both
hand-rolling the same pure transformation: convert visible `ToolSpec`s
into code-mode nested tool definitions, then sort and deduplicate by
tool name. That logic does not depend on core runtime state or handlers,
so keeping it in `codex-core` makes `spec.rs` harder to peel out later
than it needs to be.
## What Changed
- Add `collect_code_mode_tool_definitions()` to
`codex-rs/tools/src/code_mode.rs`.
- Reuse that helper from `codex-rs/core/src/tools/spec.rs` when
assembling the `exec` tool description.
- Reuse the same helper from `codex-rs/core/src/tools/code_mode/mod.rs`
when exposing nested tool metadata to the code-mode runtime.
This is intended to be a straight refactor with no behavior change and
no new test surface.
## Verification
- `cargo test -p codex-tools`
- `cargo test -p codex-core tools::spec::tests`
- `cargo test -p codex-core code_mode_only_`
## Why
`codex-mcp` already owns the shared MCP API surface, including `auth`,
`McpConfig`, `CODEX_APPS_MCP_SERVER_NAME`, and tool-name helpers in
[`codex-rs/codex-mcp/src/mcp/mod.rs`](f61e85dbfb/codex-rs/codex-mcp/src/mcp/mod.rs (L1-L35)).
Re-exporting that surface from `codex_core::mcp` gives downstream crates
two import paths for the same API and hides the real crate dependency.
This PR keeps `codex_core::mcp` focused on the local `McpManager`
wrapper in
[`codex-rs/core/src/mcp.rs`](f61e85dbfb/codex-rs/core/src/mcp.rs (L13-L40))
and makes consumers import shared MCP APIs from `codex_mcp` directly.
## What
- Remove the `codex_mcp::mcp` re-export surface from `core/src/mcp.rs`.
- Update `codex-core` internals plus `codex-app-server`, `codex-cli`,
and `codex-tui` test code to import MCP APIs from `codex_mcp::mcp`
directly.
- Add explicit `codex-mcp` dependencies where those crates now use that
API surface, and refresh `Cargo.lock`.
## Verification
- `just bazel-lock-check`
- `cargo test -p codex-core -p codex-cli -p codex-tui`
- `codex-cli` passed.
- `codex-core` still fails five unrelated config tests in
`core/src/config/config_tests.rs` (`approvals_reviewer_*` and
`smart_approvals_alias_*`).
- A broader `cargo test -p codex-core -p codex-app-server -p codex-cli
-p codex-tui` run previously hung in `codex-app-server` test
`in_process_start_uses_requested_session_source_for_thread_start`.
## Why
This is another incremental step in the `codex-core` -> `codex-tools`
migration called out in `AGENTS.md`: keep pure tool-definition and
wire-shaping logic out of `codex-core` so the core crate can stay
focused on runtime orchestration.
`request_user_input` already had its spec and mode-availability helpers
in `codex-tools` after #16471. The remaining argument validation and
normalization still lived in the core runtime handler, which left that
tool split across the two crates.
## What Changed
- Export `REQUEST_USER_INPUT_TOOL_NAME` and
`normalize_request_user_input_args()` from
`codex-rs/tools/src/request_user_input_tool.rs`.
- Use that `codex-tools` surface from `codex-rs/core/src/tools/spec.rs`
and `codex-rs/core/src/tools/handlers/request_user_input.rs`.
- Keep the core handler responsible for payload parsing, session
dispatch, cancellation handling, and response serialization.
This is intended to be a straight refactor with no behavior change.
## Verification
- `cargo test -p codex-tools`
- `cargo test -p codex-core request_user_input`
## Why
`codex-rs/tools/src/lib.rs` already defines the [canonical `codex_tools`
export
surface](bf081b9e28/codex-rs/tools/src/lib.rs (L83-L88))
for `ToolsConfig`, `ToolsConfigParams`, and the shell backend config
types. Re-exporting those same types from `core/src/tools/spec.rs` gives
`codex-core` two import paths for one API and blurs which crate owns
those config definitions.
This PR removes that duplicate path so `codex-core` callsites depend on
`codex_tools` directly.
## What
- Remove the five `codex_tools` re-exports from
`core/src/tools/spec.rs`.
- Update `codex-core` production and test callsites to import
`ShellCommandBackendConfig`, `ToolsConfig`, `ToolsConfigParams`,
`UnifiedExecShellMode`, and `ZshForkConfig` from `codex_tools`.
## Verification
- Ran `cargo test -p codex-core`.
- The package run is currently red in five unrelated config tests in
`core/src/config/config_tests.rs` (`approvals_reviewer_*` and
`smart_approvals_alias_*`), while the tool/spec and shell tests touched
by this import cleanup passed.
## Why
This is another straight-refactor step in the `codex-tools` migration.
`core/src/tools/handlers/tool_suggest.rs` still owned request/response
payload structs, elicitation metadata shaping, and connector-completion
predicates that do not depend on `codex-core` session/runtime internals.
Per the `AGENTS.md` guidance to keep shrinking `codex-core`, this moves
that pure wire-format logic into `codex-rs/tools` so the core handler
keeps only session orchestration, plugin/config refresh, and MCP cache
updates.
## What changed
- Added `codex-rs/tools/src/tool_suggest.rs` and exported its API from
`codex-rs/tools/src/lib.rs`.
- Moved `ToolSuggestArgs`, `ToolSuggestResult`, `ToolSuggestMeta`,
`build_tool_suggestion_elicitation_request()`,
`all_suggested_connectors_picked_up()`, and
`verified_connector_suggestion_completed()` into `codex-tools`.
- Rewired `core/src/tools/handlers/tool_suggest.rs` to consume those
exports directly.
- Ported the existing pure helper tests from
`core/src/tools/handlers/tool_suggest_tests.rs` to
`tools/src/tool_suggest_tests.rs` without adding new behavior coverage.
## Validation
```shell
cargo test -p codex-tools
cargo test -p codex-core tools::handlers::tool_suggest::tests
just argument-comment-lint
```
This currently contributing to `rust-ci-full.yml` being red on `main`
for windows lint builds due to the cargo/bazel coverage gap that I'm
working on. Hopefully this gets us back on track.
## Why
This is the next straight-refactor step in the `codex-tools` migration
that follows #16493.
`codex-rs/core` still owned a chunk of pure tool-discovery metadata and
response shaping even though the corresponding `tool_search` /
`tool_suggest` specs already live in `codex-rs/tools`. Per the guidance
in `AGENTS.md`, this moves that crate-agnostic logic out of `codex-core`
so the handler crate keeps only the BM25 ranking/orchestration and
runtime glue.
## What changed
- Moved the canonical `tool_search` / `tool_suggest` tool names and the
`tool_search` default limit into `codex-rs/tools/src/tool_discovery.rs`.
- Added `ToolSearchResultSource` and
`collect_tool_search_output_tools()` in `codex-tools` so namespace
grouping and deferred Responses API tool serialization happen outside
`codex-core`.
- Rewired `ToolSearchHandler`, `ToolSuggestHandler`, and
`core/src/tools/spec.rs` to consume those exports directly from
`codex-tools`.
- Ported the existing `tool_search` serializer tests from
`core/src/tools/handlers/tool_search_tests.rs` to
`tools/src/tool_discovery_tests.rs` without adding new behavior
coverage.
## Validation
```shell
cargo test -p codex-tools
cargo test -p codex-core tools::spec::tests
just argument-comment-lint
```
## Why
`core/src/tools/spec.rs` still had a few built-in tool specs assembled
inline even though those definitions are pure metadata and already live
conceptually in `codex-tools`. Keeping that construction in `codex-core`
makes `spec.rs` do more than registry orchestration and slows the
migration toward a right-sized `codex-tools` crate.
This continues the extraction stack from #16379, #16471, #16477, #16481,
and #16482.
## What Changed
- added `create_local_shell_tool()`, `create_web_search_tool(...)`, and
`create_image_generation_tool(...)` to `codex-rs/tools/src/tool_spec.rs`
- exported those helpers from `codex-rs/tools/src/lib.rs`
- switched `codex-rs/core/src/tools/spec.rs` to call those helpers
instead of constructing `ToolSpec::LocalShell`, `ToolSpec::WebSearch`,
and `ToolSpec::ImageGeneration` inline
- removed the remaining core-local web-search content-type constant and
made the affected spec test assert the literal expected values directly
This is intended to be a straight refactor: tool behavior and wire shape
should not change.
## Testing
- `cargo test -p codex-tools`
- `cargo test -p codex-core tools::spec::tests`
## Why
`codex-rs/core/src/client_common.rs` still had a `tools` re-export
module that forwarded `codex_tools` types back into `codex-core`. After
the earlier extraction work in #16379, #16471, #16477, and #16481, that
extra layer no longer adds value.
Removing it keeps dependencies explicit: the `codex-core` modules that
actually use `ToolSpec` and related types now depend on `codex_tools`
directly instead of reaching through `client_common`.
## What Changed
- removed the `client_common::tools` re-export module from
`core/src/client_common.rs`
- updated the remaining `codex-core` consumers to import `codex_tools`
directly
- adjusted the affected test code to reference
`codex_tools::ResponsesApiTool` directly as well
This is a mechanical cleanup only. It does not change tool behavior or
runtime logic.
## Testing
- `cargo test -p codex-core client_common::tests`
- `cargo test -p codex-core tools::router::tests`
- `cargo test -p codex-core tools::context::tests`
- `cargo test -p codex-core tools::spec::tests`
- Split MCP runtime/server code out of `codex-core` into the new
`codex-mcp` crate. New/moved public structs/types include `McpConfig`,
`McpConnectionManager`, `ToolInfo`, `ToolPluginProvenance`,
`CodexAppsToolsCacheKey`, and the `McpManager` API
(`codex_mcp::mcp::McpManager` plus the `codex_core::mcp::McpManager`
wrapper/shim). New/moved functions include `with_codex_apps_mcp`,
`configured_mcp_servers`, `effective_mcp_servers`,
`collect_mcp_snapshot`, `collect_mcp_snapshot_from_manager`,
`qualified_mcp_tool_name_prefix`, and the MCP auth/skill-dependency
helpers. Why: this creates a focused MCP crate boundary and shrinks
`codex-core` without forcing every consumer to migrate in the same PR.
- Move MCP server config schema and persistence into `codex-config`.
New/moved structs/enums include `AppToolApproval`,
`McpServerToolConfig`, `McpServerConfig`, `RawMcpServerConfig`,
`McpServerTransportConfig`, `McpServerDisabledReason`, and
`codex_config::ConfigEditsBuilder`. New/moved functions include
`load_global_mcp_servers` and
`ConfigEditsBuilder::replace_mcp_servers`/`apply`. Why: MCP TOML
parsing/editing is config ownership, and this keeps config
validation/round-tripping (including per-tool approval overrides and
inline bearer-token rejection) in the config crate instead of
`codex-core`.
- Rewire `codex-core`, app-server, and plugin call sites onto the new
crates. Updated `Config::to_mcp_config(&self, plugins_manager)`,
`codex-rs/core/src/mcp.rs`, `codex-rs/core/src/connectors.rs`,
`codex-rs/core/src/codex.rs`,
`CodexMessageProcessor::list_mcp_server_status_task`, and
`utils/plugins/src/mcp_connector.rs` to build/pass the new MCP
config/runtime types. Why: plugin-provided MCP servers still merge with
user-configured servers, and runtime auth (`CodexAuth`) is threaded into
`with_codex_apps_mcp` / `collect_mcp_snapshot` explicitly so `McpConfig`
stays config-only.
## Why
`codex-rs/core/src/tools/handlers/plan.rs` still owned both the
`update_plan` runtime handler and the static tool definition. The tool
definition is pure metadata, so keeping it in `codex-core` works against
the ongoing effort to move tool-spec code into `codex-tools` and keep
`codex-core` focused on orchestration and execution paths.
This continues the extraction work from #16379, #16471, and #16477.
## What Changed
- added `codex-rs/tools/src/plan_tool.rs` with
`create_update_plan_tool()`
- re-exported that constructor from `codex-rs/tools/src/lib.rs`
- updated `codex-rs/core/src/tools/spec.rs` and
`codex-rs/core/src/tools/spec_tests.rs` to use the `codex-tools` export
instead of a core-local static
- removed the old `PLAN_TOOL` definition from
`codex-rs/core/src/tools/handlers/plan.rs`; the `PlanHandler` runtime
logic still stays in `codex-core`
- tightened two `codex-core` aliases to `#[cfg(test)]` now that
production code no longer needs them
## Testing
- `cargo test -p codex-tools`
- `cargo test -p codex-core tools::spec::tests`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16481).
* #16482
* __->__ #16481
## Description
Previously the `action` field on `EventMsg::GuardianAssessment`, which
describes what Guardian is reviewing, was typed as an arbitrary JSON
blob. This PR cleans it up and defines a sum type representing all the
various actions that Guardian can review.
This is a breaking change (on purpose), which is fine because:
- the Codex app / VSCE does not actually use `action` at the moment
- the TUI code that consumes `action` is updated in this PR as well
- rollout files that serialized old `EventMsg::GuardianAssessment` will
just silently drop these guardian events
- the contract is defined as unstable, so other clients have a fair
warning :)
This will make things much easier for followup Guardian work.
## Why
The old guardian review payloads worked, but they pushed too much shape
knowledge into downstream consumers. The TUI had custom JSON parsing
logic for commands, patches, network requests, and MCP calls, and the
app-server protocol was effectively just passing through an opaque blob.
Typing this at the protocol boundary makes the contract clearer.