## Problem being solved
- We need a single, reliable way to mark app-server API surface as
experimental so that:
1. the runtime can reject experimental usage unless the client opts in
2. generated TS/JSON schemas can exclude experimental methods/fields for
stable clients.
Right now that’s easy to drift or miss when done ad-hoc.
## How to declare experimental methods and fields
- **Experimental method**: add `#[experimental("method/name")]` to the
`ClientRequest` variant in `client_request_definitions!`.
- **Experimental field**: on the params struct, derive `ExperimentalApi`
and annotate the field with `#[experimental("method/name.field")]` + set
`inspect_params: true` for the method variant so
`ClientRequest::experimental_reason()` inspects params for experimental
fields.
## How the macro solves it
- The new derive macro lives in
`codex-rs/codex-experimental-api-macros/src/lib.rs` and is used via
`#[derive(ExperimentalApi)]` plus `#[experimental("reason")]`
attributes.
- **Structs**:
- Generates `ExperimentalApi::experimental_reason(&self)` that checks
only annotated fields.
- The “presence” check is type-aware:
- `Option<T>`: `is_some_and(...)` recursively checks inner.
- `Vec`/`HashMap`/`BTreeMap`: must be non-empty.
- `bool`: must be `true`.
- Other types: considered present (returns `true`).
- Registers each experimental field in an `inventory` with `(type_name,
serialized field name, reason)` and exposes `EXPERIMENTAL_FIELDS` for
that type. Field names are converted from `snake_case` to `camelCase`
for schema/TS filtering.
- **Enums**:
- Generates an exhaustive `match` returning `Some(reason)` for annotated
variants and `None` otherwise (no wildcard arm).
- **Wiring**:
- Runtime gating uses `ExperimentalApi::experimental_reason()` in
`codex-rs/app-server/src/message_processor.rs` to reject requests unless
`InitializeParams.capabilities.experimental_api == true`.
- Schema/TS export filters use the inventory list and
`EXPERIMENTAL_CLIENT_METHODS` from `client_request_definitions!` to
strip experimental methods/fields when `experimental_api` is false.
- shows names of threads in the ResumePicker used by `/resume` and
`codex resume` if set, default to preview (previous behaviour) if none
- adds a `find_thread_names_by_ids` that maps names to IDs in
`codex-rs/core/src/rollout/session_index.rs`. It reads sequentially in
normal (instead of reverse order in `codex resume <name>`) the index
mapping file. This function is called from a list of session (default
page is 25, pages loaded depends of height of terminal), for which most
of them will always have at least one session unnamed and require the
whole file to be read therefore. Could be better and sqlite integration
will make this better
- those reads won't be needed when leveraging sqlite
Opened questions:
- We could rename the TUI "Conversation" column to "Name" or "Thread"
that would feel more accurate. Could be a fast-follow if we implement
auto-naming as it'll always be a name instead?
Similar to what @sayan-oai did in openai/codex#8956 for
`config.schema.json`, this PR updates the repo so that it includes the
output of `codex app-server generate-json-schema` and `codex app-server
generate-ts` and adds a test to verify it is in sync with the current
code.
Motivation:
- This makes any schema changes introduced by a PR transparent during
code review.
- In particular, this should help us catch PRs that would introduce a
non-backwards-compatible change to the app schema (eventually, this
should also be enforced by tooling).
- Once https://github.com/openai/codex/pull/10231 is in to formalize the
notion of "experimental" fields, we can work on ensuring the
non-experimental bits are backwards-compatible.
`codex-rs/app-server-protocol/tests/schema_fixtures.rs` was added as the
test and `just write-app-server-schema` can be use to generate the
vendored schema files.
Incidentally, when I run:
```
rg _ codex-rs/app-server-protocol/schema/typescript/v2
```
I see a number of `snake_case` names that should be `camelCase`.
## Summary
We ideally shouldn't make it to this point in the first place, but if we
do try to append a rule that already exists, we shouldn't append the
same rule twice.
## Testing
- [x] Added unit test for this case
## Summary
We should probably warn users that they have a million rules, and help
clean them up. But for now, we should handle this unbounded case.
Limit rules listed in conversations, with shortest / broadest rules
first.
## Testing
- [x] Updated unit tests
Updated system skills bundled with Codex were not correctly replacing
the user's skills in their .system folder.
- Fix `.codex-system-skills.marker` not updating by hashing embedded
system skills recursively (nested dirs + file contents), so updates
trigger a reinstall.
- Added a build Cargo hook to rerun if there are changes in
`src/skills/assets/samples/*`, ensuring embedded skill updates rebuild
correctly under caching.
- Add a small unit test to ensure nested entries are included in the
fingerprint.
## Summary
- Touch restored rollout files on `thread/unarchive` so `updatedAt`
reflects the unarchive time.
- Add a regression test to ensure unarchiving bumps `updated_at` from an
old mtime.
## Notes
This fixes the UX issue where unarchived old threads don’t reappear near
the top of recent threads.
## Summary
- Replace the “Hard interaction rule” with a clearer “Response
constraints” section that enumerates the allowed exceptions for Plan
Mode replies.
- Remove the stray Phase 1 exception line about simple questions.
- Update plan content requirements to ask for a brief summary section
and generalize API/type wording.
Summary
- expose websocket telemetry hooks through the responses client so
request durations and event processing can be reported
- record websocket request/event metrics and emit runtime telemetry
events that the history UI now surfaces
- improve tests to cover websocket telemetry reporting and guard runtime
summary updates
<img width="824" height="79" alt="Screenshot 2026-01-31 at 5 28 12 PM"
src="https://github.com/user-attachments/assets/ea9a7965-d8b4-4e3c-a984-ef4fdc44c81d"
/>
# 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.
# 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.
### Motivation
- The image referenced in the package README was 404ing on the npm
package page because it used a relative file path that doesn't resolve
on npm, so the splash image needs a GitHub-hosted URL to render
correctly.
### Description
- Update `README.md` to replace the relative image path
`./.github/codex-cli-splash.png` with the GitHub-hosted URL
`https://github.com/openai/codex/blob/main/.github/codex-cli-splash.png`.
### Testing
- No automated tests were run because this is a docs-only change and
does not affect code or test behavior.
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_697e58dbce34832d87c7847779e8f4a5)
## Summary
I have read the contribution guidelines.
All changes in this PR are limited to text corrections and do not modify
any business logic, runtime behavior, or user-facing functionality.
## Details
This PR fixes several minor typos, including:
- `create` -> `crate`
- `analagous` -> `analogous`
- `apply-patch` -> `apply_patch`
- `codecs` -> `codex`
- ` '/" ` -> ` '/' `
- `Respesent` -> `Represent`
Summary
- require `CODEX_HOME` to point to an existing directory before
canonicalizing and surface clear errors otherwise
- share the same helper logic in both `core` and `rmcp-client` and add
unit tests that cover missing, non-directory, valid, and default paths
This addresses #9222
## Summary
Fixes#9041
- Adds update_search_dir() method to FileSearchManager to allow updating
the search directory after initialization
- Calls this method when the session CWD changes: new session, resume,
or fork
## Problem
The FileSearchManager was created once with the initial search_dir and
never updated. When a user:
1. Starts Codex in a non-git directory (e.g., /tmp/random)
2. Resumes or forks a session from a different workspace
3. The @filename lookup still searched the original directory
This caused no matches to be returned even when files existed in the
current workspace.
## Solution
Update FileSearchManager.search_dir whenever the session working
directory changes:
- AppEvent::NewSession: Use current config CWD
- SessionSelection::Resume: Use resumed session CWD
- SessionSelection::Fork: Use forked session CWD
## Test plan
- [ ] Start Codex in /tmp/test-dir (non-git)
- [ ] Resume a session from a project with actual files
- [ ] Verify @filename returns matches from the resumed session
directory
---------
Co-authored-by: Eric Traut <etraut@openai.com>
seeing issues with azure after default-enabling web search: #10071,
#10257.
need to work with azure to fix api-side, for now turning off
default-enable of web_search for azure.
diff is big because i moved logic to reuse
Fixes#9559.
When `shell_snapshot` runs, it may execute user startup files (e.g.
`.bashrc`). If those files read from stdin (or if stdin is an
interactive TTY under job control), the snapshot subprocess can block or
receive `SIGTTIN` (as reported over SSH).
This change explicitly sets `stdin` to `Stdio::null()` for the snapshot
subprocess, so it can't read from the terminal.
Regression test added that would hang/timeout without this change.
Tests: `ulimit -n 4096 && cargo test -p codex-core`.
cc @dongdongbh @etraut-openai
---------
Co-authored-by: Skylar Graika <sgraika127@gmail.com>
Summary:
- Fixes issue #9932: https://github.com/openai/codex/issues/9932
- Prevents `$CODEX_HOME` (typically `~/.codex`) from being discovered as
a project `.codex` layer by skipping it during project layer traversal.
We compare both normalized absolute paths and best-effort canonicalized
paths to handle symlinks.
- Adds regression tests for home-directory invocation and for the case
where `CODEX_HOME` points to a project `.codex` directory (e.g.,
worktrees/editor integrations).
Testing:
- `cargo build -p codex-cli --bin codex`
- `cargo build -p codex-rmcp-client --bin test_stdio_server`
- `cargo test -p codex-core`
- `cargo test --all-features`
- Manual: ran `target/debug/codex` from `~` and confirmed the
disabled-folder warning and trust prompt no longer appear.
# 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.
# 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.
When using ChatGPT in names of types, we should be consistent, so this
renames some types with `ChatGpt` in the name to `Chatgpt`. From
https://rust-lang.github.io/api-guidelines/naming.html:
> In `UpperCamelCase`, acronyms and contractions of compound words count
as one word: use `Uuid` rather than `UUID`, `Usize` rather than `USize`
or `Stdin` rather than `StdIn`. In `snake_case`, acronyms and
contractions are lower-cased: `is_xid_start`.
This PR updates existing uses of `ChatGpt` and changes them to
`Chatgpt`. Though in all cases where it could affect the wire format, I
visually inspected that we don't change anything there. That said, this
_will_ change the codegen because it will affect the spelling of type
names.
For example, this renames `AuthMode::ChatGPT` to `AuthMode::Chatgpt` in
`app-server-protocol`, but the wire format is still `"chatgpt"`.
This PR also updates a number of types in `codex-rs/core/src/auth.rs`.
Title
Hide Code mode footer label/cycle hint; add Plan footer-collapse
snapshots
Summary
- Keep Code mode internal naming but suppress the footer mode label +
cycle hint when Code is active.
- Only show the cycle hint when a non‑Code mode indicator is present.
- Add Plan-mode footer collapse snapshot coverage (empty + queued,
across widths) and update existing footer collapse snapshots for the new
Code behavior.
Notes
- The test run currently fails in codex-cloud-requirements on
origin/main due to a stale auth.mode field; no fix is included in this
PR to keep the diff minimal.
Codex author
`codex resume 019c0296-cfd4-7193-9b0a-6949048e4546`
## Summary
- Stream proposed plans in Plan Mode using `<proposed_plan>` tags parsed
in core, emitting plan deltas plus a plan `ThreadItem`, while stripping
tags from normal assistant output.
- Persist plan items and rebuild them on resume so proposed plans show
in thread history.
- Wire plan items/deltas through app-server protocol v2 and render a
dedicated proposed-plan view in the TUI, including the “Implement this
plan?” prompt only when a plan item is present.
## Changes
### Core (`codex-rs/core`)
- Added a generic, line-based tag parser that buffers each line until it
can disprove a tag prefix; implements auto-close on `finish()` for
unterminated tags. `codex-rs/core/src/tagged_block_parser.rs`
- Refactored proposed plan parsing to wrap the generic parser.
`codex-rs/core/src/proposed_plan_parser.rs`
- In plan mode, stream assistant deltas as:
- **Normal text** → `AgentMessageContentDelta`
- **Plan text** → `PlanDelta` + `TurnItem::Plan` start/completion
(`codex-rs/core/src/codex.rs`)
- Final plan item content is derived from the completed assistant
message (authoritative), not necessarily the concatenated deltas.
- Strips `<proposed_plan>` blocks from assistant text in plan mode so
tags don’t appear in normal messages.
(`codex-rs/core/src/stream_events_utils.rs`)
- Persist `ItemCompleted` events only for plan items for rollout replay.
(`codex-rs/core/src/rollout/policy.rs`)
- Guard `update_plan` tool in Plan Mode with a clear error message.
(`codex-rs/core/src/tools/handlers/plan.rs`)
- Updated Plan Mode prompt to:
- keep `<proposed_plan>` out of non-final reasoning/preambles
- require exact tag formatting
- allow only one `<proposed_plan>` block per turn
(`codex-rs/core/templates/collaboration_mode/plan.md`)
### Protocol / App-server protocol
- Added `TurnItem::Plan` and `PlanDeltaEvent` to core protocol items.
(`codex-rs/protocol/src/items.rs`, `codex-rs/protocol/src/protocol.rs`)
- Added v2 `ThreadItem::Plan` and `PlanDeltaNotification` with
EXPERIMENTAL markers and note that deltas may not match the final plan
item. (`codex-rs/app-server-protocol/src/protocol/v2.rs`)
- Added plan delta route in app-server protocol common mapping.
(`codex-rs/app-server-protocol/src/protocol/common.rs`)
- Rebuild plan items from persisted `ItemCompleted` events on resume.
(`codex-rs/app-server-protocol/src/protocol/thread_history.rs`)
### App-server
- Forward plan deltas to v2 clients and map core plan items to v2 plan
items. (`codex-rs/app-server/src/bespoke_event_handling.rs`,
`codex-rs/app-server/src/codex_message_processor.rs`)
- Added v2 plan item tests.
(`codex-rs/app-server/tests/suite/v2/plan_item.rs`)
### TUI
- Added a dedicated proposed plan history cell with special background
and padding, and moved “• Proposed Plan” outside the highlighted block.
(`codex-rs/tui/src/history_cell.rs`, `codex-rs/tui/src/style.rs`)
- Only show “Implement this plan?” when a plan item exists.
(`codex-rs/tui/src/chatwidget.rs`,
`codex-rs/tui/src/chatwidget/tests.rs`)
<img width="831" height="847" alt="Screenshot 2026-01-29 at 7 06 24 PM"
src="https://github.com/user-attachments/assets/69794c8c-f96b-4d36-92ef-c1f5c3a8f286"
/>
### Docs / Misc
- Updated protocol docs to mention plan deltas.
(`codex-rs/docs/protocol_v1.md`)
- Minor plumbing updates in exec/debug clients to tolerate plan deltas.
(`codex-rs/debug-client/src/reader.rs`, `codex-rs/exec/...`)
## Tests
- Added core integration tests:
- Plan mode strips plan from agent messages.
- Missing `</proposed_plan>` closes at end-of-message.
(`codex-rs/core/tests/suite/items.rs`)
- Added unit tests for generic tag parser (prefix buffering, non-tag
lines, auto-close). (`codex-rs/core/src/tagged_block_parser.rs`)
- Existing app-server plan item tests in v2.
(`codex-rs/app-server/tests/suite/v2/plan_item.rs`)
## Notes / Behavior
- Plan output no longer appears in standard assistant text in Plan Mode;
it streams via `PlanDelta` and completes as a `TurnItem::Plan`.
- The final plan item content is authoritative and may diverge from
streamed deltas (documented as experimental).
- Reasoning summaries are not filtered; prompt instructs the model not
to include `<proposed_plan>` outside the final plan message.
## Codex Author
`codex fork 019bec2d-b09d-7450-b292-d7bcdddcdbfb`
## Summary
- Tightens Plan Mode to encourage exploration-first behavior and more
back-and-forth alignment.
- Adds a required TL;DR checkpoint before drafting the full plan.
- Clarifies client behavior that can cause premature “Implement this
plan?” prompts.
## What changed
- Require at least one targeted non-mutating exploration pass before the
first user question.
- Insert a TL;DR checkpoint between Phase 2 (intent) and Phase 3
(implementation).
- TL;DR checkpoint guidance:
- Label: “Proposed Plan (TL;DR)”
- Format: 3–5 bullets using `- `
- Options: exactly one option, “Approve”
- `isOther: true`, with explicit guidance that “None of the above” is
the edit path in the current UI.
- Require the final plan to include a TL;DR consistent with the approved
checkpoint.
## Why
- In Plan Mode, any normal assistant message at turn completion is
treated as plan content by the client. This can trigger premature
“Implement this plan?” prompts.
- The TL;DR checkpoint aligns on direction before Codex drafts a long,
decision-complete plan.
## Testing
- Manual: built the local CLI and verified the flow now explores first,
presents a TL;DR checkpoint, and only drafts the full plan after
approval.
---------
Co-authored-by: Nick Baumann <@openai.com>
`requirements.toml` should be able to specify rules which always run.
My intention here was that these rules could only ever be restrictive,
which means the decision can be "prompt" or "forbidden" but never
"allow". A requirement of "you must always allow this command" didn't
make sense to me, but happy to be gaveled otherwise.
Rules already applies the most restrictive decision, so we can safely
merge these with rules found in other config folders.
Previously, `CodexAuth` was defined as follows:
d550fbf41a/codex-rs/core/src/auth.rs (L39-L46)
But if you looked at its constructors, we had creation for
`AuthMode::ApiKey` where `storage` was built using a nonsensical path
(`PathBuf::new()`) and `auth_dot_json` was `None`:
d550fbf41a/codex-rs/core/src/auth.rs (L212-L220)
By comparison, when `AuthMode::ChatGPT` was used, `api_key` was always
`None`:
d550fbf41a/codex-rs/core/src/auth.rs (L665-L671)https://github.com/openai/codex/pull/10012 took things further because
it introduced a new `ChatgptAuthTokens` variant to `AuthMode`, which is
important in when invoking `account/login/start` via the app server, but
most logic _internal_ to the app server should just reason about two
`AuthMode` variants: `ApiKey` and `ChatGPT`.
This PR tries to clean things up as follows:
- `LoginAccountParams` and `AuthMode` in `codex-rs/app-server-protocol/`
both continue to have the `ChatgptAuthTokens` variant, though it is used
exclusively for the on-the-wire messaging.
- `codex-rs/core/src/auth.rs` now has its own `AuthMode` enum, which
only has two variants: `ApiKey` and `ChatGPT`.
- `CodexAuth` has been changed from a struct to an enum. It is a
disjoint union where each variant (`ApiKey`, `ChatGpt`, and
`ChatGptAuthTokens`) have only the associated fields that make sense for
that variant.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/10208).
* #10224
* __->__ #10208
Load requirements from Codex Backend. It only does this for enterprise
customers signed in with ChatGPT.
Todo in follow-up PRs:
* Add to app-server and exec too
* Switch from fail-open to fail-closed on failure
Session renaming:
- `/rename my_session`
- `/rename` without arg and passing an argument in `customViewPrompt`
- AppExitInfo shows resume hint using the session name if set instead of
uuid, defaults to uuid if not set
- Names are stored in `CODEX_HOME/sessions.jsonl`
Session resuming:
- codex resume <name> lookup for `CODEX_HOME/sessions.jsonl` first entry
matching the name and resumes the session
---------
Co-authored-by: jif-oai <jif@openai.com>
## Summary
- add startup tooltip for OpenAI community Discord
- add startup tooltip for Codex community forum
## Testing
- not run (text-only tooltip change)
## Summary
Let's dial in this api contract in a bit more with more robust fallback
behavior when model_instructions_template is false.
Switches to a more explicit template / variables structure, with more
fallbacks.
## Testing
- [x] Adding unit tests
- [x] Tested locally
## Problem
OpenAI employees were sent to the public GitHub issue flow after
`/feedback`, which is the wrong follow-up path internally.
## Mental model
After feedback upload completes, we render a follow-up link/message.
That link should be audience-aware but must not change the upload
pipeline itself.
## Non-goals
- Changing how feedback is captured or uploaded
- Changing external user behavior
## Tradeoffs
We detect employees via the authenticated account email suffix
(`@openai.com`). If the email is unavailable (e.g., API key auth), we
default to the external behavior.
## Architecture
- Introduce `FeedbackAudience` and thread it from `App` -> `ChatWidget`
-> `FeedbackNoteView`
- Gate internal messaging/links on `FeedbackAudience::OpenAiEmployee`
- Internal follow-up link is now `http://go/codex-feedback-internal`
- External GitHub URL remains byte-for-byte identical
## Observability
No new telemetry; this only changes rendered follow-up instructions.
## Tests
- `just fmt`
- `cargo test -p codex-tui --lib`
Add dynamic tools to rollout file for persistence & read from rollout on
resume. Ran a real example and spotted the following in the rollout
file:
```
{"timestamp":"2026-01-29T01:27:57.468Z","type":"session_meta","payload":{"id":"019c075d-3f0b-77e3-894e-c1c159b04b1e","timestamp":"2026-01-29T01:27:57.451Z","...."dynamic_tools":[{"name":"demo_tool","description":"Demo dynamic tool","inputSchema":{"additionalProperties":false,"properties":{"city":{"type":"string"}},"required":["city"],"type":"object"}}],"git":{"commit_hash":"ebc573f15c01b8af158e060cfedd401f043e9dfa","branch":"dev/cc/dynamic-tools","repository_url":"https://github.com/openai/codex.git"}}}
```
## Summary
Adds a simple `/plan` slash command in the TUI that switches the active
collaboration mode to Plan mode. The command is only available when the
`collaboration_modes` feature is enabled.
## Changes
- Add `plan_mask` helper in `codex-rs/tui/src/collaboration_modes.rs`
- Add `SlashCommand::Plan` metadata in
`codex-rs/tui/src/slash_command.rs`
- Implement and hard-gate `/plan` dispatch in
`codex-rs/tui/src/chatwidget.rs`
- Hide `/plan` when collaboration modes are disabled in
`codex-rs/tui/src/bottom_pane/slash_commands.rs`
- Update command popup tests in
`codex-rs/tui/src/bottom_pane/command_popup.rs`
- Add a focused unit test for `/plan` in
`codex-rs/tui/src/chatwidget/tests.rs`
## Behavior notes
- `/plan` is now a no-op if `Feature::CollaborationModes` is disabled.
- When enabled, `/plan` switches directly to Plan mode without opening
the picker.
## Codex author
`codex resume 019c05da-d7c3-7322-ae2c-3ca38d0ef702`
This enables a new use case where `codex app-server` is embedded into a
parent application that will directly own the user's ChatGPT auth
lifecycle, which means it owns the user’s auth tokens and refreshes it
when necessary. The parent application would just want a way to pass in
the auth tokens for codex to use directly.
The idea is that we are introducing a new "auth mode" currently only
exposed via app server: **`chatgptAuthTokens`** which consist of the
`id_token` (stores account metadata) and `access_token` (the bearer
token used directly for backend API calls). These auth tokens are only
stored in-memory. This new mode is in addition to the existing `apiKey`
and `chatgpt` auth modes.
This PR reuses the shape of our existing app-server account APIs as much
as possible:
- Update `account/login/start` with a new `chatgptAuthTokens` variant,
which will allow the client to pass in the tokens and have codex
app-server use them directly. Upon success, the server emits
`account/login/completed` and `account/updated` notifications.
- A new server->client request called
`account/chatgptAuthTokens/refresh` which the server can use whenever
the access token previously passed in has expired and it needs a new one
from the parent application.
I leveraged the core 401 retry loop which typically triggers auth token
refreshes automatically, but made it pluggable:
- **chatgpt** mode refreshes internally, as usual.
- **chatgptAuthTokens** mode calls the client via
`account/chatgptAuthTokens/refresh`, the client responds with updated
tokens, codex updates its in-memory auth, then retries. This RPC has a
10s timeout and handles JSON-RPC errors from the client.
Also some additional things:
- chatgpt logins are blocked while external auth is active (have to log
out first. typically clients will pick one OR the other, not support
both)
- `account/logout` clears external auth in memory
- Ensures that if `forced_chatgpt_workspace_id` is set via the user's
config, we respect it in both:
- `account/login/start` with `chatgptAuthTokens` (returns a JSON-RPC
error back to the client)
- `account/chatgptAuthTokens/refresh` (fails the turn, and on next
request app-server will send another `account/chatgptAuthTokens/refresh`
request to the client).
###### Problem
Users get generic 429s with no guidance when a model is at capacity.
###### Solution
Detect model-cap headers, surface a clear “try a different model”
message, and keep behavior non‑intrusive (no auto‑switch).
###### Scope
CLI/TUI only; protocol + error mapping updated to carry model‑cap info.
###### Tests
- just fmt
- cargo test -p codex-tui
- cargo test -p codex-core --lib
shell_snapshot::tests::try_new_creates_and_deletes_snapshot_file --
--nocapture (ran in isolated env)
- validate local build with backend
<img width="719" height="845" alt="image"
src="https://github.com/user-attachments/assets/1470b33d-0974-4b1f-b8e6-d11f892f4b54"
/>
## Summary
- add `codex features enable <feature>` and `codex features disable
<feature>`
- persist feature flag changes to `config.toml` (respecting profile)
- print the under-development feature warning when enabling prerelease
features
- keep `features list` behavior unchanged and add unit/integration tests
## Testing
- cargo test -p codex-cli
An experimental flow for env var skill dependencies. Skills can now
declare required env vars in SKILL.md; if missing, the CLI prompts the
user to get the value, and Core will store it in memory (eventually to a
local persistent store)
<img width="790" height="169" alt="image"
src="https://github.com/user-attachments/assets/cd928918-9403-43cb-a7e7-b8d59bcccd9a"
/>
On the back of:
https://github.com/openai/codex/pull/10138
Let's ensure that every folder with a `package.json` is listed in
`pnpm-workspace.yaml` (not sure why `docs` was in there...) and that we
are using `pnpm` over `npm` consistently (which is why this PR deletes
`codex-cli/package-lock.json`).
```
just log -h
if [ "${1:-}" = "--" ]; then shift; fi; cargo run -p codex-state --bin logs_client -- "$@"
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
Running `target/debug/logs_client -h`
Tail Codex logs from state.sqlite with simple filters
Usage: logs_client [OPTIONS]
Options:
--codex-home <CODEX_HOME> Path to CODEX_HOME. Defaults to $CODEX_HOME or ~/.codex [env: CODEX_HOME=]
--db <DB> Direct path to the SQLite database. Overrides --codex-home
--level <LEVEL> Log level to match exactly (case-insensitive)
--from <RFC3339|UNIX> Start timestamp (RFC3339 or unix seconds)
--to <RFC3339|UNIX> End timestamp (RFC3339 or unix seconds)
--module <MODULE> Substring match on module_path
--file <FILE> Substring match on file path
--backfill <BACKFILL> Number of matching rows to show before tailing [default: 200]
--poll-ms <POLL_MS> Poll interval in milliseconds [default: 500]
-h, --help Print help
```
Currently, our `npm publish` logic is failing.
There were a number of things that were merged recently that seemed to
contribute to this situation, though I think we have fixed most of them,
but this one stands out:
https://github.com/openai/codex/pull/10115
As best I can tell, we tried to fix the pnpm version to a specific hash,
but we did not do it consistently (though `shell-tool-mcp/package.json`
had it specified twice...), so for this PR, I ran:
```
$ git ls-files | grep package.json
codex-cli/package.json
codex-rs/responses-api-proxy/npm/package.json
package.json
sdk/typescript/package.json
shell-tool-mcp/package.json
```
and ensured that all of them now have this line:
```json
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264"
```
I also went and deleted all of the `corepack` stuff that was added by
https://github.com/openai/codex/pull/10115.
If someone can explain why we need it and verify it does not break `npm
publish`, then we can bring it back.
## Summary
- guard onboarding key handling to ignore KeyEventKind::Release
- handle key events at the onboarding screen boundary to avoid
double-triggering widgets
## Related
- https://github.com/ratatui/ratatui/issues/347
## Testing
- cd codex-rs && just fmt
- cd codex-rs && cargo test -p codex-tui
- [x] Support `/apps` slash command to browse the apps in tui.
- [x] Support inserting apps to prompt using `$`.
- [x] Lots of simplification/renaming from connectors to apps.
## Summary
Add personality instructions so we can let users try it out, in tandem
with making it an experimental feature
## Testing
- [x] Tested locally
## Summary
Sets up an explicit Feature flag for `/personality`, so users can now
opt in to it via `/experimental`. #10114 also updates the config
## Testing
- [x] Tested locally
we can't use runfiles directory on Windows due to path lengths, so swap
to manifest strategy. Parsing the manifest is a bit complex and the
format is changing in Bazel upstream, so pull in the official Rust
library (via a small hack to make it importable...) and cleanup all the
associated logic to work cleanly in both bazel and cargo without extra
confusion
This updates the CI workflows for shell-tool-mcp to use the pnpm version
from package.json and print it in the build for verification.
I have read the CLA Document and I hereby sign the CLA
This PR adds a new `tui.notifications_method` config option that accepts
values of "auto", "osc9" and "bel". It defaults to "auto", which
attempts to auto-detect whether the terminal supports OSC 9 escape
sequences and falls back to BEL if not.
The PR also removes the inconsistent handling of notifications on
Windows when WSL was used.
# 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.
deprecate all old web search flags and aliases, including:
- `[features].web_search_request` and `[features].web_search_cached`
- `[tools].web_search`
- `[features].web_search`
slightly rework `legacy_usages` to enable pointing to non-features from
deprecated features; we need to point to `web_search` (not under
`[features]`) from things like `[features].web_search_cached` and
`[features].web_search_request`.
Added integration tests to confirm deprecation notice is shown on
explicit enablement and disablement of deprecated flags.
I needed to upgrade bazel one to get gnullvm artifacts and then noticed
monorepo had drifted forward. They should move in lockstep. Also 1.93
already shipped so we can try that instead.
## Summary
Overhaul the ask‑user‑questions TUI to support “Other/None” answers,
better notes handling, improved option selection
UX, and a safer submission flow with confirmation for unanswered
questions.
Multiple choice (number keys for quick selection, up/down or jk for
cycling through options):
<img width="856" height="169" alt="Screenshot 2026-01-27 at 7 22 29 PM"
src="https://github.com/user-attachments/assets/cabd1b0e-25e0-4859-bd8f-9941192ca274"
/>
Tab to add notes:
<img width="856" height="197" alt="Screenshot 2026-01-27 at 7 22 45 PM"
src="https://github.com/user-attachments/assets/a807db5e-e966-412c-af91-6edc60062f35"
/>
Freeform (also note enter tooltip is highlighted on last question to
indicate questions UI will be exited upon submission):
<img width="854" height="112" alt="Screenshot 2026-01-27 at 7 23 13 PM"
src="https://github.com/user-attachments/assets/2e7b88bf-062b-4b9f-a9da-c9d8c8a59643"
/>
Confirmation dialogue (submitting with unanswered questions):
<img width="854" height="126" alt="Screenshot 2026-01-27 at 7 23 29 PM"
src="https://github.com/user-attachments/assets/93965c8f-54ac-45bc-a660-9625bcd101f8"
/>
## Key Changes
- **Options UI refresh**
- Render options as numbered entries; allow number keys to select &
submit.
- Remove “Option X/Y” header and allow the question UI height to expand
naturally.
- Keep spacing between question, options, and notes even when notes are
visible.
- Hide the title line and render the question prompt in cyan **only when
uncommitted**.
- **“Other / None of the above” support**
- Wire `isOther` to add “None of the above”.
- Add guidance text: “Optionally, add details in notes (tab).”
- **Notes composer UX**
- Remove “Notes” heading; place composer directly under the selected
option.
- Preserve pending paste placeholders across question navigation and
after submission.
- Ctrl+C clears notes **only when the notes composer has focus**.
- Ctrl+C now triggers an immediate redraw so the clear is visible.
- **Committed vs uncommitted state**
- Introduce a unified `answer_committed` flag per question.
- Editing notes (including adding text or pastes) marks the answer
uncommitted.
- Changing the option highlight (j/k, up/down) marks the answer
uncommitted.
- Clearing options (Backspace/Delete) also clears pending notes.
- Question prompt turns cyan only when the answer is uncommitted.
- **Submission safety & confirmation**
- Only submit notes/freeform text once explicitly committed.
- Last-question submit with unanswered questions shows a confirmation
dialog.
- Confirmation options:
1. **Proceed** (default)
2. **Go back**
- Description reflects count: “Submit with N unanswered question(s).”
- Esc/Backspace in confirmation returns to first unanswered question.
- Ctrl+C in confirmation interrupts and exits the overlay.
- **Footer hints**
- Cyan highlight restored for “enter to submit answer” / “enter to
submit all”.
## Codex author
`codex fork 019c00ed-323a-7000-bdb5-9f9c5a635bd9`
Add a `.sqlite` database to be used to store rollout metatdata (and
later logs)
This PR is phase 1:
* Add the database and the required infrastructure
* Add a backfill of the database
* Persist the newly created rollout both in files and in the DB
* When we need to get metadata or a rollout, consider the `JSONL` as the
source of truth but compare the results with the DB and show any errors
**Summary**
- Up/Down input history now restores image attachments and text elements
for local entries.
- Composer history stores rich local entries (text + text elements +
local image paths) while persistent history remains text-only.
- Added tests to verify history recall rehydrates image placeholders and
attachments in both `tui` and `tui2`.
**Changes**
- `tui/src/bottom_pane/chat_composer_history.rs`: store `HistoryEntry`
(text + elements + image paths) for local history; adapt navigation +
tests.
- `tui2/src/bottom_pane/chat_composer_history.rs`: same as above.
- `tui/src/bottom_pane/chat_composer.rs`: record rich history entries
and restore them on Up/Down; update Ctrl+C history and tests.
- `tui2/src/bottom_pane/chat_composer.rs`: same as above.
web_search can now be updated per-turn, for things like changes to
sandbox policy.
`SandboxPolicy::DangerFullAccess` now sets web_search to `live`, and the
default is still `cached`.
Added integration tests.
Fixes#9361
## Context
Split out from #9059 per review:
https://github.com/openai/codex/pull/9059#issuecomment-3757859033
## Summary
The shortcut overlay renders different paste-image bindings on WSL
(Ctrl+Alt+V) vs non-WSL (Ctrl+V), which makes snapshot tests
non-deterministic when run under WSL.
## Changes
- Gate WSL detection behind `cfg(not(test))` so snapshot tests are
deterministic across environments.
- Add a focused unit test that still asserts the WSL-specific
paste-image binding.
## Testing
- `just fmt`
- `just fix -p codex-tui`
- `just fix -p codex-tui2`
- `cargo test -p codex-tui`
- `cargo test -p codex-tui2`
## Summary
Polishes the `request_user_input` TUI overlay
Question 1 (unanswered)
<img width="853" height="167" alt="Screenshot 2026-01-27 at 1 30 09 PM"
src="https://github.com/user-attachments/assets/3c305644-449e-4e8d-a47b-d689ebd8702c"
/>
Tab to add notes
<img width="856" height="198" alt="Screenshot 2026-01-27 at 1 30 25 PM"
src="https://github.com/user-attachments/assets/0d2801b0-df0c-49ae-85af-e6d56fc2c67c"
/>
Question 2 (unanswered)
<img width="854" height="168" alt="Screenshot 2026-01-27 at 1 30 55 PM"
src="https://github.com/user-attachments/assets/b3723062-51f9-49c9-a9ab-bb1b32964542"
/>
Ctrl+p or h to go back to q1 (answered)
<img width="853" height="195" alt="Screenshot 2026-01-27 at 1 31 27 PM"
src="https://github.com/user-attachments/assets/c602f183-1c25-4c51-8f9f-e565cb6bd637"
/>
Unanswered freeform
<img width="856" height="126" alt="Screenshot 2026-01-27 at 1 31 42 PM"
src="https://github.com/user-attachments/assets/7e3d9d8b-820b-4b9a-9ef2-4699eed484c5"
/>
## Key changes
- Footer tips wrap at tip boundaries (no truncation mid‑tip); footer
height scales to wrapped tips.
- Keep tooltip text as Esc: interrupt in all states.
- Make the full Tab: add notes tip cyan/bold when applicable; hide notes
UI by default.
- Notes toggling/backspace:
- Tab opens notes when an option is selected; Tab again clears notes and
hides the notes UI.
- Backspace in options clears the current selection.
- Backspace in empty notes closes notes and returns to options.
- Selection/answering behavior:
- Option questions highlight a default option but are not answered until
Enter.
- Enter no longer auto‑selects when there’s no selection (prevents
accidental answers).
- Notes submission can commit the selected option when present.
- Freeform questions require Enter with non‑empty text to mark answered;
drafts are not submitted unless committed.
- Unanswered cues:
- Skipped option questions count as unanswered.
- Unanswered question titles are highlighted for visibility.
- Typing/navigation in options:
- Typing no longer opens notes; notes are Tab‑only.
- j/k move option selection; h/l switch questions (Ctrl+n/Ctrl+p still
work).
## Tests
- Added unit coverage for:
- tip‑level wrapping
- focus reset when switching questions with existing drafts
- backspace clearing selection
- backspace closing empty notes
- typing in options does not open notes
- freeform draft submission gating
- h/l question navigation in options
- Updated snapshots, including narrow footer wrap.
## Why
These changes make the ask‑user‑question overlay:
- safer (no silent auto‑selection or accidental freeform submission),
- clearer (tips wrap cleanly and unanswered states stand out),
- more ergonomic (Tab explicitly controls notes; backspace acts like
undo/close).
## Codex author
`codex fork 019bfc3c-2c42-7982-9119-fee8b9315c2f`
---------
Co-authored-by: Ahmed Ibrahim <aibrahim@openai.com>
### Motivation
- Improve UX by making it explicit that `VISUAL`/`EDITOR` must be set
before launching Codex, not during a running session.
### Description
- Update the external editor error text in `codex-rs/tui/src/app.rs` to:
`"Cannot open external editor: set $VISUAL or $EDITOR before starting
Codex."` and run `just fmt` to apply formatting.
### Testing
- Ran `just fmt` successfully; attempted `cargo test -p codex-tui` but
it failed due to network errors when fetching git dependencies (tests
did not complete).
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6972c2c984948329b1a37d5c5839aff3)
### Motivation
- The local OAuth callback server returned a generic "Invalid OAuth
callback" on failures even when the query contained an
`error_description`, making it hard to debug OAuth failures.
### Description
- Update `codex-rs/rmcp-client/src/perform_oauth_login.rs` to surface
`error_description` values from the callback query in the HTTP response.
- Introduce a `CallbackOutcome` enum and change `parse_oauth_callback`
to return it, parsing `code`, `state`, and `error_description` from the
query string.
- Change `spawn_callback_server` to match on `CallbackOutcome` and
return `OAuth error: <description>` with a 400 status when
`error_description` is present, while preserving the existing success
and invalid flows.
- Use inline formatting for the error response string.
### Testing
- Ran `just fmt` in the `codex-rs` workspace to format changes
successfully.
- Ran `cargo test -p codex-rmcp-client` and all tests passed.
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6971aadc68d0832e93159efea8cd48a9)
## What?
- Render an MCP image output cell whenever a decodable image block
exists in `CallToolResult.content` (including text-before-image or
malformed image before valid image).
## Why?
- Tool results that include caption text before the image currently drop
the image output cell.
- A malformed image block can also suppress later valid image output.
## How?
- Iterate `content` and return the first successfully decoded image
instead of only checking the first block.
- Add unit tests that cover text-before-image ordering and
invalid-image-before-valid.
## Before
```rust
let image = match result {
Ok(mcp_types::CallToolResult { content, .. }) => {
if let Some(mcp_types::ContentBlock::ImageContent(image)) = content.first() {
// decode image (fails -> None)
} else {
None
}
}
_ => None,
}?;
```
## After
```rust
let image = result
.as_ref()
.ok()?
.content
.iter()
.find_map(decode_mcp_image)?;
```
## Risk / Impact
- Low: only affects image cell creation for MCP tool results; no change
for non-image outputs.
## Tests
- [x] `just fmt`
- [x] `cargo test -p codex-tui`
- [x] Rerun after branch update (2026-01-27): `just fmt`, `cargo test -p
codex-tui`
Manual testing
# Manual testing: MCP image tool result rendering (Codex TUI)
# Build the rmcp stdio test server binary:
cd codex-rs
cargo build -p codex-rmcp-client --bin test_stdio_server
# Register the server as an MCP server (absolute path to the built binary):
codex mcp add mcpimg -- /Users/joshka/code/codex-pr-review/codex-rs/target/debug/test_stdio_server
# Then in Codex TUI, ask it to call:
- mcpimg.image_scenario({"scenario":"image_only"})
- mcpimg.image_scenario({"scenario":"text_then_image","caption":"Here is the image:"})
- mcpimg.image_scenario({"scenario":"invalid_base64_then_image"})
- mcpimg.image_scenario({"scenario":"invalid_image_bytes_then_image"})
- mcpimg.image_scenario({"scenario":"multiple_valid_images"})
- mcpimg.image_scenario({"scenario":"image_then_text","caption":"Here is the image:"})
- mcpimg.image_scenario({"scenario":"text_only","caption":"Here is the image:"})
# Expected:
# - You should see an extra history cell: "tool result (image output)" when the
# tool result contains at least one decodable image block (even if earlier
# blocks are text or invalid images).
Fixes#9814
---------
Co-authored-by: Josh McKinney <joshka@openai.com>
Auto-enable live `web_search` tool when sandbox policy is
`DangerFullAccess`.
Explicitly setting `web_search` (canonical setting), or enabling
`web_search_cached` or `web_search_request` still takes precedence over
this sandbox-policy-driven enablement.
Constructors with long param lists can be hard to reason about when a
number of the args are `None`, in practice. Introducing a struct to use
as the args type helps make things more self-documenting.
Updates the `CodexOptions` passed to the `Codex()` constructor in the
SDK to support a `config` property that is a map of configuration data
that will be transformed into `--config` flags passed to the invocation
of `codex`.
Therefore, something like this:
```typescript
const codex = new Codex({
config: {
show_raw_agent_reasoning: true,
sandbox_workspace_write: { network_access: true },
},
});
```
would result in the following args being added to the invocation of
`codex`:
```shell
--config show_raw_agent_reasoning=true --config sandbox_workspace_write.network_access=true
```
### Overview
Currently calling `thread/resume` will always bump the thread's
`updated_at` timestamp. This PR makes it the `updated_at` timestamp
changes only if a turn is triggered.
### Additonal context
What we typically do on resuming a thread is **always** writing “initial
context” to the rollout file immediately. This initial context includes:
- Developer instructions derived from sandbox/approval policy + cwd
- Optional developer instructions (if provided)
- Optional collaboration-mode instructions
- Optional user instructions (if provided)
- Environment context (cwd, shell, etc.)
This PR defers writing the “initial context” to the rollout file until
the first `turn/start`, so we don't inadvertently bump the thread's
`updated_at` timestamp until a turn is actually triggered.
This works even though both `thread/resume` and `turn/start` accept
overrides (such as `model`, `cwd`, etc.) because the initial context is
seeded from the effective `TurnContext` in memory, computed at
`turn/start` time, after both sets of overrides have been applied.
**NOTE**: This is a very short-lived solution until we introduce sqlite.
Then we can remove this.
### Summary
- Adds an optional SOCKS5 listener via `rama-socks5`
- SOCKS5 is disabled by default and gated by config
- Reuses existing policy enforcement and blocked-request recording
- Blocks SOCKS5 in limited mode to prevent method-policy bypass
- Applies bind clamping to the SOCKS5 listener
### Config
New/used fields under `network_proxy`:
- `enable_socks5`
- `socks_url`
- `enable_socks5_udp`
### Scope
- Changes limited to `codex-rs/network-proxy` (+ `codex-rs/Cargo.lock`)
### Testing
```bash
cd codex-rs
just fmt
cargo test -p codex-network-proxy --offline
## Summary
Refines the bottom footer layout to keep `% context left` right-aligned
while making the left side degrade cleanly
## Behavior with empty textarea
Full width:
<img width="607" height="62" alt="Screenshot 2026-01-26 at 2 59 59 PM"
src="https://github.com/user-attachments/assets/854f33b7-d714-40be-8840-a52eb3bda442"
/>
Less:
<img width="412" height="66" alt="Screenshot 2026-01-26 at 2 59 48 PM"
src="https://github.com/user-attachments/assets/9c501788-c3a2-4b34-8f0b-8ec4395b44fe"
/>
Min width:
<img width="218" height="77" alt="Screenshot 2026-01-26 at 2 59 33 PM"
src="https://github.com/user-attachments/assets/0bed2385-bdbf-4254-8ae4-ab3452243628"
/>
## Behavior with message in textarea and agent running (steer enabled)
Full width:
<img width="753" height="63" alt="Screenshot 2026-01-26 at 4 33 54 PM"
src="https://github.com/user-attachments/assets/1856b352-914a-44cf-813d-1cb50c7f183b"
/>
Less:
<img width="353" height="61" alt="Screenshot 2026-01-26 at 4 30 12 PM"
src="https://github.com/user-attachments/assets/d951c4d5-f3e7-4116-8fe1-6a6c712b3d48"
/>
Less:
<img width="304" height="64" alt="Screenshot 2026-01-26 at 4 30 51 PM"
src="https://github.com/user-attachments/assets/1433e994-5cbc-4e20-a98a-79eee13c8699"
/>
Less:
<img width="235" height="61" alt="Screenshot 2026-01-26 at 4 30 56 PM"
src="https://github.com/user-attachments/assets/e216c3c6-84cd-40fc-ae4d-83bf28947f0e"
/>
Less:
<img width="165" height="59" alt="Screenshot 2026-01-26 at 4 31 08 PM"
src="https://github.com/user-attachments/assets/027de5de-7185-47ce-b1cc-5363ea33d9b1"
/>
## Notes / Edge Cases
- In steer mode while typing, the queue hint no longer replaces the mode
label; it renders as `tab to queue message · {Mode}`.
- Collapse priorities differ by state:
- With the queue hint active, `% context left` is hidden before
shortening or dropping the queue hint.
- In the empty + non-running state, `? for shortcuts` is dropped first,
and `% context left` is only shown if `(shift+tab to
cycle)` can also fit.
- Transient instructional states (`?` overlay, Esc hint, Ctrl+C/D
reminders, and flash/override hints) intentionally suppress the
mode label (and context) to focus the next action.
## Implementation Notes
- Renamed the base footer modes to make the state explicit:
`ComposerEmpty` and `ComposerHasDraft`, and compute the base mode
directly from emptiness.
- Unified collapse behavior in `single_line_footer_layout` for both base
modes, with:
- Queue-hint behavior that prefers keeping the queue hint over context.
- A cycle-hint guard that prevents context from reappearing after
`(shift+tab to cycle)` is dropped.
- Kept rendering responsibilities explicit:
- `single_line_footer_layout` decides what fits.
- `render_footer_line` renders a chosen line.
- `render_footer_from_props` renders the canonical mode-to-text mapping.
- Expanded snapshot coverage:
- Added `footer_collapse_snapshots` in `chat_composer.rs` to lock the
distinct collapse states across widths.
- Consolidated the width-aware snapshot helper usage (e.g.,
`snapshot_composer_state_with_width`,
`snapshot_footer_with_mode_indicator`).
Updates pnpm to 10.28.2. to address security issues in prior versions of
pnpm that can allow deps to execute lifecycle scripts against policy.
I have read the CLA Document and I hereby sign the CLA
# 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.
# 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.
Fix resume --last prompt parsing by dropping the clap conflict on the
codex resume subcommand so a positional prompt is accepted when --last
is set. This aligns interactive resume behavior with exec-mode logic and
avoids the “--last cannot be used with SESSION_ID” error.
This addresses #6717
# 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.
# 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.
### Summary
- Parse all `web_search` tool actions (`search`, `find_in_page`,
`open_page`).
- Previously we only parsed + displayed `search`, which made the TUI
appear to pause when the other actions were being used.
- Show in progress `web_search` calls as `Searching the web`
- Previously we only showed completed tool calls
<img width="308" height="149" alt="image"
src="https://github.com/user-attachments/assets/90a4e8ff-b06a-48ff-a282-b57b31121845"
/>
### Tests
Added + updated tests, tested locally
### Follow ups
Update VSCode extension to display these as well
Reuse the shared chat composer for notes and freeform answers in
request_user_input.
- Build the overlay composer with ChatComposerConfig::plain_text.
- Wire paste-burst flushing + menu surface sizing through the bottom
pane.
## Context
Previous work in https://github.com/openai/codex/pull/9560 only rejected
`request_user_input` in Execute and Custom modes. Since then, additional
modes
(e.g., Code) were added, so the guard should be mode-agnostic.
## What changed
- Switch the handler to an allowlist: only Plan and PairProgramming are
allowed
- Return the same error for any other mode (including Code)
- Add a Code-mode rejection test alongside the existing Execute/Custom
tests
## Why
This prevents `request_user_input` from being used in modes where it is
not
intended, even as new modes are introduced.
Fixes a TUI freeze caused by awaiting `mpsc::Sender::send()` that blocks
the tokio thread, stopping the consumption runtime and creating a
deadlock. This could happen if the server was producing enough chunks to
fill the `mpsc` fast enough. To solve this we try on insert using a
`try_send()` (not requiring an `await`) and delegate to a tokio task if
this does not work
This is a temporary solution as it can contain races for delta elements
and a stronger design should come here
Fixes#9822
### Summary
Make the Homebrew upgrade command explicit by using `brew upgrade --cask
codex`.
### Motivation
During the Codex self-update, Homebrew can emit an avoidable warning
because the
name `codex` resolves to a cask:
```
Warning: Formula codex was renamed to homebrew/cask/codex.
````
While the upgrade succeeds, this relies on implicit name resolution and
produces
unnecessary output during the update flow.
### Why `--cask`
* Eliminates warning/noise for users
* Explicitly matches how Codex is distributed via Homebrew
* Avoids reliance on name resolution behavior
* Makes the command more robust if a `codex` formula is ever introduced
### Context
This restores the `--cask` flag that was removed in #6238 after being
considered
“not necessary” during review:
[https://github.com/openai/codex/pull/6238#discussion_r2505947880](https://github.com/openai/codex/pull/6238#discussion_r2505947880).
Co-authored-by: Eric Traut <etraut@openai.com>
Bumps [globset](https://github.com/BurntSushi/ripgrep) from 0.4.16 to
0.4.18.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0b0e013f5a"><code>0b0e013</code></a>
globset-0.4.18</li>
<li><a
href="cac9870a02"><code>cac9870</code></a>
doc: update date in man page template</li>
<li><a
href="24e88dc15b"><code>24e88dc</code></a>
ignore/types: add <code>ssa</code> type</li>
<li><a
href="5748f81bb1"><code>5748f81</code></a>
printer: use <code>doc_cfg</code> instead of
<code>doc_auto_cfg</code></li>
<li><a
href="d47663b1b4"><code>d47663b</code></a>
searcher: fix regression with <code>--line-buffered</code> flag</li>
<li><a
href="38d630261a"><code>38d6302</code></a>
printer: add Cursor hyperlink alias</li>
<li><a
href="b3dc4b0998"><code>b3dc4b0</code></a>
globset: improve debug log</li>
<li><a
href="ca2e34f37c"><code>ca2e34f</code></a>
grep-0.4.0</li>
<li><a
href="a0d61a063f"><code>a0d61a0</code></a>
grep-printer-0.3.0</li>
<li><a
href="c22fc0f13c"><code>c22fc0f</code></a>
deps: bump to grep-searcher 0.1.15</li>
<li>Additional commits viewable in <a
href="https://github.com/BurntSushi/ripgrep/compare/globset-0.4.16...globset-0.4.18">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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@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>
Bumps [tokio-test](https://github.com/tokio-rs/tokio) from 0.4.4 to
0.4.5.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="41d1877689"><code>41d1877</code></a>
chore: prepare tokio-test 0.4.5 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7831">#7831</a>)</li>
<li><a
href="60b083b630"><code>60b083b</code></a>
chore: prepare tokio-stream 0.1.18 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7830">#7830</a>)</li>
<li><a
href="9cc02cc88d"><code>9cc02cc</code></a>
chore: prepare tokio-util 0.7.18 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7829">#7829</a>)</li>
<li><a
href="d2799d791b"><code>d2799d7</code></a>
task: improve the docs of <code>Builder::spawn_local</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7828">#7828</a>)</li>
<li><a
href="4d4870f291"><code>4d4870f</code></a>
task: doc that task drops before JoinHandle completion (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7825">#7825</a>)</li>
<li><a
href="fdb150901a"><code>fdb1509</code></a>
fs: check for io-uring opcode support (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7815">#7815</a>)</li>
<li><a
href="426a562780"><code>426a562</code></a>
rt: remove <code>allow(dead_code)</code> after <code>JoinSet</code>
stabilization (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7826">#7826</a>)</li>
<li><a
href="e3b89bbefa"><code>e3b89bb</code></a>
chore: prepare Tokio v1.49.0 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7824">#7824</a>)</li>
<li><a
href="4f577b84e9"><code>4f577b8</code></a>
Merge 'tokio-1.47.3' into 'master'</li>
<li><a
href="f320197693"><code>f320197</code></a>
chore: prepare Tokio v1.47.3 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7823">#7823</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-test-0.4.4...tokio-test-0.4.5">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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@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>
Currently `apply_patch` will fail on Windows if the file contents happen
to have a multi-byte character at the point where the `preview` function
truncates.
I've used the existing `take_bytes_at_char_boundary` helper and added a
regression test (that fails without the fix).
This is related to #4013 but doesn't fix it.
# 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.
### Motivation
- Allow MCP OAuth flows to request scopes defined in `config.toml`
instead of requiring users to always pass `--scopes` on the CLI.
CLI/remote parameters should still override config values.
### Description
- Add optional `scopes: Option<Vec<String>>` to `McpServerConfig` and
`RawMcpServerConfig`, and propagate it through deserialization and the
built config types.
- Serialize `scopes` into the MCP server TOML via
`serialize_mcp_server_table` in `core/src/config/edit.rs` and include
`scopes` in the generated config schema (`core/config.schema.json`).
- CLI: update `codex-rs/cli/src/mcp_cmd.rs` `run_login` to fall back to
`server.scopes` when the `--scopes` flag is empty, with explicit CLI
scopes still taking precedence.
- App server: update
`codex-rs/app-server/src/codex_message_processor.rs`
`mcp_server_oauth_login` to use `params.scopes.or_else(||
server.scopes.clone())` so the RPC path also respects configured scopes.
- Update many test fixtures to initialize the new `scopes` field (set to
`None`) so test code builds with the new struct field.
### Testing
- Ran config tooling and formatters: `just write-config-schema`
(succeeded), `just fmt` (succeeded), and `just fix -p codex-core`, `just
fix -p codex-cli`, `just fix -p codex-app-server` (succeeded where
applicable).
- Ran unit tests for the CLI: `cargo test -p codex-cli` (passed).
- Ran unit tests for core: `cargo test -p codex-core` (ran; many tests
passed but several failed, including model refresh/403-related tests,
shell snapshot/timeouts, and several `unified_exec` expectations).
- Ran app-server tests: `cargo test -p codex-app-server` (ran; many
integration-suite tests failed due to mocked/remote HTTP 401/403
responses and wiremock expectations).
If you want, I can split the tests into smaller focused runs or help
debug the failing integration tests (they appear to be unrelated to the
config change and stem from external HTTP/mocking behaviors encountered
during the test runs).
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_69718f505914832ea1f334b3ba064553)
We've recently standardized a [feature maturity
model](https://developers.openai.com/codex/feature-maturity) that we're
using in our docs and support forums to communicate expectations to
users. This PR updates the internal stage names and descriptions to
match.
This change involves a simple internal rename and updates to a few
user-visible strings. No functional change.
## Summary
- Adds a new `thread/unarchive` RPC to move archived thread rollouts
back into the active `sessions/` tree.
## What changed
- **Protocol**
- Adds `thread/unarchive` request/response types and wiring.
- **Server**
- Implements `thread_unarchive` in the app server.
- Validates the archived rollout path and thread ID.
- Restores the rollout to `sessions/YYYY/MM/DD/...` based on the rollout
filename timestamp.
- **Core**
- Adds `find_archived_thread_path_by_id_str` helper for archived
rollouts.
- **Docs**
- Documents the new RPC and usage example.
- **Tests**
- Adds an end-to-end server test that:
1) starts a thread,
2) archives it,
3) unarchives it,
4) asserts the file is restored to `sessions/`.
## How to use
```json
{ "method": "thread/unarchive", "id": 24, "params": { "threadId": "<thread-id>" } }
```
## Author Codex Session
`codex resume 019bf158-54b6-7960-a696-9d85df7e1bc1` (soon I'll make this
kind of session UUID forkable by anyone with the right
`session_object_storage_url` line in their config, but for now just
pasting it here for my reference)
# 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.
Reproduce with a prompt like this with collab enabled:
```
Examine the code at <some subdirectory with a deeply nested project>. Find the most urgent issue to resolve and describe it to me.
```
Existing behavior causes the top-level agent to busy wait on subagents.
### Summary
Add `isOther` to question object from request_user_input tool input and
remove `other` option from the tool prompt to better handle tool input.
I've seen this test fail with:
```
- Mock #1.
Expected range of matching incoming requests: == 2
Number of matched incoming requests: 1
```
This is because we pop the wrong task_complete events and then the test
exits. I think this is because the MCP events are now buffered after
https://github.com/openai/codex/pull/8874.
So:
1. clear the buffer before we do any user message sending
2. additionally listen for task start before task complete
3. use the ID from task start to find the correct task complete event.
Sessions' `updated_at` times are truncated to seconds, with the UUID
session ID used to break ties. If the two test sessions are created in
the same second, AND the session B UUID < session A UUID, the test
fails.
Fix this by mutating the session mtimes, from which we derive the
updated_at time, to ensure session B is updated_at later than session A.
## Summary
Add dynamic tool injection to thread startup in API v2, wire dynamic
tool calls through the app server to clients, and plumb responses back
into the model tool pipeline.
### Flow (high level)
- Thread start injects `dynamic_tools` into the model tool list for that
thread (validation is done here).
- When the model emits a tool call for one of those names, core raises a
`DynamicToolCallRequest` event.
- The app server forwards it to the client as `item/tool/call`, waits
for the client’s response, then submits a `DynamicToolResponse` back to
core.
- Core turns that into a `function_call_output` in the next model
request so the model can continue.
### What changed
- Added dynamic tool specs to v2 thread start params and protocol types;
introduced `item/tool/call` (request/response) for dynamic tool
execution.
- Core now registers dynamic tool specs at request time and routes those
calls via a new dynamic tool handler.
- App server validates tool names/schemas, forwards dynamic tool call
requests to clients, and publishes tool outputs back into the session.
- Integration tests
## Summary
Move `model_instructions_template` config to the experimental slug while
we iterate on this feature
## Testing
- [x] Tested locally, unit tests still pass
## Summary
Adds /personality selector in the TUI, which leverages the new core
interface in #9644
Notes:
- We are doing some of our own state management for model_info loading
here, but not sure if that's ideal. open to opinions on simpler
approach, but would like to avoid blocking on a larger refactor
- Right now, the `/personality` selector just hides when the model
doesn't support it. we can update this behavior down the line
## Testing
- [x] Tested locally
- [x] Added snapshot tests
# 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.
# 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.
## Summary
This PR makes a minor formatting adjustment to a `println!` message by
removing an extra empty line and explicitly using `\n` for clarity.
## Changes
- Adjusted console output formatting for the success message.
- No functional or behavioral changes.
Keep an unmasked base collaboration mode and apply the active mask on
demand. Simplify the TUI mask helpers and update tests/docs to match the
mask contract.
# Summary
- Fix resume/fork config rebuild so cwd changes inside the TUI produce a
fully rebuilt Config (trust/approval/sandbox) instead of mutating only
the cwd.
- Preserve `--add-dir` behavior across resume/fork by normalizing
relative roots to absolute paths once (based on the original cwd).
- Prefer latest `TurnContext.cwd` for resume/fork prompts but fall back
to `SessionMeta.cwd` if the latest cwd no longer exists.
- Align resume/fork selection handling and ensure UI config matches the
resumed thread config.
- Fix Windows test TOML path escaping in trust-level test.
# Details
- Rebuild Config via `ConfigBuilder` when resuming into a different cwd;
carry forward runtime approval/sandbox overrides.
- Add `normalize_harness_overrides_for_cwd` to resolve relative
`additional_writable_roots` against the initial cwd before reuse.
- Guard `read_session_cwd` with filesystem existence check for the
latest `TurnContext.cwd`.
- Update naming/flow around cwd comparison and prompt selection.
<img width="603" height="150" alt="Screenshot 2026-01-23 at 5 42 13 PM"
src="https://github.com/user-attachments/assets/d1897386-bb28-4e8a-98cf-187fdebbecb0"
/>
And proof the model understands the new cwd:
<img width="828" height="353" alt="Screenshot 2026-01-22 at 5 36 45 PM"
src="https://github.com/user-attachments/assets/12aed8ca-dec3-4b64-8dae-c6b8cff78387"
/>
### Motivation
- The large ASCII welcome animation can push onboarding content below
the fold on default-height terminals, making the CLI appear
unresponsive; raising the breakpoint prevents that.
- The existing test measured an arbitrary row count rather than
asserting the welcome line position relative to the animation frame,
which made the intent unclear.
### Description
- Increase `MIN_ANIMATION_HEIGHT` from `20` to `37` in
`codex-rs/tui/src/onboarding/welcome.rs` so the animation is skipped
unless there is enough vertical space.
- Replace the brittle measurement logic in the welcome render test with
a `row_containing` helper and assert the welcome row equals the frame
height plus the spacer line (`frame_lines + 1`).
- Add a regression test
`welcome_skips_animation_below_height_breakpoint` that verifies the
animation is not rendered when the viewport height is one row below the
breakpoint.
### Testing
- Ran formatting with `~/.cargo/bin/just fmt` which completed
successfully.
- Ran unit tests for the crate with `cargo test -p codex-tui --lib` and
they passed (unit test suite succeeded).
- Ran `cargo test -p codex-tui` which reported a failing integration
test in this environment because the test cannot locate the `codex`
binary, so full crate tests are blocked here (environment limitation).
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6973b0a710d4832c9ff36fac26eb1519)
**Summary**
- Prevent backspace from removing a text element when the cursor is at
the element’s left edge.
- Instead just delete the char before the placeholder (moving it to the
left).
## Summary
- Remove elevated runner request files after read (best-effort cleanup
on errors)
- Add a unit test to cover request file lifecycle
## Testing
- `cargo test -p codex-windows-sandbox` (Windows)
Fixes#9315
In a [recent PR](https://github.com/openai/codex/pull/9182), I made some
improvements to config error messages so errors didn't leave app server
clients in a dead state. This is a follow-on PR to make these error
messages more readable and actionable for both TUI and GUI users. For
example, see #9668 where the user was understandably confused about the
source of the problem and how to fix it.
The improved error message:
1. Clearly identifies the config file where the error was found (which
is more important now that we support layered configs)
2. Provides a line and column number of the error
3. Displays the line where the error occurred and underlines it
For example, if my `config.toml` includes the following:
```toml
[features]
collaboration_modes = "true"
```
Here's the current CLI error message:
```
Error loading config.toml: invalid type: string "true", expected a boolean in `features`
```
And here's the improved message:
```
Error loading config.toml:
/Users/etraut/.codex/config.toml:43:23: invalid type: string "true", expected a boolean
|
43 | collaboration_modes = "true"
| ^^^^^^
```
The bulk of the new logic is contained within a new module
`config_loader/diagnostics.rs` that is responsible for calculating the
text range for a given toml path (which is more involved than I would
have expected).
In addition, this PR adds the file name and text range to the
`ConfigWarningNotification` app server struct. This allows GUI clients
to present the user with a better error message and an optional link to
open the errant config file. This was a suggestion from @.bolinfest when
he reviewed my previous PR.
This add a new crate, `codex-network-proxy`, a local network proxy
service used by Codex to enforce fine-grained network policy (domain
allow/deny) and to surface blocked network events for interactive
approvals.
- New crate: `codex-rs/network-proxy/` (`codex-network-proxy` binary +
library)
- Core capabilities:
- HTTP proxy support (including CONNECT tunneling)
- SOCKS5 proxy support (in the later PR)
- policy evaluation (allowed/denied domain lists; denylist wins;
wildcard support)
- small admin API for polling/reload/mode changes
- optional MITM support for HTTPS CONNECT to enforce “limited mode”
method restrictions (later PR)
Will follow up integration with codex in subsequent PRs.
## Testing
- `cd codex-rs && cargo build -p codex-network-proxy`
- `cd codex-rs && cargo run -p codex-network-proxy -- proxy`
### Motivation
- Remove an outdated comment in `defs.bzl` referencing
`cargo_build_script` that is no longer relevant.
### Description
- Delete the stale `# TODO(zbarsky): cargo_build_script support?` line
so the logic flows directly from `binaries` to `lib_srcs` in `defs.bzl`.
### Testing
- Ran `git diff --check` which produced no errors.
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6973d9ac757c8331be475a8fb0f90a88)
Fixes#9501
Contributing guide:
https://github.com/openai/codex/blob/main/docs/contributing.md
## Summary
The resume picker requires a session_meta line and at least one
user_message event within the initial head scan. Some rollout files
contain multiple session_meta entries before the first user_message, so
the user event can fall outside the default head window and the session
is omitted from the picker even though it is resumable by ID.
This PR keeps the head summary bounded but extends scanning for a
user_message once a session_meta has been observed. The summary still
caps stored head entries, but we allow a small, bounded extra scan to
find the first user event so valid sessions are not filtered out.
## Changes
- Continue scanning past the head limit (bounded) when session_meta is
present but no user_message has been seen yet.
- Mark session_meta as seen even if the head summary buffer is already
full.
- Add a regression test with multiple session_meta lines before the
first user_message.
## Why This Is Safe
- The head summary remains bounded to avoid unbounded memory usage.
- The extra scan is capped (USER_EVENT_SCAN_LIMIT) and only triggers
after a session_meta is seen.
- Behavior is unchanged for typical files where the user_message appears
early.
## Testing
- cargo test -p codex-core --lib
test_list_threads_scans_past_head_for_user_event
## Summary
Upgrade GitHub Actions to their latest versions to ensure compatibility
with Node 24, as Node 20 will reach end-of-life in April 2026.
## Changes
| Action | Old Version(s) | New Version | Release | Files |
|--------|---------------|-------------|---------|-------|
| `actions/cache` |
[`v4`](https://github.com/actions/cache/releases/tag/v4) |
[`v5`](https://github.com/actions/cache/releases/tag/v5) |
[Release](https://github.com/actions/cache/releases/tag/v5) | bazel.yml
|
## Context
Per [GitHub's
announcement](https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/),
Node 20 is being deprecated and runners will begin using Node 24 by
default starting March 4th, 2026.
### Why this matters
- **Node 20 EOL**: April 2026
- **Node 24 default**: March 4th, 2026
- **Action**: Update to latest action versions that support Node 24
### Security Note
Actions that were previously pinned to commit SHAs remain pinned to SHAs
(updated to the latest release SHA) to maintain the security benefits of
immutable references.
### Testing
These changes only affect CI/CD workflow configurations and should not
impact application functionality. The workflows should be tested by
running them on a branch before merging.
Signed-off-by: Salman Muin Kayser Chishti <13schishti@gmail.com>
## Summary
Fixes#7522
The `--yolo` (`--dangerously-bypass-approvals-and-sandbox`) flag is
documented to skip all confirmation prompts and execute commands without
sandboxing, intended solely for running in environments that are
externally sandboxed. However, it was not bypassing the trusted
directory (git repo) check, requiring users to also specify
`--skip-git-repo-check`.
This change makes `--yolo` also skip the git repo check, matching the
documented behavior and user expectations.
## Changes
- Modified `codex-rs/exec/src/lib.rs` to check for
`dangerously_bypass_approvals_and_sandbox` flag in addition to
`skip_git_repo_check` when determining whether to skip the git repo
check
## Testing
- Verified the code compiles with `cargo check -p codex-exec`
- Ran existing tests with `cargo test -p codex-exec` (34 passed, 8
integration tests failed due to unrelated API connectivity issues)
---
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Claude <noreply@anthropic.com>
# 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.
**Summary**
- Backtrack selection now rehydrates `text_elements` and
`local_image_paths` from the chosen user history cell so Esc‑Esc history
edits preserve image placeholders and attachments.
- Composer prefill uses the preserved elements/attachments in both `tui`
and `tui2`.
- Extended backtrack selection tests to cover image placeholder elements
and local image paths.
**Changes**
- `tui/src/app_backtrack.rs`: Backtrack selection now carries text
elements + local image paths; composer prefill uses them (removes TODO).
- `tui2/src/app_backtrack.rs`: Same as above.
- `tui/src/app.rs`: Updated backtrack test to assert restored
elements/paths.
- `tui2/src/app.rs`: Same test updates.
### The original scope of this PR (threading text elements and image
attachments through the codex harness thoroughly/persistently) was
broken into the following PRs other than this one:
The diff of this PR was reduced by changing types in a starter PR:
https://github.com/openai/codex/pull/9235
Then text element metadata was added to protocol, app server, and core
in this PR: https://github.com/openai/codex/pull/9331
Then the end-to-end flow was completed by wiring TUI/TUI2 input,
history, and restore behavior in
https://github.com/openai/codex/pull/9393
Prompt expansion was supported in this PR:
https://github.com/openai/codex/pull/9518
TextElement optional placeholder field was protected in
https://github.com/openai/codex/pull/9545
## What
Fix bash command parsing to accept double-quoted strings that contain
literal newlines so execpolicy can match allow rules.
## Why
Allow rules like [git, commit] should still match when commit messages
include a newline in a quoted argument; the parser currently rejects
these strings and falls back to the outer shell invocation.
## How
- Validate double-quoted strings by ensuring all named children are
string_content and then stripping the outer quotes from the raw node
text so embedded newlines are preserved.
- Reuse the helper for concatenated arguments.
- Ensure large SI suffix formatting uses the caller-provided locale
formatter for grouping.
- Add coverage for newline-containing quoted arguments.
Fixes#9541.
## Tests
- cargo test -p codex-core
- just fix -p codex-core
- cargo test -p codex-protocol
- just fix -p codex-protocol
- cargo test --all-features
## Summary
- hide the “(shift+tab to cycle)” suffix on the collaboration mode label
while a task is running
- keep the cycle hint visible when idle
- add a snapshot to cover the running-task label state
### Motivation
Exposes a per-thread / per-turn `personality` override in the v2
app-server API so clients can influence model communication style at
thread/turn start. Ensures the override is passed into the session
configuration resolution so it becomes effective for subsequent turns
and headless runners.
### Testing
- [x] Add an integration-style test
`turn_start_accepts_personality_override_v2` in
`codex-rs/app-server/tests/suite/v2/turn_start.rs` that verifies a
`/personality` override results in a developer update message containing
`<personality_spec>` in the outbound model request.
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6971d646b1c08322a689a54d2649f3fe)
In order to make Codex work with connectors, we add a built-in gateway
MCP that acts as a transparent proxy between the client and the
connectors. The gateway MCP collects actions that are accessible to the
user and sends them down to the user, when a connector action is chosen
to be called, the client invokes the action through the gateway MCP as
well.
- [x] Add the system built-in gateway MCP to list and run connectors.
- [x] Add the app server methods and protocol
This fixes a bug where the elevated sandbox setup encrypts sandbox user
passwords as an admin user, but normal command execution attempts to
decrypt them as a different user.
Machine scope allows all users to encyrpt/decrypt
this PR also moves the encrypted file to a different location
.codex/.sandbox-secrets which the sandbox users cannot read.
## Summary
- Replace the plan‑implementation prompt with a standard selection
popup.
- “Yes” submits a user turn in Execute via a dedicated app event to
preserve normal transcript behavior.
- “No” simply dismisses the popup.
<img width="977" height="433" alt="Screenshot 2026-01-22 at 2 00 54 PM"
src="https://github.com/user-attachments/assets/91fad06f-7b7a-4cd8-9051-f28a19b750b2"
/>
## Changes
- Add a plan‑implementation popup using `SelectionViewParams`.
- Add `SubmitUserMessageWithMode` so “Yes” routes through
`submit_user_message` (ensures user history + separator state).
- Track `saw_plan_update_this_turn` so the prompt appears even when only
`update_plan` is emitted.
- Suppress the plan popup on replayed turns, when messages are queued,
or when a rate‑limit prompt is pending.
- Add `execute_mode` helper for collaboration modes.
- Add tests for replay/queued/rate‑limit guards and plan update without
final message.
- Add snapshots for both the default and “No”‑selected popup states.
The formatting of `codex features list` made it hard to follow. This PR
introduces column width math to make things nice.
Maybe slightly hard to machine-parse (since not a simple `\t`), but we
should introduce a `--json` option if that's really important.
You can see the before/after in the screenshot:
<img width="1119" height="932" alt="image"
src="https://github.com/user-attachments/assets/c99dce85-899a-4a2d-b4af-003938f5e1df"
/>
## Summary
Support updating Personality mid-Thread via UserTurn/OverwriteTurn. This
is explicitly unused by the clients so far, to simplify PRs - app-server
and tui implementations will be follow-ups.
## Testing
- [x] added integration tests
# Summary
- Add a collaboration mode indicator rendered at the bottom-right of the
TUI composer footer.
- Style modes per design (Plan in #D72EE1, Execute matching dim context
style, Pair Programming using the same cyan as text elements).
- Add shared “(shift+tab to cycle)” hint text for all mode labels and
align the indicator with the left footer margin.
NOTE: currently this is hidden if the Collaboration Modes feature flag
is disabled, or in Custom mode. Maybe we should show it in Custom mode
too? I'll leave that out of this PR though
# UI
- Mode indicator appears below the textarea, bottom-right of the footer
line.
- Includes “(shift+tab to cycle)” and keeps right padding aligned to the
left footer indent.
<img width="983" height="200" alt="Screenshot 2026-01-21 at 7 17 54 PM"
src="https://github.com/user-attachments/assets/d1c5e4ed-7d7b-4f6c-9e71-bc3cf6400e0e"
/>
<img width="980" height="200" alt="Screenshot 2026-01-21 at 7 18 53 PM"
src="https://github.com/user-attachments/assets/d22ff0da-a406-4930-85c5-affb2234e84b"
/>
<img width="979" height="201" alt="Screenshot 2026-01-21 at 7 19 12 PM"
src="https://github.com/user-attachments/assets/862cb17f-0495-46fa-9b01-a4a9f29b52d5"
/>
### Summary
We now rely purely on `item/commandExecution/requestApproval` item to
render pending approval in VSCE and app. With v2 approach, it does not
include the actual cmd that it is attempting and therefore we can only
use `proposedExecpolicyAmendment` to render which can be incomplete.
### Reproduce
* Add `prefix_rule(pattern=["echo"], decision="prompt")` to your
`~/.codex/rules.default.rules`.
* Ask to `Run echo "approval-test" please` in VSCE or app.
* The pending approval protal does show up but with no content
#### Example screenshot
<img width="3434" height="3648" alt="Screenshot 2026-01-21 at 8 23
25 PM"
src="https://github.com/user-attachments/assets/75644837-21f1-40f8-8b02-858d361ff817"
/>
#### Sample output
```
{"method":"item/commandExecution/requestApproval","id":0,"params":{
"threadId":"019be439-5a90-7600-a7ea-2d2dcc50302a",
"turnId":"0",
"itemId":"call_usgnQ4qEX5U9roNdjT7fPzhb",
"reason":"`/bin/zsh -lc 'echo \"testing\"'` requires approval by policy",
"proposedExecpolicyAmendment":null
}}
```
### Fix
Inlude `command` string, `cwd` and `command_actions` in
`CommandExecutionRequestApprovalParams` so that consumers can display
the correct command instead of relying on exec policy output.
## What?
- Downgrade the closed-channel send error log to debug in
`codex-rs/core/src/codex.rs`.
## Why?
- `async_channel::Sender::send` only fails when the channel is closed,
so the current error-level log is noisy during normal shutdown. See
issue #9652.
## How?
- Replace the error log with a debug log on send failure.
## Tests
- `just fmt`
- `just fix -p codex-core`
- `cargo test -p codex-core`
## Summary
Adds the `/permissions` command, with a (usually) shorter set of
permissions. `/approvals` still exists, for backwards compatibility.
<img width="863" height="309" alt="Screenshot 2026-01-20 at 4 12 51 PM"
src="https://github.com/user-attachments/assets/c49b5ba5-bc47-46dd-9067-e1a5670328fe"
/>
## Testing
- [x] updated unit tests
- [x] Tested locally
## Summary
#9555 is the start of a rename, so I'm starting to standardize here.
Sets up `model_instructions` templating with a strongly-typed object for
injecting a personality block into the model instructions.
## Testing
- [x] Added tests
- [x] Ran locally
## Summary
- Retire the experimental TUI2 implementation and its feature flag.
- Remove TUI2-only config/schema/docs so the CLI stays on the
terminal-native path.
- Keep docs aligned with the legacy TUI while we focus on redraw-based
improvements.
## Customer impact
- Retires the TUI2 experiment and keeps Codex on the proven
terminal-native UI while we invest in redraw-based improvements to the
existing experience.
## Migration / compatibility
- If you previously set tui2-related options in config.toml, they are
now ignored and Codex continues using the existing terminal-native TUI
(no action required).
## Context
- What worked: a transcript-owned viewport delivered excellent resize
rewrap and high-fidelity copy (especially for code).
- Why stop: making that experience feel fully native across the
environment matrix (terminal emulator, OS, input modality, multiplexer,
font/theme, alt-screen behavior) creates a combinatorial explosion of
edge cases.
- What next: we are focusing on redraw-based improvements to the
existing terminal-native TUI so scrolling, selection, and copy remain
native while resize/redraw correctness improves.
## Testing
- just write-config-schema
- just fmt
- cargo clippy --fix --all-features --tests --allow-dirty --allow-no-vcs
-p codex-core
- cargo clippy --fix --all-features --tests --allow-dirty --allow-no-vcs
-p codex-cli
- cargo check
- cargo test -p codex-core
- cargo test -p codex-cli
## Summary
- make paste-burst tests deterministic by injecting explicit timestamps
instead of relying on wall clock timing
- add time-aware helpers for input/submission paths so tests can drive
the burst heuristic precisely
- update burst-related tests to flush using computed timeouts while
preserving behavior assertions
- increase timeout slack in
shell_tools_start_before_response_completed_when_stream_delayed to
reduce flakiness
Follow up to #8956; publish schema on new release to stable URL.
Also canonicalize schema (sort keys) when writing. This avoids reliance
on default `schema_rs` behavior and makes the schema easier to read.
## Summary
When we send multiple assistant messages, reset the timer so "Worked for
2m 36s" is the time since the last time we showed the message, rather
than an ever-increasing number.
We could instead change the copy so it's more clearly a running counter.
## Testing
- [x] ran locally
<img width="903" height="732" alt="Screenshot 2026-01-21 at 1 42 51 AM"
src="https://github.com/user-attachments/assets/bb4d827b-3a0e-48ba-bd6a-d8cd65d8e892"
/>
This PR changes the way we sort slash command by going in this order:
1. Exact match
2. Prefix
3. Fuzzy
As a result, we you type `/ps` the default command is not `/approvals`
This PR adds support for chained (layered) config.toml file merging for
clients that use the app server interface. This feature already exists
for the TUI, but it does not work for GUI clients.
It does the following:
* Changes code paths for new thread, resume thread, and fork thread to
use the effective config based on the cwd.
* Updates the `config/read` API to accept an optional `cwd` parameter.
If specified, the API returns the effective config based on that cwd
path. Also optionally includes all layers including project config
files. If cwd is not specified, the API falls back on its older behavior
where it considers only the global (non-project) config files when
computing the effective config.
The changes in codex_message_processor.rs look deceptively large. They
mostly just involve moving existing blocks of code to a later point in
some functions so it can use the cwd to calculate the config.
This PR builds upon #9509 and should be reviewed and merged after that
PR.
Tested:
* Verified change with (dependent, as-yet-uncommitted) changes to IDE
Extension and confirmed correct behavior
The full fix requires additional changes in the IDE Extension code base,
but they depend on this PR.
## Summary
- add optional `collaboration_mode` to `TurnContextItem` in rollouts
- persist the current collaboration mode when recording turn context
(sampling + compaction)
## Rationale
We already persist turn context data for resume logic. Capturing
collaboration mode in the rollout gives us the mode context for each
turn, enabling follow‑up work to diff mode instructions correctly on
resume.
## Changes
- protocol: add optional `collaboration_mode` field to `TurnContextItem`
- core: persist collaboration mode alongside other turn context settings
in rollouts
On bazel9 this lets us avoid performing some external repo downloads if
they've been previously uploaded to remote cache, downloads are deferred
until they are actually needed to execute an uncached action
### Motivation
- Prevent inputs like `~//` or `~///etc` from expanding to arbitrary
absolute paths (e.g. `/`) because `Path::join` discards the left side
when the right side is absolute, which could allow config values to
escape `HOME` and broaden writable roots.
### Description
- In `codex-rs/utils/absolute-path/src/lib.rs` update
`maybe_expand_home_directory` to trim leading separators from the suffix
and return `home` when the remainder is empty so tilde expansion stays
rooted under `HOME`.
- Add a non-Windows unit test
`home_directory_double_slash_on_non_windows_is_expanded_in_deserialization`
that validates `"~//code"` expands to `home.join("code")`.
### Testing
- Ran `just fmt` successfully.
- Ran `just fix -p codex-utils-absolute-path` (Clippy autofix)
successfully.
- Ran `cargo test -p codex-utils-absolute-path` and all tests passed.
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_697007481cac832dbeb1ee144d1e4cbe)
This PR adds 2 defensive mechanisms for shell snapshotting:
* Filter out invalid env variables (containing `-` for example) without
dropping the whole snapshot
* Validate the snapshot before considering it as valid by running a mock
command with a shell snapshot
# 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.
## Summary
- Keep `request_user_input` in the tool list but reject it at runtime in
Execute/Custom modes with a clear model-facing error.
- Add a session accessor for current collaboration mode and enforce the
gate in the request_user_input handler.
- Update core/app-server tests to use Plan mode for success and add
Execute/Custom rejection coverage.
Summary
- Preserve `text_elements` through custom prompt argument parsing and
expansion (named and numeric placeholders).
- Translate text element ranges through Shlex parsing using sentinel
substitution, and rehydrate text + element ranges per arg.
- Drop image attachments when their placeholder does not survive prompt
expansion, keeping attachments consistent with rendered elements.
- Mirror changes in TUI2 and expand tests for prompt parsing/expansion
edge cases.
Tests
- placeholders with spaces as single tokens (positional + key=value,
quoted + unquoted),
- prompt expansion with image placeholders,
- large paste + image arg combinations,
- unused image arg dropped after expansion.
Using terminal with TERM=dumb specifically mean that TUIs and the like
don't work. Ensure that codex doesn't run in these environments and exit
with odd errors like crossterm's "Error: The cursor position could not
be read within a normal duration"
---------
Co-authored-by: Josh McKinney <joshka@openai.com>
### What
Implemented detection for dangerous "force delete" commands on Windows
to trigger the user approval prompt when `--ask-for-approval on-request`
is set. This aligns Windows behavior with the existing safety checks for
`rm -rf` on Linux.
### Why
Fixes#8567 - a critical safety gap where destructive Windows commands
could bypass the approval prompt. This prevents accidental data loss by
ensuring the user explicitly confirms operations that would otherwise
suppress the OS's native confirmation prompts.
### How
Updated the Windows command safety module to identify and flag the
following patterns as dangerous:
* **PowerShell**:
* Detects `Remove-Item` (and aliases `rm`, `ri`, `del`, `erase`, `rd`,
`rmdir`) when used with the `-Force` flag.
* Uses token-based analysis to robustly detect these patterns even
inside script blocks (`{...}`), sub-expression `(...)`, or
semicolon-chained sequences.
* **CMD**:
* Detects `del /f` (force delete files).
* Detects `rd /s /q` (recursive delete quiet).
* **Command Chaining**: Added support for analyzing chained commands
(using `&`, `&&`, `|`, `||`) to separate and check individual commands
(e.g., catching `del /f` hidden in `echo log & del /f data`).
### Testing
Added comprehensive unit tests covering:
* **PowerShell**: `Remove-Item -Path 'test' -Recurse -Force` (Exact
reproduction case).
* **Complex Syntax**: Verified detection inside blocks (e.g., `if
($true) { rm -Force }`) and with trailing punctuation.
* **CMD**:
* `del /f` (Flagged).
* `rd /s /q` (Flagged).
* Chained commands: `echo hi & del /f file` (Flagged).
* **False Positives**:
* `rd /s` (Not flagged - relies on native prompt).
* Standard deletions without force flags.
Verified with `cargo test` and `cargo clippy`.
---------
Co-authored-by: Eric Traut <etraut@openai.com>
Despite good spacing between queued messages and assistant message text:
<img width="462" height="322" alt="Screenshot 2026-01-12 at 4 54 50 PM"
src="https://github.com/user-attachments/assets/e8b46252-0b33-40d2-b431-cb73b9a3bd2e"
/>
Codex has confusing spacing between queued messages and shimmering
status text (making the queued message seem like a sub-item of the
shimmering status text)
<img width="615" height="217" alt="Screenshot 2026-01-12 at 4 54 18 PM"
src="https://github.com/user-attachments/assets/ee5e6095-8fe9-4863-88d2-10472cab8bd6"
/>
This PR changes the spacing between the queued message(s) and shimmering
status text to make it less confusing:
<img width="440" height="240" alt="Screenshot 2026-01-13 at 11 20 36 AM"
src="https://github.com/user-attachments/assets/02dcc690-cbe9-4943-87de-c7300ef51120"
/>
While working on the status/queued spacing change, we noticed two
paste‑burst tests were timing‑sensitive and could fail
on slower CI. We added a small test‑only helper to keep the paste‑burst
state active and refreshed during these tests. This
removes dependence on tight timing and makes the tests deterministic
without affecting runtime behavior.
## Summary
Fixes#9520
The `bin/codex.js` file was missing execute permissions (`644` instead
of `755`), causing the `codex` command to fail after npm global
installation.
## Changes
- Added execute permission (`+x`) to `codex-cli/bin/codex.js`
## Verification
After this fix, npm tarballs will include the correct file permissions:
```bash
# Before: -rw-r--r-- (644)
# After: -rwxr-xr-x (755)
```
---
🤖 Generated with Claude Code
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Make `TextElement` placeholders private and add a text-backed accessor
to avoid assuming `Some`.
- Since they are optional in the protocol, we want to make sure any
accessors properly handle the None case (getting the placeholder using
the byte range in the text)
- Preserve placeholders during protocol/app-server conversions using the
accessor fallback.
- Update TUI composer/remap logic and tests to use the new
constructor/accessor.
We have `models.json` and `/models` response
Behavior:
1. New models from models endpoint gets added
2. Shared models get replaced by remote ones
3. Existing models in `models.json` but not `/models` are kept
4. Mark highest priority as default
## Summary
- Use `std::env::split_paths` to parse PATH entries in audit candidate
collection
- Add a unit test covering multiple PATH entries (including spaces)
## Testing
- `cargo test -p codex-windows-sandbox` (Windows)
Fixes#9317
## Summary
- Deny `.git` entries under writable roots even when `.git` is a file
(worktrees/submodules)
- Add a unit test for `.git` file handling
## Testing
- `cargo test -p codex-windows-sandbox` (Windows)
Fixes#9313
The elevated setup does not work on non-English windows installs where
Users/Administrators/etc are in different languages. This PR uses the
well-known SIDs instead, which do not vary based on locale
Remove `FileTimes::new().set_modified(SystemTime::now())` when resuming
a thread.
Context: It's awkward in UI built on top of app-server that resuming a
thread bumps the `updated_at` timestamp, even if no message is sent. So
if you open a thread (perhaps to just view its contents), it
automatically reorders it to the top which is almost certainly not what
you want.
## What
Record a model-visible `<turn_aborted>` marker in history when a turn is
interrupted, and treat it as a session prefix.
## Why
When a turn is interrupted, Codex emits `TurnAborted` but previously did
not persist anything model-visible in the conversation history. On the
next user turn, the model can’t tell the previous work was aborted and
may resume/repeat earlier actions (including duplicated side effects
like re-opening PRs).
Fixes: https://github.com/openai/codex/issues/9042
## How
On `TurnAbortReason::Interrupted`, append a hidden user message
containing a `<turn_aborted>…</turn_aborted>` marker and flush.
Treat `<turn_aborted>` like `<environment_context>` for session-prefix
filtering.
Add a regression test to ensure follow-up turns don’t repeat side
effects from an aborted turn.
## Testing
`just fmt`
`just fix -p codex-core`
`cargo test -p codex-core -- --test-threads=1`
`cargo test --all-features -- --test-threads=1`
---------
Co-authored-by: Skylar Graika <sgraika127@gmail.com>
Co-authored-by: jif-oai <jif@openai.com>
Co-authored-by: Eric Traut <etraut@openai.com>
This PR fixes a small issue with chained (layered) config.toml file
merging. The old logic didn't properly handle profiles.
In particular, if a lower-layer config overrides a profile defined in a
higher-layer config, the override did not take effect. This prevents
users from having project-specific profile overrides and contradicts the
(soon-to-be) documented behavior of config merging.
The change adds a unit test for this case. It also exposes a function
from the config crate that is needed by the app server code paths to
implement support for layered configs.
## Summary
Introduces the concept of a config model_personality. I would consider
this an MVP for testing out the feature. There are a number of
follow-ups to this PR:
- More sophisticated templating with validation
- In-product experience to manage this
## Testing
- [x] Testing locally
Fixes:
```
[sandbox_workspace_write]
writable_roots = ["~/code/"]
```
translates to
```
/Users/ccunningham/.codex/~/code
```
(i.e. the home dir symbol isn't recognized)
- Only use collaboration modes in the tui state to track model and
effort.
- No behavior change without the collaboration modes flag.
- Change model and effort on /model, /collab (behind a flag), and
shift+tab (behind flag)
Config includes multiple code execution entrypoints.
Now, we load the config from predetermined locations first
(~/.codex/config.toml etc), use those to learn which folders are
'trusted', and only load additional config from the CWD if it is
trusted.
Continuation of breaking up this PR
https://github.com/openai/codex/pull/9116
## Summary
- Thread user text element ranges through TUI/TUI2 input, submission,
queueing, and history so placeholders survive resume/edit flows.
- Preserve local image attachments alongside text elements and rehydrate
placeholders when restoring drafts.
- Keep model-facing content shapes clean by attaching UI metadata only
to user input/events (no API content changes).
## Key Changes
- TUI/TUI2 composer now captures text element ranges, trims them with
text edits, and restores them when submission is suppressed.
- User history cells render styled spans for text elements and keep
local image paths for future rehydration.
- Initial chat widget bootstraps accept empty `initial_text_elements` to
keep initialization uniform.
- Protocol/core helpers updated to tolerate the new InputText field
shape without changing payloads sent to the API.
## Summary
This PR consolidates base_instructions onto SessionMeta /
SessionConfiguration, so we ensure `base_instructions` is set once per
session and should be (mostly) immutable, unless:
- overridden by config on resume / fork
- sub-agent tasks, like review or collab
In a future PR, we should convert all references to `base_instructions`
to consistently used the typed struct, so it's less likely that we put
other strings there. See #9423. However, this PR is already quite
complex, so I'm deferring that to a follow-up.
## Testing
- [x] Added a resume test to assert that instructions are preserved. In
particular, `resume_switches_models_preserves_base_instructions` fails
against main.
Existing test coverage thats assert base instructions are preserved
across multiple requests in a session:
- Manual compact keeps baseline instructions:
core/tests/suite/compact.rs:199
- Auto-compact keeps baseline instructions:
core/tests/suite/compact.rs:1142
- Prompt caching reuses the same instructions across two requests:
core/tests/suite/prompt_caching.rs:150 and
core/tests/suite/prompt_caching.rs:157
- Prompt caching with explicit expected string across two requests:
core/tests/suite/prompt_caching.rs:213 and
core/tests/suite/prompt_caching.rs:222
- Resume with model switch keeps original instructions:
core/tests/suite/resume.rs:136
- Compact/resume/fork uses request 0 instructions for later expected
payloads: core/tests/suite/compact_resume_fork.rs:215
- `tui/` and `tui2/` submit `Op::UserTurn` and own full turn context
(cwd/approval/sandbox/model/etc.).
- `Op::UserInput` is documented as legacy in `codex-protocol` (doc-only;
no `#[deprecated]` to avoid `-D warnings` fallout).
- Remove obsolete `#[allow(deprecated)]` and the unused `ConversationId`
alias/re-export.
2026-01-19 13:40:39 -08:00
1939 changed files with 152901 additions and 87805 deletions
@@ -15,10 +15,10 @@ Things to look out for when doing the review:
## Code Organization
- Each create in the Cargo workspace in `codex-rs` has a specific purpose: make a note if you believe new code is not introduced in the correct crate.
- Each crate in the Cargo workspace in `codex-rs` has a specific purpose: make a note if you believe new code is not introduced in the correct crate.
- When possible, try to keep the `core` crate as small as possible. Non-core but shared logic is often a good candidate for `codex-rs/common`.
- Be wary of large files and offer suggestions for how to break things into more reasonably-sized files.
- Rust files should generally be organized such that the public parts of the API appear near the top of the file and helper functions go below. This is analagous to the "inverted pyramid" structure that is favored in journalism.
- Rust files should generally be organized such that the public parts of the API appear near the top of the file and helper functions go below. This is analogous to the "inverted pyramid" structure that is favored in journalism.
@@ -11,15 +11,17 @@ In the codex-rs folder where the rust code lives:
- Always collapse if statements per https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
- Always inline format! args when possible per https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args
- Use method references over closures when possible per https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
- When possible, make `match` statements exhaustive and avoid wildcard arms.
- 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`.
Run `just fmt` (in `codex-rs` directory) automatically after making Rust code changes; do not ask for approval to run it. Before finalizing a 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. Additionally, run the tests:
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`.
When running interactively, ask the user before running `just fix` to finalize. `just fmt` does not require approval. 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 --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.
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.
"description":"Use to correlate this with [codex_core::protocol::PatchApplyBeginEvent] and [codex_core::protocol::PatchApplyEndEvent].",
"type":"string"
},
"conversationId":{
"$ref":"#/definitions/ThreadId"
},
"fileChanges":{
"additionalProperties":{
"$ref":"#/definitions/FileChange"
},
"type":"object"
},
"grantRoot":{
"description":"When set, the agent is asking the user to allow writes under this root for the remainder of the session (unclear if this is honored today).",
"type":[
"string",
"null"
]
},
"reason":{
"description":"Optional explanatory reason (e.g. request for extra write access).",
"description":"User has approved this command and wants to automatically approve any future identical instances (`command` and `cwd` match exactly) for the remainder of the session.",
"enum":[
"approved_for_session"
],
"type":"string"
},
{
"description":"User has denied this command and the agent should not execute it, but it should continue the session and try something else.",
"enum":[
"denied"
],
"type":"string"
},
{
"description":"User has denied this command and the agent should not do anything until the user's next command.",
"description":"Codex attempted a backend request and received `401 Unauthorized`.",
"enum":[
"unauthorized"
],
"type":"string"
}
]
}
},
"properties":{
"previousAccountId":{
"description":"Workspace/account identifier that Codex was previously using.\n\nClients that manage multiple accounts/workspaces can use this as a hint to refresh the token for the correct workspace.\n\nThis may be `null` when the prior ID token did not include a workspace identifier (`chatgpt_account_id`) or when the token could not be parsed.",
"description":"User approved the command and future identical commands should run without prompting.",
"enum":[
"acceptForSession"
],
"type":"string"
},
{
"additionalProperties":false,
"description":"User approved the command, and wants to apply the proposed execpolicy amendment so future matching commands can run without prompting.",
"description":"(Best effort) Path to the file being read by the command. When possible, this is an absolute path, though when relative, it should be resolved against the `cwd`` that will be used to run the command to derive the absolute path.",
"type":"string"
},
"type":{
"enum":[
"read"
],
"title":"ReadParsedCommandType",
"type":"string"
}
},
"required":[
"cmd",
"name",
"path",
"type"
],
"title":"ReadParsedCommand",
"type":"object"
},
{
"properties":{
"cmd":{
"type":"string"
},
"path":{
"type":[
"string",
"null"
]
},
"type":{
"enum":[
"list_files"
],
"title":"ListFilesParsedCommandType",
"type":"string"
}
},
"required":[
"cmd",
"type"
],
"title":"ListFilesParsedCommand",
"type":"object"
},
{
"properties":{
"cmd":{
"type":"string"
},
"path":{
"type":[
"string",
"null"
]
},
"query":{
"type":[
"string",
"null"
]
},
"type":{
"enum":[
"search"
],
"title":"SearchParsedCommandType",
"type":"string"
}
},
"required":[
"cmd",
"type"
],
"title":"SearchParsedCommand",
"type":"object"
},
{
"properties":{
"cmd":{
"type":"string"
},
"type":{
"enum":[
"unknown"
],
"title":"UnknownParsedCommandType",
"type":"string"
}
},
"required":[
"cmd",
"type"
],
"title":"UnknownParsedCommand",
"type":"object"
}
]
},
"ThreadId":{
"type":"string"
}
},
"properties":{
"callId":{
"description":"Use to correlate this with [codex_core::protocol::ExecCommandBeginEvent] and [codex_core::protocol::ExecCommandEndEvent].",
"description":"User has approved this command and wants to automatically approve any future identical instances (`command` and `cwd` match exactly) for the remainder of the session.",
"enum":[
"approved_for_session"
],
"type":"string"
},
{
"description":"User has denied this command and the agent should not execute it, but it should continue the session and try something else.",
"enum":[
"denied"
],
"type":"string"
},
{
"description":"User has denied this command and the agent should not do anything until the user's next command.",
"description":"[UNSTABLE] When set, the agent is asking the user to allow writes under this root for the remainder of the session (unclear if this is honored today).",
"type":[
"string",
"null"
]
},
"itemId":{
"type":"string"
},
"reason":{
"description":"Optional explanatory reason (e.g. request for extra write access).",
"description":"Use to correlate this with [codex_core::protocol::PatchApplyBeginEvent] and [codex_core::protocol::PatchApplyEndEvent].",
"type":"string"
},
"conversationId":{
"$ref":"#/definitions/ThreadId"
},
"fileChanges":{
"additionalProperties":{
"$ref":"#/definitions/FileChange"
},
"type":"object"
},
"grantRoot":{
"description":"When set, the agent is asking the user to allow writes under this root for the remainder of the session (unclear if this is honored today).",
"type":[
"string",
"null"
]
},
"reason":{
"description":"Optional explanatory reason (e.g. request for extra write access).",
"type":[
"string",
"null"
]
}
},
"required":[
"callId",
"conversationId",
"fileChanges"
],
"type":"object"
},
"ChatgptAuthTokensRefreshParams":{
"properties":{
"previousAccountId":{
"description":"Workspace/account identifier that Codex was previously using.\n\nClients that manage multiple accounts/workspaces can use this as a hint to refresh the token for the correct workspace.\n\nThis may be `null` when the prior ID token did not include a workspace identifier (`chatgpt_account_id`) or when the token could not be parsed.",
"description":"Codex attempted a backend request and received `401 Unauthorized`.",
"enum":[
"unauthorized"
],
"type":"string"
}
]
},
"CommandAction":{
"oneOf":[
{
"properties":{
"command":{
"type":"string"
},
"name":{
"type":"string"
},
"path":{
"type":"string"
},
"type":{
"enum":[
"read"
],
"title":"ReadCommandActionType",
"type":"string"
}
},
"required":[
"command",
"name",
"path",
"type"
],
"title":"ReadCommandAction",
"type":"object"
},
{
"properties":{
"command":{
"type":"string"
},
"path":{
"type":[
"string",
"null"
]
},
"type":{
"enum":[
"listFiles"
],
"title":"ListFilesCommandActionType",
"type":"string"
}
},
"required":[
"command",
"type"
],
"title":"ListFilesCommandAction",
"type":"object"
},
{
"properties":{
"command":{
"type":"string"
},
"path":{
"type":[
"string",
"null"
]
},
"query":{
"type":[
"string",
"null"
]
},
"type":{
"enum":[
"search"
],
"title":"SearchCommandActionType",
"type":"string"
}
},
"required":[
"command",
"type"
],
"title":"SearchCommandAction",
"type":"object"
},
{
"properties":{
"command":{
"type":"string"
},
"type":{
"enum":[
"unknown"
],
"title":"UnknownCommandActionType",
"type":"string"
}
},
"required":[
"command",
"type"
],
"title":"UnknownCommandAction",
"type":"object"
}
]
},
"CommandExecutionRequestApprovalParams":{
"properties":{
"command":{
"description":"The command to be executed.",
"type":[
"string",
"null"
]
},
"commandActions":{
"description":"Best-effort parsed command actions for friendly display.",
"items":{
"$ref":"#/definitions/CommandAction"
},
"type":[
"array",
"null"
]
},
"cwd":{
"description":"The command's working directory.",
"type":[
"string",
"null"
]
},
"itemId":{
"type":"string"
},
"proposedExecpolicyAmendment":{
"description":"Optional proposed execpolicy amendment to allow similar commands without prompting.",
"items":{
"type":"string"
},
"type":[
"array",
"null"
]
},
"reason":{
"description":"Optional explanatory reason (e.g. request for network access).",
"type":[
"string",
"null"
]
},
"threadId":{
"type":"string"
},
"turnId":{
"type":"string"
}
},
"required":[
"itemId",
"threadId",
"turnId"
],
"type":"object"
},
"DynamicToolCallParams":{
"properties":{
"arguments":true,
"callId":{
"type":"string"
},
"threadId":{
"type":"string"
},
"tool":{
"type":"string"
},
"turnId":{
"type":"string"
}
},
"required":[
"arguments",
"callId",
"threadId",
"tool",
"turnId"
],
"type":"object"
},
"ExecCommandApprovalParams":{
"properties":{
"callId":{
"description":"Use to correlate this with [codex_core::protocol::ExecCommandBeginEvent] and [codex_core::protocol::ExecCommandEndEvent].",
"type":"string"
},
"command":{
"items":{
"type":"string"
},
"type":"array"
},
"conversationId":{
"$ref":"#/definitions/ThreadId"
},
"cwd":{
"type":"string"
},
"parsedCmd":{
"items":{
"$ref":"#/definitions/ParsedCommand"
},
"type":"array"
},
"reason":{
"type":[
"string",
"null"
]
}
},
"required":[
"callId",
"command",
"conversationId",
"cwd",
"parsedCmd"
],
"type":"object"
},
"FileChange":{
"oneOf":[
{
"properties":{
"content":{
"type":"string"
},
"type":{
"enum":[
"add"
],
"title":"AddFileChangeType",
"type":"string"
}
},
"required":[
"content",
"type"
],
"title":"AddFileChange",
"type":"object"
},
{
"properties":{
"content":{
"type":"string"
},
"type":{
"enum":[
"delete"
],
"title":"DeleteFileChangeType",
"type":"string"
}
},
"required":[
"content",
"type"
],
"title":"DeleteFileChange",
"type":"object"
},
{
"properties":{
"move_path":{
"type":[
"string",
"null"
]
},
"type":{
"enum":[
"update"
],
"title":"UpdateFileChangeType",
"type":"string"
},
"unified_diff":{
"type":"string"
}
},
"required":[
"type",
"unified_diff"
],
"title":"UpdateFileChange",
"type":"object"
}
]
},
"FileChangeRequestApprovalParams":{
"properties":{
"grantRoot":{
"description":"[UNSTABLE] When set, the agent is asking the user to allow writes under this root for the remainder of the session (unclear if this is honored today).",
"type":[
"string",
"null"
]
},
"itemId":{
"type":"string"
},
"reason":{
"description":"Optional explanatory reason (e.g. request for extra write access).",
"type":[
"string",
"null"
]
},
"threadId":{
"type":"string"
},
"turnId":{
"type":"string"
}
},
"required":[
"itemId",
"threadId",
"turnId"
],
"type":"object"
},
"ParsedCommand":{
"oneOf":[
{
"properties":{
"cmd":{
"type":"string"
},
"name":{
"type":"string"
},
"path":{
"description":"(Best effort) Path to the file being read by the command. When possible, this is an absolute path, though when relative, it should be resolved against the `cwd`` that will be used to run the command to derive the absolute path.",
"type":"string"
},
"type":{
"enum":[
"read"
],
"title":"ReadParsedCommandType",
"type":"string"
}
},
"required":[
"cmd",
"name",
"path",
"type"
],
"title":"ReadParsedCommand",
"type":"object"
},
{
"properties":{
"cmd":{
"type":"string"
},
"path":{
"type":[
"string",
"null"
]
},
"type":{
"enum":[
"list_files"
],
"title":"ListFilesParsedCommandType",
"type":"string"
}
},
"required":[
"cmd",
"type"
],
"title":"ListFilesParsedCommand",
"type":"object"
},
{
"properties":{
"cmd":{
"type":"string"
},
"path":{
"type":[
"string",
"null"
]
},
"query":{
"type":[
"string",
"null"
]
},
"type":{
"enum":[
"search"
],
"title":"SearchParsedCommandType",
"type":"string"
}
},
"required":[
"cmd",
"type"
],
"title":"SearchParsedCommand",
"type":"object"
},
{
"properties":{
"cmd":{
"type":"string"
},
"type":{
"enum":[
"unknown"
],
"title":"UnknownParsedCommandType",
"type":"string"
}
},
"required":[
"cmd",
"type"
],
"title":"UnknownParsedCommand",
"type":"object"
}
]
},
"RequestId":{
"anyOf":[
{
"type":"string"
},
{
"format":"int64",
"type":"integer"
}
]
},
"ThreadId":{
"type":"string"
},
"ToolRequestUserInputOption":{
"description":"EXPERIMENTAL. Defines a single selectable option for request_user_input.",
"properties":{
"description":{
"type":"string"
},
"label":{
"type":"string"
}
},
"required":[
"description",
"label"
],
"type":"object"
},
"ToolRequestUserInputParams":{
"description":"EXPERIMENTAL. Params sent with a request_user_input event.",
"description":"DEPRECATED APIs below Request to approve a patch. This request is used for Turns started via the legacy APIs (i.e. SendUserTurn, SendUserMessage).",
"properties":{
"id":{
"$ref":"#/definitions/RequestId"
},
"method":{
"enum":[
"applyPatchApproval"
],
"title":"ApplyPatchApprovalRequestMethod",
"type":"string"
},
"params":{
"$ref":"#/definitions/ApplyPatchApprovalParams"
}
},
"required":[
"id",
"method",
"params"
],
"title":"ApplyPatchApprovalRequest",
"type":"object"
},
{
"description":"Request to exec a command. This request is used for Turns started via the legacy APIs (i.e. SendUserTurn, SendUserMessage).",
"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"
},
"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 to the entire file-system.",
"properties":{
"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"
},
"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.",
"enum":[
"never"
],
"type":"string"
}
]
},
"ContentItem":{
"oneOf":[
{
"properties":{
"text":{
"type":"string"
},
"type":{
"enum":[
"input_text"
],
"title":"InputTextContentItemType",
"type":"string"
}
},
"required":[
"text",
"type"
],
"title":"InputTextContentItem",
"type":"object"
},
{
"properties":{
"image_url":{
"type":"string"
},
"type":{
"enum":[
"input_image"
],
"title":"InputImageContentItemType",
"type":"string"
}
},
"required":[
"image_url",
"type"
],
"title":"InputImageContentItem",
"type":"object"
},
{
"properties":{
"text":{
"type":"string"
},
"type":{
"enum":[
"output_text"
],
"title":"OutputTextContentItemType",
"type":"string"
}
},
"required":[
"text",
"type"
],
"title":"OutputTextContentItem",
"type":"object"
}
]
},
"FunctionCallOutputContentItem":{
"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`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses/Chat Completions APIs understand.",
"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.",
"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 to the entire file-system.",
"properties":{
"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"
},
"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."
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.