Commit Graph

3938 Commits

Author SHA1 Message Date
Felipe Coury
486f5850ca chore(tui): Cargo.lock update from main 2026-02-21 19:21:19 -03:00
Felipe Coury
44a7ba0dda feat(tui): harden theme lock recovery and ordering 2026-02-21 19:21:19 -03:00
Felipe Coury
18d62ac80f feat(tui): harden theme fallback contracts and diff wrapping 2026-02-21 19:21:19 -03:00
Felipe Coury
75c819e54a docs(tui): theme picker, highlight lifecycle, diff rendering contracts 2026-02-21 19:21:19 -03:00
Felipe Coury
5986719f96 fix(tui): resolve clippy -D warnings findings 2026-02-21 19:21:19 -03:00
Felipe Coury
801ae5e979 feat(tui): reuse diff renderer for theme preview 2026-02-21 19:21:19 -03:00
Felipe Coury
89891de55a feat(tui): diff visual gallery snapshots with syntax-highlighted cases 2026-02-21 19:21:19 -03:00
Felipe Coury
46021a3591 fix(tui): wrap syntax spans before first overflowing wide character 2026-02-21 19:21:19 -03:00
Felipe Coury
c510eec9f8 feat(tui): fallback to active theme when configured theme is unavailable 2026-02-21 19:21:18 -03:00
Felipe Coury
8c4bfa78f7 feat(tui): width-aware theme subtitle fallback and 94x35 regression test 2026-02-21 19:21:18 -03:00
Felipe Coury
f72e7bf529 feat(tui): live theme override and dynamic CODEX_HOME paths
set_theme_override now updates the runtime theme immediately instead
of only setting the OnceLock, enabling live switching from /theme.
Replace hardcoded ~/.codex/themes references with resolved
$CODEX_HOME/themes paths in warnings, schema docs, and picker subtitle.
2026-02-21 19:21:17 -03:00
Felipe Coury
987db6aaf6 fix(tui): only fire on_selection_changed when actual item changes
Extract selected_actual_idx() helper and guard move_up/move_down so
the callback is skipped when wrapping doesn't change the selected
item (e.g. single-item lists). Adds a test for the single-item case.
2026-02-21 19:21:17 -03:00
Felipe Coury
0066e85f0f fix(tui): preserve syntect parser state across diff hunk lines
Highlight each hunk as a single block instead of per-line so that
multiline constructs (e.g. strings spanning multiple lines) get
correct syntax coloring. Adds a regression test.
2026-02-21 19:21:17 -03:00
Felipe Coury
197131762b tui: harden theme picker cancel and custom theme handling 2026-02-21 19:21:17 -03:00
Felipe Coury
85e1e7bf5a tui: sync theme picker state and validate custom themes 2026-02-21 19:21:16 -03:00
Felipe Coury
f4ac1f09b3 tui: refine theme preview alignment in wide and narrow layouts 2026-02-21 19:20:48 -03:00
Felipe Coury
8ca13e5d56 tui: fix theme preview layout and terminal background rendering 2026-02-21 19:20:48 -03:00
Felipe Coury
ea57beb959 feat(tui): add /theme picker with live syntax-highlighted preview
Add a `/theme` slash command that opens an interactive picker listing
all bundled and custom (.tmTheme) syntax themes. The picker shows a
live-preview code snippet below the list that re-highlights as the
user navigates, and restores the original theme on cancel.

Key changes:
- ListSelectionView gains `footer_content`, `on_selection_changed`,
  and `on_cancel` to support rich preview and lifecycle callbacks
- highlight.rs: swap global Theme from OnceLock to RwLock for live
  swapping; add resolve/list/set/current helpers for theme management
- New theme_picker module with ThemePreviewRenderable and picker builder
- diff_render: make DiffLineType and helpers pub(crate); fix rename
  highlighting to use destination extension
- markdown_render: fix CRLF double-newlines and info-string metadata
  parsing for fenced code blocks
2026-02-21 19:20:48 -03:00
Felipe Coury
976dd6831e fix(tui): configure syntax theme from final config, not initial
`set_theme_override` was called with `initial_config.tui_theme` before
onboarding/resume/fork could reload config. Because the theme state
lives in a `OnceLock`, it was permanently locked to the initial value.

Move the call to after the last possible config reload so the theme
and its warning always reflect the final session config.
2026-02-21 19:20:48 -03:00
Felipe Coury
d32e05d4e5 fix(tui): treat tabs as visible width, preserve theme warning on resume
- Tabs in syntax-highlighted diff spans now count as 4 columns instead
  of zero, so tab-indented code wraps correctly in the diff view.

- Move theme warning injection to after the last possible config reload
  (session resume/fork) so the ⚠ banner is not silently discarded.
2026-02-21 19:20:48 -03:00
Felipe Coury
06d4c95d35 style(tui): rustfmt 2026-02-21 19:20:48 -03:00
Felipe Coury
3cd1301409 fix(tui): trim CRLF in highlighted spans, enforce diff size guardrails
Two fixes from code review:

1. `trim_end_matches('\n')` left a stray `\r` on CRLF inputs, which
   propagated into rendered code blocks and diffs. Now strips both
   `\r` and `\n`.

2. Unified diff highlighting called `highlight_code_to_styled_spans`
   per line, bypassing the global size guardrails (max bytes/lines).
   On large patches this triggered thousands of parser initializations.
   Added a pre-check on aggregate patch size that skips highlighting
   when limits are exceeded.
2026-02-21 19:20:48 -03:00
Felipe Coury
e7d4e828c3 feat(tui): support custom .tmTheme files from ~/.codex/themes/
Extend theme resolution so `theme = "foo"` checks
~/.codex/themes/foo.tmTheme before falling back to auto-detection.
Surface unresolved theme names as a ⚠ startup warning in the TUI
via the existing startup_warnings pipeline.
2026-02-21 19:20:47 -03:00
Felipe Coury
32ff9e89ed fix(tui): fix fallback phantom line and diff wrap at span boundaries
Two fixes:

1. highlight.rs: The fallback path used split('\n') which produces a
   phantom empty trailing element for inputs ending in '\n' (as
   pulldown-cmark emits for code blocks). Switch to lines() which
   handles trailing newlines correctly.

2. diff_render.rs: wrap_styled_spans only flushed the current line
   when col >= max_cols AND the current span had remaining content.
   This missed the case where one span ends exactly at max_cols and
   the next span has content — the next span's first character was
   appended to the already-full line. Remove the !remaining.is_empty()
   guard so the line flushes at span boundaries too.
2026-02-21 19:20:47 -03:00
Felipe Coury
f00f7d49dc fix(tui): preserve trailing blank lines in fenced code blocks
trim_end_matches('\n') stripped all trailing newlines from the code
block buffer before highlighting, discarding intentional blank lines
authored inside fences. pulldown-cmark appends exactly one trailing
'\n' to the text content, and LinesWithEndings handles it correctly
as a line terminator, so no trimming is needed. Remove the trim to
preserve user-authored blank lines faithfully.
2026-02-21 19:20:47 -03:00
Felipe Coury
82fc47e317 feat(tui): add theme config setting, suppress underline from themes
Add `[tui] theme = "..."` config option that lets users override the
auto-detected syntax highlighting theme with any of the 32 bundled
two-face themes by kebab-case name. Invalid names log a warning and
fall back to adaptive detection.

Also suppress FontStyle::UNDERLINE in convert_style — themes like
Dracula mark type scopes with underline which produces distracting
underlines on type/module names in terminal output.
2026-02-21 19:20:47 -03:00
Felipe Coury
9878b734a0 refactor(tui): remove normalize_lang, delegate to two-face directly
two-face's ~250 language syntax set resolves almost all names and
extensions on its own. Remove the redundant normalize_lang layer and
keep only 3 patched aliases (golang, python3, shell) that two-face
cannot resolve. This enables highlighting for any language in bat's
syntax set (elixir, haskell, scala, dart, php, html, css, etc.)
without needing explicit entries.
2026-02-21 19:20:47 -03:00
Felipe Coury
5f92cb4e58 feat(tui): use two-face for extended syntax highlighting and adaptive theme
Replace syntect's default ~40 language set with two-face's bat-sourced
~250 language set, adding support for TypeScript, TSX, Kotlin, Swift,
and Zig. Auto-detect terminal background to pick CatppuccinMocha (dark)
or CatppuccinLatte (light). Suppress italic modifier by default since
many terminals render it poorly.
2026-02-21 19:20:47 -03:00
Felipe Coury
d97bd689dd chore: remove orphaned tree-sitter-highlight workspace dependency
No crate references it after the TUI switched to syntect.
2026-02-21 19:20:47 -03:00
Felipe Coury
252b574c65 fix(tui): update exec approval snapshot for syntect bash highlighting
The exec approval modal's command line now gets syntax-colored spans
from syntect (base16-ocean.dark) instead of tree-sitter-bash.
2026-02-21 19:20:47 -03:00
Felipe Coury
1ddc3d7b00 fix(tui): wrap syntax-highlighted diff lines, add highlight guardrails
Integrate syntax highlighting into diff rendering and markdown code
blocks, with proper line wrapping that preserves styled spans.

- Add detect_lang_for_path() for extension-based language detection
- Syntax-aware diff rendering for Add/Delete/Update file changes
- wrap_styled_spans() splits long highlighted lines across display rows
- Buffer fenced code blocks in markdown_render for batch highlighting
- Add snapshot tests for highlighted diff wrapping
- Replace code_block_unhighlighted test with 3 syntax color assertions
2026-02-21 19:20:47 -03:00
Felipe Coury
776e4b0aa8 feat(tui): add multi-language syntax highlighting via syntect
Replace tree-sitter-bash and tree-sitter-highlight with syntect for
code highlighting. Syntect's HighlightLines API resolves overlapping
spans internally, significantly simplifying the implementation.

- Add syntect workspace dependency, remove tree-sitter-bash/highlight from tui
- Rewrite highlight.rs with syntect singletons (SyntaxSet + base16-ocean.dark theme)
- Normalize language aliases (js→javascript, py→python, rs→rust, etc.)
- Multi-strategy syntax lookup: token, name, case-insensitive name, extension
- Add guardrails: skip highlighting for inputs >512KB or >10K lines
- Comprehensive tests for style conversion, language resolution, content preservation
2026-02-21 19:20:47 -03:00
pakrym-oai
b17148f13a Prefer v2 websockets if available (#12428)
And also cleanup settings flow to avoid reading many separate flags.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-02-21 20:08:04 +00:00
Eric Traut
a6b2bacb5b Prevent replayed runtime events from forcing active status (#12420)
Fixes #11852

Resume replay was applying transient runtime events (`TurnStarted`,
`StreamError`) as if they were live, which could leave the TUI stuck in
a stale `Working` / `Reconnecting...` state after resuming an
interrupted reconnect.

This change makes replay transcript-oriented for these events by:
- skipping retry-status restoration for replayed non-stream events
- ignoring replayed `TurnStarted` for task-running state
- ignoring replayed `StreamError` for reconnect/status UI

Also adds TUI regression tests and snapshot coverage for the interrupted
reconnect replay case.
2026-02-21 11:55:03 -08:00
sayan-oai
5a635f3427 profile-level model_catalog_json overrie (#12410)
enable `model-catalog_json` config value on `ConfigProfile` as well
2026-02-21 19:39:02 +00:00
viyatb-oai
b3202cbd58 feat(linux-sandbox): implement proxy-only egress via TCP-UDS-TCP bridge (#11293)
## Summary
- Implement Linux proxy-only routing in `codex-rs/linux-sandbox` with a
two-stage bridge: host namespace `loopback TCP proxy endpoint -> UDS`,
then bwrap netns `loopback TCP listener -> host UDS`.
- Add hidden `--proxy-route-spec` plumbing for outer-to-inner stage
handoff.
- Fail closed in proxy mode when no valid loopback proxy endpoints can
be routed.
- Introduce explicit network seccomp modes: `Restricted` (legacy
restricted networking) and `ProxyRouted` (allow INET/INET6 for routed
proxy access, deny `AF_UNIX` and `socketpair`).
- Enforce that proxy bridge/routing is bwrap-only by validating
`--apply-seccomp-then-exec` requires `--use-bwrap-sandbox`.
- Keep landlock-only flows unchanged (no proxy bridge behavior outside
bwrap).

---------

Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-02-21 18:16:34 +00:00
pakrym-oai
e7b6f38b58 Delete AggregatedStream (#12441)
Used only in test
2026-02-21 08:50:27 +00:00
Michael Bolin
f5d7a74568 chore: delete empty codex-rs/code file (#12440)
This file was added in https://github.com/openai/codex/pull/4195, but I
think it may have been a mistake?
2026-02-21 08:44:55 +00:00
Michael Bolin
85ce91a5b3 refactor(core): move embedded system skills into codex-skills crate (#12435)
## Why

`codex-core` was carrying the embedded system-skill sample assets (and a
`build.rs` that walks those files to register rerun triggers). Those
assets change infrequently, but any change under `codex-core` still ties
them to `codex-core`'s build/cache lifecycle.

This change moves the embedded system-skills packaging into a dedicated
`codex-skills` crate so it can be cached independently. That reduces
unnecessary invalidation/rebuild pressure on `codex-core` when the
skills bundle is the only thing that changes.

## What Changed

- Added a new `codex-rs/skills` crate (`codex-skills`) with:
  - `Cargo.toml`
  - `BUILD.bazel`
  - `build.rs` to track skill asset file changes for Cargo rebuilds
- `src/lib.rs` containing the embedded system-skills install/cache logic
previously in `codex-core`
- Moved the embedded sample skill assets from
`codex-rs/core/src/skills/assets/samples` to
`codex-rs/skills/src/assets/samples`.
- Updated `codex-rs/core/Cargo.toml` to depend on `codex-skills` and
removed `codex-core`'s direct `include_dir` dependency.
- Removed `codex-core`'s `build.rs`.
- Replaced `codex-rs/core/src/skills/system.rs` implementation with a
thin re-export wrapper to keep existing `codex-core` call sites
unchanged.
- Updated workspace manifests/lockfile (`codex-rs/Cargo.toml`,
`codex-rs/Cargo.lock`) for the new crate.
2026-02-21 08:34:08 +00:00
Michael Bolin
2fe4be1aa9 fix: codex-arg0 no longer depends on codex-core (#12434)
## Why

`codex-rs/arg0` only needed two things from `codex-core`:

- the `find_codex_home()` wrapper
- the special argv flag used for the internal `apply_patch`
self-invocation path

That made `codex-arg0` depend on `codex-core` for a very small surface
area. This change removes that dependency edge and moves the shared
`apply_patch` invocation flag to a more natural boundary
(`codex-apply-patch`) while keeping the contract explicitly documented.

## What Changed

- Moved the internal `apply_patch` argv[1] flag constant out of
`codex-core` and into `codex-apply-patch`.
- Renamed the constant to `CODEX_CORE_APPLY_PATCH_ARG1` and documented
that it is part of the Codex core process-invocation contract (even
though it now lives in `codex-apply-patch`).
- Updated `arg0`, the core apply-patch runtime, and the `codex-exec`
apply-patch test to import the constant from `codex-apply-patch`.
- Updated `codex-rs/arg0` to call
`codex_utils_home_dir::find_codex_home()` directly instead of
`codex_core::config::find_codex_home()`.
- Removed the `codex-core` dependency from `codex-rs/arg0` and added the
needed direct dependency on `codex-utils-home-dir`.
- Added `codex-apply-patch` as a dev-dependency for `codex-rs/exec`
tests (the apply-patch test now imports the moved constant directly).

## Verification

- `cargo test -p codex-apply-patch`
- `cargo test -p codex-arg0`
- `cargo test -p codex-core --lib apply_patch`
- `cargo test -p codex-exec
test_standalone_exec_cli_can_use_apply_patch`
- `cargo shear`
2026-02-21 00:20:42 -08:00
Michael Bolin
1af2a37ada chore: remove codex-core public protocol/shell re-exports (#12432)
## Why

`codex-rs/core/src/lib.rs` re-exported a broad set of types and modules
from `codex-protocol` and `codex-shell-command`. That made it easy for
workspace crates to import those APIs through `codex-core`, which in
turn hides dependency edges and makes it harder to reduce compile-time
coupling over time.

This change removes those public re-exports so call sites must import
from the source crates directly. Even when a crate still depends on
`codex-core` today, this makes dependency boundaries explicit and
unblocks future work to drop `codex-core` dependencies where possible.

## What Changed

- Removed public re-exports from `codex-rs/core/src/lib.rs` for:
- `codex_protocol::protocol` and related protocol/model types (including
`InitialHistory`)
  - `codex_protocol::config_types` (`protocol_config_types`)
- `codex_shell_command::{bash, is_dangerous_command, is_safe_command,
parse_command, powershell}`
- Migrated workspace Rust call sites to import directly from:
  - `codex_protocol::protocol`
  - `codex_protocol::config_types`
  - `codex_protocol::models`
  - `codex_shell_command`
- Added explicit `Cargo.toml` dependencies (`codex-protocol` /
`codex-shell-command`) in crates that now import those crates directly.
- Kept `codex-core` internal modules compiling by using `pub(crate)`
aliases in `core/src/lib.rs` (internal-only, not part of the public
API).
- Updated the two utility crates that can already drop a `codex-core`
dependency edge entirely:
  - `codex-utils-approval-presets`
  - `codex-utils-cli`

## Verification

- `cargo test -p codex-utils-approval-presets`
- `cargo test -p codex-utils-cli`
- `cargo check --workspace --all-targets`
- `just clippy`
2026-02-20 23:45:35 -08:00
pakrym-oai
a87c9c3299 Collapse waited message (#12430)
<img width="1349" height="148" alt="image"
src="https://github.com/user-attachments/assets/98c96523-4cec-4bb1-9998-59d38e0bebb8"
/>
2026-02-20 23:32:59 -08:00
Michael Bolin
1a220ad77d chore: move config diagnostics out of codex-core (#12427)
## Why

Compiling `codex-rs/core` is a bottleneck for local iteration, so this
change continues the ongoing extraction of config-related functionality
out of `codex-core` and into `codex-config`.

The goal is not just to move code, but to reduce `codex-core` ownership
and indirection so more code depends on `codex-config` directly.

## What Changed

- Moved config diagnostics logic from
`core/src/config_loader/diagnostics.rs` into
`config/src/diagnostics.rs`.
- Updated `codex-core` to use `codex-config` diagnostics types/functions
directly where possible.
- Removed the `core/src/config_loader/diagnostics.rs` shim module
entirely; the remaining `ConfigToml`-specific calls are in
`core/src/config_loader/mod.rs`.
- Moved `CONFIG_TOML_FILE` into `codex-config` and updated existing
references to use `codex_config::CONFIG_TOML_FILE` directly.
- Added a direct `codex-config` dependency to `codex-cli` for its
`CONFIG_TOML_FILE` use.
2026-02-20 23:19:29 -08:00
Charley Cunningham
bb0ac5be70 Fix compaction context reinjection and model baselines (#12252)
## Summary
- move regular-turn context diff/full-context persistence into
`run_turn` so pre-turn compaction runs before incoming context updates
are recorded
- after successful pre-turn compaction, rely on a cleared
`reference_context_item` to trigger full context reinjection on the
follow-up regular turn (manual `/compact` keeps replacement history
summary-only and also clears the baseline)
- preserve `<model_switch>` when full context is reinjected, and inject
it *before* the rest of the full-context items
- scope `reference_context_item` and `previous_model` to regular user
turns only so standalone tasks (`/compact`, shell, review, undo) cannot
suppress future reinjection or `<model_switch>` behavior
- make context-diff persistence + `reference_context_item` updates
explicit in the regular-turn path, with clearer docs/comments around the
invariant
- stop persisting local `/compact` `RolloutItem::TurnContext` snapshots
(only regular turns persist `TurnContextItem` now)
- simplify resume/fork previous-model/reference-baseline hydration by
looking up the last surviving turn context from rollout lifecycle
events, including rollback and compaction-crossing handling
- remove the legacy fallback that guessed from bare `TurnContext`
rollouts without lifecycle events
- update compaction/remote-compaction/model-visible snapshots and
compact test assertions (including remote compaction mock response
shape)

## Why
We were persisting incoming context items before spawning the regular
turn task, which let pre-turn compaction requests accidentally include
incoming context diffs without the new user message. Fixing that exposed
follow-on baseline issues around `/compact`, resume/fork, and standalone
tasks that could cause duplicate context injection or suppress
`<model_switch>` instructions.

This PR re-centers the invariants around regular turns:
- regular turns persist model-visible context diffs/full reinjection and
update the `reference_context_item`
- standalone tasks do not advance those regular-turn baselines
- compaction clears the baseline when replacement history may have
stripped the referenced context diffs

## Follow-ups (TODOs left in code)
- `TODO(ccunningham)`: fix rollback/backtracking baseline handling more
comprehensively
- `TODO(ccunningham)`: include pending incoming context items in
pre-turn compaction threshold estimation
- `TODO(ccunningham)`: inject updated personality spec alongside
`<model_switch>` so some model-switch paths can avoid forced full
reinjection
- `TODO(ccunningham)`: review task turn lifecycle
(`TurnStarted`/`TurnComplete`) behavior and emit task-start context
diffs for task types that should have them (excluding `/compact`)

## Validation
- `just fmt`
- CI should cover the updated compaction/resume/model-visible snapshot
expectations and rollout-hydration behavior
- I did **not** rerun the full local test suite after the latest
resume-lookup / rollout-persistence simplifications
2026-02-20 23:13:08 -08:00
Michael Bolin
264fc444b6 feat: discourage the use of the --all-features flag (#12429)
## Why

Developers are frequently running low on disk space, and routine use of
`--all-features` contributes to larger Cargo build caches in `target/`
by compiling additional feature combinations.

This change updates local workflow guidance to avoid `--all-features` by
default and reserve it for cases where full feature coverage is
specifically needed.

## What Changed

- Updated `AGENTS.md` guidance for `codex-rs` to recommend `cargo test`
/ `just test` for full-suite local runs, and to call out the disk-usage
cost of routine `--all-features` usage.
- Updated the root `justfile` so `just fix` and `just clippy` no longer
pass `--all-features` by default.
- Updated `docs/install.md` to explicitly describe `cargo test
--all-features` as an optional heavier-weight run (more build time and
`target/` disk usage).

## Verification

- Confirmed the `justfile` parses and the recipes list successfully with
`just --list`.
2026-02-20 23:02:24 -08:00
Dylan Hurd
a8b4b569fb fix(core) Filter non-matching prefix rules (#12314)
## Summary
`gpt-5.3-codex` really likes to write complicated shell scripts, and
suggest a partial prefix_rule that wouldn't actually approve the
command. We should only show the `prefix_rule` suggestion from the model
if it would actually fully approve the command the user is seeing.

This will technically cause more instances of overly-specific
suggestions when we fallback, but I think the UX is clearer,
particularly when the model doesn't necessarily understand the current
limitations of execpolicy parsing.

## Testing
 - [x] Add unit tests
 - [x] Add integration tests
2026-02-20 22:02:35 -08:00
Michael Bolin
1779feb6a7 ignore v1 in JSON schema codegen (#12408)
## Why

The generated unnamespaced JSON envelope schemas (`ClientRequest` and
`ServerNotification`) still contained both v1 and v2 variants, which
pulled legacy v1/core types and v2 types into the same `definitions`
graph. That caused `schemars` to produce numeric suffix names (for
example `AskForApproval2`, `ByteRange2`, `MessagePhase2`).

This PR moves JSON codegen toward v2-only output while preserving the
unnamespaced envelope artifacts, and avoids reintroducing numeric-suffix
tolerance by removing the v1/internal-only variants that caused the
collisions in those envelope schemas.

## What Changed

- In `codex-rs/app-server-protocol/src/export.rs`, JSON generation now
excludes v1 schema artifacts (`v1/*`) while continuing to emit
unnamespaced/root JSON schemas and the JSON bundle.
- Added a narrow JSON v1 allowlist (`JSON_V1_ALLOWLIST`) so
`InitializeParams` and `InitializeResponse` are still emitted.
- Added JSON-only post-processing for the mixed envelope schemas before
collision checks run:
- `ClientRequest`: strips v1 request variants from the generated `oneOf`
using the temporary `V1_CLIENT_REQUEST_METHODS` list
- `ServerNotification`: strips v1 notifications plus the internal-only
`rawResponseItem/completed` notification using the temporary
`EXCLUDED_SERVER_NOTIFICATION_METHODS_FOR_JSON` list
- Added a temporary local-definition pruning pass for those envelope
schemas so now-unreferenced v1/core definitions are removed from
`definitions` after method filtering.
- Updated the variant-title naming heuristic for single-property literal
object variants to use the literal value (when available), avoiding
collisions like multiple `state`-only variants all deriving the same
title.
- Collision handling remains fail-fast (no numeric suffix fallback map
in this PR path).

## Verification

- `just write-app-server-schema`

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/12408).
* __->__ #12408
* #12406
2026-02-20 21:36:12 -08:00
Yaroslav Volovich
dca9c40dd5 test(app-server): wait for turn/completed in turn_start tests (#12376)
## Summary
- switch a few app-server `turn_start` tests from
`codex/event/task_complete` waits to `turn/completed` waits
- avoid matching unrelated/background `task_complete` events
- keep this flaky test fix separate from the /title feature PR

## Why
On Windows ARM CI, these tests can return early after observing a
generic `codex/event/task_complete` notification from another task. That
can leave the mock Responses server with fewer calls than expected and
fail the test with a wiremock verification mismatch.

Using `turn/completed` matches the app-server turn lifecycle
notification the tests actually care about.

## Validation
- `cargo test -p codex-app-server
turn_start_updates_sandbox_and_cwd_between_turns_v2 -- --nocapture`
- `cargo test -p codex-app-server turn_start_exec_approval_ --
--nocapture`
- `just fmt`
2026-02-20 21:15:21 -08:00
Michael Bolin
48af93399e feat: use OAI Responses API MessagePhase type directly in App Server v2 (#12422)
https://github.com/openai/codex/pull/10455 introduced the `phase` field,
and then https://github.com/openai/codex/pull/12072 introduced a
`MessagePhase` type in `v2.rs` that paralleled the `MessagePhase` type
in `codex-rs/protocol/src/models.rs`.

The app server protocol prefers `camelCase` while the Responses API uses
`snake_case`, so this meant we had two versions of `MessagePhase` with
different serialization rules. When the app server protocol refers to
types from the Responses API, we use the wire format of the the
Responses API even though it is inconsistent with the app server API.

This PR deletes `MessagePhase` from `v2.rs` and consolidates on the
Responses API version to eliminate confusion.
2026-02-20 20:43:36 -08:00
Michael Bolin
a73efab8dd fix: address flakiness in thread_resume_rejoins_running_thread_even_with_override_mismatch (#12381)
## Why
`thread/resume` responses for already-running threads can be reported as
`Idle` even while a turn is still in progress. This is caused by a
timing window where the runtime watch state has not yet observed the
running-thread transition, so API clients can receive stale status
information at resume time.

Possibly related: https://github.com/openai/codex/pull/11786

## What
- Add a shared status normalization helper, `resolve_thread_status`, in
`codex-rs/app-server/src/thread_status.rs` that resolves
`Idle`/`NotLoaded` to `Active { active_flags: [] }` when an in-progress
turn is known.
- Reuse this helper across thread response paths in
`codex-rs/app-server/src/codex_message_processor.rs` (including
`thread/start`, `thread/unarchive`, `thread/read`, `thread/resume`,
`thread/fork`, and review/thread-started notification responses).
- In `handle_pending_thread_resume_request`, use both the in-memory
`active_turn_snapshot` and the resumed rollout turns to decide whether a
turn is in progress before resolving thread status for the response.
- Extend `thread_status` tests to validate the new status-resolution
behavior directly.

## Verification
- `cargo test -p codex-app-server
suite::v2::thread_resume::thread_resume_rejoins_running_thread_even_with_override_mismatch`
2026-02-20 20:36:04 -08:00