## 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`
## 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.
## 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
## 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`.
## 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
## 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
## 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`
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.
## 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`
- add top-level `experimental_realtime_ws_backend_prompt` config key
(experimental / do not use) and include it in config schema
- apply the override only to `Op::RealtimeConversation` websocket
`backend_prompt`, with config + realtime tests
Addresses https://github.com/openai/codex/issues/11013
## Summary
- add a Plan implementation path in the TUI that lets users choose
reasoning before switching to Default mode and implementing
- add Plan-mode reasoning scope handling (Plan-only override vs
all-modes default), including config/schema/docs plumbing for
`plan_mode_reasoning_effort`
- remove the hardcoded Plan preset medium default and make the reasoning
popup reflect the active Plan override as `(current)`
- split the collaboration-mode switch notification UI hint into #12307
to keep this diff focused
If I have `plan_mode_reasoning_effort = "medium"` set in my
`config.toml`:
<img width="699" height="127" alt="Screenshot 2026-02-20 at 6 59 37 PM"
src="https://github.com/user-attachments/assets/b33abf04-6b7a-49ed-b2e9-d24b99795369"
/>
If I don't have `plan_mode_reasoning_effort` set in my `config.toml`:
<img width="704" height="129" alt="Screenshot 2026-02-20 at 7 01 51 PM"
src="https://github.com/user-attachments/assets/88a086d4-d2f1-49c7-8be4-f6f0c0fa1b8d"
/>
## Codex author
`codex resume 019c78a2-726b-7fe3-adac-3fa4523dcc2a`
- add top-level `experimental_realtime_ws_base_url` config key
(experimental / do not use) and include it in config schema
- apply the override only to `Op::RealtimeConversation` websocket
transport, with config + realtime tests
commit 923f931121 introduced a dependency
on `libcap`. This PR fixes the nix build by including `libcap` in nix's
build inputs
issue number: #12102. @etraut-openai gave me permission to open pr
Testing:
running `nix run .#codex-rs` works on both macos (aarch64) and nixos
(x86-64)
- Introduce `RealtimeConversationManager` for realtime API management
- Add `op::conversation` to start conversation, insert audio, insert
text, and close conversation.
- emit conversation lifecycle and realtime events.
- Move shared realtime payload types into codex-protocol and add core
e2e websocket tests for start/replace/transport-close paths.
Things to consider:
- Should we use the same `op::` and `Events` channel to carry audio? I
think we should try this simple approach and later we can create
separate one if the channels got congested.
- Sending text updates to the client: we can start simple and later
restrict that.
- Provider auth isn't wired for now intentionally
Exposes through the app server updated names set for a thread. This
enables other surfaces to use the core as the source of truth for thread
naming. `threadName` is gathered using the helper functions used to
interact with `session_index.jsonl`, and is hydrated in:
- `thread/list`
- `thread/read`
- `thread/resume`
- `thread/unarchive`
- `thread/rollback`
We don't do this for `thread/start` and `thread/fork`.
## Why
JSON schema codegen was silently resolving naming collisions by
appending numeric suffixes (for example `...2`, `...3`). That makes the
generated schema names unstable: removing an earlier colliding type can
cause a later type to be renumbered, which is a breaking change for
consumers that referenced the old generated name.
This PR makes those collisions explicit and reviewable.
Though note that once we remove `v1` from the codegen, we will no longer
support naming collisions. Or rather, naming collisions will have to be
handled explicitly rather than the numeric suffix approach.
## What Changed
- In `codex-rs/app-server-protocol/src/export.rs`, replaced implicit
numeric suffix collision handling for generated variant titles with
explicit special-case maps.
- Added a panic when a collision occurs without an entry in the map, so
new collisions fail loudly instead of silently renaming generated schema
types.
- Added the currently required special cases so existing generated names
remain stable.
- Extended the same approach to numbered `definitions` / `$defs`
collisions (for example `MessagePhase2`-style names) so those are also
explicitly tracked.
## Verification
- Ran targeted generator-path test:
- `cargo test -p codex-app-server-protocol
generate_json_filters_experimental_fields_and_methods -- --nocapture`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/12406).
* #12408
* __->__ #12406
## Problem
The TUI's "edit queued message" shortcut (Alt+Up) is either silently
swallowed or recognized as another key combination by Apple Terminal,
Warp, and VSCode's integrated terminal on macOS. Users in those
environments see the hint but pressing the keys does nothing.
## Mental model
When a model turn is in progress the user can still type follow-up
messages. These are queued and displayed below the composer with a hint
line showing how to pop the most recent one back into the editor. The
hint text and the actual key handler must agree on which shortcut is
used, and that shortcut must actually reach the TUI—i.e. it must not be
intercepted by the host terminal.
Three terminals are known to intercept Alt+Up: Apple Terminal (remaps it
to cursor movement), Warp (consumes it for its own command palette), and
VSCode (maps it to "move line up"). For these we use Shift+Left instead.
<p align="center">
<img width="283" height="182" alt="image"
src="https://github.com/user-attachments/assets/4a9c5d13-6e47-4157-bb41-28b4ce96a914"
/>
</p>
| macOS Native Terminal | Warp | VSCode Terminal |
|---|---|---|
| <img width="1557" height="1010" alt="SCR-20260219-kigi"
src="https://github.com/user-attachments/assets/f4ff52f8-119e-407b-a3f3-52f564c36d70"
/> | <img width="1479" height="1261" alt="SCR-20260219-krrf"
src="https://github.com/user-attachments/assets/5807d7c4-17ae-4a2b-aa27-238fd49d90fd"
/> | <img width="1612" height="1312" alt="SCR-20260219-ksbz"
src="https://github.com/user-attachments/assets/1cedb895-6966-4d63-ac5f-0eea0f7057e8"
/> |
## Non-goals
- Making the binding user-configurable at runtime (deferred to a broader
keybinding-config effort).
- Remapping any other shortcuts that might be terminal-specific.
## Tradeoffs
- **Exhaustive match instead of a wildcard default.** The
`queued_message_edit_binding_for_terminal` function explicitly lists
every `TerminalName` variant. This is intentional: adding a new terminal
to the enum will produce a compile error, forcing the author to decide
which binding that terminal should use.
- **Binding lives on `ChatWidget`, hint lives on `QueuedUserMessages`.**
The key event handler that actually acts on the press is in
`ChatWidget`, but the rendered hint text is inside `QueuedUserMessages`.
These are kept in sync by `ChatWidget` calling
`bottom_pane.set_queued_message_edit_binding(self.queued_message_edit_binding)`
during construction. A mismatch would show the wrong hint but would not
lose data.
## Architecture
```mermaid
graph TD
TI["terminal_info().name"] --> FN["queued_message_edit_binding_for_terminal(name)"]
FN --> KB["KeyBinding"]
KB --> CW["ChatWidget.queued_message_edit_binding<br/><i>key event matching</i>"]
KB --> BP["BottomPane.set_queued_message_edit_binding()"]
BP --> QUM["QueuedUserMessages.edit_binding<br/><i>rendered in hint line</i>"]
subgraph "Special terminals (Shift+Left)"
AT["Apple Terminal"]
WT["Warp"]
VS["VSCode"]
end
subgraph "Default (Alt+Up)"
GH["Ghostty"]
IT["iTerm2"]
OT["Others…"]
end
AT --> FN
WT --> FN
VS --> FN
GH --> FN
IT --> FN
OT --> FN
```
No new crates or public API surface. The only cross-crate dependency
added is `codex_core::terminal::{TerminalName, terminal_info}`, which
already existed for telemetry.
## Observability
No new logging. Terminal detection already emits a `tracing::debug!` log
line at startup with the detected terminal name, which is sufficient to
diagnose binding mismatches.
## Tests
- Existing `alt_up_edits_most_recent_queued_message` test is preserved
and explicitly sets the Alt+Up binding to isolate from the host
terminal.
- New parameterized async tests verify Shift+Left works for Apple
Terminal, Warp, and VSCode.
- A sync unit test asserts the mapping table covers the three special
terminals (Shift+Left) and that iTerm2 still gets Alt+Up.
Fixes#4490
## Summary
- show an info message when switching collaboration modes changes the
effective model or reasoning
- include the target mode in the message (for example `... for Plan
mode.`)
- add TUI tests for model-change and reasoning-only change notifications
on mode switch
<img width="715" height="184" alt="Screenshot 2026-02-20 at 2 01 40 PM"
src="https://github.com/user-attachments/assets/18d1beb3-ab87-4e1c-9ada-a10218520420"
/>
# External (non-OpenAI) Pull Request Requirements
Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md
If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.
Include a link to a bug report or enhancement request.
## What
Updates the optional `cargo-nextest` install command in
`docs/install.md`:
- `cargo install cargo-nextest` -> `cargo install --locked
cargo-nextest`
## Why
The current docs command can fail during source install because recent
`cargo-nextest` releases intentionally require `--locked`.
Repro (macOS, but likely not platform-specific):
- `cargo install cargo-nextest`
- Fails with a compile error from `locked-tripwire` indicating:
- `Nextest does not support being installed without --locked`
- suggests `cargo install --locked cargo-nextest`
Using the locked command succeeds:
- `cargo install --locked cargo-nextest`
## How
Single-line docs change in `docs/install.md` to match current
`cargo-nextest` install requirements.
## Validation
- Reproduced failure locally using a temporary `CARGO_HOME` directory
(clean Cargo home)
- Example command used: `CARGO_HOME=/tmp/cargo-home-test cargo install
cargo-nextest`
- Confirmed success with `cargo install --locked cargo-nextest`
## Summary
- switch the review test SSE mock helper to use the shared hermetic mock
server setup
- ensure review tests always have a default `/v1/models` stub during
Codex session bootstrap
- remove the race that caused intermittent `/v1/models` connection
failures and flaky ETag refresh assertions
## Testing
- `just fmt`
- `cargo test -p codex-core --test all
refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch`
- `cargo test -p codex-core --test all
review_uses_custom_review_model_from_config`
- repeated both targeted tests 5x in a loop
- `cargo clippy -p codex-core --tests -- -D warnings`
Hardens codex-rs/app-server connection lifecycle and outbound routing
for websocket clients. Fixes some FUD I was having
- Added per-connection disconnect signaling (CancellationToken) for
websocket transports.
- Split websocket handling into independent inbound/outbound tasks
coordinated by cancellation.
- Changed outbound routing so websocket connections use non-blocking
try_send; slow/full websocket writers are disconnected instead of
stalling broadcast delivery.
- Kept stdio behavior blocking-on-send (no forced disconnect) so local
stdio clients are not dropped when queues are temporarily full.
- Simplified outbound router flow by removing deferred
pending_closed_connections handling.
- Added guards to drop incoming response/notification/error messages
from unknown connections.
- Fixed listener teardown race in thread listener tasks using a
listener_generation check so stale tasks do not clear newer listeners.
Fixes
https://linear.app/openai/issue/CODEX-4966/multiclient-handle-slow-notification-consumers
## Tests
Added/updated transport tests covering:
- broadcast does not block on a slow/full websocket connection
- stdio connection waits instead of disconnecting on full queue
I (maxj) have tested manually and will retest before landing
Summary
- ensure destructive tool annotations short-circuit to require approval
- simplify approval logic to only require read/write + open-world when
destructive is false
- update the unit test to cover the new destructive behavior
Testing
- Not run (not requested)
## Summary
Install Node in the Bazel remote execution image using the version
pinned in `codex-rs/node-version.txt`.
## Why
`js_repl` tests run under Bazel remote execution and require a modern
Node runtime. Runner-level `setup-node` does not guarantee Node is
available (or recent enough) inside the remote worker container.
## What changed
- Updated `.github/workflows/Dockerfile.bazel` to install Node from
official tarballs at image build time.
- Added `xz-utils` for extracting `.tar.xz` archives.
- Copied `codex-rs/node-version.txt` into the image build context and
used it as the single source of truth for Node version.
- Added architecture mapping for multi-arch builds:
- `amd64 -> x64`
- `arm64 -> arm64`
- Verified install during image build with:
- `node --version`
- `npm --version`
## Impact
- Bazel remote workers should now have the required Node version
available for `js_repl` tests.
- Keeps Node version synchronized with repo policy via
`codex-rs/node-version.txt`.
## Testing
- Verified Dockerfile changes and build steps locally (build-time
commands are deterministic and fail fast on unsupported arch/version
fetch issues).
## Follow-up
- Rebuild and publish the Bazel runner image for both `linux/amd64` and
`linux/arm64`.
- Update image digests in `rbe.bzl` to roll out this runtime update in
CI.
#### [git stack](https://github.com/magus/git-stack-cli)
- ✅ `1` https://github.com/openai/codex/pull/12300
- ✅ `2` https://github.com/openai/codex/pull/12275
- 👉 `3` https://github.com/openai/codex/pull/12205
- ⏳ `4` https://github.com/openai/codex/pull/12185
- ⏳ `5` https://github.com/openai/codex/pull/10673
## Problem
Users without Codex access can hit a confusing local login loop. In the
denial case, the callback could fall through to generic behavior
(including a plain "Missing authorization code" page) instead of clearly
explaining that access was denied.
<img width="842" height="464" alt="Screenshot 2026-02-19 at 11 43 45 PM"
src="https://github.com/user-attachments/assets/f7a25e1d-e480-4ac2-b0ff-8bfe31003e66"
/>
<img width="842" height="464" alt="Screenshot 2026-02-19 at 11 44 53 PM"
src="https://github.com/user-attachments/assets/8a4fe6e4-b27b-483c-9f0c-60164933221d"
/>
## Scope
This PR improves local login error clarity only. It does not change
entitlement policy, RBAC rules, or who is allowed to use Codex.
## What Changed
- The local OAuth callback handler now parses `error` and
`error_description` on `/auth/callback` and exits the callback loop with
a real failure.
- Callback failures render a branded local Codex error page instead of a
generic/plain page.
- `access_denied` + `missing_codex_entitlement` is now mapped to an
explicit user-facing message telling the user Codex is not enabled for
their workspace and to contact their workspace administrator for access.
- Unknown OAuth callback errors continue to use a generic error page
while preserving the OAuth error code/details for debugging.
- Added the login error page template to Bazel assets so the local
binary can render it in Bazel builds.
## Non-goals
- No TUI onboarding/toast changes in this PR.
- No backend entitlement or policy changes.
## Tests
- Added an end-to-end `codex-login` test for `access_denied` +
`missing_codex_entitlement` and verified the page shows the actionable
admin guidance.
- Added an end-to-end `codex-login` test for a generic `access_denied`
reason to verify we keep a generic fallback page/message.
## Summary
Adds support for a Unix socket escape hatch so we can bypass socket
allowlisting when explicitly enabled.
## Description
* added a new flag, `network.dangerously_allow_all_unix_sockets` as an
explicit escape hatch
* In codex-network-proxy, enabling that flag now allows any absolute
Unix socket path from x-unix-socket instead of requiring each path to be
explicitly allowlisted. Relative paths are still rejected.
* updated the macOS seatbelt path in core so it enforces the same Unix
socket behavior:
* allowlisted sockets generate explicit network* subpath rules
* allow-all generates a broad network* (subpath "/") rule
---------
Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
## Summary
Tighten the `js_repl` freeform Lark grammar to block the most common
malformed payload wrappers before they reach runtime validation.
## What Changed
- Replaced the overly permissive `js_repl` freeform grammar (`start:
/[\s\S]*/`) with a structured grammar that still supports:
- plain JS source
- optional first-line `// codex-js-repl:` pragma followed by JS source
- Added grammar-level filtering for common bad payload shapes by
rejecting inputs whose first significant token starts with:
- `{` (JSON object wrapper like `{"code":"..."}`)
- `"` (quoted code string)
- `` ``` `` (markdown code fences)
- Implemented the grammar without regex lookahead/lookbehind because the
API-side Lark regex engine does not support look-around.
- Added a unit test to validate the grammar shape and guard against
reintroducing unsupported lookaround.
## Why
`js_repl` is a freeform tool, but the model sometimes emits wrapped
payloads (JSON, quoted strings, markdown fences) instead of raw
JavaScript. We already reject those at runtime, but this change moves
the constraint into the tool grammar so the model is less likely to
generate invalid tool-call payloads in the first place.
## Testing
- `cargo test -p codex-core
js_repl_freeform_grammar_blocks_common_non_js_prefixes`
- `cargo test -p codex-core parse_freeform_args_rejects_`
## Notes
- This intentionally over-blocks a few uncommon valid JS starts (for
example top-level `{ ... }` blocks or top-level quoted directives like
`"use strict";`) in exchange for preventing the common wrapped-payload
mistakes.
#### [git stack](https://github.com/magus/git-stack-cli)
- 👉 `1` https://github.com/openai/codex/pull/12300
- ⏳ `2` https://github.com/openai/codex/pull/12275
- ⏳ `3` https://github.com/openai/codex/pull/12205
- ⏳ `4` https://github.com/openai/codex/pull/12185
- ⏳ `5` https://github.com/openai/codex/pull/10673
## Summary
Simplify network approvals by removing per-attempt proxy correlation and
moving to session-level approval dedupe keyed by (host, protocol, port).
Instead of encoding attempt IDs into proxy credentials/URLs, we now
treat approvals as a destination policy decision.
- Concurrent calls to the same destination share one approval prompt.
- Different destinations (or same host on different ports) get separate
prompts.
- Allow once approves the current queued request group only.
- Allow for session caches that (host, protocol, port) and auto-allows
future matching requests.
- Never policy continues to deny without prompting.
Example:
- 3 calls:
- a.com (line 443)
- b.com (line 443)
- a.com (line 443)
=> 2 prompts total (a, b), second a waits on the first decision.
- a.com:80 is treated separately from a.com line 443
## Testing
- `just fmt` (in `codex-rs`)
- `cargo test -p codex-core tools::network_approval::tests`
- `cargo test -p codex-core` (unit tests pass; existing
integration-suite failures remain in this environment)
- add `LOG_FORMAT=json` support for app-server tracing logs via
`tracing_subscriber`'s built-in JSON formatter
- keep the default human-readable format unchanged and keep `RUST_LOG`
filtering behavior
- document the env var and update lockfile
Summary
- simplify the macOS sleep inhibitor FFI by replacing `dlopen` / `dlsym`
/ `transmute` with normal IOKit extern calls and `SAFETY` comments
- switch to cfg-selected platform implementations
(`imp::SleepInhibitor`) instead of `Box<dyn ...>`
- check in minimal IOKit bindings generated with `bindgen` and include
them from the macOS backend
- enable direct IOKit linkage in Bazel macOS builds by registering
`IOKit` in the Bazel `osx.framework(...)` toolchain extension list
- update `Cargo.lock` and `MODULE.bazel.lock` after removing the
build-time `bindgen` dependency path
Testing
- `just fmt`
- `cargo clippy -p codex-utils-sleep-inhibitor --all-targets -- -D
warnings`
- `cargo test -p codex-utils-sleep-inhibitor`
- `bazel test //codex-rs/utils/sleep-inhibitor:all --test_output=errors`
- `just bazel-lock-update`
- `just bazel-lock-check`
Context
- follow-up to #11711 addressing Ryan's review comments
- `bindgen` is used to generate the checked-in bindings file, but not at
build time
Summary
- capture the origin for each configured MCP server and expose it via
the connection manager
- plumb MCP server name/origin into tool logging and emit
codex.tool_result events with those fields
- add unit coverage for origin parsing and extend OTEL tests to assert
empty MCP fields for non-MCP tools
- currently not logging full urls or url paths to prevent logging
potentially sensitive data
Testing
- Not run (not requested)
## Summary
- add reasoning effort constants for the memories phase one and phase
two agents
- wire the constants into phase1 request creation and phase2 agent
configuration so the default efforts are always applied
## Testing
- Not run (not requested)
## Summary
- Add `rollout_summary_file: <generated>.md` to each thread header in
`raw_memories.md` so Phase 2 can reliably reference the canonical
rollout summary filename.
- Update the memory prompts/templates (`stage_one_system`,
`consolidation`, `read_path`) for the new task-oriented raw-memory /
MEMORY.md schema and stronger consolidation guidance.
## Details
- `codex-rs/core/src/memories/storage.rs`
- Writes the generated `rollout_summary_file` path into the per-thread
metadata header when rebuilding `raw_memories.md`.
- `codex-rs/core/src/memories/tests.rs`
- Verifies the canonical `rollout_summary_file` header is present and
ordered after `updated_at`/`cwd` in `raw_memories.md`.
- Verifies task-structured raw-memory content is preserved while the
canonical header is added.
- `codex-rs/core/templates/memories/*.md`
- Updates the stage-1 raw-memory format to task-grouped sections
(`task`, `task_group`, `task_outcome`).
- Updates Phase 2 consolidation guidance around recency (`updated_at`),
task-oriented `MEMORY.md` blocks, and richer evidence-backed
consolidation.
- Tweaks the quick memory pass wording to emphasize topics/workflows in
addition to keywords.
## Testing
- `cargo test -p codex-core memories`
We now write MCP tools from installed apps to disk cache so that they
can be picked up instantly at startup. We still do a fresh fetch from
remote MCP server but it's non blocking unless there's a cache miss.
- [x] Store apps tool cache in disk to reduce startup time.
thread/resume response includes latest turn with all items, in band so
no events are stale or lost
Testing
- e2e tested using app-server-test-client using flow described in
"Testing Thread Rejoin Behavior" in
codex-rs/app-server-test-client/README.md
- e2e tested in codex desktop by reconnecting to a running turn
## Why
`app/list` emits `app/list/updated` after whichever async load finishes
first (directory connectors or accessible tools). This test assumed the
directory-backed update always arrived first because it injected a tools
delay, but that assumption is not stable when the process-global Codex
Apps tools cache is already warm. In that case the accessible-tools path
can return immediately and the first notification shape flips, which
makes the assertion flaky.
Relevant code paths:
-
[`codex-rs/app-server/src/codex_message_processor.rs`](13ec97d72e/codex-rs/app-server/src/codex_message_processor.rs (L4949-L5034))
(concurrent loads + per-load `app/list/updated` notifications)
-
[`codex-rs/core/src/mcp_connection_manager.rs`](13ec97d72e/codex-rs/core/src/mcp_connection_manager.rs (L1182-L1197))
(Codex Apps tools cache hit path)
## What Changed
Updated
`suite::v2::app_list::list_apps_returns_connectors_with_accessible_flags`
in `codex-rs/app-server/tests/suite/v2/app_list.rs` to accept either
valid first `app/list/updated` payload:
- the directory-first snapshot
- the accessible-tools-first snapshot
The test still keeps the later assertions strict:
- the second `app/list/updated` notification must be the fully merged
result
- the final `app/list` response must match the same merged result
I also added an inline comment explaining why the first notification is
intentionally order-insensitive.
## Verification
- `cargo test -p codex-app-server`
## Why
Several tests intentionally exercise behavior while a turn is still
active. The cleanup sequence for those tests (`turn/interrupt` + waiting
for `codex/event/turn_aborted`) was duplicated across files, which made
the rationale easy to lose and the pattern easy to apply inconsistently.
This change centralizes that cleanup in one place with a single
explanatory doc comment.
## What Changed
### Added shared helper
In `codex-rs/app-server/tests/common/mcp_process.rs`:
- Added `McpProcess::interrupt_turn_and_wait_for_aborted(...)`.
- Added a doc comment explaining why explicit interrupt + terminal wait
is required for tests that intentionally leave a turn in-flight.
### Migrated call sites
Replaced duplicated interrupt/aborted blocks with the helper in:
- `codex-rs/app-server/tests/suite/v2/thread_resume.rs`
- `thread_resume_rejects_history_when_thread_is_running`
- `thread_resume_rejects_mismatched_path_when_thread_is_running`
- `codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs`
- `turn_start_shell_zsh_fork_executes_command_v2`
-
`turn_start_shell_zsh_fork_subcommand_decline_marks_parent_declined_v2`
- `codex-rs/app-server/tests/suite/v2/turn_steer.rs`
- `turn_steer_returns_active_turn_id`
### Existing cleanup retained
In `codex-rs/app-server/tests/suite/v2/turn_start.rs`:
- `turn_start_accepts_local_image_input` continues to explicitly wait
for `turn/completed` so the turn lifecycle is fully drained before test
exit.
## Verification
- `cargo test -p codex-app-server`
## Why
`thread_resume` tests can intentionally create an in-flight turn, assert
a `thread/resume` error path, and return immediately. That leaves turn
work active during teardown, which can surface as intermittent `LEAK`
failures.
Sample output that motivated this investigation (reported during test
runs):
```text
LEAK ... codex-app-server::all suite::v2::thread_resume::thread_resume_rejoins_running_thread_even_with_override_mismatch
```
## What Changed
Updated only `codex-rs/app-server/tests/suite/v2/thread_resume.rs`:
- `thread_resume_rejects_history_when_thread_is_running`
- `thread_resume_rejects_mismatched_path_when_thread_is_running`
Both tests now:
1. capture the running turn id from `TurnStartResponse`
2. assert the expected `thread/resume` error
3. call `turn/interrupt` for that running turn
4. wait for `codex/event/turn_aborted` before returning
## Why This Is The Correct Fix
These tests are specifically validating resume behavior while a turn is
active. They should also own cleanup of that active turn before exiting.
Explicitly interrupting and waiting for the terminal abort notification
removes teardown races and avoids relying on process-drop behavior to
clean up in-flight work.
## Repro / Verification
Repro command used for investigation:
```bash
cargo nextest run -p codex-app-server -j 2 --no-fail-fast --stress-count 50 --status-level leak --final-status-level fail -E 'test(suite::v2::thread_resume::thread_resume_rejoins_running_thread_even_with_override_mismatch) | test(suite::v2::thread_resume::thread_resume_rejects_history_when_thread_is_running) | test(suite::v2::thread_resume::thread_resume_rejects_mismatched_path_when_thread_is_running) | test(suite::v2::thread_resume::thread_resume_keeps_in_flight_turn_streaming)'
```
Observed before this change: intermittent `LEAK` in
`thread_resume_rejects_history_when_thread_is_running`.
Also verified with:
- `cargo test -p codex-app-server`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/12269).
* #12271
* __->__ #12269
## Summary
Implements the `ConfigToml.permissions.network` and uses it to populate
`NetworkProxyConfig`. We now parse a new nested permissions/network
config shape which is converted into the proxy’s runtime config.
When managed requirements exist, we still apply those constraints on top
of user settings (so managed policy still wins).
* Cleaned up the old constructor path so it now accepts both user config
+ managed constraints directly.
* Updated the reload path so live proxy config reloads respect
[permissions.network] too, while still supporting the existing top-level
[network] format.
### Behavior
- User-defined `[permissions.network]` values are now honored.
- Managed constraints still take effect and are validated against the
resulting policy.
If a first-party proc_macro crate has tests/binaries that would get
autogenerated by the macro, it was being handled incorrectly. Found by
an external OS contributor!
## Summary
Implements a configurable MCP OAuth callback URL override for `codex mcp
login` and app-server OAuth login flows, including support for non-local
callback endpoints (for example, devbox ingress URLs).
## What changed
- Added new config key: `mcp_oauth_callback_url` in
`~/.codex/config.toml`.
- OAuth authorization now uses `mcp_oauth_callback_url` as
`redirect_uri` when set.
- Callback handling validates the callback path against the configured
redirect URI path.
- Listener bind behavior is now host-aware:
- local callback URL hosts (`localhost`, `127.0.0.1`, `::1`) bind to
`127.0.0.1`
- non-local callback URL hosts bind to `0.0.0.0`
- `mcp_oauth_callback_port` remains supported and is used for the
listener port.
- Wired through:
- CLI MCP login flow
- App-server MCP OAuth login flow
- Skill dependency OAuth login flow
- Updated config schema and config tests.
## Why
Some environments need OAuth callbacks to land on a specific reachable
URL (for example ingress in remote devboxes), not loopback. This change
allows that while preserving local defaults for existing users.
## Backward compatibility
- No behavior change when `mcp_oauth_callback_url` is unset.
- Existing `mcp_oauth_callback_port` behavior remains intact.
- Local callback flows continue binding to loopback by default.
## Testing
- `cargo test -p codex-rmcp-client callback -- --nocapture`
- `cargo test -p codex-core --lib mcp_oauth_callback -- --nocapture`
- `cargo check -p codex-cli -p codex-app-server -p codex-rmcp-client`
## Example config
```toml
mcp_oauth_callback_port = 5555
mcp_oauth_callback_url = "https://<devbox>-<namespace>.gateway.<cluster>.internal.api.openai.org/callback"
## Summary
- The experimental Bazel CI builds fail on all platforms because askama
resolves template paths relative to `CARGO_MANIFEST_DIR`, which points
outside the Bazel sandbox. This produces errors like:
```
error: couldn't read
`codex-rs/core/src/memories/../../../../../../../../../../../work/codex/codex/codex-rs/core/templates/memories/consolidation.md`:
No such file or directory
```
- Replaced `#[derive(Template)]` + `#[template(path = "...")]` with
`include_str!` + `str::replace()` for the three affected templates
(`consolidation.md`, `stage_one_input.md`, `read_path.md`).
`include_str!` resolves paths relative to the source file, which works
correctly in both Cargo and Bazel builds.
- The templates only use simple `{{ variable }}` substitution with no
control flow or filters, so no askama functionality is lost.
- Removes the `askama` dependency from `codex-core` since it was the
only crate using it. The workspace-level dependency definition is left
in place.
- This matches the existing pattern used throughout the codebase — e.g.
`codex-rs/core/src/memories/mod.rs` already uses
`include_str!("../../templates/memories/stage_one_system.md")` for the
fourth template file.
## Test plan
- [ ] Verify Bazel (experimental) CI passes on all platforms
- [ ] Verify rust-ci (Cargo) builds and tests continue to pass
- [ ] Verify `cargo test -p codex-core` passes locally
## Why
`cargo nextest` was intermittently reporting `LEAK` for
`codex-app-server` tests even when assertions passed. This adds noise
and flakiness to local/CI signals.
Sample output used as the basis of this investigation:
```text
LEAK [ 7.578s] ( 149/3663) codex-app-server::all suite::output_schema::send_user_turn_output_schema_is_per_turn_v1
LEAK [ 7.383s] ( 210/3663) codex-app-server::all suite::v2::dynamic_tools::dynamic_tool_call_round_trip_sends_text_content_items_to_model
LEAK [ 7.768s] ( 213/3663) codex-app-server::all suite::v2::dynamic_tools::thread_start_injects_dynamic_tools_into_model_requests
LEAK [ 8.841s] ( 224/3663) codex-app-server::all suite::v2::output_schema::turn_start_accepts_output_schema_v2
LEAK [ 8.151s] ( 225/3663) codex-app-server::all suite::v2::plan_item::plan_mode_uses_proposed_plan_block_for_plan_item
LEAK [ 8.230s] ( 232/3663) codex-app-server::all suite::v2::safety_check_downgrade::openai_model_header_mismatch_emits_model_rerouted_notification_v2
LEAK [ 6.472s] ( 273/3663) codex-app-server::all suite::v2::turn_start::turn_start_accepts_collaboration_mode_override_v2
LEAK [ 6.107s] ( 275/3663) codex-app-server::all suite::v2::turn_start::turn_start_accepts_personality_override_v2
```
## How I Reproduced
I focused on the suspect tests and ran them under `nextest` stress mode
with leak reporting enabled.
```bash
cargo nextest run -p codex-app-server -j 2 --no-fail-fast --stress-count 25 --status-level leak --final-status-level fail -E 'test(suite::output_schema::send_user_turn_output_schema_is_per_turn_v1) | test(suite::v2::dynamic_tools::dynamic_tool_call_round_trip_sends_text_content_items_to_model) | test(suite::v2::dynamic_tools::thread_start_injects_dynamic_tools_into_model_requests) | test(suite::v2::output_schema::turn_start_accepts_output_schema_v2) | test(suite::v2::plan_item::plan_mode_uses_proposed_plan_block_for_plan_item) | test(suite::v2::safety_check_downgrade::openai_model_header_mismatch_emits_model_rerouted_notification_v2) | test(suite::v2::turn_start::turn_start_accepts_collaboration_mode_override_v2) | test(suite::v2::turn_start::turn_start_accepts_personality_override_v2)'
```
This reproduced intermittent `LEAK` statuses while tests still passed.
## What Changed
In `codex-rs/app-server/tests/common/mcp_process.rs`:
- Changed `stdin: ChildStdin` to `stdin: Option<ChildStdin>` so teardown
can explicitly close stdin.
- In `Drop`, close stdin first to trigger EOF-based graceful shutdown.
- Wait briefly for graceful exit.
- If still running, fall back to `start_kill()` and the existing bounded
`try_wait()` loop.
- Updated send-path handling to bail if stdin is already closed.
## Why This Is the Right Fix
The leak signal was caused by child-process teardown timing, not
test-logic assertion failure. The helper previously relied mostly on
force-kill timing in `Drop`; that can race with nextest leak detection.
Closing stdin first gives `codex-app-server` a deterministic, graceful
shutdown path before force-kill. Keeping the force-kill fallback
preserves robustness if graceful shutdown does not complete in time.
## Verification
- `cargo test -p codex-app-server`
- Re-ran the stress repro above after this change: no `LEAK` statuses
observed.
- Additional high-signal stress run also showed no leaks:
```bash
cargo nextest run -p codex-app-server -j 2 --no-fail-fast --stress-count 100 --status-level leak --final-status-level fail -E 'test(suite::output_schema::send_user_turn_output_schema_is_per_turn_v1) | test(suite::v2::dynamic_tools::dynamic_tool_call_round_trip_sends_text_content_items_to_model)'
```
## Summary
- Require revised `<proposed_plan>` blocks in the same planning session
to be complete replacements, not partial/delta plans.
- Scope that cumulative replacement rule to the current planning session
only.
- Clarify that after leaving Plan mode (for example switching to Default
mode to implement) or when explicitly asked for a new plan, the model
should produce a new self-contained plan without inheriting prior plan
blocks unless requested.
## Testing
- Not run (prompt/template text-only change).
Summary
- avoid emitting metrics for features marked as `Stage::Removed`
- keep feature metrics aligned with active and planned states only
Testing
- Not run (not requested)
## Why
`McpConnectionManager` used a two-phase setup (`new()` followed by
`initialize()`), which forced call sites to construct placeholder state
and then mutate it asynchronously. That made MCP startup/refresh flows
harder to follow and easier to misuse, especially around cancellation
token ownership.
## What changed
- Replaced the two-phase initialization flow with a single async
constructor: `McpConnectionManager::new(...) -> (Self,
CancellationToken)`.
- Added `McpConnectionManager::new_uninitialized()` for places that need
an empty manager before async startup begins.
- Added `McpConnectionManager::new_mcp_connection_manager_for_tests()`
for test-only construction.
- Updated MCP startup and refresh call sites in
`codex-rs/core/src/codex.rs` to build a fresh manager via `new(...)`,
swap it in, and update the startup cancellation token consistently.
- Updated MCP snapshot/connector call sites in
`codex-rs/core/src/mcp/mod.rs` and `codex-rs/core/src/connectors.rs` to
use the consolidated constructor.
- Removed the now-obsolete `reset_mcp_startup_cancellation_token()`
helper in favor of explicit token replacement at the call sites.
## Testing
- Not run (refactor-only change; no new behavior was intended).
Summary
- expose `agents.max_depth` in config schema and toml parsing, with
defaults and validation
- thread-spawn depth guards and multi-agent handler now respect the
configured limit instead of a hardcoded value
- ensure documentation and helpers account for agent depth limits
TL;DR
Add top-level `model_catalog_json` config support so users can supply a
local model catalog override from a JSON file path (including adding new
models) without backend changes.
### Problem
Codex previously had no clean client-side way to replace/overlay model
catalog data for local testing of model metadata and new model entries.
### Fix
- Add top-level `model_catalog_json` config field (JSON file path).
- Apply catalog entries when resolving `ModelInfo`:
1. Base resolved model metadata (remote/fallback)
2. Catalog overlay from `model_catalog_json`
3. Existing global top-level overrides (`model_context_window`,
`model_supports_reasoning_summaries`, etc.)
### Note
Will revisit per-field overrides in a follow-up
### Tests
Added tests
## Summary
- add `previous_context_item: Option<TurnContextItem>` to
`ContextManager`
- expose session/state accessors for reading and updating the stored
previous context item
- switch settings diffing to use `TurnContextItem` instead of
`TurnContext`
- remove submission-loop local `previous_context` and persist the
previous context item in history
## Testing
- `just fmt`
- `just fix -p codex-core`
- `cargo test -p codex-core --test all model_switching::`
- `cargo test -p codex-core --test all collaboration_instructions::`
- `cargo test -p codex-core --test all personality::`
- `cargo test -p codex-core --test all
permissions_messages::permissions_message_not_added_when_no_change`
Summary
This PR expands MCP client-side approval behavior beyond codex_apps and
tightens elicitation capability signaling.
- Removed the codex_apps-only gate in MCP tool approval checks, so
local/custom MCP servers are now eligible for the same client-side
approval prompt flow when tool annotations indicate side effects.
- Updated approval memory keying to support tools without a connector ID
(connector_id: Option<String>), allowing “Approve this Session” to be
remembered even when connector metadata is missing.
- Updated prompt text for non-codex_apps tools to identify origin as The
<server> MCP server instead of This app.
- Added MCP initialization capability policy so only codex_apps
advertises MCP elicitation capability; other servers advertise no
elicitation support.
- Added regression tests for:
server-specific prompt copy behavior
codex-apps-only elicitation capability advertisement
Testing
- Not run (not requested)
- Summary
- raise `DEFAULT_MEMORIES_MAX_ROLLOUTS_PER_STARTUP` to 16 so more
rollouts are allowed per startup
- lower `DEFAULT_MEMORIES_MIN_ROLLOUT_IDLE_HOURS` to 6 to make rollouts
eligible sooner
- Testing
- Not run (not requested)
Summary
- replace the stale `docs/config.md#feature-flags` reference in the
legacy feature notice with the canonical published URL
- align the deprecation notice test to expect the new link
This addresses #12123
## Summary
- Updates the Linux bubblewrap sandbox args to mount a minimal `/dev`
using `--dev /dev` instead of only binding `/dev/null`. tools needing
entropy (git, crypto libs, etc.) can fail.
- Changed mount order so `--dev /dev` is added before writable-root
`--bind` mounts, preserving writable `/dev/*` submounts like `/dev/shm`
## Why
Fixes sandboxed command failures when reading `/dev/urandom` (and
similar standard device-node access).
Fixes https://github.com/openai/codex/issues/12056
## Summary
- stabilize
`thread_resume_rejoins_running_thread_even_with_override_mismatch` by
using a valid delayed second SSE response instead of an intentionally
truncated stream
- set `RUST_MIN_STACK=4194304` for spawned app-server test processes in
`McpProcess` to avoid stack-sensitive CI overflows in detached review
tests
## Why
- the thread-resume assertion could race with a mocked stream-disconnect
error and intermittently observe `systemError`
- detached review startup is stack-sensitive in some CI environments;
pinning a larger stack in the test harness removes that flake without
changing product behavior
## Validation
- `just fmt`
- `cargo test -p codex-app-server --test all
suite::v2::thread_resume::thread_resume_rejoins_running_thread_even_with_override_mismatch`
- `cargo test -p codex-app-server --test all
suite::v2::review::review_start_with_detached_delivery_returns_new_thread_id`
Motivation
- Today, a newly connected client has no direct way to determine the
current runtime status of threads from read/list responses alone.
- This forces clients to infer state from transient events, which can
lead to stale or inconsistent UI when reconnecting or attaching late.
Changes
- Add `status` to `thread/read` responses.
- Add `statuses` to `thread/list` responses.
- Emit `thread/status/changed` notifications with `threadId` and the new
status.
- Track runtime status for all loaded threads and default unknown
threads to `idle`.
- Update protocol/docs/tests/schema fixtures for the revised API.
Testing
- Validated protocol API changes with automated protocol tests and
regenerated schema/type fixtures.
- Validated app-server behavior with unit and integration test suites,
including status transitions and notifications.
app-server support for initiating Windows sandbox setup.
server responds quickly to setup request and makes a future RPC call
back to client when the setup finishes.
The TUI implementation is unaffected but in a future PR I'll update the
TUI to use the shared setup helper
(`windows_sandbox.run_windows_sandbox_setup`)
## Summary
Fix `js_repl` package-resolution boundary checks for macOS temp
directory path aliasing (`/var` vs `/private/var`).
## Problem
`js_repl` verifies that resolved bare-package imports stay inside a
configured `node_modules` root.
On macOS, temp directories are commonly exposed as `/var/...` but
canonicalize to `/private/var/...`.
Because the boundary check compared raw paths with `path.relative(...)`,
valid resolutions under temp dirs could be misclassified as escaping the
allowed base, causing false `Module not found` errors.
## Changes
- Add `fs` import in the JS kernel.
- Add `canonicalizePath()` using `fs.realpathSync.native(...)` (with
safe fallback).
- Canonicalize both `base` and `resolvedPath` before running the
`node_modules` containment check.
## Impact
- Fixes false-negative boundary checks for valid package resolutions in
macOS temp-dir scenarios.
- Keeps the existing security boundary behavior intact.
- Scope is limited to `js_repl` kernel module path validation logic.
#### [git stack](https://github.com/magus/git-stack-cli)
- 👉 `1` https://github.com/openai/codex/pull/12177
- ⏳ `2` https://github.com/openai/codex/pull/10673
## Summary
Increase the rollout summary filename slug cap from 20 to 60 characters
in memory storage.
## What changed
- Updated `ROLLOUT_SLUG_MAX_LEN` from `20` to `60` in:
- `codex-rs/core/src/memories/storage.rs`
- Updated slug truncation test to verify 60-char behavior.
## Why
This preserves more semantic context in rollout summary filenames while
keeping existing normalization behavior unchanged.
## Testing
- `just fmt`
- `cargo test -p codex-core
memories::storage::tests::rollout_summary_file_stem_sanitizes_and_truncates_slug
-- --exact`
The issue was that the file_watcher never unsubscribe a file watch. All
of them leave in the owning of the ThreadManager. As a result, for each
newly created thread we create a new file watcher but this one never get
deleted even if we close the thread. On Unix system, a file watcher uses
an `inotify` and after some time we end up having consumed all of them.
This PR adds a mechanism to unsubscribe a file watcher when a thread is
dropped
We've continued to receive reports from users that they're seeing the
error message "Your access token could not be refreshed because your
refresh token was already used. Please log out and sign in again." This
PR fixes two holes in the token refresh logic that lead to this
condition.
Background: A previous change in token refresh introduced the
`UnauthorizedRecovery` object. It implements a state machine in the core
agent loop that first performs a load of the on-disk auth information
guarded by a check for matching account ID. If it finds that the on-disk
version has been updated by another instance of codex, it uses the
reloaded auth tokens. If the on-disk version hasn't been updated, it
issues a refresh request from the token authority.
There are two problems that this PR addresses:
Problem 1: We weren't doing the same thing for the code path used by the
app server interface. This PR effectively replicates the
`UnauthorizedRecovery` logic for that code path.
Problem 2: The `UnauthorizedRecovery` logic contained a hole in the
`ReloadOutcome::Skipped` case. Here's the scenario. A user starts two
instances of the CLI. Instance 1 is active (working on a task), instance
2 is idle. Both instances have the same in-memory cached tokens. The
user then runs `codex logout` or `codex login` to log in to a separate
account, which overwrites the `auth.json` file. Instance 1 receives a
401 and refreshes its token, but it doesn't write the new token to the
`auth.json` file because the account ID doesn't match. Instance 2 is
later activated and presented with a new task. It immediately hits a 401
and attempts to refresh its token but fails because its cached refresh
token is now invalid. To avoid this situation, I've changed the logic to
immediately fail a token refresh if the user has since logged out or
logged in to another account. This will still be seen as an error by the
user, but the cause will be clearer.
I also took this opportunity to clean up the names of existing functions
to make their roles clearer.
* `try_refresh_token` is renamed `request_chatgpt_token_refresh`
* the existing `refresh_token` is renamed `refresh_token_from_authority`
(there's a new higher-level function named `refresh_token` now)
* `refresh_tokens` is renamed `refresh_and_persist_chatgpt_token`, and
it now implicitly reloads
* `update_tokens` is renamed `persist_tokens`
Summary
- prevent delegated review agents from re-enabling blocked tools by
explicitly disabling the Collab feature alongside web search and view
image controls
Testing
- Not run (not requested)
## Summary
This change removes tool-list filtering in `js_repl_tools_only` mode and
relies on the normal model tool descriptions, while still enforcing that
tool execution must go through `js_repl` + `codex.tool(...)`.
## Motivation
The previous `js_repl_tools_only` filtering hid most tools from the
model request, which diverged from standard tool-list behavior and made
signatures less discoverable. I tested that this filtering is not
needed, and the model can follow the prompt to only call tools via
`js_repl`.
## What Changed
- `filter_tools_for_model(...)` in `core/src/tools/spec.rs` is now a
pass-through (no filtering when `js_repl_tools_only` is enabled).
- Updated tests to assert that model tools are not filtered in
`js_repl_tools_only` mode.
- Updated dynamic-tool test to assert dynamic tools remain visible in
model tool specs.
- Removed obsolete test helper used only by the old filtering
assertions.
## Safety / Behavior
- This commit does **not** relax execution policy.
- Direct model tool calls remain blocked in `js_repl_tools_only` mode
(except internal `js_repl` tools), and callers are instructed to use
`js_repl` + `codex.tool(...)`.
## Testing
- `cargo test -p codex-core js_repl_tools_only`
- Manual rollout validation showed the model can follow the `js_repl`
routing instructions without needing filtered tool lists.
#### [git stack](https://github.com/magus/git-stack-cli)
- 👉 `1` https://github.com/openai/codex/pull/12069
- ⏳ `2` https://github.com/openai/codex/pull/10673
- ⏳ `3` https://github.com/openai/codex/pull/10670
…fault
Update the list of platform defaults included for `ReadOnlyAccess`.
When `ReadOnlyAccess::Restricted::include_platform_defaults` is `true`,
the policy defined in
`codex-rs/core/src/seatbelt_platform_defaults.sbpl` is appended to
enable macOS programs to function properly.
# External (non-OpenAI) Pull Request Requirements
In `js_repl` mode, module resolution currently starts from
`js_repl_kernel.js`, which is written to a per-kernel temp dir. This
effectively means that bare imports will not resolve.
This PR adds a new config option, `js_repl_node_module_dirs`, which is a
list of dirs that are used (in order) to resolve a bare import. If none
of those work, the current working directory of the thread is used.
For example:
```toml
js_repl_node_module_dirs = [
"/path/to/node_modules/",
"/other/path/to/node_modules/",
]
```
## Summary
- add a dedicated `core/tests/suite/model_visible_layout.rs` snapshot
suite to materialize model-visible request layout in high-value
scenarios
- add three reviewer-focused snapshot scenarios:
- turn-level context updates (cwd / permissions / personality)
- first post-resume turn with model hydration + personality change
- first post-resume turn where pre-turn model override matches rollout
model
- wire the new suite into `core/tests/suite/mod.rs`
- commit generated `insta` snapshots under `core/tests/suite/snapshots/`
## Why
This creates a stable, reviewable baseline of model-visible context
layout against `main` before follow-on context-management refactors. It
lets subsequent PRs show focused snapshot diffs for behavior changes
instead of introducing the test surface and behavior changes at once.
## Testing
- `just fmt`
- `INSTA_UPDATE=always cargo test -p codex-core model_visible_layout`
## Summary
- add realtime websocket client transport in codex-api
- send session.create on connect with backend prompt and optional
conversation_id
- keep session.update for prompt changes after connect
- switch inbound event parsing to a tagged enum (typed variants instead
of optional field bag)
- add a websocket e2e integration test in
codex-rs/codex-api/tests/realtime_websocket_e2e.rs
## Why
This moves the realtime transport to an explicit session-create
handshake and improves protocol safety with typed inbound events.
## Testing
- Added e2e integration test coverage for session create + event flow in
the API crate.
zsh fork PR stack:
- https://github.com/openai/codex/pull/12051
- https://github.com/openai/codex/pull/12052👈
### Summary
This PR introduces a feature-gated native shell runtime path that routes
shell execution through a patched zsh exec bridge, removing MCP-specific
behavior from the shell hot path while preserving existing
CommandExecution lifecycle semantics.
When shell_zsh_fork is enabled, shell commands run via patched zsh with
per-`execve` interception through EXEC_WRAPPER. Core receives wrapper
IPC requests over a Unix socket, applies existing approval policy, and
returns allow/deny before the subcommand executes.
### What’s included
**1) New zsh exec bridge runtime in core**
- Wrapper-mode entrypoint (maybe_run_zsh_exec_wrapper_mode) for
EXEC_WRAPPER invocations.
- Per-execution Unix-socket IPC handling for wrapper requests/responses.
- Approval callback integration using existing core approval
orchestration.
- Streaming stdout/stderr deltas to existing command output event
pipeline.
- Error handling for malformed IPC, denial/abort, and execution
failures.
**2) Session lifecycle integration**
SessionServices now owns a `ZshExecBridge`.
Session startup initializes bridge state; shutdown tears it down
cleanly.
**3) Shell runtime routing (feature-gated)**
When `shell_zsh_fork` is enabled:
- Build execution env/spec as usual.
- Add wrapper socket env wiring.
- Execute via `zsh_exec_bridge.execute_shell_request(...)` instead of
the regular shell path.
- Non-zsh-fork behavior remains unchanged.
**4) Config + feature wiring**
- Added `Feature::ShellZshFork` (under development).
- Added config support for `zsh_path` (optional absolute path to patched
zsh):
- `Config`, `ConfigToml`, `ConfigProfile`, overrides, and schema.
- Session startup validates that `zsh_path` exists/usable when zsh-fork
is enabled.
- Added startup test for missing `zsh_path` failure mode.
**5) Seatbelt/sandbox updates for wrapper IPC**
- Extended seatbelt policy generation to optionally allow outbound
connection to explicitly permitted Unix sockets.
- Wired sandboxing path to pass wrapper socket path through to seatbelt
policy generation.
- Added/updated seatbelt tests for explicit socket allow rule and
argument emission.
**6) Runtime entrypoint hooks**
- This allows the same binary to act as the zsh wrapper subprocess when
invoked via `EXEC_WRAPPER`.
**7) Tool selection behavior**
- ToolsConfig now prefers ShellCommand type when shell_zsh_fork is
enabled.
- Added test coverage for precedence with unified-exec enabled.
## Summary
- standardize remote compaction test mocking around one default behavior
in shared helpers
- make default remote compact mocks mirror production shape: keep
`message/user` + `message/developer`, drop assistant/tool artifacts,
then append a summary user message
- switch non-special `compact_remote` tests to the shared default mock
instead of ad-hoc JSON payloads
## Special-case tests that still use explicit mocks
- remote compaction error payload / HTTP failure behavior
- summary-only compact output behavior
- manual `/compact` with no prior user messages
- stale developer-instruction injection coverage
## Why
This removes inconsistent manual remote compaction fixtures and gives us
one source of truth for normal remote compact behavior, while preserving
explicit mocks only where tests intentionally cover non-default
behavior.
zsh fork PR stack:
- https://github.com/openai/codex/pull/12051👈
- https://github.com/openai/codex/pull/12052
With upcoming support for a fork of zsh that allows us to intercept
`execve` and run execpolicy checks for each subcommand as part of a
`CommandExecution`, it will be possible for there to be multiple
approval requests for a shell command like `/path/to/zsh -lc 'git status
&& rg \"TODO\" src && make test'`.
To support that, this PR introduces a new `approval_id` field across
core, protocol, and app-server so that we can associate approvals
properly for subcommands.
### Summary
Ensure that we use the model value from the response header only so that
we are guaranteed with the correct slug name. We are no longer checking
against the model value from response so that we are less likely to have
false positive.
There are two different treatments - for SSE we use the header from the
response and for websocket we check top-level events.
* Add v2 server notifications `thread/archived` and `thread/unarchived`
with a `threadId` payload.
* Wire new events into `thread/archive` and `thread/unarchive` success
paths.
* Update app-server protocol/schema/docs accordingly.
Testing:
- Updated archive/unarchive end-to-end tests to verify both
notifications are emitted with the expected thread id payload.
## Summary
- change the cwd-change prompt (shown when resuming/forking across
different directories) so `Ctrl+C`/`Ctrl+D` exits the session instead of
implicitly selecting "Use session directory"
- introduce explicit prompt and resolver exit outcomes so this intent is
propagated cleanly through both startup resume/fork and in-app `/resume`
flows
- add a unit test that verifies `Ctrl+C` exits rather than selecting an
option
## Why
Previously, pressing `Ctrl+C` on this prompt silently picked one of the
options, which made it hard to abort. This aligns the prompt with the
expected quit behavior.
## Codex author
`codex resume 019c6d39-bbfb-7dc3-8008-1388a054e86d`
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.8
to 0.11.9.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rust-cli/env_logger/releases">env_logger's
releases</a>.</em></p>
<blockquote>
<h2>v0.11.9</h2>
<h2>[0.11.9] - 2026-02-11</h2>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md">env_logger's
changelog</a>.</em></p>
<blockquote>
<h2>[0.11.9] - 2026-02-11</h2>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f06b4c7cf"><code>2f06b4c</code></a>
chore: Release</li>
<li><a
href="57e13acb42"><code>57e13ac</code></a>
chore: Release</li>
<li><a
href="4f9066d8af"><code>4f9066d</code></a>
Merge pull request <a
href="https://redirect.github.com/rust-cli/env_logger/issues/393">#393</a>
from rust-cli/renovate/crate-ci-typos-1.x</li>
<li><a
href="3e4709a266"><code>3e4709a</code></a>
chore(deps): Update Rust crate snapbox to v0.6.24 (<a
href="https://redirect.github.com/rust-cli/env_logger/issues/394">#394</a>)</li>
<li><a
href="80ff83adba"><code>80ff83a</code></a>
chore(deps): Update pre-commit hook crate-ci/typos to v1.42.3</li>
<li><a
href="76891b9e32"><code>76891b9</code></a>
Merge pull request <a
href="https://redirect.github.com/rust-cli/env_logger/issues/392">#392</a>
from epage/template</li>
<li><a
href="14cda4a666"><code>14cda4a</code></a>
chore: Update from _rust template</li>
<li><a
href="e4f2b351a3"><code>e4f2b35</code></a>
chore(ci): Update action</li>
<li><a
href="6d0d36b072"><code>6d0d36b</code></a>
chore(ci): Clean up previous branch in case it was leaked</li>
<li><a
href="30b3b14bd6"><code>30b3b14</code></a>
chore(ci): Fix how rustfmt jobs run</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-cli/env_logger/compare/v0.11.8...v0.11.9">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eric Traut <etraut@openai.com>
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.56 to 4.5.58.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/releases">clap's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.58</h2>
<h2>[4.5.58] - 2026-02-11</h2>
<h2>v4.5.57</h2>
<h2>[4.5.57] - 2026-02-03</h2>
<h3>Fixes</h3>
<ul>
<li>Regression from 4.5.55 where having an argument with
<code>.value_terminator("--")</code> caused problems with an
argument with <code>.last(true)</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/blob/master/CHANGELOG.md">clap's
changelog</a>.</em></p>
<blockquote>
<h2>[4.5.58] - 2026-02-11</h2>
<h2>[4.5.57] - 2026-02-03</h2>
<h3>Fixes</h3>
<ul>
<li>Regression from 4.5.55 where having an argument with
<code>.value_terminator("--")</code> caused problems with an
argument with <code>.last(true)</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="88f13cb4b0"><code>88f13cb</code></a>
chore: Release</li>
<li><a
href="fe2d731605"><code>fe2d731</code></a>
docs: Update changelog</li>
<li><a
href="b256739045"><code>b256739</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6131">#6131</a>
from mernen/do-not-suggest-opts-after-escape</li>
<li><a
href="8aaf704f56"><code>8aaf704</code></a>
fix(complete): Do not suggest options after "--"</li>
<li><a
href="4a86fee1b5"><code>4a86fee</code></a>
test(complete): Illustrate current behavior</li>
<li><a
href="281f8aec7c"><code>281f8ae</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6126">#6126</a>
from epage/p</li>
<li><a
href="3cbce42cc2"><code>3cbce42</code></a>
docs(cookbook): Make typed-derive easier to maintain</li>
<li><a
href="9fd4dc9e4e"><code>9fd4dc9</code></a>
docs(cookbook): Provide a custom TypedValueParser</li>
<li><a
href="8f8e861345"><code>8f8e861</code></a>
docs(cookbook): Add local enum to typed-derive</li>
<li><a
href="926bafef0b"><code>926bafe</code></a>
docs(cookbook): Hint at overriding value_name</li>
<li>Additional commits viewable in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.5.56...clap_complete-v4.5.58">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eric Traut <etraut@openai.com>
Bumps [rust-toolchain](https://github.com/rust-lang/rust) from 1.93.0 to
1.93.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/rust/releases">rust-toolchain's
releases</a>.</em></p>
<blockquote>
<h2>Rust 1.93.1</h2>
<p><!-- raw HTML omitted --><!-- raw HTML omitted --></p>
<ul>
<li><a
href="https://redirect.github.com/rust-lang/rust/pull/150590">Don't try
to recover keyword as non-keyword identifier</a>, fixing an ICE that
especially <a
href="https://redirect.github.com/rust-lang/rustfmt/issues/6739">affected
rustfmt</a>.</li>
<li><a
href="https://redirect.github.com/rust-lang/rust-clippy/pull/16196">Fix
<code>clippy::panicking_unwrap</code> false-positive on field access
with implicit deref</a>.</li>
<li><a
href="https://redirect.github.com/rust-lang/rust/pull/152259">Revert
"Update wasm-related dependencies in CI"</a>, fixing file
descriptor leaks on the <code>wasm32-wasip2</code> target.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/rust/blob/main/RELEASES.md">rust-toolchain's
changelog</a>.</em></p>
<blockquote>
<h1>Version 1.93.1 (2026-02-12)</h1>
<p><!-- raw HTML omitted --><!-- raw HTML omitted --></p>
<ul>
<li><a
href="https://redirect.github.com/rust-lang/rust/pull/150590">Don't try
to recover keyword as non-keyword identifier</a>, fixing an ICE that
especially <a
href="https://redirect.github.com/rust-lang/rustfmt/issues/6739">affected
rustfmt</a>.</li>
<li><a
href="https://redirect.github.com/rust-lang/rust-clippy/pull/16196">Fix
<code>clippy::panicking_unwrap</code> false-positive on field access
with implicit deref</a>.</li>
<li><a
href="https://redirect.github.com/rust-lang/rust/pull/152259">Revert
"Update wasm-related dependencies in CI"</a>, fixing file
descriptor leaks on the <code>wasm32-wasip2</code> target.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="01f6ddf758"><code>01f6ddf</code></a>
Auto merge of <a
href="https://redirect.github.com/rust-lang/rust/issues/152450">#152450</a>
- cuviper:stable-next, r=cuviper</li>
<li><a
href="674ccdd847"><code>674ccdd</code></a>
Release 1.93.1</li>
<li><a
href="f0867bf650"><code>f0867bf</code></a>
Sync release note changes from main</li>
<li><a
href="b8cc170b70"><code>b8cc170</code></a>
Remove the 4 failing tests from rustdoc-gui</li>
<li><a
href="128b1c9f64"><code>128b1c9</code></a>
Remove rustdoc GUI flaky test</li>
<li><a
href="f8cf317da3"><code>f8cf317</code></a>
Revert "Update wasm-related dependencies in CI"</li>
<li><a
href="9c13ace16d"><code>9c13ace</code></a>
fix: <code>panicking_unwrap</code> FP on field access with implicit
deref</li>
<li><a
href="feb759bb79"><code>feb759b</code></a>
Don't try to recover keyword as non-keyword identifier</li>
<li><a
href="f691f9a0ec"><code>f691f9a</code></a>
Add regression tests for keyword-in-identifier-position recovery
ICE</li>
<li>See full diff in <a
href="https://github.com/rust-lang/rust/compare/1.93.0...1.93.1">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eric Traut <etraut@openai.com>
Bumps [arc-swap](https://github.com/vorner/arc-swap) from 1.8.0 to
1.8.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vorner/arc-swap/blob/master/CHANGELOG.md">arc-swap's
changelog</a>.</em></p>
<blockquote>
<h1>1.8.2</h1>
<ul>
<li>Proper gate of <code>Pin</code> (since 1.39 - we are not using only
<code>Pin</code>, but also
<code>Pin::into_inner</code>, <a
href="https://redirect.github.com/vorner/arc-swap/issues/197">#197</a>).</li>
</ul>
<h1>1.8.1</h1>
<ul>
<li>Some more careful orderings (<a
href="https://redirect.github.com/vorner/arc-swap/issues/195">#195</a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="19f0d661a2"><code>19f0d66</code></a>
Version 1.8.2</li>
<li><a
href="c222a22864"><code>c222a22</code></a>
Release 1.8.1</li>
<li><a
href="cccf3548a8"><code>cccf354</code></a>
Upgrade the other ordering too, for transitivity</li>
<li><a
href="e94df5511a"><code>e94df55</code></a>
Merge pull request <a
href="https://redirect.github.com/vorner/arc-swap/issues/195">#195</a>
from 0xfMel/master</li>
<li><a
href="bd5d3276e4"><code>bd5d327</code></a>
Fix Debt::pay failure ordering</li>
<li><a
href="22431daf64"><code>22431da</code></a>
Merge pull request <a
href="https://redirect.github.com/vorner/arc-swap/issues/189">#189</a>
from atouchet/rdm</li>
<li><a
href="b142bd81da"><code>b142bd8</code></a>
Update Readme</li>
<li>See full diff in <a
href="https://github.com/vorner/arc-swap/compare/v1.8.0...v1.8.2">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
rm `remote_models` feature flag.
We see issues like #11527 when a user has `remote_models` disabled, as
we always use the default fallback `ModelInfo`. This causes issues with
model performance.
Builds on #11690, which helps by warning the user when they are using
the default fallback. This PR will make that happen much less frequently
as an accidental consequence of disabling `remote_models`.
### Summary
Builiding off
5c75aa7b89 (diff-058ae8f109a8b84b4b79bbfa45f522c2233b9d9e139696044ae374d50b6196e0),
we have created a `model/rerouted` notification that captures the event
so that consumers can render as expected. Keep the `EventMsg::Warning`
path in core so that this does not affect TUI rendering.
`model/rerouted` is meant to be generic to account for future usage
including capacity planning etc.
The Mac and Linux implementations of the sandbox recently added write
protections for `.codex` and `.agents` subdirectories in all writable
roots. When adding documentation for this, I noticed that this change
was never made for the Windows sandbox.
Summary
- make compute_allow_paths treat .codex/.agents as protected alongside
.git, and cover their behavior in new tests
- wire protect_workspace_agents_dir through the sandbox lib and setup
path to apply deny ACEs when `.agents` exists
- factor shared ACL logic for workspace subdirectories
## Summary
This PR centralizes model-visible state diffing for turn context updates
into one module, while keeping existing behavior and call sites stable.
### What changed
- Added `core/src/context_updates.rs` with the consolidated diffing
logic for:
- environment context updates
- permissions/policy updates
- collaboration mode updates
- model-instruction switch updates
- personality updates
- Added `BuildSettingsUpdateItemsParams` so required dependencies are
passed explicitly.
- Updated `Session::build_settings_update_items` in `core/src/codex.rs`
to delegate to the centralized module.
- Reused the same centralized `personality_message_for` helper from
initial-context assembly to avoid duplicated logic.
- Registered the new module in `core/src/lib.rs`.
## Why
This is a minimal, shippable step toward the model-visible-state design:
all state diff decisions for turn-context update items now live in one
place, improving reviewability and reducing drift risk without expanding
scope.
## Behavior
- Intended to be behavior-preserving.
- No protocol/schema changes.
- No call-site behavior changes beyond routing through the new
centralized logic.
## Testing
Ran targeted tests in this worktree:
- `cargo test -p codex-core
build_settings_update_items_emits_environment_item_for_network_changes`
- `cargo test -p codex-core collaboration_instructions --test all`
Both passed.
## Codex author
`codex resume 019c540f-3951-7352-a3fa-6f07b834d4ce`
The `model_supports_reasoning_summaries` config option was originally
added so users could enable reasoning for custom models (models that
codex doesn't know about). This is how it was documented in the source,
but its implementation didn't match. It was implemented such that it can
also be used to disable reasoning for models that otherwise support
reasoning. This leads to bad behavior for some reasoning models like
`gpt-5.3-codex`. Diagnosing this is difficult, and it has led to many
support issues.
This PR changes the handling of `model_supports_reasoning_summaries` so
it matches its original documented behavior. If it is set to false, it
is a no-op. That is, it never disables reasoning for models that are
known to support reasoning. It can still be used for its intended
purpose -- to enable reasoning for unknown models.
Summary:
- add a `--compact` flag to the logs client to suppress thread/target
info
- format rows and timestamps differently when compact mode is enabled so
only hour time, level, and message remain
Summary
- extend the log client to accept an optional `--search` substring
filter when querying codex-state logs
- propagate the filter through `LogQuery` and apply it in
`push_log_filters` via `INSTR(message, ...)`
- add an integration test that exercises the new search filtering
behavior
Testing
- Not run (not requested)
Summary
- wait for the initial session startup loop to finish and handle exit
before waiting for the first message in fresh sessions
- propagate AppRunControl::Exit to return immediately when
initialization fails
## Summary
We're seeing failures for shell-tool-mcp.yml during git checkouts. This
is a quick attempt to unblock releases - we should revisit this build
pipeline since we've hit a number of errors.
js_repl_reset previously raced with in-flight/new js_repl executions
because reset() could clear exec_tool_calls without synchronizing with
execute(). In that window, a running exec could lose its per-exec
tool-call context, and subsequent kernel RunTool messages would fail
with js_repl exec context not found. The fix serializes reset and
execute on the same exec_lock, so reset cannot run concurrently with
exec setup/teardown. We also keep the timeout path safe by performing
reset steps inline while execute() already holds the lock, avoiding
re-entrant lock acquisition. A regression test now verifies that reset
waits for the exec lock and does not clear tool-call state early.
Summary
- filter command popup builtins to remove any `/debug*` entries so they
stay usable but are not listed
- added regression tests to ensure the popup hides debug commands while
dispatch still resolves them
Remove the waiting loop in `reset` so it no longer blocks on potentially
hanging exec tool calls + add `clear_all_exec_tool_calls_map` to drain
the map and notify waiters so `reset` completes immediately
## Summary
Fixes a few things in our exec_policy handling of prefix_rules:
1. Correctly match redirects specifically for exec_policy parsing. i.e.
if you have `prefix_rule(["echo"], decision="allow")` then `echo hello >
output.txt` should match - this should fix#10321
2. If there already exists any rule that would match our prefix rule
(not just a prompt), then drop it, since it won't do anything.
## Testing
- [x] Updated unit tests, added approvals ScenarioSpecs
Add per-turn notice when a request is downgraded to a fallback model due
to cyber safety checks.
**Changes**
- codex-api: Emit a ServerModel event based on the openai-model response
header and/or response payload (SSE + WebSocket), including when the
model changes mid-stream.
- core: When the server-reported model differs from the requested model,
emit a single per-turn warning explaining the reroute to gpt-5.2 and
directing users to Trusted
Access verification and the cyber safety explainer.
- app-server (v2): Surface these cyber model-routing warnings as
synthetic userMessage items with text prefixed by Warning: (and document
this behavior).
The `tui.animations` switch should gate all animations in the TUI, but a
recent change introduced a regression that didn't include the gate. This
makes it difficult to use the TUI with a screen reader.
This fix addresses #11856
## Summary
This feature is now reasonably stable, let's remove it so we can
simplify our upcoming iterations here.
## Testing
- [x] Existing tests pass
Summary
- rename the `collab` handlers and UI files to `multi_agents` to match
the new naming
- update module references and specs so the handlers and TUI widgets
consistently use the renamed files
- keep the existing functionality while aligning file and module names
with the multi-agent terminology
The idea is to have 2 family of agents.
1. Built-in that we packaged directly with Codex
2. User defined that are defined using the `agents_config.toml` file. It
can reference config files that will override the agent config. This
looks like this:
```
version = 1
[agents.explorer]
description = """Use `explorer` for all codebase questions.
Explorers are fast and authoritative.
Always prefer them over manual search or file reading.
Rules:
- Ask explorers first and precisely.
- Do not re-read or re-search code they cover.
- Trust explorer results without verification.
- Run explorers in parallel when useful.
- Reuse existing explorers for related questions."""
config_file = "explorer.toml"
```
Summary
- rename the collab feature key to multi_agent while keeping the Feature
enum unchanged
- add legacy alias support so both "multi_agent" and "collab" map to the
same feature
- cover the alias behavior with a new unit test
## Summary
- make `rust_test` targets generated from `tests/*.rs` use Cargo-style
crate names (file stem) so snapshot names match Cargo (`all__...`
instead of Bazel-derived names)
- split lib vs `tests/*.rs` test env wiring in `codex_rust_crate` to
keep existing lib snapshot behavior while applying Bazel
runfiles-compatible workspace root for `tests/*.rs`
- compute the `tests/*.rs` snapshot workspace root from package depth so
`insta` resolves committed snapshots under Bazel `--noenable_runfiles`
## Validation
- `bazelisk test //codex-rs/core:core-all-test
--test_arg=suite::compact:: --cache_test_results=no`
- `bazelisk test //codex-rs/core:core-all-test
--test_arg=suite::compact_remote:: --cache_test_results=no`
###### Context
unknown model warning added in #11690 has
[issues](https://github.com/openai/codex/actions/runs/22047424710/job/63700733887)
on ubuntu runners because we potentially emit it on all new turns,
including ones with intentionally fake models (i.e., `mock-model` in a
test).
###### Fix
change the warning to only emit on user turns/review turns.
###### Tests
CI now passes on ubuntu, still passes locally
### What changed
1. Removed per-turn MCP selection reset in `core/src/tasks/mod.rs`.
2. Added `SessionState::set_mcp_tool_selection(Vec<String>)` in
`core/src/state/session.rs` for authoritative restore behavior (deduped,
order-preserving, empty clears).
3. Added rollout parsing in `core/src/codex.rs` to recover
`active_selected_tools` from prior `search_tool_bm25` outputs:
- tracks matching `call_id`s
- parses function output text JSON
- extracts `active_selected_tools`
- latest valid payload wins
- malformed/non-matching payloads are ignored
4. Applied restore logic to resumed and forked startup paths in
`core/src/codex.rs`.
5. Updated instruction text to session/thread scope in
`core/templates/search_tool/tool_description.md`.
6. Expanded tests in `core/tests/suite/search_tool.rs`, plus unit
coverage in:
- `core/src/codex.rs`
- `core/src/state/session.rs`
### Behavior after change
1. Search activates matched tools.
2. Additional searches union into active selection.
3. Selection survives new turns in the same thread.
4. Resume/fork restores selection from rollout history.
5. Separate threads do not inherit selection unless forked.
### What
It's currently unclear when the harness falls back to the default,
generic `ModelInfo`. This happens when the `remote_models` feature is
disabled or the model is truly unknown, and can lead to bad performance
and issues in the harness.
Add a user-facing warning when this happens so they are aware when their
setup is broken.
### Tests
Added tests, tested locally.
This PR keeps compaction context-layout test coverage separate from
runtime compaction behavior changes, so runtime logic review can stay
focused.
## Included
- Adds reusable context snapshot helpers in
`core/tests/common/context_snapshot.rs` for rendering model-visible
request/history shapes.
- Standardizes helper naming for readability:
- `format_request_input_snapshot`
- `format_response_items_snapshot`
- `format_labeled_requests_snapshot`
- `format_labeled_items_snapshot`
- Expands snapshot coverage for both local and remote compaction flows:
- pre-turn auto-compaction
- pre-turn failure/context-window-exceeded paths
- mid-turn continuation compaction
- manual `/compact` with and without prior user turns
- Captures both sides where relevant:
- compaction request shape
- post-compaction history layout shape
- Adds/uses shared request-inspection helpers so assertions target
structured request content instead of ad-hoc JSON string parsing.
- Aligns snapshots/assertions to current behavior and leaves explicit
`TODO(ccunningham)` notes where behavior is known and intentionally
deferred.
## Not Included
- No runtime compaction logic changes.
- No model-visible context/state behavior changes.
## Summary
This PR is the first slice of the per-session `/feedback` logging work:
it adds a process-unique identifier to SQLite log rows.
It does **not** change `/feedback` sourcing behavior yet.
## Changes
- Add migration `0009_logs_process_id.sql` to extend `logs` with:
- `process_uuid TEXT`
- `idx_logs_process_uuid` index
- Extend state log models:
- `LogEntry.process_uuid: Option<String>`
- `LogRow.process_uuid: Option<String>`
- Stamp each log row with a stable per-process UUID in the sqlite log
layer:
- generated once per process as `pid:<pid>:<uuid>`
- Update sqlite log insert/query paths to persist and read
`process_uuid`:
- `INSERT INTO logs (..., process_uuid, ...)`
- `SELECT ..., process_uuid, ... FROM logs`
## Why
App-server runs many sessions in one process. This change provides a
process-scoping primitive we need for follow-up `/feedback` work, so
threadless/process-level logs can be associated with the emitting
process without mixing across processes.
## Non-goals in this PR
- No `/feedback` transport/source changes
- No attachment size changes
- No sqlite retention/trim policy changes
## Testing
- `just fmt`
- CI will run the full checks
## Summary
- add a distinct `linux_bubblewrap` sandbox tag when the Linux
bubblewrap pipeline feature is enabled
- thread the bubblewrap feature flag into sandbox tag generation for:
- turn metadata header emission
- tool telemetry metric tags and after-tool-use hooks
- add focused unit tests for `sandbox_tag` precedence and Linux
bubblewrap behavior
## Validation
- `just fmt`
- `cargo clippy -p codex-core --all-targets`
- `cargo test -p codex-core sandbox_tags::tests`
- started `cargo test -p codex-core` and stopped it per request
Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
### Description
#### Summary
Adds the TUI UX layer for structured network approvals
#### What changed
- Updated approval overlay to display network-specific approval context
(host/protocol).
- Added/updated TUI wiring so approval prompts show correct network
messaging.
- Added tests covering the new approval overlay behavior.
#### Why
Core orchestration can now request structured network approvals; this
ensures users see clear, contextual prompts in the TUI.
#### Notes
- UX behavior activates only when network approval context is present.
---------
Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
### Description
#### Summary
Introduces the core plumbing required for structured network approvals
#### What changed
- Added structured network policy decision modeling in core.
- Added approval payload/context types needed for network approval
semantics.
- Wired shell/unified-exec runtime plumbing to consume structured
decisions.
- Updated related core error/event surfaces for structured handling.
- Updated protocol plumbing used by core approval flow.
- Included small CLI debug sandbox compatibility updates needed by this
layer.
#### Why
establishes the minimal backend foundation for network approvals without
yet changing high-level orchestration or TUI behavior.
#### Notes
- Behavior remains constrained by existing requirements/config gating.
- Follow-up PRs in the stack handle orchestration, UX, and app-server
integration.
---------
Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
## Why this change
When Cargo dependencies change, it is easy to end up with an unexpected
local diff in
`MODULE.bazel.lock` after running Bazel. That creates noisy working
copies and pushes lockfile fixes
later in the cycle. This change addresses that pain point directly.
## What this change enforces
The expected invariant is: after dependency updates, `MODULE.bazel.lock`
is already in sync with
Cargo resolution. In practice, running `bazel mod deps` should not
mutate the lockfile in a clean
state. If it does, the dependency update is incomplete.
## How this is enforced
This change adds a single lockfile check script that snapshots
`MODULE.bazel.lock`, runs
`bazel mod deps`, and fails if the file changes. The same check is wired
into local workflow
commands (`just bazel-lock-update` and `just bazel-lock-check`) and into
Bazel CI (Linux x86_64 job)
so drift is caught early and consistently. The developer documentation
is updated in
`codex-rs/docs/bazel.md` and `AGENTS.md` to make the expected flow
explicit.
`MODULE.bazel.lock` is also refreshed in this PR to match the current
Cargo dependency resolution.
## Expected developer workflow
After changing `Cargo.toml` or `Cargo.lock`, run `just
bazel-lock-update`, then run
`just bazel-lock-check`, and include any resulting `MODULE.bazel.lock`
update in the same change.
## Testing
Ran `just bazel-lock-check` locally.
## Summary
This PR fixes a race in `js_repl` tool-call draining that could leave an
exec waiting indefinitely for in-flight tool calls to finish.
The fix is in:
-
`/Users/fjord/code/codex-jsrepl-seq/codex-rs/core/src/tools/js_repl/mod.rs`
## Problem
`js_repl` tracks in-flight tool calls per exec and waits for them to
drain on completion/timeout/cancel paths.
The previous wait logic used a check-then-wait pattern with `Notify`
that could miss a wakeup:
1. Observe `in_flight > 0`
2. Drop lock
3. Register wait (`notified().await`)
If `notify_waiters()` happened between (2) and (3), the waiter could
sleep until another notification that never comes.
## What changed
- Updated all exec-tool-call wait loops to create an owned notification
future while holding the lock:
- use `Arc<Notify>::notified_owned()` instead of cloning notify and
awaiting later.
- Applied this consistently to:
- `wait_for_exec_tool_calls`
- `wait_for_all_exec_tool_calls`
- `wait_for_exec_tool_calls_map`
This preserves existing behavior while eliminating the lost-wakeup
window.
## Test coverage
Added a regression test:
- `wait_for_exec_tool_calls_map_drains_inflight_calls_without_hanging`
The test repeatedly races waiter/finisher tasks and asserts bounded
completion to catch hangs.
## Impact
- No API changes.
- No user-facing behavior changes intended.
- Improves reliability of exec lifecycle boundaries when tool calls are
still in flight.
#### [git stack](https://github.com/magus/git-stack-cli)
- ✅ `1` https://github.com/openai/codex/pull/11796
- 👉 `2` https://github.com/openai/codex/pull/11800
- ⏳ `3` https://github.com/openai/codex/pull/10673
- ⏳ `4` https://github.com/openai/codex/pull/10670
## Summary
Fixes a flaky/panicking `js_repl` image-path test by running it on a
multi-thread Tokio runtime and tightening assertions to focus on real
behavior.
## Problem
`js_repl_can_attach_image_via_view_image_tool` in
`/Users/fjord/code/codex-jsrepl-seq/codex-rs/core/src/tools/js_repl/mod.rs`
can panic under single-thread test runtime with:
`can call blocking only when running on the multi-threaded runtime`
It also asserted a brittle user-facing text string.
## Changes
1. Updated the test runtime to:
`#[tokio::test(flavor = "multi_thread", worker_threads = 2)]`
2. Removed the brittle `"attached local image path"` string assertion.
3. Kept the concrete side-effect assertions:
- tool call succeeds
- image is actually injected into pending input (`InputImage` with
`data:image/png;base64,...`)
## Why this is safe
This is test-only behavior. No production runtime code paths are
changed.
## Validation
- Ran:
`cargo test -p codex-core
tools::js_repl::tests::js_repl_can_attach_image_via_view_image_tool --
--nocapture`
- Result: pass
#### [git stack](https://github.com/magus/git-stack-cli)
- 👉 `1` https://github.com/openai/codex/pull/11796
- ⏳ `2` https://github.com/openai/codex/pull/11800
- ⏳ `3` https://github.com/openai/codex/pull/10673
- ⏳ `4` https://github.com/openai/codex/pull/10670
Fixes Bazel build failure in //codex-rs/protocol:protocol-unit-tests.
The test used include_bytes! to read a PNG from codex-core assets; Cargo
can read it,
but Bazel sandboxing can't, so the crate fails to compile.
This change inlines a tiny valid PNG in the test to keep it hermetic.
Related regression: #10590 (cc: @charley-oai)
### What
to unblock filtering models in VSCE, change `model/list` app-server
endpoint to send all models + visibility field `showInPicker` so
filtering can be done in VSCE if desired.
### Tests
Updated tests.
## Summary
- always rejoin an in-memory running thread on `thread/resume`, even
when overrides are present
- reject `thread/resume` when `history` is provided for a running thread
- reject `thread/resume` when `path` mismatches the running thread
rollout path
- warn (but do not fail) on override mismatches for running threads
- add more `thread_resume` integration tests and fixes; including
restart-based resume-with-overrides coverage
## Validation
- `just fmt`
- `cargo test -p codex-app-server --test all thread_resume`
- manual test with app-server-test-client
https://github.com/openai/codex/pull/11755
- manual test both stdio and websocket in app
## Summary
This PR makes app-server-provided image URLs first-class attachments in
TUI, so they survive resume/backtrack/history recall and are resubmitted
correctly.
<img width="715" height="491" alt="Screenshot 2026-02-12 at 8 27 08 PM"
src="https://github.com/user-attachments/assets/226cbd35-8f0c-4e51-a13e-459ef5dd1927"
/>
Can delete the attached image upon backtracking:
<img width="716" height="301" alt="Screenshot 2026-02-12 at 8 27 31 PM"
src="https://github.com/user-attachments/assets/4558d230-f1bd-4eed-a093-8e1ab9c6db27"
/>
In both history and composer, remote images are rendered as normal
`[Image #N]` placeholders, with numbering unified with local images.
## What changed
- Plumb remote image URLs through TUI message state:
- `UserHistoryCell`
- `BacktrackSelection`
- `ChatComposerHistory::HistoryEntry`
- `ChatWidget::UserMessage`
- Show remote images as placeholder rows inside the composer box (above
textarea), and in history cells.
- Support keyboard selection/deletion for remote image rows in composer
(`Up`/`Down`, `Delete`/`Backspace`).
- Preserve remote-image-only turns in local composer history (Up/Down
recall), including restore after backtrack.
- Ensure submit/queue/backtrack resubmit include remote images in model
input (`UserInput::Image`), and keep request shape stable for
remote-image-only turns.
- Keep image numbering contiguous across remote + local images:
- remote images occupy `[Image #1]..[Image #M]`
- local images start at `[Image #M+1]`
- deletion renumbers consistently.
- In protocol conversion, increment shared image index for remote images
too, so mixed remote/local image tags stay in a single sequence.
- Simplify restore logic to trust in-memory attachment order (no
placeholder-number parsing path).
- Backtrack/replay rollback handling now queues trims through
`AppEvent::ApplyThreadRollback` and syncs transcript overlay/deferred
lines after trims, so overlay/transcript state stays consistent.
- Trim trailing blank rendered lines from user history rendering to
avoid oversized blank padding.
## Docs + tests
- Updated: `docs/tui-chat-composer.md` (remote image flow,
selection/deletion, numbering offsets)
- Added/updated tests across `tui/src/chatwidget/tests.rs`,
`tui/src/app.rs`, `tui/src/app_backtrack.rs`, `tui/src/history_cell.rs`,
and `tui/src/bottom_pane/chat_composer.rs`
- Added snapshot coverage for remote image composer states, including
deleting the first of two remote images.
## Validation
- `just fmt`
- `cargo test -p codex-tui`
## Codex author
`codex fork 019c2636-1571-74a1-8471-15a3b1c3f49d`
## Summary
- When building via `nix build`, the binary reports `codex-cli 0.0.0`
because the workspace `Cargo.toml` uses `0.0.0` as a placeholder on
`main`. This causes the update checker to always prompt users to upgrade
even when running the latest code.
- Reads the version from `codex-rs/Cargo.toml` at flake evaluation time
using `builtins.fromTOML` and patches it into the workspace `Cargo.toml`
before cargo builds via `postPatch`.
- On release commits (e.g. tag `rust-v0.101.0`), the real version is
used as-is. On `main` branch builds, falls back to
`0.0.0-dev+<shortRev>` (or `0.0.0-dev+dirty`), which the update
checker's `parse_version` ignores — suppressing the spurious upgrade
prompt.
| Scenario | Cargo.toml version | Nix `version` | Binary reports |
Upgrade nag? |
|---|---|---|---|---|
| Release commit (e.g. `rust-v0.101.0`) | `0.101.0` | `0.101.0` |
`codex-cli 0.101.0` | Only if newer exists |
| Main branch (committed) | `0.0.0` | `0.0.0-dev+b934ffc` | `codex-cli
0.0.0-dev+b934ffc` | No |
| Main branch (uncommitted) | `0.0.0` | `0.0.0-dev+dirty` | `codex-cli
0.0.0-dev+dirty` | No |
## Test plan
- [ ] `nix build` from `main` branch and verify `codex --version`
reports `0.0.0-dev+<shortRev>` instead of `0.0.0`
- [ ] Verify the update checker does not show a spurious upgrade prompt
for dev builds
- [ ] Confirm that on a release commit where `Cargo.toml` has a real
version, the binary reports that version correctly
…d two-pass Codex search strategy with deterministic fallback behavior,
and remove an obsolete prompt file that was no longer used.
### Changes
- Updated `workflows/issue-deduplicator.yml`:
- Added richer issue input fields (`state`, `updatedAt`, `labels`) for
model context.
- Added two candidate pools:
- `codex-existing-issues-all.json` (`--state all`)
- `codex-existing-issues-open.json` (`--state open`)
- Added body truncation during JSON preparation to reduce prompt noise.
- Added **Pass 1** Codex run over all issues.
- Added normalization/validation step for Pass 1 output:
- tolerant JSON parsing
- self-issue filtering
- deduplication
- cap to 5 results
- Added **Pass 2 fallback** Codex run over open issues only, triggered
only when Pass 1 has no usable matches.
- Added normalization/validation step for Pass 2 output (same
filtering/dedup/cap behavior).
- Added final deterministic selector:
- prefer pass 2 if it finds matches
- otherwise use pass 1
- otherwise return no matches
- Added observability logs:
- pool sizes
- per-pass parse/match status
- final pass selected and final duplicate count
- Kept public issue-comment format unchanged.
- Added comment documenting that prompt text now lives inline in
workflow.
- Deleted obsolete file:
- `/prompts/issue-deduplicator.txt`
### Behavior Impact
- Better duplicate recall when broad search fails by retrying against
active issues only.
- More deterministic/noise-resistant output handling.
- No change to workflow trigger conditions, permissions, or issue
comment structure.
## Summary
- Created branch zuxin/read-path-update from main.
- Copied codex-rs/core/templates/memories/read_path.md from the current
branch.
- Committed the content change.
## Testing
Not run (content copy + commit only).
Currently, if there are syntax errors detected in the starlark rules
file, the entire policy is silently ignored by the CLI. The app server
correctly emits a message that can be displayed in a GUI.
This PR changes the CLI (both the TUI and non-interactive exec) to fail
when the rules file can't be parsed. It then prints out an error message
and exits with a non-zero exit code. This is consistent with the
handling of errors in the config file.
This addresses #11603
## Summary
- add a shared `codex-core` sleep inhibitor that uses native macOS IOKit
assertions (`IOPMAssertionCreateWithName` / `IOPMAssertionRelease`)
instead of spawning `caffeinate`
- wire sleep inhibition to turn lifecycle in `tui` (`TurnStarted`
enables; `TurnComplete` and abort/error finalization disable)
- gate this behavior behind a `/experimental` feature toggle
(`[features].prevent_idle_sleep`) instead of a dedicated `[tui]` config
flag
- expose the toggle in `/experimental` on macOS; keep it under
development on other platforms
- keep behavior no-op on non-macOS targets
<img width="1326" height="577" alt="image"
src="https://github.com/user-attachments/assets/73fac06b-97ae-46a2-800a-30f9516cf8a3"
/>
## Testing
- `cargo check -p codex-core -p codex-tui`
- `cargo test -p codex-core sleep_inhibitor::tests -- --nocapture`
- `cargo test -p codex-core
tui_config_missing_notifications_field_defaults_to_enabled --
--nocapture`
- `cargo test -p codex-core prevent_idle_sleep_is_ -- --nocapture`
## Semantics and API references
- This PR targets `caffeinate -i` semantics: prevent *idle system sleep*
while allowing display idle sleep.
- `caffeinate -i` mapping in Apple open source (`assertionMap`):
- `kIdleAssertionFlag -> kIOPMAssertionTypePreventUserIdleSystemSleep`
- Source:
https://github.com/apple-oss-distributions/PowerManagement/blob/PowerManagement-1846.60.12/caffeinate/caffeinate.c#L52-L54
- Apple IOKit docs for assertion types and API:
-
https://developer.apple.com/documentation/iokit/iopmlib_h/iopmassertiontypes
-
https://developer.apple.com/documentation/iokit/1557092-iopmassertioncreatewithname
- https://developer.apple.com/library/archive/qa/qa1340/_index.html
## Codex Electron vs this PR (full stack path)
- Codex Electron app requests sleep blocking with
`powerSaveBlocker.start("prevent-app-suspension")`:
-
https://github.com/openai/codex/blob/main/codex/codex-vscode/electron/src/electron-message-handler.ts
- Electron maps that string to Chromium wake lock type
`kPreventAppSuspension`:
-
https://github.com/electron/electron/blob/main/shell/browser/api/electron_api_power_save_blocker.cc
- Chromium macOS backend maps wake lock types to IOKit assertion
constants and calls IOKit:
- `kPreventAppSuspension -> kIOPMAssertionTypeNoIdleSleep`
- `kPreventDisplaySleep / kPreventDisplaySleepAllowDimming ->
kIOPMAssertionTypeNoDisplaySleep`
-
https://github.com/chromium/chromium/blob/main/services/device/wake_lock/power_save_blocker/power_save_blocker_mac.cc
## Why this PR uses a different macOS constant name
- This PR uses `"PreventUserIdleSystemSleep"` directly, via
`IOPMAssertionCreateWithName`, in
`codex-rs/core/src/sleep_inhibitor.rs`.
- Apple’s IOKit header documents `kIOPMAssertionTypeNoIdleSleep` as
deprecated and recommends `kIOPMAssertPreventUserIdleSystemSleep` /
`kIOPMAssertionTypePreventUserIdleSystemSleep`:
-
https://github.com/apple-oss-distributions/IOKitUser/blob/IOKitUser-100222.60.2/pwr_mgt.subproj/IOPMLib.h#L1000-L1030
- So Chromium and this PR are using different constant names, but
semantically equivalent idle-system-sleep prevention behavior.
## Future platform support
The architecture is intentionally set up for multi-platform extensions:
- UI code (`tui`) only calls `SleepInhibitor::set_turn_running(...)` on
turn lifecycle boundaries.
- Platform-specific behavior is isolated in
`codex-rs/core/src/sleep_inhibitor.rs` behind `cfg(...)` blocks.
- Feature exposure is centralized in `core/src/features.rs` and surfaced
via `/experimental`.
- Adding new OS backends should not require additional TUI wiring; only
the backend internals and feature stage metadata need to change.
Potential follow-up implementations:
- Windows:
- Add a backend using Win32 power APIs
(`SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED)` as
baseline).
- Optionally move to `PowerCreateRequest` / `PowerSetRequest` /
`PowerClearRequest` for richer assertion semantics.
- Linux:
- Add a backend using logind inhibitors over D-Bus
(`org.freedesktop.login1.Manager.Inhibit` with `what="sleep"`).
- Keep a no-op fallback where logind/D-Bus is unavailable.
This PR keeps the cross-platform API surface minimal so future PRs can
add Windows/Linux support incrementally with low churn.
---------
Co-authored-by: jif-oai <jif@openai.com>
[codex-generated]
## Updated PR Description (Ready To Paste)
## Problem
When a sub-agent thread emits `ShutdownComplete`, the TUI switches back
to the primary thread.
That was also happening for user-requested exits (for example `Ctrl+C`),
which could prevent a
clean app exit and unexpectedly resurrect the main thread.
## Mental model
The app has one primary thread and one active thread. A non-primary
active thread shutting down
usually means "agent died, fail back to primary," but during
`ExitMode::ShutdownFirst` shutdown
means "the user is exiting," not "recover this session."
## Non-goals
No change to thread lifecycle, thread-manager ownership, or shutdown
protocol wire format.
No behavioral changes to non-shutdown events.
## Tradeoffs
This adds a small local marker (`pending_shutdown_exit_thread_id`)
instead of inferring intent
from event timing. It is deterministic and simple, but relies on
correctly setting and clearing
that marker around exit.
## Architecture
`App` tracks which thread is intentionally being shut down for exit.
`active_non_primary_shutdown_target` centralizes failover eligibility
for `ShutdownComplete` and
skips failover when shutdown matches the pending-exit thread.
`handle_active_thread_event` handles non-primary failover before generic
forwarding and clears the
pending-exit marker only when the matching active thread completes
shutdown.
## Observability
User-facing info/error messages continue to indicate whether failover to
the main thread succeeded.
The shutdown-intent path is now explicitly documented inline for easier
debugging.
## Tests
Added targeted tests for `active_non_primary_shutdown_target` covering
non-shutdown events,
primary-thread shutdown, non-primary shutdown failover, pending exit on
active thread (no failover),
and pending exit for another thread (still failover).
Validated with:
- `cargo test -p codex-tui` (pass)
---------
Co-authored-by: Josh McKinney <joshka@openai.com>
## Why
`compact_resume_after_second_compaction_preserves_history` has been
intermittently flaky in Windows CI.
The test had two one-shot request matchers in the second compact/resume
phase that could overlap, and it waited for the first `Warning` event
after compaction. In practice, that made the test sensitive to
platform/config-specific prompt shape and unrelated warning timing.
## What Changed
- Hardened the second compaction matcher in
`codex-rs/core/tests/suite/compact_resume_fork.rs` so it accepts
expected compact-request variants while explicitly excluding the
`AFTER_SECOND_RESUME` payload.
- Updated `compact_conversation()` to wait for the specific compaction
warning (`COMPACT_WARNING_MESSAGE`) rather than any `Warning` event.
- Added an inline comment explaining why the matcher is intentionally
broad but disjoint from the follow-up resume matcher.
## Test Plan
- `cargo test -p codex-core --test all
suite::compact_resume_fork::compact_resume_after_second_compaction_preserves_history
-- --exact`
- Repeated the same test in a loop (40 runs) to check for local
nondeterminism.
## Summary
- Limit `search_tool_bm25` indexing to `codex_apps` tools only, so
non-Apps MCP servers are no longer discoverable through this search
path.
- Move search-tool discovery guidance into the `search_tool_bm25` tool
description (via template include) instead of injecting it as a separate
developer message.
- Update Apps discovery guidance wording to clarify when to use
`search_tool_bm25` for Apps-backed systems (for example Slack, Google
Drive, Jira, Notion) and when to call tools directly.
- Remove dead `core` helper code (`filter_codex_apps_mcp_tools` and
`codex_apps_connector_id`) that is no longer used after the
tool-selection refactor.
- Update `core` search-tool tests to assert codex-apps-only behavior and
to validate guidance from the tool description.
## Validation
- ✅ `just fmt`
- ✅ `cargo test -p codex-core search_tool`
- ⚠️ `cargo test -p codex-core` was attempted, but the run repeatedly
stalled on
`tools::js_repl::tests::js_repl_can_attach_image_via_view_image_tool`.
## Tickets
- None
Summary
- make the phase1 memories schema require `rollout_slug` while still
allowing it to be `null`
- update the corresponding test to check the required fields and
nullable type list
Testing
- Not run (not requested)
Switched arg0 runtime initialization from tokio::runtime::Runtime::new()
to an explicit multi-thread builder that sets the thread stack size to
16MiB.
This is only for Windows for now but we might need to do this for others
in the future. This is required because Codex becomes quite large and
Windows tends to consume stack a little bit faster (this is a known
thing even though everyone seems to have different theory on it)
## Summary
Based on our most recent [release
attempt](https://github.com/openai/codex/actions/runs/21980518940/job/63501739210)
we are not building the shell-tool-mcp job correctly. This one is
outside my expertise, but seems mostly reasonable.
## Testing
- [x] We really need dry runs of these
## Summary
When network requests were blocked, downstream code often had to infer
ask vs deny from free-form response text. That was brittle and led to
incorrect approval behavior.
This PR fixes the proxy side so blocked decisions are structured and
request metadata survives reliably.
## Description
- Blocked proxy responses now carry consistent structured policy
decision data.
- Request attempt metadata is preserved across proxy env paths
(including ALL_PROXY flows).
- Header stripping was tightened so we still remove unsafe forwarding
headers, but keep metadata needed for policy handling.
- Block messages were clarified (for example, allowlist miss vs explicit
deny).
- Added unified violation log entries so policy failures can be
inspected in one place.
- Added/updated tests for these behaviors.
---------
Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
## Summary
CI is broken on main because our CI toolchain is trying to run 1.93.1
while our rust toolchain is locked at 1.93.0. I'm sure it's likely safe
to upgrade, but let's keep things stable for now.
## Testing
- [x] CI should hopefully pass
## Summary
If the model suggests a bad rule, don't show it to the user. This does
not impact the parsing of existing rules, just the ones we show.
## Testing
- [x] Added unit tests
- [x] Ran locally
When `app/list` is called with `force_refetch=True`, we should seed the
results with what is already cached instead of starting from an empty
list. Otherwise when we send app/list/updated events, the client will
first see an empty list of accessible apps and then get the updated one.
We've had a few cases recently where someone enabled a feature flag for
a feature that's still under development or experimental. This test
should prevent this.
### Motivation
- Git subcommand matching was being classified as "dangerous" and caused
benign developer workflows (for example `git push --force-with-lease`)
to be blocked by the preflight policy.
- The change aligns behavior with the intent to reserve the dangerous
checklist for truly destructive shell ops (e.g. `rm -rf`) and avoid
surprising developer-facing blocks.
### Description
- Remove git-specific subcommand checks from
`is_dangerous_to_call_with_exec` in
`codex-rs/shell-command/src/command_safety/is_dangerous_command.rs`,
leaving only explicit `rm` and `sudo` passthrough checks.
- Deleted the git-specific helper logic that classified `reset`,
`branch`-delete, `push` (force/delete/refspec) and `clean --force` as
dangerous.
- Updated unit tests in the same file to assert that various `git
reset`/`git branch`/`git push`/`git clean` variants are no longer
classified as dangerous.
- Kept `find_git_subcommand` (used by safe-command classification)
intact so safe/unsafe parsing elsewhere remains functional.
### Testing
- Ran formatter with `just fmt` successfully.
- Ran unit tests with `cargo test -p codex-shell-command` and all tests
passed (`144 passed; 0 failed`).
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_698d19dedb4883299c3ceb5bbc6a0dcf)
## Summary
This PR delivers the first small, shippable step toward model-visible
state diffing by making
`TurnContextItem` more complete and standardizing how it is built.
Specifically, it:
- Adds persisted network context to `TurnContextItem`.
- Introduces a single canonical `TurnContext -> TurnContextItem`
conversion path.
- Routes existing rollout write sites through that canonical conversion
helper.
No context injection/diff behavior changes are included in this PR.
## Why this change
The design goal is to make `TurnContextItem` the canonical source of
truth for context-diff
decisions.
Before this PR:
- `TurnContextItem` did not include all TurnContext-derived environment
inputs needed for v1
completeness.
- Construction was duplicated at multiple write sites.
This PR addresses both with a minimal, reviewable change.
## Changes
### 1) Extend `TurnContextItem` with network state
- Added `TurnContextNetworkItem { allowed_domains, denied_domains }`.
- Added `network: Option<TurnContextNetworkItem>` to `TurnContextItem`.
- Kept backward compatibility by making the new field optional and
skipped when absent.
Files:
- `codex-rs/protocol/src/protocol.rs`
### 2) Canonical conversion helper
- Added `TurnContext::to_turn_context_item(collaboration_mode)` in core.
- Added internal helper to derive network fields from
`config_layer_stack.requirements().network`.
Files:
- `codex-rs/core/src/codex.rs`
### 3) Use canonical conversion at rollout write sites
- Replaced ad hoc `TurnContextItem { ... }` construction with
`to_turn_context_item(...)` in:
- sampling request path
- compaction path
Files:
- `codex-rs/core/src/codex.rs`
- `codex-rs/core/src/compact.rs`
### 4) Update fixtures/tests for new optional field
- Updated existing `TurnContextItem` literals in tests to include
`network: None`.
- Added protocol tests for:
- deserializing old payloads with no `network`
- serializing when `network` is present
Files:
- `codex-rs/core/tests/suite/resume_warning.rs`
- No replay/diff logic changes.
- Persisted rollout `TurnContextItem` now carries additional network
context when available.
- Older rollout lines without `network` remain readable.
Adds a new apps_mcp_gateway flag to route Apps MCP calls through
https://api.openai.com/v1/connectors/mcp/ when enabled, while keeping
legacy MCP routing as default.
Propagate client JSON-RPC errors for app-server request callbacks.
Previously a number of possible errors were collapsed to `channel
closed`. Now we should be able to see the underlying client error.
### Summary
This change stops masking client JSON-RPC error responses as generic
callback cancellation in app-server server->client request flows.
Previously, when the client responded with a JSON-RPC error, we removed
the callback entry but did not send anything to the waiting oneshot
receiver. Waiters then observed channel closure (for example, auth
refresh request canceled: channel closed), which hid the actual client
error.
Now, client JSON-RPC errors are forwarded through the callback channel
and handled explicitly by request consumers.
### User-visible behavior
- External auth refresh now surfaces real client JSON-RPC errors when
provided.
- True transport/callback-drop cases still report
canceled/channel-closed semantics.
### Example: client JSON-RPC error is now propagated (not masked as
"canceled")
When app-server asks the client to refresh ChatGPT auth tokens, it sends
a server->client JSON-RPC request like:
```json
{
"id": 42,
"method": "account/chatgptAuthTokens/refresh",
"params": {
"reason": "unauthorized",
"previousAccountId": "org-abc"
}
}
```
If the client cannot refresh and responds with a JSON-RPC error:
```
{
"id": 42,
"error": {
"code": -32000,
"message": "refresh failed",
"data": null
}
}
```
app-server now forwards that error through the callback path and
surfaces:
`auth refresh request failed: code=-32000 message=refresh failed`
Previously, this same case could be reported as:
`auth refresh request canceled: channel closed`
## Why
`review_start_with_detached_delivery_returns_new_thread_id` has been
failing on Windows CI. The failure mode is a process crash
(`tokio-runtime-worker` stack overflow) during detached review setup,
which causes EOF in the test harness.
This test is intended to validate detached review thread identity, not
shell snapshot behavior. We also still want detached review to avoid
unnecessary rollout-path rediscovery when the parent thread is already
loaded.
## What Changed
- Updated detached review startup in
`codex-rs/app-server/src/codex_message_processor.rs`:
- `start_detached_review` now receives the loaded parent thread.
- It prefers `parent_thread.rollout_path()`.
- It falls back to `find_thread_path_by_id_str(...)` only if the
in-memory path is unavailable.
- Hardened the review test fixture in
`codex-rs/app-server/tests/suite/v2/review.rs` by setting
`shell_snapshot = false` in test config, so this test no longer depends
on unrelated Windows PowerShell snapshot initialization.
## Verification
- `cargo test -p codex-app-server`
- Verified
`suite::v2::review::review_start_with_detached_delivery_returns_new_thread_id`
passes locally.
## Notes
- Related context: rollout-path lookup behavior changed in #10532.
## Why
`suite::v2::review::review_start_with_detached_delivery_returns_new_thread_id`
was failing on Windows CI due to an unrelated process crash during shell
snapshot initialization (`tokio-runtime-worker` stack overflow).
This review test suite validates review API behavior and should not
depend on shell snapshot behavior. Keeping shell snapshot enabled in
this fixture made the test flaky for reasons outside the scenario under
test.
## What Changed
- Updated the review suite test config in
`codex-rs/app-server/tests/suite/v2/review.rs` to set:
- `shell_snapshot = false`
This keeps the review tests focused on review behavior by disabling
shell snapshot initialization in this fixture.
## Verification
- `cargo test -p codex-app-server`
- Confirmed the previously failing Windows CI job for this test now
passes on this PR.
Adds an explicit requirement in AGENTS.md that any user-visible UI
change includes corresponding insta snapshot coverage and that snapshots
are reviewed/accepted in the PR.
Tests: N/A (docs only)
## Why
We currently carry multiple permission-related concepts directly on
`Config` for shell/unified-exec behavior (`approval_policy`,
`sandbox_policy`, `network`, `shell_environment_policy`,
`windows_sandbox_mode`).
Consolidating these into one in-memory struct makes permission handling
easier to reason about and sets up the next step: supporting named
permission profiles (`[permissions.PROFILE_NAME]`) without changing
behavior now.
This change is mostly mechanical: it updates existing callsites to go
through `config.permissions`, but it does not yet refactor those
callsites to take a single `Permissions` value in places where multiple
permission fields are still threaded separately.
This PR intentionally **does not** change the on-disk `config.toml`
format yet and keeps compatibility with legacy config keys.
## What Changed
- Introduced `Permissions` in `core/src/config/mod.rs`.
- Added `Config::permissions` and moved effective runtime permission
fields under it:
- `approval_policy`
- `sandbox_policy`
- `network`
- `shell_environment_policy`
- `windows_sandbox_mode`
- Updated config loading/building so these effective values are still
derived from the same existing config inputs and constraints.
- Updated Windows sandbox helpers/resolution to read/write via
`permissions`.
- Threaded the new field through all permission consumers across core
runtime, app-server, CLI/exec, TUI, and sandbox summary code.
- Updated affected tests to reference `config.permissions.*`.
- Renamed the struct/field from
`EffectivePermissions`/`effective_permissions` to
`Permissions`/`permissions` and aligned variable naming accordingly.
## Verification
- `just fix -p codex-core -p codex-tui -p codex-cli -p codex-app-server
-p codex-exec -p codex-utils-sandbox-summary`
- `cargo build -p codex-core -p codex-tui -p codex-cli -p
codex-app-server -p codex-exec -p codex-utils-sandbox-summary`
## Summary
In an effort to start simplifying our sandbox setup, we're announcing
this approval_policy as deprecated. In general, it performs worse than
`on-request`, and we're focusing on making fewer sandbox configurations
perform much better.
## Testing
- [x] Tested locally
- [x] Existing tests pass
There is an edge case where a directory is not readable by the sandbox.
In practice, we've seen very little of it, but it can happen so this
slash command unlocks users when it does.
Future idea is to make this a tool that the agent knows about so it can
be more integrated.
## Summary
This PR adds host-integrated helper APIs for `js_repl` and updates model
guidance so the agent can use them reliably.
### What’s included
- Add `codex.tool(name, args?)` in the JS kernel so `js_repl` can call
normal Codex tools.
- Keep persistent JS state and scratch-path helpers available:
- `codex.state`
- `codex.tmpDir`
- Wire `js_repl` tool calls through the standard tool router path.
- Add/align `js_repl` execution completion/end event behavior with
existing tool logging patterns.
- Update dynamic prompt injection (`project_doc`) to document:
- how to call `codex.tool(...)`
- raw output behavior
- image flow via `view_image` (`codex.tmpDir` +
`codex.tool("view_image", ...)`)
- stdio safety guidance (`console.log` / `codex.tool`, avoid direct
`process.std*`)
## Why
- Standardize JS-side tool usage on `codex.tool(...)`
- Make `js_repl` behavior more consistent with existing tool execution
and event/logging patterns.
- Give the model enough runtime guidance to use `js_repl` safely and
effectively.
## Testing
- Added/updated unit and runtime tests for:
- `codex.tool` calls from `js_repl` (including shell/MCP paths)
- image handoff flow via `view_image`
- prompt-injection text for `js_repl` guidance
- execution/end event behavior and related regression coverage
#### [git stack](https://github.com/magus/git-stack-cli)
- ✅ `1` https://github.com/openai/codex/pull/10674
- 👉 `2` https://github.com/openai/codex/pull/10672
- ⏳ `3` https://github.com/openai/codex/pull/10671
- ⏳ `4` https://github.com/openai/codex/pull/10673
- ⏳ `5` https://github.com/openai/codex/pull/10670
This PR adds an experimental `persist_extended_history` bool flag to
app-server thread APIs so rollout logs can retain a richer set of
EventMsgs for non-lossy Thread > Turn > ThreadItems reconstruction (i.e.
on `thread/resume`).
### Motivation
Today, our rollout recorder only persists a small subset (e.g. user
message, reasoning, assistant message) of `EventMsg` types, dropping a
good number (like command exec, file change, etc.) that are important
for reconstructing full item history for `thread/resume`, `thread/read`,
and `thread/fork`.
Some clients want to be able to resume a thread without lossiness. This
lossiness is primarily a UI thing, since what the model sees are
`ResponseItem` and not `EventMsg`.
### Approach
This change introduces an opt-in `persist_full_history` flag to preserve
those events when you start/resume/fork a thread (defaults to `false`).
This is done by adding an `EventPersistenceMode` to the rollout
recorder:
- `Limited` (existing behavior, default)
- `Extended` (new opt-in behavior)
In `Extended` mode, persist additional `EventMsg` variants needed for
non-lossy app-server `ThreadItem` reconstruction. We now store the
following ThreadItems that we didn't before:
- web search
- command execution
- patch/file changes
- MCP tool calls
- image view calls
- collab tool outcomes
- context compaction
- review mode enter/exit
For **command executions** in particular, we truncate the output using
the existing `truncate_text` from core to store an upper bound of 10,000
bytes, which is also the default value for truncating tool outputs shown
to the model. This keeps the size of the rollout file and command
execution items returned over the wire reasonable.
And we also persist `EventMsg::Error` which we can now map back to the
Turn's status and populates the Turn's error metadata.
#### Updates to EventMsgs
To truly make `thread/resume` non-lossy, we also needed to persist the
`status` on `EventMsg::CommandExecutionEndEvent` and
`EventMsg::PatchApplyEndEvent`. Previously it was not obvious whether a
command failed or was declined (similar for apply_patch). These
EventMsgs were never persisted before so I made it a required field.
This PR introduces a skill-expansion mechanism for mentions so nested or
skill or connection mentions are expanded if present in skills invoked
by the user. This keeps behavior aligned with existing mention handling
while extending coverage to deeper scenarios. With these changes, users
can create skills that invoke connectors, and skills that invoke other
skills.
Replaces #10863, which is not needed with the addition of
[search_tool_bm25](https://github.com/openai/codex/issues/10657)
## Summary
Preserve the specified model slug when we get a prefix-based match
## Testing
- [x] added unit test
---------
Co-authored-by: Ahmed Ibrahim <aibrahim@openai.com>
For macOS and Linux: copy the output of `uname -mprs`
For Windows: copy the output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in the PowerShell console
You are an assistant that reviews GitHub issues for the repository.
Your job is to choose the most appropriate existing labels for the issue described later in this prompt.
Follow these rules:
- Only pick labels out of the list below.
- Prefer a small set of precise labels over many broad ones.
- If none of the labels fit, respond with an empty JSON array: []
- Output must be a JSON array of label names (strings) with no additional commentary.
Labels to apply:
1. bug — Reproducible defects in Codex products (CLI, VS Code extension, web, auth).
2. enhancement — Feature requests or usability improvements that ask for new capabilities, better ergonomics, or quality-of-life tweaks.
3. extension — VS Code (or other IDE) extension-specific issues.
4. windows-os — Bugs or friction specific to Windows environments (PowerShell behavior, path handling, copy/paste, OS-specific auth or tooling failures).
5. mcp — Topics involving Model Context Protocol servers/clients.
6. codex-web — Issues targeting the Codex web UI/Cloud experience.
8. azure — Problems or requests tied to Azure OpenAI deployments.
9. documentation — Updates or corrections needed in docs/README/config references (broken links, missing examples, outdated keys, clarification requests).
10. model-behavior — Undesirable LLM behavior: forgetting goals, refusing work, hallucinating environment details, quota misreports, or other reasoning/performance anomalies.
Issue information is available in environment variables:
You will receive the following JSON files located in the current working directory:
- `codex-current-issue.json`: JSON object describing the newly created issue (fields: number, title, body).
- `codex-existing-issues.json`: JSON array of recent issues (each element includes number, title, body, createdAt).
- `codex-existing-issues-all.json`: JSON array of recent issues with states, timestamps, and labels.
Instructions:
- Compare the current issue against the existing issues to find up to five that appear to describe the same underlying problem or request.
- Focus on the underlying intent and context of each issue—such as reported symptoms, feature requests, reproduction steps, or error messages—rather than relying solely on string similarity or synthetic metrics.
- After your analysis, validate your results in 1-2 lines explaining your decision to return the selected matches.
- When unsure, prefer returning fewer matches.
- Include at most five numbers.
- Prioritize concrete overlap in symptoms, reproduction details, error signatures, and user intent.
- Prefer active unresolved issues when confidence is similar.
- Closed issues can still be valid duplicates if they clearly match.
- Return fewer matches rather than speculative ones.
- If confidence is low, return an empty list.
- Include at most five issue numbers.
- After analysis, provide a short reason for your decision.
@@ -15,12 +15,16 @@ In the codex-rs folder where the rust code lives:
- When writing tests, prefer comparing the equality of entire objects over fields one by one.
- When making a change that adds or changes an API, ensure that the documentation in the `docs/` folder is up to date if applicable.
- If you change `ConfigToml` or nested config types, run `just write-config-schema` to update `codex-rs/core/config.schema.json`.
- If you change Rust dependencies (`Cargo.toml` or `Cargo.lock`), run `just bazel-lock-update` from the
repo root to refresh `MODULE.bazel.lock`, and include that lockfile update in the same change.
- After dependency changes, run `just bazel-lock-check` from the repo root so lockfile drift is caught
locally before CI.
- Do not create small helper methods that are referenced only once.
Run `just fmt` (in `codex-rs` directory) automatically after you have finished making Rust code changes; do not ask for approval to run it. Additionally, run the tests:
1. Run the test for the specific project that was changed. For example, if changes were made in `codex-rs/tui`, run `cargo test -p codex-tui`.
2. Once those pass, if any changes were made in common, core, or protocol, run the complete test suite with `cargo test --all-features`. project-specific or individual tests can be run without asking the user, but do ask the user before running the complete test suite.
2. Once those pass, if any changes were made in common, core, or protocol, run the complete test suite with `cargo test` (or `just test` if `cargo-nextest` is installed). Avoid `--all-features` for routine local runs because it expands the build matrix and can significantly increase `target/` disk usage; use it only when you specifically need full feature coverage. project-specific or individual tests can be run without asking the user, but do ask the user before running the complete test suite.
Before finalizing a large change to `codex-rs`, run `just fix -p <project>` (in `codex-rs` directory) to fix any linter issues in the code. Prefer scoping with `-p` to avoid slow workspace‑wide Clippy builds; only run `just fix` without `-p` if you changed shared crates. Do not re-run tests after running `fix` or `fmt`.
@@ -60,7 +64,14 @@ See `codex-rs/tui/styles.md`.
### Snapshot tests
This repo uses snapshot tests (via `insta`), especially in `codex-rs/tui`, to validate rendered output. When UI or text output changes intentionally, update the snapshots as follows:
This repo uses snapshot tests (via `insta`), especially in `codex-rs/tui`, to validate rendered output.
**Requirement:** any change that affects user-visible UI (including adding new UI) must include
corresponding `insta` snapshot coverage (add a new snapshot test if one doesn't exist yet, or
update the existing snapshot). Review and accept snapshot updates as part of the PR so UI impact
is easy to review and future diffs stay visual.
When UI or text output changes intentionally, update the snapshots as follows:
- Run tests to generate any updated snapshots:
-`cargo test -p codex-tui`
@@ -158,3 +169,5 @@ These guidelines apply to app-server protocol work in `codex-rs`, especially:
`just write-app-server-schema`
(and `just write-app-server-schema --experimental` when experimental API fixtures are affected).
- Validate with `cargo test -p codex-app-server-protocol`.
- Avoid boilerplate tests that only assert experimental field markers for individual
request fields in `common.rs`; rely on schema generation/tests and behavioral coverage instead.
If you want Codex in your code editor (VS Code, Cursor, Windsurf), <a href="https://developers.openai.com/codex/ide">install in your IDE.</a>
</br>If you want the desktop app experience, run <code>codex app</code> or visit <a href="https://chatgpt.com/codex?app-landing-page=true">the Codex App page</a>.
</br>If you are looking for the <em>cloud-based agent</em> from OpenAI, <strong>Codex Web</strong>, go to <a href="https://chatgpt.com/codex">chatgpt.com/codex</a>.</p>
The security is essential to OpenAI's mission. We appreciate the work of security researchers acting in good faith to identify and responsibly report potential vulnerabilities, helping us maintain strong privacy and security standards for our users and technology.
Our security program is managed through Bugcrowd, and we ask that any validated vulnerabilities be reported via the [Bugcrowd program](https://bugcrowd.com/engagements/openai).
## Vulnerability Disclosure Program
Our Vulnerability Program Guidelines are defined on our [Bugcrowd program page](https://bugcrowd.com/engagements/openai).
"description":"Unique identifier for this specific approval callback.\n\nFor regular shell/unified_exec approvals, this is null.\n\nFor zsh-exec-bridge subcommand approvals, multiple callbacks can belong to one parent `itemId`, so `approvalId` is a distinct opaque callback id (a UUID) used to disambiguate routing.",
"type":[
"string",
"null"
]
},
"command":{
"description":"The command to be executed.",
"type":[
@@ -140,6 +171,17 @@
"itemId":{
"type":"string"
},
"networkApprovalContext":{
"anyOf":[
{
"$ref":"#/definitions/NetworkApprovalContext"
},
{
"type":"null"
}
],
"description":"Optional context for managed-network approval prompts."
},
"proposedExecpolicyAmendment":{
"description":"Optional proposed execpolicy amendment to allow similar commands without prompting.",
"description":"*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.",
"description":"DEPRECATED: *All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox. Prefer `OnRequest` for interactive runs or `Never` for non-interactive runs.",
"enum":[
"on-failure"
],
@@ -117,6 +117,20 @@
],
"type":"string"
},
{
"additionalProperties":false,
"description":"Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties":{
"reject":{
"$ref":"#/definitions/RejectConfig"
}
},
"required":[
"reject"
],
"title":"RejectAskForApproval",
"type":"object"
},
{
"description":"Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum":[
@@ -282,6 +296,75 @@
}
]
},
"CollabAgentRef":{
"properties":{
"agent_nickname":{
"description":"Optional nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agent_role":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"thread_id":{
"allOf":[
{
"$ref":"#/definitions/ThreadId"
}
],
"description":"Thread ID of the receiver/new agent."
}
},
"required":[
"thread_id"
],
"type":"object"
},
"CollabAgentStatusEntry":{
"properties":{
"agent_nickname":{
"description":"Optional nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agent_role":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"status":{
"allOf":[
{
"$ref":"#/definitions/AgentStatus"
}
],
"description":"Last known status of the agent."
},
"thread_id":{
"allOf":[
{
"$ref":"#/definitions/ThreadId"
}
],
"description":"Thread ID of the receiver/new agent."
"description":"Realtime conversation lifecycle close event.",
"properties":{
"reason":{
"type":[
"string",
"null"
]
},
"type":{
"enum":[
"realtime_conversation_closed"
],
"title":"RealtimeConversationClosedEventMsgType",
"type":"string"
}
},
"required":[
"type"
],
"title":"RealtimeConversationClosedEventMsg",
"type":"object"
},
{
"description":"Model routing changed from the requested model to a different model.",
"properties":{
"from_model":{
"type":"string"
},
"reason":{
"$ref":"#/definitions/ModelRerouteReason"
},
"to_model":{
"type":"string"
},
"type":{
"enum":[
"model_reroute"
],
"title":"ModelRerouteEventMsgType",
"type":"string"
}
},
"required":[
"from_model",
"reason",
"to_model",
"type"
],
"title":"ModelRerouteEventMsg",
"type":"object"
},
{
"description":"Conversation history was compacted (either automatically or manually).",
"properties":{
@@ -620,6 +799,17 @@
"message":{
"type":"string"
},
"phase":{
"anyOf":[
{
"$ref":"#/definitions/MessagePhase"
},
{
"type":"null"
}
],
"default":null
},
"type":{
"enum":[
"agent_message"
@@ -1349,6 +1539,14 @@
"default":"agent",
"description":"Where the command originated. Defaults to Agent for backward compatibility."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ExecCommandStatus"
}
],
"description":"Completion status for this command execution."
},
"stderr":{
"description":"Captured stderr",
"type":"string"
@@ -1377,6 +1575,7 @@
"exit_code",
"formatted_output",
"parsed_cmd",
"status",
"stderr",
"stdout",
"turn_id",
@@ -1414,8 +1613,15 @@
},
{
"properties":{
"approval_id":{
"description":"Identifier for this specific approval callback.\n\nWhen absent, the approval is for the command item itself (`call_id`). This is present for subcommand approvals (via execve intercept).",
"type":[
"string",
"null"
]
},
"call_id":{
"description":"Identifier for the associated exec call, if available.",
"description":"Identifier for the associated command execution item.",
"type":"string"
},
"command":{
@@ -1429,6 +1635,17 @@
"description":"The command's working directory.",
"type":"string"
},
"network_approval_context":{
"anyOf":[
{
"$ref":"#/definitions/NetworkApprovalContext"
},
{
"type":"null"
}
],
"description":"Optional network context for a blocked request that can be approved."
},
"parsed_cmd":{
"items":{
"$ref":"#/definitions/ParsedCommand"
@@ -1805,6 +2022,14 @@
"description":"The changes that were applied (mirrors PatchApplyBeginEvent::changes).",
"type":"object"
},
"status":{
"allOf":[
{
"$ref":"#/definitions/PatchApplyStatus"
}
],
"description":"Completion status for this patch application."
"description":"Realtime conversation lifecycle close event.",
"properties":{
"reason":{
"type":[
"string",
"null"
]
},
"type":{
"enum":[
"realtime_conversation_closed"
],
"title":"RealtimeConversationClosedEventMsgType",
"type":"string"
}
},
"required":[
"type"
],
"title":"RealtimeConversationClosedEventMsg",
"type":"object"
},
{
"description":"Model routing changed from the requested model to a different model.",
"properties":{
"from_model":{
"type":"string"
},
"reason":{
"$ref":"#/definitions/ModelRerouteReason"
},
"to_model":{
"type":"string"
},
"type":{
"enum":[
"model_reroute"
],
"title":"ModelRerouteEventMsgType",
"type":"string"
}
},
"required":[
"from_model",
"reason",
"to_model",
"type"
],
"title":"ModelRerouteEventMsg",
"type":"object"
},
{
"description":"Conversation history was compacted (either automatically or manually).",
"properties":{
@@ -5456,6 +6044,17 @@
"message":{
"type":"string"
},
"phase":{
"anyOf":[
{
"$ref":"#/definitions/MessagePhase"
},
{
"type":"null"
}
],
"default":null
},
"type":{
"enum":[
"agent_message"
@@ -6185,6 +6784,14 @@
"default":"agent",
"description":"Where the command originated. Defaults to Agent for backward compatibility."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ExecCommandStatus"
}
],
"description":"Completion status for this command execution."
},
"stderr":{
"description":"Captured stderr",
"type":"string"
@@ -6213,6 +6820,7 @@
"exit_code",
"formatted_output",
"parsed_cmd",
"status",
"stderr",
"stdout",
"turn_id",
@@ -6250,8 +6858,15 @@
},
{
"properties":{
"approval_id":{
"description":"Identifier for this specific approval callback.\n\nWhen absent, the approval is for the command item itself (`call_id`). This is present for subcommand approvals (via execve intercept).",
"type":[
"string",
"null"
]
},
"call_id":{
"description":"Identifier for the associated exec call, if available.",
"description":"Identifier for the associated command execution item.",
"type":"string"
},
"command":{
@@ -6265,6 +6880,17 @@
"description":"The command's working directory.",
"type":"string"
},
"network_approval_context":{
"anyOf":[
{
"$ref":"#/definitions/NetworkApprovalContext"
},
{
"type":"null"
}
],
"description":"Optional network context for a blocked request that can be approved."
},
"parsed_cmd":{
"items":{
"$ref":"#/definitions/ParsedCommand"
@@ -6641,6 +7267,14 @@
"description":"The changes that were applied (mirrors PatchApplyBeginEvent::changes).",
"type":"object"
},
"status":{
"allOf":[
{
"$ref":"#/definitions/PatchApplyStatus"
}
],
"description":"Completion status for this patch application."
"description":"Use to correlate this with [codex_core::protocol::PatchApplyBeginEvent] and [codex_core::protocol::PatchApplyEndEvent].",
"description":"Use to correlate this with [codex_protocol::protocol::PatchApplyBeginEvent] and [codex_protocol::protocol::PatchApplyEndEvent].",
"type":"string"
},
"conversationId":{
@@ -179,6 +179,13 @@
},
"CommandExecutionRequestApprovalParams":{
"properties":{
"approvalId":{
"description":"Unique identifier for this specific approval callback.\n\nFor regular shell/unified_exec approvals, this is null.\n\nFor zsh-exec-bridge subcommand approvals, multiple callbacks can belong to one parent `itemId`, so `approvalId` is a distinct opaque callback id (a UUID) used to disambiguate routing.",
"type":[
"string",
"null"
]
},
"command":{
"description":"The command to be executed.",
"type":[
@@ -206,6 +213,17 @@
"itemId":{
"type":"string"
},
"networkApprovalContext":{
"anyOf":[
{
"$ref":"#/definitions/NetworkApprovalContext"
},
{
"type":"null"
}
],
"description":"Optional context for managed-network approval prompts."
},
"proposedExecpolicyAmendment":{
"description":"Optional proposed execpolicy amendment to allow similar commands without prompting.",
"items":{
@@ -264,8 +282,15 @@
},
"ExecCommandApprovalParams":{
"properties":{
"approvalId":{
"description":"Identifier for this specific approval callback.",
"type":[
"string",
"null"
]
},
"callId":{
"description":"Use to correlate this with [codex_core::protocol::ExecCommandBeginEvent] and [codex_core::protocol::ExecCommandEndEvent].",
"description":"Use to correlate this with [codex_protocol::protocol::ExecCommandBeginEvent] and [codex_protocol::protocol::ExecCommandEndEvent].",
"description":"Authentication mode for OpenAI-backed providers.",
"oneOf":[
{
"description":"OpenAI API key provided by the caller and stored by Codex.",
"enum":[
"apikey"
],
"type":"string"
},
{
"description":"ChatGPT OAuth managed by Codex (tokens persisted and refreshed by Codex).",
"enum":[
"chatgpt"
],
"type":"string"
},
{
"description":"[UNSTABLE] FOR OPENAI INTERNAL USE ONLY - DO NOT USE.\n\nChatGPT auth tokens are supplied by an external host app and are only stored in memory. Token refresh must be handled by the external host app.",
"enum":[
"chatgptAuthTokens"
],
"type":"string"
}
]
}
},
"description":"Deprecated notification. Use AccountUpdatedNotification instead.",
"description":"A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type":"string"
},
"NetworkAccess":{
"description":"Represents whether outbound network access is available to the agent.",
"enum":[
"restricted",
"enabled"
],
"type":"string"
},
"ReadOnlyAccess":{
"description":"Determines how read-only file access is granted inside a restricted sandbox.",
"oneOf":[
{
"description":"Restrict reads to an explicit set of roots.\n\nWhen `include_platform_defaults` is `true`, platform defaults required for basic execution are included in addition to `readable_roots`.",
"properties":{
"include_platform_defaults":{
"default":true,
"description":"Include built-in platform read roots required for basic process execution.",
"type":"boolean"
},
"readable_roots":{
"description":"Additional absolute roots that should be readable.",
"items":{
"$ref":"#/definitions/AbsolutePathBuf"
},
"type":"array"
},
"type":{
"enum":[
"restricted"
],
"title":"RestrictedReadOnlyAccessType",
"type":"string"
}
},
"required":[
"type"
],
"title":"RestrictedReadOnlyAccess",
"type":"object"
},
{
"description":"Allow unrestricted file reads.",
"properties":{
"type":{
"enum":[
"full-access"
],
"title":"FullAccessReadOnlyAccessType",
"type":"string"
}
},
"required":[
"type"
],
"title":"FullAccessReadOnlyAccess",
"type":"object"
}
]
},
"SandboxPolicy":{
"description":"Determines execution restrictions for model shell commands.",
"oneOf":[
{
"description":"No restrictions whatsoever. Use with caution.",
"properties":{
"type":{
"enum":[
"danger-full-access"
],
"title":"DangerFullAccessSandboxPolicyType",
"type":"string"
}
},
"required":[
"type"
],
"title":"DangerFullAccessSandboxPolicy",
"type":"object"
},
{
"description":"Read-only access configuration.",
"properties":{
"access":{
"allOf":[
{
"$ref":"#/definitions/ReadOnlyAccess"
}
],
"description":"Read access granted while running under this policy."
},
"type":{
"enum":[
"read-only"
],
"title":"ReadOnlySandboxPolicyType",
"type":"string"
}
},
"required":[
"type"
],
"title":"ReadOnlySandboxPolicy",
"type":"object"
},
{
"description":"Indicates the process is already in an external sandbox. Allows full disk access while honoring the provided network setting.",
"properties":{
"network_access":{
"allOf":[
{
"$ref":"#/definitions/NetworkAccess"
}
],
"default":"restricted",
"description":"Whether the external sandbox permits outbound network traffic."
},
"type":{
"enum":[
"external-sandbox"
],
"title":"ExternalSandboxSandboxPolicyType",
"type":"string"
}
},
"required":[
"type"
],
"title":"ExternalSandboxSandboxPolicy",
"type":"object"
},
{
"description":"Same as `ReadOnly` but additionally grants write access to the current working directory (\"workspace\").",
"properties":{
"exclude_slash_tmp":{
"default":false,
"description":"When set to `true`, will NOT include the `/tmp` among the default writable roots on UNIX. Defaults to `false`.",
"type":"boolean"
},
"exclude_tmpdir_env_var":{
"default":false,
"description":"When set to `true`, will NOT include the per-user `TMPDIR` environment variable among the default writable roots. Defaults to `false`.",
"type":"boolean"
},
"network_access":{
"default":false,
"description":"When set to `true`, outbound network access is allowed. `false` by default.",
"type":"boolean"
},
"read_only_access":{
"allOf":[
{
"$ref":"#/definitions/ReadOnlyAccess"
}
],
"description":"Read access granted while running under this policy."
},
"type":{
"enum":[
"workspace-write"
],
"title":"WorkspaceWriteSandboxPolicyType",
"type":"string"
},
"writable_roots":{
"description":"Additional folders (beyond cwd and possibly TMPDIR) that should be writable from within the sandbox.",
"description":"Determines the conditions under which the user is consulted to approve running the command proposed by Codex.",
"oneOf":[
{
"description":"Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** are auto‑approved. Everything else will ask the user to approve.",
"enum":[
"untrusted"
],
"type":"string"
},
{
"description":"*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.",
"enum":[
"on-failure"
],
"type":"string"
},
{
"description":"The model decides when to ask the user for approval.",
"enum":[
"on-request"
],
"type":"string"
},
{
"description":"Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"description":"Authentication mode for OpenAI-backed providers.",
"oneOf":[
{
"description":"OpenAI API key provided by the caller and stored by Codex.",
"enum":[
"apikey"
],
"type":"string"
},
{
"description":"ChatGPT OAuth managed by Codex (tokens persisted and refreshed by Codex).",
"enum":[
"chatgpt"
],
"type":"string"
},
{
"description":"[UNSTABLE] FOR OPENAI INTERNAL USE ONLY - DO NOT USE.\n\nChatGPT auth tokens are supplied by an external host app and are only stored in memory. Token refresh must be handled by the external host app.",
"description":"A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type":"string"
},
"AskForApproval":{
"description":"Determines the conditions under which the user is consulted to approve running the command proposed by Codex.",
"oneOf":[
{
"description":"Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** are auto‑approved. Everything else will ask the user to approve.",
"enum":[
"untrusted"
],
"type":"string"
},
{
"description":"*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.",
"enum":[
"on-failure"
],
"type":"string"
},
{
"description":"The model decides when to ask the user for approval.",
"enum":[
"on-request"
],
"type":"string"
},
{
"description":"Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"description":"A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries",
"oneOf":[
{
"enum":[
"auto",
"concise",
"detailed"
],
"type":"string"
},
{
"description":"Option to disable reasoning summaries.",
"enum":[
"none"
],
"type":"string"
}
]
},
"SandboxMode":{
"enum":[
"read-only",
"workspace-write",
"danger-full-access"
],
"type":"string"
},
"SandboxSettings":{
"properties":{
"excludeSlashTmp":{
"type":[
"boolean",
"null"
]
},
"excludeTmpdirEnvVar":{
"type":[
"boolean",
"null"
]
},
"networkAccess":{
"type":[
"boolean",
"null"
]
},
"writableRoots":{
"default":[],
"items":{
"$ref":"#/definitions/AbsolutePathBuf"
},
"type":"array"
}
},
"type":"object"
},
"Tools":{
"properties":{
"viewImage":{
"type":[
"boolean",
"null"
]
},
"webSearch":{
"type":[
"boolean",
"null"
]
}
},
"type":"object"
},
"UserSavedConfig":{
"properties":{
"approvalPolicy":{
"anyOf":[
{
"$ref":"#/definitions/AskForApproval"
},
{
"type":"null"
}
]
},
"forcedChatgptWorkspaceId":{
"type":[
"string",
"null"
]
},
"forcedLoginMethod":{
"anyOf":[
{
"$ref":"#/definitions/ForcedLoginMethod"
},
{
"type":"null"
}
]
},
"model":{
"type":[
"string",
"null"
]
},
"modelReasoningEffort":{
"anyOf":[
{
"$ref":"#/definitions/ReasoningEffort"
},
{
"type":"null"
}
]
},
"modelReasoningSummary":{
"anyOf":[
{
"$ref":"#/definitions/ReasoningSummary"
},
{
"type":"null"
}
]
},
"modelVerbosity":{
"anyOf":[
{
"$ref":"#/definitions/Verbosity"
},
{
"type":"null"
}
]
},
"profile":{
"type":[
"string",
"null"
]
},
"profiles":{
"additionalProperties":{
"$ref":"#/definitions/Profile"
},
"type":"object"
},
"sandboxMode":{
"anyOf":[
{
"$ref":"#/definitions/SandboxMode"
},
{
"type":"null"
}
]
},
"sandboxSettings":{
"anyOf":[
{
"$ref":"#/definitions/SandboxSettings"
},
{
"type":"null"
}
]
},
"tools":{
"anyOf":[
{
"$ref":"#/definitions/Tools"
},
{
"type":"null"
}
]
}
},
"required":[
"profiles"
],
"type":"object"
},
"Verbosity":{
"description":"Controls output length/detail on GPT-5 models via the Responses API. Serialized with lowercase values to match the OpenAI API.",
"description":"Determines the conditions under which the user is consulted to approve running the command proposed by Codex.",
"oneOf":[
{
"description":"Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** are auto‑approved. Everything else will ask the user to approve.",
"enum":[
"untrusted"
],
"type":"string"
},
{
"description":"*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.",
"enum":[
"on-failure"
],
"type":"string"
},
{
"description":"The model decides when to ask the user for approval.",
"enum":[
"on-request"
],
"type":"string"
},
{
"description":"Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"description":"Determines the conditions under which the user is consulted to approve running the command proposed by Codex.",
"oneOf":[
{
"description":"Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** are auto‑approved. Everything else will ask the user to approve.",
"enum":[
"untrusted"
],
"type":"string"
},
{
"description":"*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.",
"enum":[
"on-failure"
],
"type":"string"
},
{
"description":"The model decides when to ask the user for approval.",
"enum":[
"on-request"
],
"type":"string"
},
{
"description":"Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"description":"Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.",
"description":"The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.",
"properties":{
"body":{
"$ref":"#/definitions/FunctionCallOutputBody"
},
"success":{
"type":[
"boolean",
"null"
]
}
},
"required":[
"body"
],
"type":"object"
},
"GhostCommit":{
"description":"Details of a ghost commit created from a repository state.",
"properties":{
"id":{
"type":"string"
},
"parent":{
"type":[
"string",
"null"
]
},
"preexisting_untracked_dirs":{
"items":{
"type":"string"
},
"type":"array"
},
"preexisting_untracked_files":{
"items":{
"type":"string"
},
"type":"array"
}
},
"required":[
"id",
"preexisting_untracked_dirs",
"preexisting_untracked_files"
],
"type":"object"
},
"LocalShellAction":{
"oneOf":[
{
"properties":{
"command":{
"items":{
"type":"string"
},
"type":"array"
},
"env":{
"additionalProperties":{
"type":"string"
},
"type":[
"object",
"null"
]
},
"timeout_ms":{
"format":"uint64",
"minimum":0.0,
"type":[
"integer",
"null"
]
},
"type":{
"enum":[
"exec"
],
"title":"ExecLocalShellActionType",
"type":"string"
},
"user":{
"type":[
"string",
"null"
]
},
"working_directory":{
"type":[
"string",
"null"
]
}
},
"required":[
"command",
"type"
],
"title":"ExecLocalShellAction",
"type":"object"
}
]
},
"LocalShellStatus":{
"enum":[
"completed",
"in_progress",
"incomplete"
],
"type":"string"
},
"MessagePhase":{
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"description":"A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type":"string"
},
"AskForApproval":{
"description":"Determines the conditions under which the user is consulted to approve running the command proposed by Codex.",
"oneOf":[
{
"description":"Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** are auto‑approved. Everything else will ask the user to approve.",
"enum":[
"untrusted"
],
"type":"string"
},
{
"description":"*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.",
"enum":[
"on-failure"
],
"type":"string"
},
{
"description":"The model decides when to ask the user for approval.",
"enum":[
"on-request"
],
"type":"string"
},
{
"description":"Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum":[
"never"
],
"type":"string"
}
]
},
"InputItem":{
"oneOf":[
{
"properties":{
"data":{
"properties":{
"text":{
"type":"string"
},
"text_elements":{
"default":[],
"description":"UI-defined spans within `text` used to render or persist special elements.",
"items":{
"$ref":"#/definitions/V1TextElement"
},
"type":"array"
}
},
"required":[
"text"
],
"type":"object"
},
"type":{
"enum":[
"text"
],
"title":"TextInputItemType",
"type":"string"
}
},
"required":[
"data",
"type"
],
"title":"TextInputItem",
"type":"object"
},
{
"properties":{
"data":{
"properties":{
"image_url":{
"type":"string"
}
},
"required":[
"image_url"
],
"type":"object"
},
"type":{
"enum":[
"image"
],
"title":"ImageInputItemType",
"type":"string"
}
},
"required":[
"data",
"type"
],
"title":"ImageInputItem",
"type":"object"
},
{
"properties":{
"data":{
"properties":{
"path":{
"type":"string"
}
},
"required":[
"path"
],
"type":"object"
},
"type":{
"enum":[
"localImage"
],
"title":"LocalImageInputItemType",
"type":"string"
}
},
"required":[
"data",
"type"
],
"title":"LocalImageInputItem",
"type":"object"
}
]
},
"NetworkAccess":{
"description":"Represents whether outbound network access is available to the agent.",
"enum":[
"restricted",
"enabled"
],
"type":"string"
},
"ReadOnlyAccess":{
"description":"Determines how read-only file access is granted inside a restricted sandbox.",
"oneOf":[
{
"description":"Restrict reads to an explicit set of roots.\n\nWhen `include_platform_defaults` is `true`, platform defaults required for basic execution are included in addition to `readable_roots`.",
"properties":{
"include_platform_defaults":{
"default":true,
"description":"Include built-in platform read roots required for basic process execution.",
"type":"boolean"
},
"readable_roots":{
"description":"Additional absolute roots that should be readable.",
"description":"A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries",
"oneOf":[
{
"enum":[
"auto",
"concise",
"detailed"
],
"type":"string"
},
{
"description":"Option to disable reasoning summaries.",
"enum":[
"none"
],
"type":"string"
}
]
},
"SandboxPolicy":{
"description":"Determines execution restrictions for model shell commands.",
"oneOf":[
{
"description":"No restrictions whatsoever. Use with caution.",
"properties":{
"type":{
"enum":[
"danger-full-access"
],
"title":"DangerFullAccessSandboxPolicyType",
"type":"string"
}
},
"required":[
"type"
],
"title":"DangerFullAccessSandboxPolicy",
"type":"object"
},
{
"description":"Read-only access configuration.",
"properties":{
"access":{
"allOf":[
{
"$ref":"#/definitions/ReadOnlyAccess"
}
],
"description":"Read access granted while running under this policy."
},
"type":{
"enum":[
"read-only"
],
"title":"ReadOnlySandboxPolicyType",
"type":"string"
}
},
"required":[
"type"
],
"title":"ReadOnlySandboxPolicy",
"type":"object"
},
{
"description":"Indicates the process is already in an external sandbox. Allows full disk access while honoring the provided network setting.",
"properties":{
"network_access":{
"allOf":[
{
"$ref":"#/definitions/NetworkAccess"
}
],
"default":"restricted",
"description":"Whether the external sandbox permits outbound network traffic."
},
"type":{
"enum":[
"external-sandbox"
],
"title":"ExternalSandboxSandboxPolicyType",
"type":"string"
}
},
"required":[
"type"
],
"title":"ExternalSandboxSandboxPolicy",
"type":"object"
},
{
"description":"Same as `ReadOnly` but additionally grants write access to the current working directory (\"workspace\").",
"properties":{
"exclude_slash_tmp":{
"default":false,
"description":"When set to `true`, will NOT include the `/tmp` among the default writable roots on UNIX. Defaults to `false`.",
"type":"boolean"
},
"exclude_tmpdir_env_var":{
"default":false,
"description":"When set to `true`, will NOT include the per-user `TMPDIR` environment variable among the default writable roots. Defaults to `false`.",
"type":"boolean"
},
"network_access":{
"default":false,
"description":"When set to `true`, outbound network access is allowed. `false` by default.",
"type":"boolean"
},
"read_only_access":{
"allOf":[
{
"$ref":"#/definitions/ReadOnlyAccess"
}
],
"description":"Read access granted while running under this policy."
},
"type":{
"enum":[
"workspace-write"
],
"title":"WorkspaceWriteSandboxPolicyType",
"type":"string"
},
"writable_roots":{
"description":"Additional folders (beyond cwd and possibly TMPDIR) that should be writable from within the sandbox.",
"items":{
"$ref":"#/definitions/AbsolutePathBuf"
},
"type":"array"
}
},
"required":[
"type"
],
"title":"WorkspaceWriteSandboxPolicy",
"type":"object"
}
]
},
"ThreadId":{
"type":"string"
},
"V1ByteRange":{
"properties":{
"end":{
"description":"End byte offset (exclusive) within the UTF-8 text buffer.",
"format":"uint",
"minimum":0.0,
"type":"integer"
},
"start":{
"description":"Start byte offset (inclusive) within the UTF-8 text buffer.",
"format":"uint",
"minimum":0.0,
"type":"integer"
}
},
"required":[
"end",
"start"
],
"type":"object"
},
"V1TextElement":{
"properties":{
"byteRange":{
"allOf":[
{
"$ref":"#/definitions/V1ByteRange"
}
],
"description":"Byte range in the parent `text` buffer that this element occupies."
},
"placeholder":{
"description":"Optional human-readable placeholder for the element, displayed in the UI.",
"type":[
"string",
"null"
]
}
},
"required":[
"byteRange"
],
"type":"object"
}
},
"properties":{
"approvalPolicy":{
"$ref":"#/definitions/AskForApproval"
},
"conversationId":{
"$ref":"#/definitions/ThreadId"
},
"cwd":{
"type":"string"
},
"effort":{
"anyOf":[
{
"$ref":"#/definitions/ReasoningEffort"
},
{
"type":"null"
}
]
},
"items":{
"items":{
"$ref":"#/definitions/InputItem"
},
"type":"array"
},
"model":{
"type":"string"
},
"outputSchema":{
"description":"Optional JSON Schema used to constrain the final assistant message for this turn."
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"enum":[
"final_answer"
],
"type":"string"
}
]
},
"NetworkAccess":{
"enum":[
"restricted",
@@ -666,7 +718,8 @@
{
"enum":[
"review",
"compact"
"compact",
"memory_consolidation"
],
"type":"string"
},
@@ -675,6 +728,20 @@
"properties":{
"thread_spawn":{
"properties":{
"agent_nickname":{
"default":null,
"type":[
"string",
"null"
]
},
"agent_role":{
"default":null,
"type":[
"string",
"null"
]
},
"depth":{
"format":"int32",
"type":"integer"
@@ -736,6 +803,20 @@
},
"Thread":{
"properties":{
"agentNickname":{
"description":"Optional random unique nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agentRole":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"cliVersion":{
"description":"Version of the CLI that created the thread.",
"type":"string"
@@ -767,6 +848,13 @@
"description":"Model provider used for this thread (for example, 'openai').",
"description":"[UNSTABLE] Path to the thread on disk.",
"type":[
@@ -786,6 +874,14 @@
],
"description":"Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ThreadStatus"
}
],
"description":"Current runtime status for the thread."
},
"turns":{
"description":"Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"enum":[
"final_answer"
],
"type":"string"
}
]
},
"PatchApplyStatus":{
"enum":[
"inProgress",
@@ -472,7 +491,8 @@
{
"enum":[
"review",
"compact"
"compact",
"memory_consolidation"
],
"type":"string"
},
@@ -481,6 +501,20 @@
"properties":{
"thread_spawn":{
"properties":{
"agent_nickname":{
"default":null,
"type":[
"string",
"null"
]
},
"agent_role":{
"default":null,
"type":[
"string",
"null"
]
},
"depth":{
"format":"int32",
"type":"integer"
@@ -542,6 +576,20 @@
},
"Thread":{
"properties":{
"agentNickname":{
"description":"Optional random unique nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agentRole":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"cliVersion":{
"description":"Version of the CLI that created the thread.",
"type":"string"
@@ -573,6 +621,13 @@
"description":"Model provider used for this thread (for example, 'openai').",
"description":"[UNSTABLE] Path to the thread on disk.",
"type":[
@@ -592,6 +647,14 @@
],
"description":"Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ThreadStatus"
}
],
"description":"Current runtime status for the thread."
},
"turns":{
"description":"Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"enum":[
"final_answer"
],
"type":"string"
}
]
},
"PatchApplyStatus":{
"enum":[
"inProgress",
@@ -472,7 +491,8 @@
{
"enum":[
"review",
"compact"
"compact",
"memory_consolidation"
],
"type":"string"
},
@@ -481,6 +501,20 @@
"properties":{
"thread_spawn":{
"properties":{
"agent_nickname":{
"default":null,
"type":[
"string",
"null"
]
},
"agent_role":{
"default":null,
"type":[
"string",
"null"
]
},
"depth":{
"format":"int32",
"type":"integer"
@@ -542,6 +576,20 @@
},
"Thread":{
"properties":{
"agentNickname":{
"description":"Optional random unique nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agentRole":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"cliVersion":{
"description":"Version of the CLI that created the thread.",
"type":"string"
@@ -573,6 +621,13 @@
"description":"Model provider used for this thread (for example, 'openai').",
"description":"[UNSTABLE] Path to the thread on disk.",
"type":[
@@ -592,6 +647,14 @@
],
"description":"Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ThreadStatus"
}
],
"description":"Current runtime status for the thread."
},
"turns":{
"description":"Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"enum":[
"final_answer"
],
"type":"string"
}
]
},
"NetworkAccess":{
"enum":[
"restricted",
@@ -666,7 +718,8 @@
{
"enum":[
"review",
"compact"
"compact",
"memory_consolidation"
],
"type":"string"
},
@@ -675,6 +728,20 @@
"properties":{
"thread_spawn":{
"properties":{
"agent_nickname":{
"default":null,
"type":[
"string",
"null"
]
},
"agent_role":{
"default":null,
"type":[
"string",
"null"
]
},
"depth":{
"format":"int32",
"type":"integer"
@@ -736,6 +803,20 @@
},
"Thread":{
"properties":{
"agentNickname":{
"description":"Optional random unique nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agentRole":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"cliVersion":{
"description":"Version of the CLI that created the thread.",
"type":"string"
@@ -767,6 +848,13 @@
"description":"Model provider used for this thread (for example, 'openai').",
"description":"[UNSTABLE] Path to the thread on disk.",
"type":[
@@ -786,6 +874,14 @@
],
"description":"Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ThreadStatus"
}
],
"description":"Current runtime status for the thread."
},
"turns":{
"description":"Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"enum":[
"final_answer"
],
"type":"string"
}
]
},
"PatchApplyStatus":{
"enum":[
"inProgress",
@@ -472,7 +491,8 @@
{
"enum":[
"review",
"compact"
"compact",
"memory_consolidation"
],
"type":"string"
},
@@ -481,6 +501,20 @@
"properties":{
"thread_spawn":{
"properties":{
"agent_nickname":{
"default":null,
"type":[
"string",
"null"
]
},
"agent_role":{
"default":null,
"type":[
"string",
"null"
]
},
"depth":{
"format":"int32",
"type":"integer"
@@ -542,6 +576,20 @@
},
"Thread":{
"properties":{
"agentNickname":{
"description":"Optional random unique nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agentRole":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"cliVersion":{
"description":"Version of the CLI that created the thread.",
"type":"string"
@@ -573,6 +621,13 @@
"description":"Model provider used for this thread (for example, 'openai').",
"description":"[UNSTABLE] Path to the thread on disk.",
"type":[
@@ -592,6 +647,14 @@
],
"description":"Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ThreadStatus"
}
],
"description":"Current runtime status for the thread."
},
"turns":{
"description":"Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"enum":[
"final_answer"
],
"type":"string"
}
]
},
"NetworkAccess":{
"enum":[
"restricted",
@@ -666,7 +718,8 @@
{
"enum":[
"review",
"compact"
"compact",
"memory_consolidation"
],
"type":"string"
},
@@ -675,6 +728,20 @@
"properties":{
"thread_spawn":{
"properties":{
"agent_nickname":{
"default":null,
"type":[
"string",
"null"
]
},
"agent_role":{
"default":null,
"type":[
"string",
"null"
]
},
"depth":{
"format":"int32",
"type":"integer"
@@ -736,6 +803,20 @@
},
"Thread":{
"properties":{
"agentNickname":{
"description":"Optional random unique nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agentRole":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"cliVersion":{
"description":"Version of the CLI that created the thread.",
"type":"string"
@@ -767,6 +848,13 @@
"description":"Model provider used for this thread (for example, 'openai').",
"description":"[UNSTABLE] Path to the thread on disk.",
"type":[
@@ -786,6 +874,14 @@
],
"description":"Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ThreadStatus"
}
],
"description":"Current runtime status for the thread."
},
"turns":{
"description":"Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"enum":[
"final_answer"
],
"type":"string"
}
]
},
"PatchApplyStatus":{
"enum":[
"inProgress",
@@ -472,7 +491,8 @@
{
"enum":[
"review",
"compact"
"compact",
"memory_consolidation"
],
"type":"string"
},
@@ -481,6 +501,20 @@
"properties":{
"thread_spawn":{
"properties":{
"agent_nickname":{
"default":null,
"type":[
"string",
"null"
]
},
"agent_role":{
"default":null,
"type":[
"string",
"null"
]
},
"depth":{
"format":"int32",
"type":"integer"
@@ -542,6 +576,20 @@
},
"Thread":{
"properties":{
"agentNickname":{
"description":"Optional random unique nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agentRole":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"cliVersion":{
"description":"Version of the CLI that created the thread.",
"type":"string"
@@ -573,6 +621,13 @@
"description":"Model provider used for this thread (for example, 'openai').",
"description":"[UNSTABLE] Path to the thread on disk.",
"type":[
@@ -592,6 +647,14 @@
],
"description":"Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ThreadStatus"
}
],
"description":"Current runtime status for the thread."
},
"turns":{
"description":"Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.",
"description":"Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf":[
{
"description":"Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum":[
"commentary"
],
"type":"string"
},
{
"description":"The assistant's terminal answer text for the current turn.",
"enum":[
"final_answer"
],
"type":"string"
}
]
},
"PatchApplyStatus":{
"enum":[
"inProgress",
@@ -472,7 +491,8 @@
{
"enum":[
"review",
"compact"
"compact",
"memory_consolidation"
],
"type":"string"
},
@@ -481,6 +501,20 @@
"properties":{
"thread_spawn":{
"properties":{
"agent_nickname":{
"default":null,
"type":[
"string",
"null"
]
},
"agent_role":{
"default":null,
"type":[
"string",
"null"
]
},
"depth":{
"format":"int32",
"type":"integer"
@@ -542,6 +576,20 @@
},
"Thread":{
"properties":{
"agentNickname":{
"description":"Optional random unique nickname assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"agentRole":{
"description":"Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type":[
"string",
"null"
]
},
"cliVersion":{
"description":"Version of the CLI that created the thread.",
"type":"string"
@@ -573,6 +621,13 @@
"description":"Model provider used for this thread (for example, 'openai').",
"description":"[UNSTABLE] Path to the thread on disk.",
"type":[
@@ -592,6 +647,14 @@
],
"description":"Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)."
},
"status":{
"allOf":[
{
"$ref":"#/definitions/ThreadStatus"
}
],
"description":"Current runtime status for the thread."
},
"turns":{
"description":"Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.",
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.