### Motivation
- Avoid placing PATH entries under the system temp directory by creating
the helper directory under `CODEX_HOME` instead of
`std::env::temp_dir()`.
- Fail fast on unsafe configuration by rejecting `CODEX_HOME` values
that live under the system temp root to prevent writable PATH entries.
### Testing
- Ran `just fmt`, which completed with a non-blocking
`imports_granularity` warning.
- Ran `just fix -p codex-arg0` (Clippy fixes) which completed
successfully.
- Ran `cargo test -p codex-arg0` and the test run completed
successfully.
This PR configures Codex CLI so it can be built with
[Bazel](https://bazel.build) in addition to Cargo. The `.bazelrc`
includes configuration so that remote builds can be done using
[BuildBuddy](https://www.buildbuddy.io).
If you are familiar with Bazel, things should work as you expect, e.g.,
run `bazel test //... --keep-going` to run all the tests in the repo,
but we have also added some new aliases in the `justfile` for
convenience:
- `just bazel-test` to run tests locally
- `just bazel-remote-test` to run tests remotely (currently, the remote
build is for x86_64 Linux regardless of your host platform). Note we are
currently seeing the following test failures in the remote build, so we
still need to figure out what is happening here:
```
failures:
suite::compact::manual_compact_twice_preserves_latest_user_messages
suite::compact_resume_fork::compact_resume_after_second_compaction_preserves_history
suite::compact_resume_fork::compact_resume_and_fork_preserve_model_history_view
```
- `just build-for-release` to build release binaries for all
platforms/architectures remotely
To setup remote execution:
- [Create a buildbuddy account](https://app.buildbuddy.io/) (OpenAI
employees should also request org access at
https://openai.buildbuddy.io/join/ with their `@openai.com` email
address.)
- [Copy your API key](https://app.buildbuddy.io/docs/setup/) to
`~/.bazelrc` (add the line `build
--remote_header=x-buildbuddy-api-key=YOUR_KEY`)
- Use `--config=remote` in your `bazel` invocations (or add `common
--config=remote` to your `~/.bazelrc`, or use the `just` commands)
## CI
In terms of CI, this PR introduces `.github/workflows/bazel.yml`, which
uses Bazel to run the tests _locally_ on Mac and Linux GitHub runners
(we are working on supporting Windows, but that is not ready yet). Note
that the failures we are seeing in `just bazel-remote-test` do not occur
on these GitHub CI jobs, so everything in `.github/workflows/bazel.yml`
is green right now.
The `bazel.yml` uses extra config in `.github/workflows/ci.bazelrc` so
that macOS CI jobs build _remotely_ on Linux hosts (using the
`docker://docker.io/mbolin491/codex-bazel` Docker image declared in the
root `BUILD.bazel`) using cross-compilation to build the macOS
artifacts. Then these artifacts are downloaded locally to GitHub's macOS
runner so the tests can be executed natively. This is the relevant
config that enables this:
```
common:macos --config=remote
common:macos --strategy=remote
common:macos --strategy=TestRunner=darwin-sandbox,local
```
Because of the remote caching benefits we get from BuildBuddy, these new
CI jobs can be extremely fast! For example, consider these two jobs that
ran all the tests on Linux x86_64:
- Bazel 1m37s
https://github.com/openai/codex/actions/runs/20861063212/job/59940545209?pr=8875
- Cargo 9m20s
https://github.com/openai/codex/actions/runs/20861063192/job/59940559592?pr=8875
For now, we will continue to run both the Bazel and Cargo jobs for PRs,
but once we add support for Windows and running Clippy, we should be
able to cutover to using Bazel exclusively for PRs, which should still
speed things up considerably. We will probably continue to run the Cargo
jobs post-merge for commits that land on `main` as a sanity check.
Release builds will also continue to be done by Cargo for now.
Earlier attempt at this PR: https://github.com/openai/codex/pull/8832
Earlier attempt to add support for Buck2, now abandoned:
https://github.com/openai/codex/pull/8504
---------
Co-authored-by: David Zbarsky <dzbarsky@gmail.com>
Co-authored-by: Michael Bolin <mbolin@openai.com>
Fixes#2558
Codex uses alternate screen mode (CSI 1049) which, per xterm spec,
doesn't support scrollback. Zellij follows this strictly, so users can't
scroll back through output.
**Changes:**
- Add `tui.alternate_screen` config: `auto` (default), `always`, `never`
- Add `--no-alt-screen` CLI flag
- Auto-detect Zellij and skip alt screen (uses existing `ZELLIJ` env var
detection)
**Usage:**
```bash
# CLI flag
codex --no-alt-screen
# Or in config.toml
[tui]
alternate_screen = "never"
```
With default `auto` mode, Zellij users get working scrollback without
any config changes.
---------
Co-authored-by: Josh McKinney <joshka@openai.com>
Some enterprises do not want their users to be able to `/feedback`.
<img width="395" height="325" alt="image"
src="https://github.com/user-attachments/assets/2dae9c0b-20c3-4a15-bcd3-0187857ebbd8"
/>
Adds to `config.toml`:
```toml
[feedback]
enabled = false
```
I've deliberately decided to:
1. leave other references to `/feedback` (e.g. in the interrupt message,
tips of the day) unchanged. I think we should continue to promote the
feature even if it is not usable currently.
2. leave the `/feedback` menu item selectable and display an error
saying it's disabled, rather than remove the menu item (which I believe
would raise more questions).
but happy to discuss these.
This will be followed by a change to requirements.toml that admins can
use to force the value of feedback.enabled.
**Motivation**
The `originator` header is important for codex-backend’s Responses API
proxy because it identifies the real end client (codex cli, codex vscode
extension, codex exec, future IDEs) and is used to categorize requests
by client for our enterprise compliance API.
Today the `originator` header is set by either:
- the `CODEX_INTERNAL_ORIGINATOR_OVERRIDE` env var (our VSCode extension
does this)
- calling `set_default_originator()` which sets a global immutable
singleton (`codex exec` does this)
For `codex app-server`, we want the `initialize` JSON-RPC request to set
that header because it is a natural place to do so. Example:
```json
{
"method": "initialize",
"id": 0,
"params": {
"clientInfo": {
"name": "codex_vscode",
"title": "Codex VS Code Extension",
"version": "0.1.0"
}
}
}
```
and when app-server receives that request, it can call
`set_default_originator()`. This is a much more natural interface than
asking third party developers to set an env var.
One hiccup is that `originator()` reads the global singleton and locks
in the value, preventing a later `set_default_originator()` call from
setting it. This would be fine but is brittle, since any codepath that
calls `originator()` before app-server can process an `initialize`
JSON-RPC call would prevent app-server from setting it. This was
actually the case with OTEL initialization which runs on boot, but I
also saw this behavior in certain tests.
Instead, what we now do is:
- [unchanged] If `CODEX_INTERNAL_ORIGINATOR_OVERRIDE` env var is set,
`originator()` would return that value and `set_default_originator()`
with some other value does NOT override it.
- [new] If no env var is set, `originator()` would return the default
value which is `codex_cli_rs` UNTIL `set_default_originator()` is called
once, in which case it is set to the new value and becomes immutable.
Later calls to `set_default_originator()` returns
`SetOriginatorError::AlreadyInitialized`.
**Other notes**
- I updated `codex_core::otel_init::build_provider` to accepts a service
name override, and app-server sends a hardcoded `codex_app_server`
service name to distinguish it from `codex_cli_rs` used by default (e.g.
TUI).
**Next steps**
- Update VSCE to set the proper value for `clientInfo.name` on
`initialize` and drop the `CODEX_INTERNAL_ORIGINATOR_OVERRIDE` env var.
- Delete support for `CODEX_INTERNAL_ORIGINATOR_OVERRIDE` in codex-rs.
This is a proposed fix for #8912
Information provided by Codex:
no_proxy means “don’t use any system proxy settings for this client,”
even if macOS has proxies configured in System Settings or via
environment. On macOS, reqwest’s proxy discovery can call into the
system-configuration framework; that’s the code path that was panicking
with “Attempted to create a NULL object.” By forcing a direct connection
for the OAuth discovery request, we avoid that proxy-resolution path
entirely, so the system-configuration crate never gets invoked and the
panic disappears.
Effectively:
With proxies: reqwest asks the OS for proxy config →
system-configuration gets touched → panic.
With no_proxy: reqwest skips proxy lookup → no system-configuration call
→ no panic.
So the fix doesn’t change any MCP protocol behavior; it just prevents
the OAuth discovery probe from touching the macOS proxy APIs that are
crashing in the reported environment.
This fix changes behavior for the OAuth discovery probe used in codex
mcp list/auth status detection. With no_proxy, that probe won’t use
system or env proxy settings, so:
If a server is only reachable via a proxy, the discovery call may fail
and we’ll show auth as Unsupported/NotLoggedIn incorrectly.
If the server is reachable directly (common case), behavior is
unchanged.
As an alternative, we could try to get a fix into the
[system-configuration](https://github.com/mullvad/system-configuration-rs)
library. It looks like this library is still under development but has
slow release pace.
As explained in https://github.com/openai/codex/issues/8945 and
https://github.com/openai/codex/issues/8472, there are legitimate cases
where users expect processes spawned by Codex to inherit environment
variables such as `LD_LIBRARY_PATH` and `DYLD_LIBRARY_PATH`, where
failing to do so can cause significant performance issues.
This PR removes the use of
`codex_process_hardening::pre_main_hardening()` in Codex CLI (which was
added not in response to a known security issue, but because it seemed
like a prudent thing to do from a security perspective:
https://github.com/openai/codex/pull/4521), but we will continue to use
it in `codex-responses-api-proxy`. At some point, we probably want to
introduce a slightly different version of
`codex_process_hardening::pre_main_hardening()` in Codex CLI that
excludes said environment variables from the Codex process itself, but
continues to propagate them to subprocesses.
Handle null tool arguments in the MCP resource handler so optional
resource tools accept null without failing, preserving normal JSON
parsing for non-null payloads and improving robustness when models emit
null; this avoids spurious argument parse errors for list/read MCP
resource calls.
Elevated Sandbox NUX:
* prompt for elevated sandbox setup when agent mode is selected (via
/approvals or at startup)
* prompt for degraded sandbox if elevated setup is declined or fails
* introduce /elevate-sandbox command to upgrade from degraded
experience.
This seems to be necessary to get the Bazel builds on ARM Linux to go
green on https://github.com/openai/codex/pull/8875.
I don't feel great about timeout-whack-a-mole, but we're still learning
here...
Fix flakiness of CI test:
https://github.com/openai/codex/actions/runs/20350530276/job/58473691434?pr=8282
This PR does two things:
1. move the flakiness test to use responses API instead of chat
completion API
2. make mcp_process agnostic to the order of
responses/notifications/requests that come in, by buffering messages not
read
I have seen this test flake out sometimes when running the macOS build
using Bazel in CI: https://github.com/openai/codex/pull/8875. Perhaps
Bazel runs with greater parallelism, inducing a heavier load, causing an
issue?
Historically we started with a CodexAuth that knew how to refresh it's
own tokens and then added AuthManager that did a different kind of
refresh (re-reading from disk).
I don't think it makes sense for both `CodexAuth` and `AuthManager` to
be mutable and contain behaviors.
Move all refresh logic into `AuthManager` and keep `CodexAuth` as a data
object.
This updates core shell environment policy handling to match Windows
case-insensitive variable names and adds a Windows-only regression test,
so Path/TEMP are no longer dropped when inherit=core.
Fix flakiness of CI tests:
https://github.com/openai/codex/actions/runs/20350530276/job/58473691443?pr=8282
This PR does two things:
1. test with responses API instead of chat completions API in
thread_resume tests;
2. have a new responses API fixture that mocks out arbitrary numbers of
responses API calls (including no calls) and have the same repeated
response.
Tested by CI
**Before:**
```
Error loading configuration: value `Never` is not in the allowed set [OnRequest]
```
**After:**
```
Error loading configuration: invalid value for `approval_policy`: `Never` is not in the
allowed set [OnRequest] (set by MDM com.openai.codex:requirements_toml_base64)
```
Done by introducing a new struct `ConfigRequirementsWithSources` onto
which we `merge_unset_fields` now. Also introduces a pair of requirement
value and its `RequirementSource` (inspired by `ConfigLayerSource`):
```rust
pub struct Sourced<T> {
pub value: T,
pub source: RequirementSource,
}
```
## Summary
- avoid setting a new process group when stdio is inherited (keeps child
in foreground PG)
- keep process-group isolation when stdio is redirected so killpg
cleanup still works
- prevents macOS job-control SIGTTIN stops that look like hangs after
output
## Testing
- `cargo build -p codex-cli`
- `GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1
CARGO_BIN_EXE_codex=/Users/denis/Code/codex/codex-rs/target/debug/codex
/opt/homebrew/bin/timeout 30m cargo test -p codex-core -p codex-exec`
## Context
This fixes macOS sandbox hangs for commands like `elixir -v` / `erl
-noshell`, where the child was moved into a new process group while
still attached to the controlling TTY. See issue #8690.
## Authorship & collaboration
- This change and analysis were authored by **Codex** (AI coding agent).
- Human collaborator: @seeekr provided repro environment, context, and
review guidance.
- CLI used: `codex-cli 0.77.0`.
- Model: `gpt-5.2-codex (xhigh)`.
Co-authored-by: Eric Traut <etraut@openai.com>
Include project-level AGENTS.md and skills in /review sessions so the
review sub-agent uses the same instruction pipeline as standard runs,
keeping reviewer context aligned with normal sessions.
Sort list_dir entries before applying offset/limit so pagination matches
the displayed order, update pagination/truncation expectations, and add
coverage for sorted pagination. This ensures stable, predictable
directory pages when list_dir is enabled.
Add metrics capabilities to Codex. The `README.md` is up to date.
This will not be merged with the metrics before this PR of course:
https://github.com/openai/codex/pull/8350
## Summary
This PR builds _heavily_ on the work from @occurrent in #8021 - I've
only added a small fix, added additional tests, and propagated the
changes to tui2.
From the original PR:
> On Windows, Codex relies on PasteBurst for paste detection because
bracketed paste is not reliably available via crossterm.
>
> When pasted content starts with non-ASCII characters, input is routed
through handle_non_ascii_char, which bypasses the normal paste burst
logic. This change extends the paste burst window for that path, which
should ensure that Enter is correctly grouped as part of the paste.
## Testing
- [x] tested locally cross-platform
- [x] added regression tests
---------
Co-authored-by: occur <occurring@outlook.com>
https://github.com/openai/codex/pull/8879 introduced the
`find_resource!` macro, but now that I am about to use it in more
places, I realize that it should take care of this normalization case
for callers.
Note the `use $crate::path_absolutize::Absolutize;` line is there so
that users of `find_resource!` do not have to explicitly include
`path-absolutize` to their own `Cargo.toml`.
To support Bazelification in https://github.com/openai/codex/pull/8875,
this PR introduces a new `find_resource!` macro that we use in place of
our existing logic in tests that looks for resources relative to the
compile-time `CARGO_MANIFEST_DIR` env var.
To make this work, we plan to add the following to all `rust_library()`
and `rust_test()` Bazel rules in the project:
```
rustc_env = {
"BAZEL_PACKAGE": native.package_name(),
},
```
Our new `find_resource!` macro reads this value via
`option_env!("BAZEL_PACKAGE")` so that the Bazel package _of the code
using `find_resource!`_ is injected into the code expanded from the
macro. (If `find_resource()` were a function, then
`option_env!("BAZEL_PACKAGE")` would always be
`codex-rs/utils/cargo-bin`, which is not what we want.)
Note we only consider the `BAZEL_PACKAGE` value when the `RUNFILES_DIR`
environment variable is set at runtime, indicating that the test is
being run by Bazel. In this case, we have to concatenate the runtime
`RUNFILES_DIR` with the compile-time `BAZEL_PACKAGE` value to build the
path to the resource.
In testing this change, I discovered one funky edge case in
`codex-rs/exec-server/tests/common/lib.rs` where we have to _normalize_
(but not canonicalize!) the result from `find_resource!` because the
path contains a `common/..` component that does not exist on disk when
the test is run under Bazel, so it must be semantically normalized using
the [`path-absolutize`](https://crates.io/crates/path-absolutize) crate
before it is passed to `dotslash fetch`.
Because this new behavior may be non-obvious, this PR also updates
`AGENTS.md` to make humans/Codex aware that this API is preferred.
The Bazelification work in-flight over at
https://github.com/openai/codex/pull/8832 needs this fix so that Bazel
can find the path to the DotSlash file for `bash`.
With this change, the following almost works:
```
bazel test --test_output=errors //codex-rs/exec-server:exec-server-all-test
```
That is, now the `list_tools` test passes, but
`accept_elicitation_for_prompt_rule` still fails because it runs
Seatbelt itself, so it needs to be run outside Bazel's local sandboxing.
Skills discovery now follows symlink entries for SkillScope::User
($CODEX_HOME/skills) and SkillScope::Admin (e.g. /etc/codex/skills).
Added cycle protection: directories are canonicalized and tracked in a
visited set to prevent infinite traversal from circular links.
Added per-root traversal limits to avoid accidentally scanning huge
trees:
- max depth: 6
- max directories: 2000 (logs a warning if truncated)
For now, symlink stat failures and traversal truncation are logged
rather than surfaced as UI “invalid SKILL.md” warnings.
<img width="763" height="349" alt="Screenshot 2026-01-07 at 18 37 59"
src="https://github.com/user-attachments/assets/569d01cb-ea91-4113-889b-ba74df24adaf"
/>
It may not make sense to use the `/model` menu with a custom
OPENAI_BASE_URL. But some model proxies may support it, so we shouldn't
disable it completely. A warning is a reasonable compromise.
Fixes#8609
# Summary
Emphasize single-line name/description values and quoting when values
could be interpreted as YAML syntax.
# Testing
Not run (skill-only change.)
Adds a new feature
`enable_request_compression` that will compress using zstd requests to
the codex-backend. Currently only enabled for codex-backend so only enabled for openai providers when using chatgpt::auth even when the feature is enabled
Added a new info log line too for evaluating the compression ratio and
overhead off compressing before requesting. You can enable with
`RUST_LOG=$RUST_LOG,codex_client::transport=info`
```
2026-01-06T00:09:48.272113Z INFO codex_client::transport: Compressed request body with zstd pre_compression_bytes=28914 post_compression_bytes=11485 compression_duration_ms=0
```
We used to override truncation policy by comparing model info vs config
value in context manager. A better way to do it is to construct model
info using the config value
**Summary**
This PR makes “ApprovalDecision::AcceptForSession / don’t ask again this
session” actually work for `apply_patch` approvals by caching approvals
based on absolute file paths in codex-core, properly wiring it through
app-server v2, and exposing the choice in both TUI and TUI2.
- This brings `apply_patch` calls to be at feature-parity with general
shell commands, which also have a "Yes, and don't ask again" option.
- This also fixes VSCE's "Allow this session" button to actually work.
While we're at it, also split the app-server v2 protocol's
`ApprovalDecision` enum so execpolicy amendments are only available for
command execution approvals.
**Key changes**
- Core: per-session patch approval allowlist keyed by absolute file
paths
- Handles multi-file patches and renames/moves by recording both source
and destination paths for `Update { move_path: Some(...) }`.
- Extend the `Approvable` trait and `ApplyPatchRuntime` to work with
multiple keys, because an `apply_patch` tool call can modify multiple
files. For a request to be auto-approved, we will need to check that all
file paths have been approved previously.
- App-server v2: honor AcceptForSession for file changes
- File-change approval responses now map AcceptForSession to
ReviewDecision::ApprovedForSession (no longer downgraded to plain
Approved).
- Replace `ApprovalDecision` with two enums:
`CommandExecutionApprovalDecision` and `FileChangeApprovalDecision`
- TUI / TUI2: expose “don’t ask again for these files this session”
- Patch approval overlays now include a third option (“Yes, and don’t
ask again for these files this session (s)”).
- Snapshot updates for the approval modal.
**Tests added/updated**
- Core:
- Integration test that proves ApprovedForSession on a patch skips the
next patch prompt for the same file
- App-server:
- v2 integration test verifying
FileChangeApprovalDecision::AcceptForSession works properly
**User-visible behavior**
- When the user approves a patch “for session”, future patches touching
only those previously approved file(s) will no longer prompt gain during
that session (both via app-server v2 and TUI/TUI2).
**Manual testing**
Tested both TUI and TUI2 - see screenshots below.
TUI:
<img width="1082" height="355" alt="image"
src="https://github.com/user-attachments/assets/adcf45ad-d428-498d-92fc-1a0a420878d9"
/>
TUI2:
<img width="1089" height="438" alt="image"
src="https://github.com/user-attachments/assets/dd768b1a-2f5f-4bd6-98fd-e52c1d3abd9e"
/>
> // todo(aibrahim): why are we passing model here while it can change?
we update it on each turn with `.with_model`
> //TODO(aibrahim): run CI in release mode.
although it's good to have, release builds take double the time tests
take.
> // todo(aibrahim): make this async function
we figured out another way of doing this sync
- Merge ModelFamily into ModelInfo
- Remove logic for adding instructions to apply patch
- Add compaction limit and visible context window to `ModelInfo`
See https://rustsec.org/advisories/RUSTSEC-2026-0002.
Though our `ratatui` fork has a transitive dep on an older version of
the `lru` crate, so to get CI green ASAP, this PR also adds an exception
to `deny.toml` for `RUSTSEC-2026-0002`, but hopefully this will be
short-lived.
Fixes CodexExec to avoid missing early process exits by registering the
exit handler up front and deferring the error until after stdout is
drained, and adds a regression test that simulates a fast-exit child
while still producing output so hangs are caught.
Handle /review <instructions> in the TUI and TUI2 by routing it as a
custom review command instead of plain text, wiring command dispatch and
adding composer coverage so typing /review text starts a review directly
rather than posting a message. User impact: /review with arguments now
kicks off the review flow, previously it would just forward as a plain
command and not actually start a review.
Fixes apply.rs path parsing so
- quoted diff headers are tokenized and extracted correctly,
- /dev/null headers are ignored before prefix stripping to avoid bogus
dev/null paths, and
- git apply output paths are unescaped from C-style quoting.
**Why**
This prevents potentially missed staging and misclassified paths when
applying or reverting patches, which could lead to incorrect behavior
for repos with spaces or escaped characters in filenames.
**Impact**
I checked and this is only used in the cloud tasks support and `codex
apply <task_id>` flow.
Done to avoid spammy warnings to end up in the model context without
having to switch to nightly
```
Warning: can't set `imports_granularity = Item`, unstable features are only available in nightly channel.
```
Set login=false for the shell tool in the timing-based parallelism test
so it does not depend on slow user login shells, making the test
deterministic without user-facing changes. This prevents occasional
flakes when running locally.
With `config.toml`:
```
model = "gpt-5.1-codex"
```
(where `gpt-5.1-codex` has `show_in_picker: false` in
[`model_presets.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/src/models_manager/model_presets.rs);
this happens if the user hasn't used codex in a while so they didn't see
the popup before their model was changed to `show_in_picker: false`)
The upgrade picker used to not show (because `gpt-5.1-codex` was
filtered out of the model list in code). Now, the filtering is done
downstream in tui and app-server, so the model upgrade popup shows:
<img width="1503" height="227" alt="Screenshot 2026-01-06 at 5 04 37 PM"
src="https://github.com/user-attachments/assets/26144cc2-0b3f-4674-ac17-e476781ec548"
/>
Use the contents of the commit message from the commit associated with
the tag (that contains the version bump) as the release notes by writing
them to a file and then specifying the file as the `body_path` of
`softprops/action-gh-release@v2`.
Add `web_search_cached` feature to config. Enables `web_search` tool
with access only to cached/indexed results (see
[docs](https://platform.openai.com/docs/guides/tools-web-search#live-internet-access)).
This takes precedence over the existing `web_search_request`, which
continues to enable `web_search` over live results as it did before.
`web_search_cached` is disabled for review mode, as `web_search_request`
is.
Add `thread/rollback` to app-server to support IDEs undo-ing the last N
turns of a thread.
For context, an IDE partner will be supporting an "undo" capability
where the IDE (the app-server client) will be responsible for reverting
the local changes made during the last turn. To support this well, we
also need a way to drop the last turn (or more generally, the last N
turns) from the agent's context. This is what `thread/rollback` does.
**Core idea**: A Thread rollback is represented as a persisted event
message (EventMsg::ThreadRollback) in the rollout JSONL file, not by
rewriting history. On resume, both the model's context (core replay) and
the UI turn list (app-server v2's thread history builder) apply these
markers so the pruned history is consistent across live conversations
and `thread/resume`.
Implementation notes:
- Rollback only affects agent context and appends to the rollout file;
clients are responsible for reverting files on disk.
- If a thread rollback is currently in progress, subsequent
`thread/rollback` calls are rejected.
- Because we use `CodexConversation::submit` and codex core tracks
active turns, returning an error on concurrent rollbacks is communicated
via an `EventMsg::Error` with a new variant
`CodexErrorInfo::ThreadRollbackFailed`. app-server watches for that and
sends the BAD_REQUEST RPC response.
Tests cover thread rollbacks in both core and app-server, including when
`num_turns` > existing turns (which clears all turns).
**Note**: this explicitly does **not** behave like `/undo` which we just
removed from the CLI, which does the opposite of what `thread/rollback`
does. `/undo` reverts local changes via ghost commits/snapshots and does
not modify the agent's context / conversation history.
### Motivation
- Fix a visual bug where transcript text could bleed through the
on-screen copy "pill" overlay.
- Ensure the copy affordance fully covers the underlying buffer so the
pill background is solid and consistent with styling.
- Document the approach in-code to make the background-clearing
rationale explicit.
### Description
- Clear the pill area before drawing by iterating `Rect::positions()`
and calling `cell.set_symbol(" ")` and `cell.set_style(base_style)` in
`render_copy_pill` in `transcript_copy_ui.rs`.
- Added an explanatory comment for why the pill background is explicitly
cleared.
- Added a unit test `copy_pill_clears_background` and committed the
corresponding snapshot file to validate the rendering behavior.
### Testing
- Ran `just fmt` (formatting completed; non-blocking environment warning
may appear).
- Ran `just fix -p codex-tui2` to apply lints/fixes (completed).
- Ran `cargo test -p codex-tui2` and all tests passed (snapshot updated
and tests succeeded).
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_695c9b23e9b8832997d5a457c4d83410)
Added an agent control plane that lets sessions spawn or message other
conversations via `AgentControl`.
`AgentBus` (core/src/agent/bus.rs) keeps track of the last known status
of a conversation.
ConversationManager now holds shared state behind an Arc so AgentControl
keeps only a weak back-reference, the goal is just to avoid explicit
cycle reference.
Follow-ups:
* Build a small tool in the TUI to be able to see every agent and send
manual message to each of them
* Handle approval requests in this TUI
* Add tools to spawn/communicate between agents (see related design)
* Define agent types
Force an announcement tooltip in the CLI. This query the gh repo on this
[file](https://raw.githubusercontent.com/openai/codex/main/announcement_tip.toml)
which contains announcements in TOML looking like this:
```
# Example announcement tips for Codex TUI.
# Each [[announcements]] entry is evaluated in order; the last matching one is shown.
# Dates are UTC, formatted as YYYY-MM-DD. The from_date is inclusive and the to_date is exclusive.
# version_regex matches against the CLI version (env!("CARGO_PKG_VERSION")); omit to apply to all versions.
# target_app specify which app should display the announcement (cli, vsce, ...).
[[announcements]]
content = "Welcome to Codex! Check out the new onboarding flow."
from_date = "2024-10-01"
to_date = "2024-10-15"
version_regex = "^0\\.0\\.0$"
target_app = "cli"
```
To make this efficient, the announcement is queried on a best effort
basis at the launch of the CLI (no refresh made after this).
This is done in an async way and we display the announcement (with 100%
probability) iff the announcement is available, the cache is correctly
warmed and there is a matching announcement (matching is recomputed for
each new session).
Fixes ReadinessFlag::subscribe to avoid handing out token 0 or duplicate
tokens on i32 wrap-around, adds regression tests, and prevents readiness
gates from getting stuck waiting on an unmarkable or mis-authorized
token.
Background
Streaming assistant prose in tui2 was being rendered with viewport-width
wrapping during streaming, then stored in history cells as already split
`Line`s. Those width-derived breaks became indistinguishable from hard
newlines, so the transcript could not "un-split" on resize. This also
degraded copy/paste, since soft wraps looked like hard breaks.
What changed
- Introduce width-agnostic `MarkdownLogicalLine` output in
`tui2/src/markdown_render.rs`, preserving markdown wrap semantics:
initial/subsequent indents, per-line style, and a preformatted flag.
- Update the streaming collector (`tui2/src/markdown_stream.rs`) to emit
logical lines (newline-gated) and remove any captured viewport width.
- Update streaming orchestration (`tui2/src/streaming/*`) to queue and
emit logical lines, producing `AgentMessageCell::new_logical(...)`.
- Make `AgentMessageCell` store logical lines and wrap at render time in
`HistoryCell::transcript_lines_with_joiners(width)`, emitting joiners so
copy/paste can join soft-wrap continuations correctly.
Overlay deferral
When an overlay is active, defer *cells* (not rendered `Vec<Line>`) and
render them at overlay close time. This avoids baking width-derived
wraps based on a stale width.
Tests + docs
- Add resize/reflow regression tests + snapshots for streamed agent
output.
- Expand module/API docs for the new logical-line streaming pipeline and
clarify joiner semantics.
- Align scrollback-related docs/comments with current tui2 behavior
(main draw loop does not flush queued "history lines" to the terminal).
More details
See `codex-rs/tui2/docs/streaming_wrapping_design.md` for the full
problem statement and solution approach, and
`codex-rs/tui2/docs/tui_viewport_and_history.md` for viewport vs printed
output behavior.
Trim whitespace when validating '*** Begin Patch'/'*** End Patch'
markers in codex-apply-patch so padded marker lines parse as intended,
and add regression coverage (unit + fixture scenario); this avoids
apply_patch failures when models include extra spacing. Tested with
cargo test -p codex-apply-patch.
**Motivation**
- Bring `codex exec resume` to parity with top‑level flags so global
options (git check bypass, json, model, sandbox toggles) work after the
subcommand, including when outside a git repo.
**Description**
- Exec CLI: mark `--skip-git-repo-check`, `--json`, `--model`,
`--full-auto`, and `--dangerously-bypass-approvals-and-sandbox` as
global so they’re accepted after `resume`.
- Tests: add `exec_resume_accepts_global_flags_after_subcommand` to
verify those flags work when passed after `resume`.
**Testing**
- `just fmt`
- `cargo test -p codex-exec` (pass; ran with elevated perms to allow
network/port binds)
- Manual: exercised `codex exec resume` with global flags after the
subcommand to confirm behavior.
We've seen reports that people who try to login on a remote/headless
machine will open the login link on their own machine and got errors.
Update the instructions to ask those users to use `codex login
--device-auth` instead.
<img width="1434" height="938" alt="CleanShot 2026-01-05 at 11 35 02@2x"
src="https://github.com/user-attachments/assets/2b209953-6a42-4eb0-8b55-bb0733f2e373"
/>
Adds an optional `justification` parameter to the `prefix_rule()`
execpolicy DSL so policy authors can attach human-readable rationale to
a rule. That justification is propagated through parsing/matching and
can be surfaced to the model (or approval UI) when a command is blocked
or requires approval.
When a command is rejected (or gated behind approval) due to policy, a
generic message makes it hard for the model/user to understand what went
wrong and what to do instead. Allowing policy authors to supply a short
justification improves debuggability and helps guide the model toward
compliant alternatives.
Example:
```python
prefix_rule(
pattern = ["git", "push"],
decision = "forbidden",
justification = "pushing is blocked in this repo",
)
```
If Codex tried to run `git push origin main`, now the failure would
include:
```
`git push origin main` rejected: pushing is blocked in this repo
```
whereas previously, all it was told was:
```
execpolicy forbids this command
```
The elevated sandbox creates two new Windows users - CodexSandboxOffline
and CodexSandboxOnline. This is necessary, so this PR does all that it
can to "hide" those users. It uses the registry plus directory flags (on
their home directories) to get them to show up as little as possible.
## Summary
When using device-code login with a custom issuer
(`--experimental_issuer`), Codex correctly uses that issuer for the auth
flow — but the **terminal prompt still told users to open the default
OpenAI device URL** (`https://auth.openai.com/codex/device`). That’s
confusing and can send users to the **wrong domain** (especially for
enterprise/staging issuers). This PR updates the prompt (and related
URLs) to consistently use the configured issuer. 🎯
---
## 🔧 What changed
* 🔗 **Device auth prompt link** now uses the configured issuer (instead
of a hard-coded OpenAI URL)
* 🧭 **Redirect callback URL** is derived from the same issuer for
consistency
* 🧼 Minor cleanup: normalize the issuer base URL once and reuse it
(avoids formatting quirks like trailing `/`)
---
## 🧪 Repro + Before/After
### ▶️ Command
```bash
codex login --device-auth --experimental_issuer https://auth.example.com
```
### ❌ Before (wrong link shown)
```text
1. Open this link in your browser and sign in to your account
https://auth.openai.com/codex/device
```
### ✅ After (correct link shown)
```text
1. Open this link in your browser and sign in to your account
https://auth.example.com/codex/device
```
Full example output (same as before, but with the correct URL):
```text
Welcome to Codex [v0.72.0]
OpenAI's command-line coding agent
Follow these steps to sign in with ChatGPT using device code authorization:
1. Open this link in your browser and sign in to your account
https://auth.example.com/codex/device
2. Enter this one-time code (expires in 15 minutes)
BUT6-0M8K4
Device codes are a common phishing target. Never share this code.
```
---
## ✅ Test plan
* 🟦 `codex login --device-auth` (default issuer): output remains
unchanged
* 🟩 `codex login --device-auth --experimental_issuer
https://auth.example.com`:
* prompt link points to the issuer ✅
* callback URL is derived from the same issuer ✅
* no double slashes / mismatched domains ✅
Co-authored-by: Eric Traut <etraut@openai.com>
This change improves the skills render section
- Separate the skills list from usage rules with clear subheadings
- Define skill more clearly upfront
- Remove confusing trigger/discovery wording and make reference-following guidance more actionable
Never treat .codex or .codex/.sandbox as a workspace root.
Handle write permissions to .codex/.sandbox in a single method so that
the sandbox setup/runner can write logs and other setup files to that
directory.
What changed
- Added `outputSchema` support to the app-server APIs, mirroring `codex
exec --output-schema` behavior.
- V1 `sendUserTurn` now accepts `outputSchema` and constrains the final
assistant message for that turn.
- V2 `turn/start` now accepts `outputSchema` and constrains the final
assistant message for that turn (explicitly per-turn only).
Core behavior
- `Op::UserTurn` already supported `final_output_json_schema`; now V1
`sendUserTurn` forwards `outputSchema` into that field.
- `Op::UserInput` now carries `final_output_json_schema` for per-turn
settings updates; core maps it into
`SessionSettingsUpdate.final_output_json_schema` so it applies to the
created turn context.
- V2 `turn/start` does NOT persist the schema via `OverrideTurnContext`
(it’s applied only for the current turn). Other overrides
(cwd/model/etc) keep their existing persistent behavior.
API / docs
- `codex-rs/app-server-protocol/src/protocol/v1.rs`: add `output_schema:
Option<serde_json::Value>` to `SendUserTurnParams` (serialized as
`outputSchema`).
- `codex-rs/app-server-protocol/src/protocol/v2.rs`: add `output_schema:
Option<JsonValue>` to `TurnStartParams` (serialized as `outputSchema`).
- `codex-rs/app-server/README.md`: document `outputSchema` for
`turn/start` and clarify it applies only to the current turn.
- `codex-rs/docs/codex_mcp_interface.md`: document `outputSchema` for v1
`sendUserTurn` and v2 `turn/start`.
Tests added/updated
- New app-server integration tests asserting `outputSchema` is forwarded
into outbound `/responses` requests as `text.format`:
- `codex-rs/app-server/tests/suite/output_schema.rs`
- `codex-rs/app-server/tests/suite/v2/output_schema.rs`
- Added per-turn semantics tests (schema does not leak to the next
turn):
- `send_user_turn_output_schema_is_per_turn_v1`
- `turn_start_output_schema_is_per_turn_v2`
- Added protocol wire-compat tests for the merged op:
- serialize omits `final_output_json_schema` when `None`
- deserialize works when field is missing
- serialize includes `final_output_json_schema` when `Some(schema)`
Call site updates (high level)
- Updated all `Op::UserInput { .. }` constructions to include
`final_output_json_schema`:
- `codex-rs/app-server/src/codex_message_processor.rs`
- `codex-rs/core/src/codex_delegate.rs`
- `codex-rs/mcp-server/src/codex_tool_runner.rs`
- `codex-rs/tui/src/chatwidget.rs`
- `codex-rs/tui2/src/chatwidget.rs`
- plus impacted core tests.
Validation
- `just fmt`
- `cargo test -p codex-core`
- `cargo test -p codex-app-server`
- `cargo test -p codex-mcp-server`
- `cargo test -p codex-tui`
- `cargo test -p codex-tui2`
- `cargo test -p codex-protocol`
- `cargo clippy --all-features --tests --profile dev --fix -- -D
warnings`
Load managed requirements from MDM key `requirements_toml_base64`.
Tested on my Mac (using `defaults` to set the preference, though this
would be set by MDM in production):
```
➜ codex git:(gt/mdm-requirements) defaults read com.openai.codex requirements_toml_base64 | base64 -d
allowed_approval_policies = ["on-request"]
➜ codex git:(gt/mdm-requirements) just c --yolo
cargo run --bin codex -- "$@"
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.26s
Running `target/debug/codex --yolo`
Error loading configuration: value `Never` is not in the allowed set [OnRequest]
error: Recipe `codex` failed on line 11 with exit code 1
➜ codex git:(gt/mdm-requirements) defaults delete com.openai.codex requirements_toml_base64
➜ codex git:(gt/mdm-requirements) just c --yolo
cargo run --bin codex -- "$@"
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/codex --yolo`
╭──────────────────────────────────────────────────────────╮
│ >_ OpenAI Codex (v0.0.0) │
│ │
│ model: codex-auto-balanced medium /model to change │
│ directory: ~/code/codex/codex-rs │
╰──────────────────────────────────────────────────────────╯
Tip: Start a fresh idea with /new; the previous session stays in history.
```
Context
- This code parses Server-Sent Events (SSE) from the legacy Chat
Completions streaming API (wire_api = "chat").
- The upstream protocol terminates a stream with a final sentinel event:
data: [DONE].
- Some of our test stubs/helpers historically end the stream with data:
DONE (no brackets).
How this was found
- GitHub Actions on Windows failed in codex-app-server integration tests
with wiremock verification errors (expected multiple POSTs, got 1).
Diagnosis
- The job logs included: codex_api::sse::chat: Failed to parse
ChatCompletions SSE event ... data: DONE.
- eventsource_stream surfaces the sentinel as a normal SSE event; it
does not automatically close the stream.
- The parser previously attempted to JSON-decode every data: payload.
The sentinel is not JSON, so we logged and skipped it, then continued
polling.
- On servers that keep the HTTP connection open after emitting the
sentinel (notably wiremock on Windows), skipping the sentinel meant we
never emitted ResponseEvent::Completed.
- Higher layers wait for completion before progressing (emitting
approval requests and issuing follow-up model calls), so the test never
reached the subsequent requests and wiremock panicked when its
expected-call count was not met.
Fix
- Treat both data: [DONE] and data: DONE as explicit end-of-stream
sentinels.
- When a sentinel is seen, flush any pending assistant/reasoning items
and emit ResponseEvent::Completed once.
Tests
- Add a regression unit test asserting we complete on the sentinel even
if the underlying connection is not closed.
## Summary
- Add a transcript scrollbar in `tui2` using `tui-scrollbar`.
- Reserve 2 columns on the right (1 empty gap + 1 scrollbar track) and
plumb the reduced width through wrapping/selection/copy so rendering and
interactions match.
- Auto-hide the scrollbar when the transcript is pinned to the bottom
(columns remain reserved).
- Add mouse click/drag support for the scrollbar, with pointer-capture
so drags don’t fall through into transcript selection.
- Skip scrollbar hit-testing when auto-hidden to avoid an invisible
interactive region.
## Notes
- Styling is theme-aware: in light themes the thumb is darker than the
track; in dark themes it reads as an “indented” element without going
full-white.
- Pre-Ratatui 0.30 (ratatui-core split) requires a small scratch-buffer
bridge; this should simplify once we move to Ratatui 0.30.
## Testing
- `just fmt`
- `just fix -p codex-tui2 --allow-no-vcs`
- `cargo test -p codex-tui2`
The Responses API requires that all tool names conform to
'^[a-zA-Z0-9_-]+$'. This PR replaces all non-conforming characters with
`_` to ensure that they can be used.
Fixes#8174
Fixes /review base-branch prompt resolution to use the session/turn cwd
(respecting runtime cwd overrides) so merge-base/diff guidance is
computed from the intended repo; adds a regression test for cwd
overrides; tested with cargo test -p codex-core --test all
review_uses_overridden_cwd_for_base_branch_merge_base.
Fix this: https://github.com/openai/codex/issues/8479
The issue is that chat completion API expect all the tool calls in a
single assistant message and then all the tool call output in a single
response message
Bumps [tokio-stream](https://github.com/tokio-rs/tokio) from 0.1.17 to
0.1.18.
<details>
<summary>Commits</summary>
<ul>
<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><a
href="ea6b144cd1"><code>ea6b144</code></a>
ci: freeze rustc on nightly-2025-01-25 in <code>netlify.toml</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7652">#7652</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-stream-0.1.17...tokio-stream-0.1.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 [clap_complete](https://github.com/clap-rs/clap) from 4.5.57 to
4.5.64.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e115243369"><code>e115243</code></a>
chore: Release</li>
<li><a
href="d4c34fa2b8"><code>d4c34fa</code></a>
docs: Update changelog</li>
<li><a
href="ab4f438860"><code>ab4f438</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6203">#6203</a>
from jpgrayson/fix/zsh-space-after-dir-completions</li>
<li><a
href="5571b83c8a"><code>5571b83</code></a>
fix(complete): Trailing space after zsh directory completions</li>
<li><a
href="06a2311586"><code>06a2311</code></a>
chore: Release</li>
<li><a
href="bed131f7ae"><code>bed131f</code></a>
docs: Update changelog</li>
<li><a
href="a61c53e6dd"><code>a61c53e</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6202">#6202</a>
from iepathos/6201-symlink-path-completions</li>
<li><a
href="c3b440570e"><code>c3b4405</code></a>
fix(complete): Follow symlinks in path completion</li>
<li><a
href="a794395340"><code>a794395</code></a>
test(complete): Add symlink path completion tests</li>
<li><a
href="ca0aeba31f"><code>ca0aeba</code></a>
chore: Release</li>
<li>Additional commits viewable in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.5.57...clap_complete-v4.5.64">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>
Clicking the transcript copy pill or pressing the copy shortcut now
copies the selected transcript text and clears the highlight.
Show transient footer feedback ("Copied"/"Copy failed") after a copy
attempt, with logic in transcript_copy_action to keep app.rs smaller and
closer to tui for long-term diffs.
Update footer snapshots and add tiny unit tests for feedback expiry.
https://github.com/user-attachments/assets/c36c8163-11c5-476b-b388-e6fbe0ff6034
When the selection ends on the last visible row, the copy affordance had
no space below and never rendered. Fall back to placing it above (or on
the same row for 1-row viewports) and add a regression test.
Mouse/trackpad scrolling in tui2 applies deltas in visual lines, but the
transcript scroll state was anchored only to CellLine entries.
When a 1-line scroll landed on the synthetic inter-cell Spacer row
(inserted between non-continuation cells),
`TranscriptScroll::anchor_for` would skip that row and snap back to the
adjacent cell line. That makes the resolved top offset unchanged for
small/coalesced scroll deltas, so scrolling appears to get stuck right
before certain cells (commonly user prompts and command output cells).
Fix this by making spacer rows a first-class scroll anchor:
- Add `TranscriptScroll::ScrolledSpacerBeforeCell` and resolve it back
to the spacer row index when present.
- Update `anchor_for`/`scrolled_by` to preserve spacers instead of
skipping them.
- Treat the new variant as "already anchored" in
`lock_transcript_scroll_to_current_view`.
Tests:
- cargo test -p codex-tui2
## Summary
Forked repositories inherit GitHub Actions workflows including scheduled
ones. This causes:
1. **Wasted Actions minutes** - Scheduled workflows run on forks even
though they will fail
2. **Failed runs** - Workflows requiring `CODEX_OPENAI_API_KEY` fail
immediately on forks
3. **Noise** - Fork owners see failed workflow runs they didn't trigger
This PR adds `if: github.repository == 'openai/codex'` guards to
workflows that should only run on the upstream repository.
### Affected workflows
| Workflow | Trigger | Issue |
|----------|---------|-------|
| `rust-release-prepare` | `schedule: */4 hours` | Runs 6x/day on every
fork |
| `close-stale-contributor-prs` | `schedule: daily` | Runs daily on
every fork |
| `issue-deduplicator` | `issues: opened` | Requires
`CODEX_OPENAI_API_KEY` |
| `issue-labeler` | `issues: opened` | Requires `CODEX_OPENAI_API_KEY` |
### Note
`cla.yml` already has this guard (`github.repository_owner ==
'openai'`), so it was not modified.
## Test plan
- [ ] Verify workflows still run correctly on `openai/codex`
- [ ] Verify workflows are skipped on forks (can check via Actions tab
on any fork)
The transcript viewport draws every frame. Ratatui's Line::render_ref
does grapheme segmentation and span layout, so repeated redraws can burn
CPU during streaming even when the visible transcript hasn't changed.
Introduce TranscriptViewCache to reduce per-frame work:
- WrappedTranscriptCache memoizes flattened+wrapped transcript lines per
width, appends incrementally as new cells arrive, and rebuilds on width
change, truncation (backtrack), or transcript replacement.
- TranscriptRasterCache caches rasterized rows (Vec<Cell>) per line
index and user-row styling; redraws copy cells instead of rerendering
spans.
The caches are width-scoped and store base transcript content only;
selection highlighting and copy affordances are applied after drawing.
User rows include the row-wide base style in the cached raster.
Refactor transcript_render to expose append_wrapped_transcript_cell for
incremental building and add a test that incremental append matches the
full build.
Add docs/tui2/performance-testing.md as a playbook for macOS sample
profiles and hotspot greps.
Expand transcript_view_cache tests to cover rebuild conditions, raster
equivalence vs direct rendering, user-row caching, and eviction.
Test: cargo test -p codex-tui2
last token count in context manager is initialized to 0. Gets populated
only on events from server.
This PR populates it on resume so we can decide if we need to compact or
not.
This reduces unnecessary frame scheduling in codex-tui2.
Changes:
- Gate redraw scheduling for streaming deltas when nothing visible
changes.
- Avoid a redraw feedback loop from footer transcript UI state updates.
Why:
- Streaming deltas can arrive at very high frequency; redrawing on every
delta can drive a near-constant render loop.
- BottomPane was requesting another frame after every Draw even when the
derived transcript UI state was unchanged.
Testing:
- cargo test -p codex-tui2
Manual sampling:
- sample "$(pgrep -n codex-tui2)" 3 -file
/tmp/tui2.idle.after.sample.txt
- sample "$(pgrep -n codex-tui2)" 3 -file
/tmp/tui2.streaming.after.sample.txt
This eliminates redundant user documentation and allows us to focus our
documentation investments.
I left tombstone files for most of the existing ".md" docs files to
avoid broken links. These now contain brief links to the developers docs
site.
This is more future-proof if we ever decide to add additional Sandbox
Users for new functionality
This also moves some more user-related code into a new file for code
cleanliness
Bumps [regex-lite](https://github.com/rust-lang/regex) from 0.1.7 to
0.1.8.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/regex/blob/master/CHANGELOG.md">regex-lite's
changelog</a>.</em></p>
<blockquote>
<h1>0.1.80</h1>
<ul>
<li>[PR <a
href="https://redirect.github.com/rust-lang/regex/issues/292">#292</a>](<a
href="https://redirect.github.com/rust-lang/regex/pull/292">rust-lang/regex#292</a>):
Fixes bug <a
href="https://redirect.github.com/rust-lang/regex/issues/291">#291</a>,
which was introduced by PR <a
href="https://redirect.github.com/rust-lang/regex/issues/290">#290</a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="140f8949da"><code>140f894</code></a>
regex-lite-0.1.8</li>
<li><a
href="27d6d65263"><code>27d6d65</code></a>
1.12.1</li>
<li><a
href="85398ad500"><code>85398ad</code></a>
changelog: 1.12.1</li>
<li><a
href="764efbd305"><code>764efbd</code></a>
api: tweak the lifetime of <code>Captures::get_match</code></li>
<li><a
href="ee6aa55e01"><code>ee6aa55</code></a>
rure-0.2.4</li>
<li><a
href="42076c6bca"><code>42076c6</code></a>
1.12.0</li>
<li><a
href="aef2153e31"><code>aef2153</code></a>
deps: bump to regex-automata 0.4.12</li>
<li><a
href="459dbbeaa9"><code>459dbbe</code></a>
regex-automata-0.4.12</li>
<li><a
href="610bf2d76e"><code>610bf2d</code></a>
regex-syntax-0.8.7</li>
<li><a
href="7dbb384dd0"><code>7dbb384</code></a>
changelog: 1.12.0</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-lang/regex/compare/regex-lite-0.1.7...regex-lite-0.1.8">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 [toml_edit](https://github.com/toml-rs/toml) from 0.23.7 to
0.24.0+spec-1.1.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2e09401567"><code>2e09401</code></a>
chore: Release</li>
<li><a
href="e32c7a2f9b"><code>e32c7a2</code></a>
chore: Release</li>
<li><a
href="df1c3286de"><code>df1c328</code></a>
docs: Update changelog</li>
<li><a
href="b826cf4914"><code>b826cf4</code></a>
feat(edit)!: Allow <code>set_position(None)</code> (<a
href="https://redirect.github.com/toml-rs/toml/issues/1080">#1080</a>)</li>
<li><a
href="8043f20af7"><code>8043f20</code></a>
feat(edit)!: Allow <code>set_position(None)</code></li>
<li><a
href="a02c0db59f"><code>a02c0db</code></a>
feat: Support TOML 1.1 (<a
href="https://redirect.github.com/toml-rs/toml/issues/1079">#1079</a>)</li>
<li><a
href="5cfb838b15"><code>5cfb838</code></a>
feat(edit): Support TOML 1.1</li>
<li><a
href="1eb4d606d3"><code>1eb4d60</code></a>
feat(toml): Support TOML 1.1</li>
<li><a
href="695d7883d8"><code>695d788</code></a>
feat(edit)!: Multi-line inline tables with trailing commas</li>
<li><a
href="cc4f7acd94"><code>cc4f7ac</code></a>
feat(toml): Multi-line inline tables with trailing commas</li>
<li>Additional commits viewable in <a
href="https://github.com/toml-rs/toml/compare/v0.23.7...v0.24.0">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>
I attempted to build codex on LoongArch Linux and encountered
compilation errors.
After investigation, the errors were traced to certain `windows-sys`
features
which rely on platform-specific cfgs that only support x86 and aarch64.
With this change applied, the project now builds and runs successfully
on my
platform:
- OS: AOSC OS (loongarch64)
- Kernel: Linux 6.17
- CPU: Loongson-3A6000
Please let me know if this approach is reasonable, or if there is a
better way
to support additional platforms.
### What
Builds on #8293.
Add `additional_details`, which contains the upstream error message, to
relevant structures used to pass along retryable `StreamError`s.
Uses the new TUI status indicator's `details` field (shows under the
status header) to display the `additional_details` error to the user on
retryable `Reconnecting...` errors. This adds clarity for users for
retryable errors.
Will make corresponding change to VSCode extension to show
`additional_details` as expandable from the `Reconnecting...` cell.
Examples:
<img width="1012" height="326" alt="image"
src="https://github.com/user-attachments/assets/f35e7e6a-8f5e-4a2f-a764-358101776996"
/>
<img width="1526" height="358" alt="image"
src="https://github.com/user-attachments/assets/0029cbc0-f062-4233-8650-cc216c7808f0"
/>
This PR introduces a `codex-utils-cargo-bin` utility crate that
wraps/replaces our use of `assert_cmd::Command` and
`escargot::CargoBuild`.
As you can infer from the introduction of `buck_project_root()` in this
PR, I am attempting to make it possible to build Codex under
[Buck2](https://buck2.build) as well as `cargo`. With Buck2, I hope to
achieve faster incremental local builds (largely due to Buck2's
[dice](https://buck2.build/docs/insights_and_knowledge/modern_dice/)
build strategy, as well as benefits from its local build daemon) as well
as faster CI builds if we invest in remote execution and caching.
See
https://buck2.build/docs/getting_started/what_is_buck2/#why-use-buck2-key-advantages
for more details about the performance advantages of Buck2.
Buck2 enforces stronger requirements in terms of build and test
isolation. It discourages assumptions about absolute paths (which is key
to enabling remote execution). Because the `CARGO_BIN_EXE_*` environment
variables that Cargo provides are absolute paths (which
`assert_cmd::Command` reads), this is a problem for Buck2, which is why
we need this `codex-utils-cargo-bin` utility.
My WIP-Buck2 setup sets the `CARGO_BIN_EXE_*` environment variables
passed to a `rust_test()` build rule as relative paths.
`codex-utils-cargo-bin` will resolve these values to absolute paths,
when necessary.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8496).
* #8498
* __->__ #8496
Clamp frame draw notifications in the `FrameRequester` scheduler so we
don't redraw more frequently than a user can perceive.
This applies to both `codex-tui` and `codex-tui2`, and keeps the
draw/dispatch loops simple by centralizing the rate limiting in a small
helper module.
- Add `FrameRateLimiter` (pure, unit-tested) to clamp draw deadlines
- Apply the limiter in the scheduler before emitting `TuiEvent::Draw`
- Use immediate redraw requests for scroll paths (scheduler now
coalesces + clamps)
- Add scheduler tests covering immediate/delayed interactions
This isn't very useful parameter.
logic:
```
if model puts `**` in their reasoning, trim it and visualize the header.
if couldn't trim: don't render
if model doesn't support: don't render
```
We can simplify to:
```
if could trim, visualize header.
if not, don't render
```
I am trying to support building with [Buck2](https://buck2.build), which
reports which files have changed between invocations of `buck2 test` and
`tmp_delete_example.txt` came up. This turned out to be the reason.
When rg download fails during npm package staging, log the
target/platform/url and preserve the original exception as the cause.
Emit GitHub Actions log groups and error annotations so the failure is
easier to spot.
Document why a urlopen timeout is set (the default can hang
indefinitely).
This is to make failures in the specific build step easier to understand
/ work out what's failing rather than having a big wall of text (or at
least having an obvious part of it that helps narrow that wall)
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Support multi-click transcript selection using transcript/viewport
coordinates
(wrapped visual line index + content column), not terminal buffer
positions.
Gestures:
- double click: select word-ish token under cursor
- triple click: select entire wrapped line
- quad click: select paragraph (contiguous non-empty wrapped lines)
- quint+ click: select the entire history cell (all wrapped lines
belonging to a
single `HistoryCell`, including blank lines inside the cell)
Selection expansion rebuilds the wrapped transcript view from
`HistoryCell::display_lines(width)` so boundaries match on-screen
wrapping during
scroll/resize/streaming reflow. Click grouping is resilient to minor
drag jitter
(some terminals emit tiny Drag events during clicks) and becomes more
tolerant as
the sequence progresses so quad/quint clicks are practical.
Tests cover expansion (word/line/paragraph/cell), sequence resets
(timing, motion,
line changes, real drags), drag jitter, and behavior on spacer lines
between
history cells (paragraph/cell selection prefers the cell above).
Avoid distracting 1-cell highlights on simple click by tracking an
anchor on mouse down and only creating a visible selection once the
mouse is dragged (selection head set).
When dragging while following the bottom during streaming, request a
scroll lock so the viewport stops moving under the active selection.
Move selection state transitions into transcript_selection helpers
(returning change/lock outcomes for the caller) and add unit tests for
the state machine.
### Motivation
- Persist richer per-turn configuration in rollouts so resumed/forked
sessions and tooling can reason about the exact instruction inputs and
output constraints used for a turn.
### Description
- Extend `TurnContextItem` to include optional `base_instructions`,
`user_instructions`, and `developer_instructions`.
- Record the optional `final_output_json_schema` associated with a turn.
- Add an optional `truncation_policy` to `TurnContextItem` and populate
it when writing turn-context rollout items.
- Introduce a protocol-level `TruncationPolicy` representation and
convert from core truncation policy when recording.
### Testing
- `cargo test -p codex-protocol` (pass)
Summary
Fixes intermittent screen corruption in tui2 (random stale characters)
by
addressing two terminal state desyncs: nested alt-screen transitions and
the
first-draw viewport clear.
- Make alt-screen enter/leave re-entrant via a small nesting guard so
closing
- Ensure the first viewport draw clears after the viewport is sized,
preventing
old terminal contents from leaking through when diff-based rendering
skips
space cells.
- Add docs + a small unit test for the alt-screen nesting behavior.
Testing
- cargo test -p codex-tui2
- cargo clippy -p codex-tui2 --all-features --tests
- Manual:
- Opened the transcript overlay and dismissed it repeatedly; verified
the
normal view redraws cleanly with no leftover characters.
- Ran tui2 in a new folder with no trust settings (and also cleared the
trust setting from config to re-trigger the prompt); verified the
initial
trust/onboarding screen renders without artifacts.
This adds logic to load `/etc/codex/config.toml` and associate it with
`ConfigLayerSource::System` on UNIX. I refactored the code so it shares
logic with the creation of the `ConfigLayerSource::User` layer.
https://github.com/openai/codex/pull/8354 added support for in-repo
`.config/` files, so this PR updates the logic for loading `*.rules`
files to load `*.rules` files from all relevant layers. The main change
to the business logic is `load_exec_policy()` in
`codex-rs/core/src/exec_policy.rs`.
Note this adds a `config_folder()` method to `ConfigLayerSource` that
returns `Option<AbsolutePathBuf>` so that it is straightforward to
iterate over the sources and get the associated config folder, if any.
This is necessary so that `$CODEX_HOME/skills` and `$CODEX_HOME/rules`
still get loaded even if `$CODEX_HOME/config.toml` does not exist. See
#8453.
For now, it is possible to omit this layer when creating a dummy
`ConfigLayerStack` in a test. We can revisit that later, if it turns out
to be the right thing to do.
with_target(true) is the default for tracing-subscriber, but we
previously disabled it for file output.
Keep it enabled so we can selectively enable specific targets/events at
runtime via RUST_LOG=..., and then grep by target/module in the log file
during troubleshooting.
before and after:
<img width="629" height="194" alt="image"
src="https://github.com/user-attachments/assets/33f7df3f-0c5d-4d3f-b7b7-80b03d4acd21"
/>
Copy now operates on the full logical selection range (anchor..head),
not just the visible viewport, so selections that include offscreen
lines copy the expected text.
Selection extraction is factored into `transcript_selection` to make the
logic easier to test and reason about. It reconstructs the wrapped
visual transcript, renders each wrapped line into a 1-row offscreen
Buffer, and reads the selected cells. This keeps clipboard text aligned
with what is rendered (gutter, indentation, wrapping).
Additional behavior:
- Skip continuation cells for wide glyphs (e.g. CJK) so copied text does
not include spurious spaces like "コ X".
- Avoid copying right-margin padding spaces.
Manual tested performed:
- "tell me a story" a few times
- scroll up, select text, scroll down, copy text
- confirm copied text is what you expect
Add `ctrl+g` shortcut to enable opening current prompt in configured
editor (`$VISUAL` or `$EDITOR`).
- Prompt is updated with editor's content upon editor close.
- Paste placeholders are automatically expanded when opening the
external editor, and are not "recompressed" on close
- They could be preserved in the editor, but it would be hard to prevent
the user from modifying the placeholder text directly, which would drop
the mapping to the `pending_paste` value
- Image placeholders stay as-is
- `ctrl+g` explanation added to shortcuts menu, snapshot tests updated
https://github.com/user-attachments/assets/4ee05c81-fa49-4e99-8b07-fc9eef0bbfce
The elevated setup synchronously applies read/write ACLs to any
workspace roots.
However, until we apply *read* permission to the full path, powershell
cannot use some roots as a cwd as it needs access to all parts of the
path in order to apply it as the working directory for a command.
The solution is, while the async read-ACL part of setup is running, use
a "junction" that lives in C:\Users\CodexSandbox{Offline|Online} that
points to the cwd.
Once the read ACLs are applied, we stop using the junction.
-----
this PR also removes some dead code and overly-verbose logging, and has
some light refactoring to the ACL-related functions
The bash command parser in exec_policy was failing to parse commands
with concatenated flag-value patterns like `-g"*.py"` (no space between
flag and quoted value). This caused policy rules like
`prefix_rule(pattern=["rg"])` to not match commands such as `rg -n "foo"
-g"*.py"`.
When tree-sitter-bash parses `-g"*.py"`, it creates a "concatenation"
node containing a word (`-g`) and a string (`"*.py"`). The parser
previously rejected any node type not in the ALLOWED_KINDS list, causing
the entire command parsing to fail and fall back to matching against the
wrapped `bash -lc` command instead of the inner command.
This change:
- Adds "concatenation" to ALLOWED_KINDS in
try_parse_word_only_commands_sequence
- Adds handling for concatenation nodes in parse_plain_command_from_node
that recursively extracts and joins word/string/raw_string children
- Adds test cases for concatenated flag patterns with double and single
quotes
Fixes#8394
- allow configuring `project_root_markers` in `config.toml`
(user/system/MDM) to control project discovery beyond `.git`
- honor the markers after merging pre-project layers; default to
`[".git"]` when unset and skip ancestor walk when set to an empty array
- document the option and add coverage for alternate markers in config
loader tests
- We now support `.codex/config.toml` in repo (from `cwd` up to the
first `.git` found, if any) as layers in `ConfigLayerStack`. A new
`ConfigLayerSource::Project` variant was added to support this.
- In doing this work, I realized that we were resolving relative paths
in `config.toml` after merging everything into one `toml::Value`, which
is wrong: paths should be relativized with respect to the folder
containing the `config.toml` that was deserialized. This PR introduces a
deserialize/re-serialize strategy to account for this in
`resolve_config_paths()`. (This is why `Serialize` is added to so many
types as part of this PR.)
- Added tests to verify this new behavior.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8354).
* #8359
* __->__ #8354
## Summary
Adds a FeatureFlag to enforce UTF8 encoding in powershell, particularly
Windows Powershell v5. This should help address issues like #7290.
Notably, this PR does not include the ability to parse `apply_patch`
invocations within UTF8 shell commands (calls to the freeform tool
should not be impacted). I am leaving this out of scope for now. We
should address before this feature becomes Stable, but those cases are
not the default behavior at this time so we're okay for experimentation
phase. We should continue cleaning up the `apply_patch::invocation`
logic and then can handle it more cleanly.
## Testing
- [x] Adds additional testing
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.47 to 4.5.53.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/releases">clap's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.53</h2>
<h2>[4.5.53] - 2025-11-19</h2>
<h3>Features</h3>
<ul>
<li>Add <code>default_values_if</code>,
<code>default_values_ifs</code></li>
</ul>
<h2>v4.5.52</h2>
<h2>[4.5.52] - 2025-11-17</h2>
<h3>Fixes</h3>
<ul>
<li>Don't panic when <code>args_conflicts_with_subcommands</code>
conflicts with an <code>ArgGroup</code></li>
</ul>
<h2>v4.5.51</h2>
<h2>[4.5.51] - 2025-10-29</h2>
<h3>Fixes</h3>
<ul>
<li><em>(help)</em> Correctly calculate padding for short flags that
take a value</li>
<li><em>(help)</em> Don't panic on short flags using
<code>ArgAction::Count</code></li>
</ul>
<h2>v4.5.50</h2>
<h2>[4.5.50] - 2025-10-20</h2>
<h3>Features</h3>
<ul>
<li>Accept <code>Cow</code> where <code>String</code> and
<code>&str</code> are accepted</li>
</ul>
<h2>v4.5.48</h2>
<h2>[4.5.48] - 2025-09-19</h2>
<h3>Documentation</h3>
<ul>
<li>Add a new CLI Concepts document as another way of framing clap</li>
<li>Expand the <code>typed_derive</code> cookbook entry</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/blob/master/CHANGELOG.md">clap's
changelog</a>.</em></p>
<blockquote>
<h2>[4.5.53] - 2025-11-19</h2>
<h3>Features</h3>
<ul>
<li>Add <code>default_values_if</code>,
<code>default_values_ifs</code></li>
</ul>
<h2>[4.5.52] - 2025-11-17</h2>
<h3>Fixes</h3>
<ul>
<li>Don't panic when <code>args_conflicts_with_subcommands</code>
conflicts with an <code>ArgGroup</code></li>
</ul>
<h2>[4.5.51] - 2025-10-29</h2>
<h3>Fixes</h3>
<ul>
<li><em>(help)</em> Correctly calculate padding for short flags that
take a value</li>
<li><em>(help)</em> Don't panic on short flags using
<code>ArgAction::Count</code></li>
</ul>
<h2>[4.5.50] - 2025-10-20</h2>
<h3>Features</h3>
<ul>
<li>Accept <code>Cow</code> where <code>String</code> and
<code>&str</code> are accepted</li>
</ul>
<h2>[4.5.49] - 2025-10-13</h2>
<h3>Fixes</h3>
<ul>
<li><em>(help)</em> Correctly wrap when ANSI escape codes are
present</li>
</ul>
<h2>[4.5.48] - 2025-09-19</h2>
<h3>Documentation</h3>
<ul>
<li>Add a new CLI Concepts document as another way of framing clap</li>
<li>Expand the <code>typed_derive</code> cookbook entry</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3716f9f428"><code>3716f9f</code></a>
chore: Release</li>
<li><a
href="613b69a6b7"><code>613b69a</code></a>
docs: Update changelog</li>
<li><a
href="d117f7acde"><code>d117f7a</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6028">#6028</a>
from epage/arg</li>
<li><a
href="cb8255d2f3"><code>cb8255d</code></a>
feat(builder): Allow quoted id's for arg macro</li>
<li><a
href="1036060f13"><code>1036060</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6025">#6025</a>
from AldaronLau/typos-in-faq</li>
<li><a
href="2fcafc0aee"><code>2fcafc0</code></a>
docs: Fix minor grammar issues in FAQ</li>
<li><a
href="a380b65fe9"><code>a380b65</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6023">#6023</a>
from epage/template</li>
<li><a
href="4d7ab1483c"><code>4d7ab14</code></a>
chore: Update from _rust/main template</li>
<li><a
href="b8a7ea49d9"><code>b8a7ea4</code></a>
chore(deps): Update Rust Stable to v1.87 (<a
href="https://redirect.github.com/clap-rs/clap/issues/18">#18</a>)</li>
<li><a
href="f9842b3b3f"><code>f9842b3</code></a>
chore: Avoid MSRV problems out of the box</li>
<li>Additional commits viewable in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.5.47...clap_complete-v4.5.53">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 [test-log](https://github.com/d-e-s-o/test-log) from 0.2.18 to
0.2.19.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/d-e-s-o/test-log/releases">test-log's
releases</a>.</em></p>
<blockquote>
<h2>v0.2.19</h2>
<h2>What's Changed</h2>
<ul>
<li>Adjusted <code>tracing</code> output to log to
<code>stderr</code></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/dbdr"><code>@dbdr</code></a> made their
first contribution in <a
href="https://redirect.github.com/d-e-s-o/test-log/pull/64">d-e-s-o/test-log#64</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/d-e-s-o/test-log/compare/v0.2.18...v0.2.19">https://github.com/d-e-s-o/test-log/compare/v0.2.18...v0.2.19</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/d-e-s-o/test-log/blob/main/CHANGELOG.md">test-log's
changelog</a>.</em></p>
<blockquote>
<h2>0.2.19</h2>
<ul>
<li>Adjusted <code>tracing</code> output to log to
<code>stderr</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b4cd4a3ab6"><code>b4cd4a3</code></a>
Bump version to 0.2.19</li>
<li><a
href="bafe834fe7"><code>bafe834</code></a>
Emit tracing output to stderr</li>
<li><a
href="9e7aafbdcc"><code>9e7aafb</code></a>
Bump actions/checkout from 5 to 6</li>
<li><a
href="7fc59759d8"><code>7fc5975</code></a>
Suggest using [dev-dependencies] instead of [dependencies] in
README.md</li>
<li><a
href="25e7c367e6"><code>25e7c36</code></a>
Bump actions/checkout from 4 to 5</li>
<li><a
href="b633926871"><code>b633926</code></a>
Address clippy reported issue</li>
<li><a
href="628feeea5a"><code>628feee</code></a>
Don't specify patch level for dev-dependencies</li>
<li><a
href="d1a217e2e4"><code>d1a217e</code></a>
Update rstest requirement from 0.25.0 to 0.26.1</li>
<li><a
href="a2c6ba206e"><code>a2c6ba2</code></a>
Document private items in documentation CI job</li>
<li>See full diff in <a
href="https://github.com/d-e-s-o/test-log/compare/v0.2.18...v0.2.19">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>
Updates the configuration documentation to clarify and improve the
description of the `developer_instructions` and `instructions` fields.
Documentation updates:
* Added a description for the `developer_instructions` field in
`docs/config.md`, clarifying that it provides additional developer
instructions.
* Updated the comments in `docs/example-config.md` to specify that
`developer_instructions` is injected before `AGENTS.md`, and clarified
that the `instructions` field is ignored and that `AGENTS.md` is
preferred.
___
ref #7973
Thanks to @miraclebakelaser for the message. I have double-confirmed
that developer instructions are always injected before user
instructions. According to the source code
[codex_core::codex::Session::build_initial_context](https://github.com/openai/codex/blob/rust-v0.77.0-alpha.2/codex-rs/core/src/codex.rs#L1279),
we can see the specific order of these instructions.
Ignore mouse events outside the transcript region so composer/footer
interactions do not start or mutate transcript selection state.
A left-click outside the transcript also cancels any active selection.
Selection changes schedule a redraw because mouse events don't
inherently trigger a frame.
Codex Unified Exec injects NO_COLOR=1 (and TERM=dumb) into shell tool
commands to keep output stable. Crossterm respects NO_COLOR and
suppresses ANSI escapes, which breaks our VT100-backed tests that assert
on parsed ANSI color output (they see vt100::Color::Default everywhere).
Force ANSI color output back on in the VT100 test backend by overriding
crossterm's memoized NO_COLOR setting in VT100Backend::new. This keeps
Unified Exec behavior unchanged while making the VT100 tests meaningful
and deterministic under Codex.
> [!WARNING]
> it's possible that this might be a race condition problem for this and
need to be solved a different way. Feel free to revert if it causes the
opposite problem for other tests that assume NOCOLOR is set. If it does
then we need to probably add some extra AGENTS.md lines for how to run
tests when using unified exec.
(this same change was made in tui, so it's probably safe).
### Summary
With codesigning on Mac, Windows and Linux, we should be able to safely
remove `features.rmcp_client` and `use_experimental_use_rmcp_client`
check from the codebase now.
## TUI2: Normalize Mouse Scroll Input Across Terminals (Wheel +
Trackpad)
This changes TUI2 scrolling to a stream-based model that normalizes
terminal scroll event density into consistent wheel behavior (default:
~3 transcript lines per physical wheel notch) while keeping trackpad
input higher fidelity via fractional accumulation.
Primary code: `codex-rs/tui2/src/tui/scrolling/mouse.rs`
Doc of record (model + probe-derived data):
`codex-rs/tui2/docs/scroll_input_model.md`
### Why
Terminals encode both mouse wheels and trackpads as discrete scroll
up/down events with direction but no magnitude, and they vary widely in
how many raw events they emit per physical wheel notch (commonly 1, 3,
or 9+). Timing alone doesn’t reliably distinguish wheel vs trackpad, so
cadence-based heuristics are unstable across terminals/hardware.
This PR treats scroll input as short *streams* separated by silence or
direction flips, normalizes raw event density into tick-equivalents,
coalesces redraws for dense streams, and exposes explicit config
overrides.
### What Changed
#### Scroll Model (TUI2)
- Stream detection
- Start a stream on the first scroll event.
- End a stream on an idle gap (`STREAM_GAP_MS`) or a direction flip.
- Normalization
- Convert raw events into tick-equivalents using per-terminal
`tui.scroll_events_per_tick`.
- Wheel-like vs trackpad-like behavior
- Wheel-like: fixed “classic” lines per wheel notch; flush immediately
for responsiveness.
- Trackpad-like: fractional accumulation + carry across stream
boundaries; coalesce flushes to ~60Hz to avoid floods and reduce “stop
lag / overshoot”.
- Trackpad divisor is intentionally capped: `min(scroll_events_per_tick,
3)` so terminals with dense wheel ticks (e.g. 9 events per notch) don’t
make trackpads feel artificially slow.
- Auto mode (default)
- Start conservatively as trackpad-like (avoid overshoot).
- Promote to wheel-like if the first tick-worth of events arrives
quickly.
- Fallback for 1-event-per-tick terminals (no tick-completion timing
signal).
#### Trackpad Acceleration
Some terminals produce relatively low vertical event density for
trackpad gestures, which makes large/faster swipes feel sluggish even
when small motions feel correct. To address that, trackpad-like streams
apply a bounded multiplier based on event count:
- `multiplier = clamp(1 + abs(events) / scroll_trackpad_accel_events,
1..scroll_trackpad_accel_max)`
The multiplier is applied to the trackpad stream’s computed line delta
(including carried fractional remainder). Defaults are conservative and
bounded.
#### Config Knobs (TUI2)
All keys live under `[tui]`:
- `scroll_wheel_lines`: lines per physical wheel notch (default: 3).
- `scroll_events_per_tick`: raw vertical scroll events per physical
wheel notch (terminal-specific default; fallback: 3).
- Wheel-like per-event contribution: `scroll_wheel_lines /
scroll_events_per_tick`.
- `scroll_trackpad_lines`: baseline trackpad sensitivity (default: 1).
- Trackpad-like per-event contribution: `scroll_trackpad_lines /
min(scroll_events_per_tick, 3)`.
- `scroll_trackpad_accel_events` / `scroll_trackpad_accel_max`: bounded
trackpad acceleration (defaults: 30 / 3).
- `scroll_mode = auto|wheel|trackpad`: force behavior or use the
heuristic (default: `auto`).
- `scroll_wheel_tick_detect_max_ms`: auto-mode promotion threshold (ms).
- `scroll_wheel_like_max_duration_ms`: auto-mode fallback for
1-event-per-tick terminals (ms).
- `scroll_invert`: invert scroll direction (applies to wheel +
trackpad).
Config docs: `docs/config.md` and field docs in
`codex-rs/core/src/config/types.rs`.
#### App Integration
- The app schedules follow-up ticks to close idle streams (via
`ScrollUpdate::next_tick_in` and `schedule_frame_in`) and finalizes
streams on draw ticks.
- `codex-rs/tui2/src/app.rs`
#### Docs
- Single doc of record describing the model + preserved probe
findings/spec:
- `codex-rs/tui2/docs/scroll_input_model.md`
#### Other (jj-only friendliness)
- `codex-rs/tui2/src/diff_render.rs`: prefer stable cwd-relative paths
when the file is under the cwd even if there’s no `.git`.
### Terminal Defaults
Per-terminal defaults are derived from scroll-probe logs (see doc).
Notable:
- Ghostty currently defaults to `scroll_events_per_tick = 3` even though
logs measured ~9 in one setup. This is a deliberate stopgap; if your
Ghostty build emits ~9 events per wheel notch, set:
```toml
[tui]
scroll_events_per_tick = 9
```
### Testing
- `just fmt`
- `just fix -p codex-core --allow-no-vcs`
- `cargo test -p codex-core --lib` (pass)
- `cargo test -p codex-tui2` (scroll tests pass; remaining failures are
known flaky VT100 color tests in `insert_history`)
### Review Focus
- Stream finalization + frame scheduling in `codex-rs/tui2/src/app.rs`.
- Auto-mode promotion thresholds and the 1-event-per-tick fallback
behavior.
- Trackpad divisor cap (`min(events_per_tick, 3)`) and acceleration
defaults.
- Ghostty default tradeoff (3 vs ~9) and whether we should change it.
`load_config_layers_state()` should load config from a
`.codex/config.toml` in any folder between the `cwd` for a thread and
the project root. Though in order to do that,
`load_config_layers_state()` needs to know what the `cwd` is, so this PR
does the work to thread the `cwd` through for existing callsites.
A notable exception is the `/config` endpoint in app server for which a
`cwd` is not guaranteed to be associated with the query, so the `cwd`
param is `Option<AbsolutePathBuf>` to account for this case.
The logic to make use of the `cwd` will be done in a follow-up PR.
# 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.
This adds support for `allowed_sandbox_modes` in `requirements.toml` and
provides legacy support for constraining sandbox modes in
`managed_config.toml`. This is converted to `Constrained<SandboxPolicy>`
in `ConfigRequirements` and applied to `Config` such that constraints
are enforced throughout the harness.
Note that, because `managed_config.toml` is deprecated, we do not add
support for the new `external-sandbox` variant recently introduced in
https://github.com/openai/codex/pull/8290. As noted, that variant is not
supported in `config.toml` today, but can be configured programmatically
via app server.
## Summary
- centralize file name derivation in codex-file-search
- reuse the helper in app-server fuzzy search to avoid duplicate logic
- add unit tests for file_name_from_path
## Testing
- cargo test -p codex-file-search
- cargo test -p codex-app-server
Problem
- Mouse wheel events were scheduling a redraw on every event, which
could backlog and create lag during fast scrolling.
Solution
- Schedule transcript scroll redraws with a short delay (16ms) so the
frame requester coalesces bursts into fewer draws.
Why
- Smooths rapid wheel scrolling while keeping the UI responsive.
Testing
- Manual: Scrolled in iTerm and Ghostty; no lag observed.
- `cargo clippy --fix --all-features --tests --allow-dirty
--allow-no-vcs -p codex-tui2`
This will make it easier to test for expected errors in unit tests since
we can compare based on the field values rather than the message (which
might change over time). See https://github.com/openai/codex/pull/8298
for an example.
It also ensures more consistency in the way a `ConstraintError` is
constructed.
Fixes#8214 by removing the '--staged' flag from the undo git restore
command. This ensures that while the working tree is reverted to the
snapshot state, the user's staged changes (index) are preserved,
preventing data loss. Also adds a regression test.
# 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.
We were assembling the skill roots in two different places, and the
admin root was missing in one of them. This change centralizes root
selection into a helper so both paths stay in sync.
## Description
Introduced `ExternalSandbox` policy to cover use case when sandbox
defined by outside environment, effectively it translates to
`SandboxMode#DangerFullAccess` for file system (since sandbox configured
on container level) and configurable `network_access` (either Restricted
or Enabled by outside environment).
as example you can configure `ExternalSandbox` policy as part of
`sendUserTurn` v1 app_server API:
```
{
"conversationId": <id>,
"cwd": <cwd>,
"approvalPolicy": "never",
"sandboxPolicy": {
"type": ""external-sandbox",
"network_access": "enabled"/"restricted"
},
"model": <model>,
"effort": <effort>,
....
}
```
https://github.com/openai/codex/pull/8235 introduced `ConfigBuilder` and
this PR updates all call non-test call sites to use it instead of
`Config::load_from_base_config_with_overrides()`.
This is important because `load_from_base_config_with_overrides()` uses
an empty `ConfigRequirements`, which is a reasonable default for testing
so the tests are not influenced by the settings on the host. This method
is now guarded by `#[cfg(test)]` so it cannot be used by business logic.
Because `ConfigBuilder::build()` is `async`, many of the test methods
had to be migrated to be `async`, as well. On the bright side, this made
it possible to eliminate a bunch of `block_on_future()` stuff.
Historically, `accept_elicitation_for_prompt_rule()` was flaky because
we were using a notification to update the sandbox followed by a `shell`
tool request that we expected to be subject to the new sandbox config,
but because [rmcp](https://crates.io/crates/rmcp) MCP servers delegate
each incoming message to a new Tokio task, messages are not guaranteed
to be processed in order, so sometimes the `shell` tool call would run
before the notification was processed.
Prior to this PR, we relied on a generous `sleep()` between the
notification and the request to reduce the change of the test flaking
out.
This PR implements a proper fix, which is to use a _request_ instead of
a notification for the sandbox update so that we can wait for the
response to the sandbox request before sending the request to the
`shell` tool call. Previously, `rmcp` did not support custom requests,
but I fixed that in
https://github.com/modelcontextprotocol/rust-sdk/pull/590, which made it
into the `0.12.0` release (see #8288).
This PR updates `shell-tool-mcp` to expect
`"codex/sandbox-state/update"` as a _request_ instead of a notification
and sends the appropriate ack. Note this behavior is tied to our custom
`codex/sandbox-state` capability, which Codex honors as an MCP client,
which is why `core/src/mcp_connection_manager.rs` had to be updated as
part of this PR, as well.
This PR also updates the docs at `shell-tool-mcp/README.md`.
This implements the new config design where config _requirements_ are
loaded separately (and with a special schema) as compared to config
_settings_. In particular, on UNIX, with this PR, you could define
`/etc/codex/requirements.toml` with:
```toml
allowed_approval_policies = ["never", "on-request"]
```
to enforce that `Config.approval_policy` must be one of those two values
when Codex runs.
We plan to expand the set of things that can be restricted by
`/etc/codex/requirements.toml` in short order.
Note that requirements can come from several sources:
- new MDM key on macOS (not implemented yet)
- `/etc/codex/requirements.toml`
- re-interpretation of legacy MDM key on macOS
(`com.openai.codex/config_toml_base64`)
- re-interpretation of legacy `/etc/codex/managed_config.toml`
So our resolution strategy is to load TOML data from those sources, in
order. Later TOMLs are "merged" into previous TOMLs, but any field that
is already set cannot be overwritten. See
`ConfigRequirementsToml::merge_unset_fields()`.
See snapshots for view of edge cases
This is still named `UnifiedExecSessions` for consistency across the
code but should be renamed to `BackgroundTerminals` in a follow-up
Example:
<img width="945" height="687" alt="Screenshot 2025-12-18 at 20 12 53"
src="https://github.com/user-attachments/assets/92f39ff2-243c-4006-b402-e3fa9e93c952"
/>
# Terminal Detection Metadata for Per-Terminal Scroll Scaling
## Summary
Expand terminal detection into structured metadata (`TerminalInfo`) with
multiplexer awareness, plus a testable environment shim and
characterization tests.
## Context / Motivation
- TUI2 owns its viewport and scrolling model (see
`codex-rs/tui2/docs/tui_viewport_and_history.md`), so scroll behavior
must be consistent across terminals and independent of terminal
scrollback quirks.
- Prior investigations show mouse wheel scroll deltas vary noticeably by
terminal. To tune scroll scaling (line increments per wheel tick) we
need reliable terminal identification, including when running inside
tmux/zellij.
- tmux is especially tricky because it can mask the underlying terminal;
we now consult `tmux display-message` client termtype/name to attribute
sessions to the actual terminal rather than tmux itself.
- This remains backwards compatible with the existing OpenTelemetry
user-agent token because `user_agent()` is still derived from the same
environment signals (now via `TerminalInfo`).
## Changes
- Introduce `TerminalInfo`, `TerminalName`, and `Multiplexer` with
`TERM_PROGRAM`/`TERM`/multiplexer detection and user-agent formatting in
`codex-rs/core/src/terminal.rs`.
- Add an injectable `Environment` trait + `FakeEnvironment` for testing,
and comprehensive characterization tests covering known terminals, tmux
client termtype/name, and zellij.
- Document module usage and detection order; update `terminal_info()` to
be the primary interface for callers.
## Testing
- `cargo test -p codex-core terminal::tests`
- manually checked ghostty, iTerm2, Terminal.app, vscode, tmux, zellij,
Warp, alacritty, kitty.
```
2025-12-18T07:07:49.191421Z INFO Detected terminal info terminal=TerminalInfo { name: Iterm2, term_program: Some("iTerm.app"), version: Some("3.6.6"), term: None, multiplexer: None }
2025-12-18T07:07:57.991776Z INFO Detected terminal info terminal=TerminalInfo { name: AppleTerminal, term_program: Some("Apple_Terminal"), version: Some("455.1"), term: None, multiplexer: None }
2025-12-18T07:08:07.732095Z INFO Detected terminal info terminal=TerminalInfo { name: WarpTerminal, term_program: Some("WarpTerminal"), version: Some("v0.2025.12.10.08.12.stable_03"), term: None, multiplexer: None }
2025-12-18T07:08:24.860316Z INFO Detected terminal info terminal=TerminalInfo { name: Kitty, term_program: None, version: None, term: None, multiplexer: None }
2025-12-18T07:08:38.302761Z INFO Detected terminal info terminal=TerminalInfo { name: Alacritty, term_program: None, version: None, term: None, multiplexer: None }
2025-12-18T07:08:50.887748Z INFO Detected terminal info terminal=TerminalInfo { name: VsCode, term_program: Some("vscode"), version: Some("1.107.1"), term: None, multiplexer: None }
2025-12-18T07:10:01.309802Z INFO Detected terminal info terminal=TerminalInfo { name: WezTerm, term_program: Some("WezTerm"), version: Some("20240203-110809-5046fc22"), term: None, multiplexer: None }
2025-12-18T08:05:17.009271Z INFO Detected terminal info terminal=TerminalInfo { name: Ghostty, term_program: Some("ghostty"), version: Some("1.2.3"), term: None, multiplexer: None }
2025-12-18T08:05:23.819973Z INFO Detected terminal info terminal=TerminalInfo { name: Ghostty, term_program: Some("ghostty"), version: Some("1.2.3"), term: Some("xterm-ghostty"), multiplexer: Some(Tmux { version: Some("3.6a") }) }
2025-12-18T08:05:35.572853Z INFO Detected terminal info terminal=TerminalInfo { name: Ghostty, term_program: Some("ghostty"), version: Some("1.2.3"), term: None, multiplexer: Some(Zellij) }
```
## Notes / Follow-ups
- Next step is to wire `TerminalInfo` into TUI2’s scroll scaling
configuration and add a per-terminal tuning table.
- The log output in TUI2 helps validate real-world detection before
applying behavior changes.
when granting read access to the sandbox user, grant the
codex/command-runner exe directory first so commands can run before the
entire read ACL process is finished.
Add a dmg target that bundles the codex and codex responses api proxy
binaries for MacOS. this target is signed and notarized.
Verified by triggering a build here:
https://github.com/openai/codex/actions/runs/20318136302/job/58367155205.
Downloaded the artifact and verified that the dmg is signed and
notarized, and the codex binary contained works as expected.
# 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.
This is a significant change to how layers of configuration are applied.
In particular, the `ConfigLayerStack` now has two important fields:
- `layers: Vec<ConfigLayerEntry>`
- `requirements: ConfigRequirements`
We merge `TomlValue`s across the layers, but they are subject to
`ConfigRequirements` before creating a `Config`.
How I would review this PR:
- start with `codex-rs/app-server-protocol/src/protocol/v2.rs` and note
the new variants added to the `ConfigLayerSource` enum:
`LegacyManagedConfigTomlFromFile` and `LegacyManagedConfigTomlFromMdm`
- note that `ConfigLayerSource` now has a `precedence()` method and
implements `PartialOrd`
- `codex-rs/core/src/config_loader/layer_io.rs` is responsible for
loading "admin" preferences from `/etc/codex/managed_config.toml` and
MDM. Because `/etc/codex/managed_config.toml` is now deprecated in favor
of `/etc/codex/requirements.toml` and `/etc/codex/config.toml`, we now
include some extra information on the `LoadedConfigLayers` returned in
`layer_io.rs`.
- `codex-rs/core/src/config_loader/mod.rs` has major changes to
`load_config_layers_state()`, which is what produces `ConfigLayerStack`.
The docstring has the new specification and describes the various layers
that will be loaded and the precedence order.
- It uses the information from `LoaderOverrides` "twice," both in the
spirit of legacy support:
- We use one instances to derive an instance of `ConfigRequirements`.
Currently, the only field in `managed_config.toml` that contributes to
`ConfigRequirements` is `approval_policy`. This PR introduces
`Constrained::allow_only()` to support this.
- We use a clone of `LoaderOverrides` to derive
`ConfigLayerSource::LegacyManagedConfigTomlFromFile` and
`ConfigLayerSource::LegacyManagedConfigTomlFromMdm` layers, as
appropriate. As before, this ends up being a "best effort" at enterprise
controls, but is enforcement is not guaranteed like it is for
`ConfigRequirements`.
- Now we only create a "user" layer if `$CODEX_HOME/config.toml` exists.
(Previously, a user layer was always created for `ConfigLayerStack`.)
- Similarly, we only add a "session flags" layer if there are CLI
overrides.
- `config_loader/state.rs` contains the updated implementation for
`ConfigLayerStack`. Note the public API is largely the same as before,
but the implementation is quite different. We leverage the fact that
`ConfigLayerSource` is now `PartialOrd` to ensure layers are in the
correct order.
- A `Config` constructed via `ConfigBuilder.build()` will use
`load_config_layers_state()` to create the `ConfigLayerStack` and use
the associated `ConfigRequirements` when constructing the `Config`
object.
- That said, a `Config` constructed via
`Config::load_from_base_config_with_overrides()` does _not_ yet use
`ConfigBuilder`, so it creates a `ConfigRequirements::default()` instead
of loading a proper `ConfigRequirements`. I will fix this in a
subsequent PR.
Then the following files are mostly test changes:
```
codex-rs/app-server/tests/suite/v2/config_rpc.rs
codex-rs/core/src/config/service.rs
codex-rs/core/src/config_loader/tests.rs
```
Again, because we do not always include "user" and "session flags"
layers when the contents are empty, `ConfigLayerStack` sometimes has
fewer layers than before (and the precedence order changed slightly),
which is the main reason integration tests changed.
This pull request makes a small update to the session picker
documentation for `codex resume`. The main change clarifies how to view
the original working directory (CWD) for sessions and when the Git
branch is shown.
- The session picker now displays the recorded Git branch when
available, and instructions are added for showing the original working
directory by using the `--all` flag, which also disables CWD filtering
and adds a `CWD` column.
# 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.
This pull request updates the ChatGPT login description in the
onboarding authentication widgets to clarify which plans include usage.
The description now lists "Business" rather than "Team" and adds
"Education" plans in addition to the previously mentioned plans.
I have read the CLA Document and I hereby sign the CLAs.
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Introduce `ConfigBuilder` as an alternative to our existing `Config`
constructors.
I noticed that the existing constructors,
`Config::load_with_cli_overrides()` and
`Config::load_with_cli_overrides_and_harness_overrides()`, did not take
`codex_home` as a parameter, which can be a problem.
Historically, when Codex was purely a CLI, we wanted to be extra sure
that the creation of `codex_home` was always done via
`find_codex_home()`, so we did not expose `codex_home` as a parameter
when creating `Config` in business logic. But in integration tests,
`codex_home` nearly always needs to be configured (as a temp directory),
which is why callers would have to go through
`Config::load_from_base_config_with_overrides()` instead.
Now that the Codex harness also functions as an app server, which could
conceivably load multiple threads where `codex_home` is parameterized
differently in each one, I think it makes sense to make this
configurable. Going to a builder pattern makes it more flexible to
ensure an arbitrary permutation of options can be set when constructing
a `Config` while using the appropriate defaults for the options that
aren't set explicitly.
Ultimately, I think this should make it possible for us to make
`Config::load_from_base_config_with_overrides()` private because all
integration tests should be able to leverage `ConfigBuilder` instead.
Though there could be edge cases, so I'll pursue that migration after we
get through the current config overhaul.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8235).
* #8237
* __->__ #8235
1. Remove PUBLIC skills and introduce SYSTEM skills embedded in the
binary and installed into $CODEX_HOME/skills/.system at startup.
2. Skills are now always enabled (feature flag removed).
3. Update skills/list to accept forceReload and plumb it through (not
used by clients yet).
# 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.
This PR does various types of cleanup before I can proceed with more
ambitious changes to config loading.
First, I noticed duplicated code across these two methods:
774bd9e432/codex-rs/core/src/config/mod.rs (L314-L324)774bd9e432/codex-rs/core/src/config/mod.rs (L334-L344)
This has now been consolidated in
`load_config_as_toml_with_cli_overrides()`.
Further, I noticed that `Config::load_with_cli_overrides()` took two
similar arguments:
774bd9e432/codex-rs/core/src/config/mod.rs (L308-L311)
The difference between `cli_overrides` and `overrides` was not
immediately obvious to me. At first glance, it appears that one should
be able to be expressed in terms of the other, but it turns out that
some fields of `ConfigOverrides` (such as `cwd` and
`codex_linux_sandbox_exe`) are, by design, not configurable via a
`.toml` file or a command-line `--config` flag.
That said, I discovered that many callers of
`Config::load_with_cli_overrides()` were passing
`ConfigOverrides::default()` for `overrides`, so I created two separate
methods:
- `Config::load_with_cli_overrides(cli_overrides: Vec<(String,
TomlValue)>)`
- `Config::load_with_cli_overrides_and_harness_overrides(cli_overrides:
Vec<(String, TomlValue)>, harness_overrides: ConfigOverrides)`
The latter has a long name, as it is _not_ what should be used in the
common case, so the extra typing is designed to draw attention to this
fact. I tried to update the existing callsites to use the shorter name,
where possible.
Further, in the cases where `ConfigOverrides` is used, usually only a
limited subset of fields are actually set, so I updated the declarations
to leverage `..Default::default()` where possible.
# 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.
- Batch read ACL creation for online/offline sandbox user
- creates a new ACL helper process that is long-lived and runs in the
background
- uses a mutex so that only one helper process is running at a time.
### Summary
* Make `app_server.list_models` to be non-blocking and consumers (i.e.
extension) can manage the flow themselves.
* Force config to use remote models and therefore fetch codex-auto model
list.
We should not have any `PathBuf` fields in `ConfigToml` or any of the
transitive structs we include, as we should use `AbsolutePathBuf`
instead so that we do not have to keep track of the file from which
`ConfigToml` was loaded such that we need it to resolve relative paths
later when the values of `ConfigToml` are used.
I only found two instances of this: `experimental_instructions_file` and
`experimental_compact_prompt_file`. Incidentally, when these were
specified as relative paths, they were resolved against `cwd` rather
than `config.toml`'s parent, which seems wrong to me. I changed the
behavior so they are resolved against the parent folder of the
`config.toml` being parsed, which we get "for free" due to the
introduction of `AbsolutePathBufGuard ` in
https://github.com/openai/codex/pull/7796.
While it is not great to change the behavior of a released feature,
these fields are prefixed with `experimental_`, which I interpret to
mean we have the liberty to change the contract.
For reference:
- `experimental_instructions_file` was introduced in
https://github.com/openai/codex/pull/1803
- `experimental_compact_prompt_file` was introduced in
https://github.com/openai/codex/pull/5959
The problem is that the `tokio` task own an `Arc` reference of the
session and that this task only exit with the broadcast channel get
closed. But this never get closed if the session is not dropped. So it's
a snake biting his tail basically
The most notable result was that non of the `Drop` implementation were
triggered (temporary files, shell snapshots, session cleaning etc etc)
when closing the session (through a `/new` for example)
The fix is just to weaken the `Arc` and upgrade it on the fly
Constrain `approval_policy` through new `admin_policy` config.
This PR will:
1. Add a `admin_policy` section to config, with a single field (for now)
`allowed_approval_policies`. This list constrains the set of
user-settable `approval_policy`s.
2. Introduce a new `Constrained<T>` type, which combines a current value
and a validator function. The validator function ensures disallowed
values are not set.
3. Change the type of `approval_policy` on `Config` and
`SessionConfiguration` from `AskForApproval` to
`Constrained<AskForApproval>`. The validator function is set by the
values passed into `allowed_approval_policies`.
4. `GenericDisplayRow`: add a `disabled_reason: Option<String>`. When
set, it disables selection of the value and indicates as such in the
menu. This also makes it unselectable with arrow keys or numbers. This
is used in the `/approvals` menu.
Follow ups are:
1. Do the same thing to `sandbox_policy`.
2. Propagate the allowed set of values through app-server for the
extension (though already this should prevent app-server from setting
this values, it's just that we want to disable UI elements that are
unsettable).
Happy to split this PR up if you prefer, into the logical numbered areas
above. Especially if there are parts we want to gavel on separately
(e.g. admin_policy).
Disabled full access:
<img width="1680" height="380" alt="image"
src="https://github.com/user-attachments/assets/1fb61c8c-1fcb-4dc4-8355-2293edb52ba0"
/>
Disabled `--yolo` on startup:
<img width="749" height="76" alt="image"
src="https://github.com/user-attachments/assets/0a1211a0-6eb1-40d6-a1d7-439c41e94ddb"
/>
CODEX-4087
This attempts to tighten up the types related to "config layers."
Currently, `ConfigLayerEntry` is defined as follows:
bef36f4ae7/codex-rs/core/src/config_loader/state.rs (L19-L25)
but the `source` field is a bit of a lie, as:
- for `ConfigLayerName::Mdm`, it is
`"com.openai.codex/config_toml_base64"`
- for `ConfigLayerName::SessionFlags`, it is `"--config"`
- for `ConfigLayerName::User`, it is `"config.toml"` (just the file
name, not the path to the `config.toml` on disk that was read)
- for `ConfigLayerName::System`, it seems like it is usually
`/etc/codex/managed_config.toml` in practice, though on Windows, it is
`%CODEX_HOME%/managed_config.toml`:
bef36f4ae7/codex-rs/core/src/config_loader/layer_io.rs (L84-L101)
All that is to say, in three out of the four `ConfigLayerName`, `source`
is a `PathBuf` that is not an absolute path (or even a true path).
This PR tries to uplevel things by eliminating `source` from
`ConfigLayerEntry` and turning `ConfigLayerName` into a disjoint union
named `ConfigLayerSource` that has the appropriate metadata for each
variant, favoring the use of `AbsolutePathBuf` where appropriate:
```rust
pub enum ConfigLayerSource {
/// Managed preferences layer delivered by MDM (macOS only).
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
Mdm { domain: String, key: String },
/// Managed config layer from a file (usually `managed_config.toml`).
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
System { file: AbsolutePathBuf },
/// Session-layer overrides supplied via `-c`/`--config`.
SessionFlags,
/// User config layer from a file (usually `config.toml`).
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
User { file: AbsolutePathBuf },
}
```
1. Adds SkillScope::Public end-to-end (core + protocol) and loads skills
from the public cache directory
2. Improves repo skill discovery by searching upward for the nearest
.codex/skills within a git repo
3. Deduplicates skills by name with deterministic ordering to avoid
duplicates across sources
4. Fixes garbled “Skill errors” overlay rendering by preventing pending
history lines from being injected during the modal
5. Updates the project docs “Skills” intro wording to avoid hardcoded
paths
## Summary
Adds a unicode scenario, and fills in files on failing scenarios to
ensure directory state is unchanged, for completeness
## Testing
- [x] only changes tests
The Windows Elevated Sandbox uses two new binaries:
codex-windows-sandbox-setup.exe
codex-command-runner.exe
This PR includes them when installing native deps and packaging for npm
Update the tui2 viewport/history design doc with current status and a
prioritized roadmap (scroll feel, selection/copy correctness, streaming
wrap polish, terminal integration, and longer-term per-cell
interactivity ideas).
## Summary:
This PR is a pure copy and paste of tests from lib.rs into
invocation.rs, to colocate logic and tests.
## Testing
- [x] Purely a test refactor
This reverts commit 291b54a762.
This commit was intended to prevent the model from making code changes
during `/review`, which is sometimes does. Unfortunately, it has other
unintended side effects that cause `/review` to fail in a variety of
ways. See #8115 and #7815. We've therefore decided to revert this
change.
I'm not sure if this fix is correct for the intended change in #7601,
but at least the compilation error is fixed.
regression: #7601
```
error[E0004]: non-exhaustive patterns: `TuiEvent::Mouse(_)` not covered
--> tui2/src/update_prompt.rs:57:19
|
57 | match event {
| ^^^^^ pattern `TuiEvent::Mouse(_)` not covered
|
note: `TuiEvent` defined here
--> tui2/src/tui.rs:122:10
|
122 | pub enum TuiEvent {
| ^^^^^^^^
...
126 | Mouse(crossterm::event::MouseEvent),
| ----- not covered
= note: the matched value is of type `TuiEvent`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
64 ~ },
65 + TuiEvent::Mouse(_) => todo!()
|
```
Signed-off-by: Koichi Shiraishi <zchee.io@gmail.com>
## 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/setup-node` |
[`v5`](https://github.com/actions/setup-node/releases/tag/v5) |
[`v6`](https://github.com/actions/setup-node/releases/tag/v6) |
[Release](https://github.com/actions/setup-node/releases/tag/v6) |
ci.yml, rust-release.yml, sdk.yml, shell-tool-mcp-ci.yml,
shell-tool-mcp.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.
lib.rs has grown quite large, and mixes two responsibilities:
1. executing patch operations
2. parsing apply_patch invocations via a shell command
This PR splits out (2) into its own file, so we can work with it more
easily. We are explicitly NOT moving tests in this PR, to ensure
behavior stays the same and we can avoid losing coverage via merge
conflicts. Tests are moved in a subsequent PR.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8110).
* #8111
* __->__ #8110
a few fixes based on testing feedback:
* ensure cap_sid file is always written by elevated setup.
* always log to same file whether using elevated sandbox or not
* process potentially slow ACE write operations in parallel
* dedupe write roots so we don't double process any
* don't try to create read/write ACEs on the same directories, due to
race condition
Introduces an `EventBroker` between the crossterm `EventStream` source
and the consumers in the TUI. This enables dropping + recreating the
`crossterm_events` without invalidating the consumer.
Dropping and recreating the crossterm event stream enables us to fully
relinquish `stdin` while the app keeps running. If the stream is not
dropped, it will continue to read from `stdin` even when it is not
actively being polled, potentially stealing input from other processes.
See
[here](https://www.reddit.com/r/rust/comments/1f3o33u/myterious_crossterm_input_after_running_vim/?utm_source=chatgpt.com)
and [here](https://ratatui.rs/recipes/apps/spawn-vim/) for details.
### Tests
Added tests for new `EventBroker` setup, existing tests pass, tested
locally.
In preparation for in-repo configuration support, this updates
`WritableRoot::get_writable_roots_with_cwd()` to include the `.codex`
subfolder in `WritableRoot.read_only_subpaths`, if it exists, as we
already do for `.git`.
As noted, currently, like `.git`, `.codex` will only be read-only under
macOS Seatbelt, but we plan to bring support to other OSes, as well.
Updated the integration test in `seatbelt.rs` so that it actually
attempts to run the generated Seatbelt commands, verifying that:
- trying to write to `.codex/config.toml` in a writable root fails
- trying to write to `.git/hooks/pre-commit` in a writable root fails
- trying to write to the writable root containing the `.codex` and
`.git` subfolders succeeds
This is a pure refactor only change.
Replace the flattened transcript line metadata from `Option<(usize,
usize)>` to an explicit
`TranscriptLineMeta::{CellLine { cell_index, line_in_cell }, Spacer}`
enum.
This makes spacer rows unambiguous, removes “tuple semantics” from call
sites, and keeps the
scroll anchoring model clearer and aligned with the viewport/history
design notes.
Changes:
- Introduce `TranscriptLineMeta` and update `TranscriptScroll` helpers
to consume it.
- Update `App::build_transcript_lines` and downstream consumers
(scrolling, row classification, ANSI rendering).
- Refresh scrolling module docs to describe anchors + spacer semantics
in context.
- Add tests and docs about the behavior
Tests:
- just fmt
- cargo test -p codex-tui2 tui::scrolling
Manual testing:
- Scroll the inline transcript with mouse wheel + PgUp/PgDn/Home/End,
then resize the terminal while staying scrolled up; verify the same
anchored content stays in view and you don’t jump to bottom
unexpectedly.
- Create a gap case (multiple non-continuation cells) and scroll so a
blank spacer row is at/near the top; verify scrolling doesn’t get stuck
on spacers and still anchors to nearby real lines.
- Start a selection while the assistant is streaming; verify the view
stops auto-following, the selection stays on the intended content, and
subsequent scrolling still behaves normally.
- Exit the TUI and confirm scrollback rendering still styles user rows
as blocks (background padding) and non-user rows as expected.
> large behavior change to how the TUI owns its viewport, history, and
suspend behavior.
> Core model is in place; a few items are still being polished before
this is ready to merge.
We've moved this over to a new tui2 crate from being directly on the tui
crate.
To enable use --enable tui2 (or the equivalent in your config.toml). See
https://developers.openai.com/codex/local-config#feature-flags
Note that this serves as a baseline for the changes that we're making to
be applied rapidly. Tui2 may not track later changes in the main tui.
It's experimental and may not be where we land on things.
---
## Summary
This PR moves the Codex TUI off of “cooperating” with the terminal’s
scrollback and onto a model
where the in‑memory transcript is the single source of truth. The TUI
now owns scrolling, selection,
copy, and suspend/exit printing based on that transcript, and only
writes to terminal scrollback in
append‑only fashion on suspend/exit. It also fixes streaming wrapping so
streamed responses reflow
with the viewport, and introduces configuration to control whether we
print history on suspend or
only on exit.
High‑level goals:
- Ensure history is complete, ordered, and never silently dropped.
- Print each logical history cell at most once into scrollback, even
with resizes and suspends.
- Make scrolling, selection, and copy match the visible transcript, not
the terminal’s notion of
scrollback.
- Keep suspend/alt‑screen behavior predictable across terminals.
---
## Core Design Changes
### Transcript & viewport ownership
- Treat the transcript as a list of **cells** (user prompts, agent
messages, system/info rows,
streaming segments).
- On each frame:
- Compute a **transcript region** as “full terminal frame minus the
bottom input area”.
- Flatten all cells into visual lines plus metadata (which cell + which
line within that cell).
- Use scroll state to choose which visual line is at the top of the
region.
- Clear that region and draw just the visible slice of lines.
- The terminal’s scrollback is no longer part of the live layout
algorithm; it is only ever written
to when we decide to print history.
### User message styling
- User prompts now render as clear blocks with:
- A blank padding line above and below.
- A full‑width background for every line in the block (including the
prompt line itself).
- The same block styling is used when we print history into scrollback,
so the transcript looks
consistent whether you are in the TUI or scrolling back after
exit/suspend.
---
## Scrolling, Mouse, Selection, and Copy
### Scrolling
- Scrolling is defined in terms of the flattened transcript lines:
- Mouse wheel scrolls up/down by fixed line increments.
- PgUp/PgDn/Home/End operate on the same scroll model.
- The footer shows:
- Whether you are “following live output” vs “scrolled up”.
- Current scroll position (line / total).
- When there is no history yet, the bottom pane is **pegged high** and
gradually moves down as the
transcript fills, matching the existing UX.
### Selection
- Click‑and‑drag defines a **linear selection** over transcript
line/column coordinates, not raw
screen rows.
- Selection is **content‑anchored**:
- When you scroll, the selection moves with the underlying lines instead
of sticking to a fixed
Y position.
- This holds both when scrolling manually and when new content streams
in, as long as you are in
“follow” mode.
- The selection only covers the “transcript text” area:
- Left gutter/prefix (bullets, markers) is intentionally excluded.
- This keeps copy/paste cleaner and avoids including structural margin
characters.
### Copy (`Ctrl+Y`)
- Introduce a small clipboard abstraction (`ClipboardManager`‑style) and
use a cross‑platform
clipboard crate under the hood.
- When `Ctrl+Y` is pressed and a non‑empty selection exists:
- Re‑render the transcript region off‑screen using the same wrapping as
the visible viewport.
- Walk the selected line/column range over that buffer to reconstruct
the exact text:
- Includes spaces between words.
- Preserves empty lines within the selection.
- Send the resulting text to the system clipboard.
- Show a short status message in the footer indicating success/failure.
- Copy is **best‑effort**:
- Clipboard failures (headless environment, sandbox, remote sessions)
are handled gracefully via
status messages; they do not crash the TUI.
- Copy does *not* insert a new history entry; it only affects the status
bar.
---
## Streaming and Wrapping
### Previous behavior
Previously, streamed markdown:
- Was wrapped at a fixed width **at commit time** inside the streaming
collector.
- Those wrapped `Line<'static>` values were then wrapped again at
display time.
- As a result, streamed paragraphs could not “un‑wrap” when the terminal
width increased; they were
permanently split according to the width at the start of the stream.
### New behavior
This PR implements the first step from
`codex-rs/tui/streaming_wrapping_design.md`:
- Streaming collector is constructed **without** a fixed width for
wrapping.
- It still:
- Buffers the full markdown source for the current stream.
- Commits only at newline boundaries.
- Emits logical lines as new content becomes available.
- Agent message cells now wrap streamed content only at **display
time**, based on the current
viewport width, just like non‑streaming messages.
- Consequences:
- Streamed responses reflow correctly when the terminal is resized.
- Animation steps are per logical line instead of per “pre‑wrapped”
visual line; this makes some
commits slightly larger but keeps the behavior simple and predictable.
Streaming responses are still represented as a sequence of logical
history entries (first line +
continuations) and integrate with the same scrolling, selection, and
printing model.
---
## Printing History on Suspend and Exit
### High‑water mark and append‑only scrollback
- Introduce a **cell‑based high‑water mark** (`printed_history_cells`)
on the transcript:
- Represents “how many cells at the front of the transcript have already
been printed”.
- Completely independent of wrapped line counts or terminal geometry.
- Whenever we print history (suspend or exit):
- Take the suffix of `transcript_cells` beyond `printed_history_cells`.
- Render just that suffix into styled lines at the **current** width.
- Write those lines to stdout.
- Advance `printed_history_cells` to cover all cells we just printed.
- Older cells are never re‑rendered for scrollback. They stay in
whatever wrapping they had when
printed, which is acceptable as long as the logical content is present
once.
### Suspend (`Ctrl+Z`)
- On suspend:
- Leave alt screen if active and restore normal terminal modes.
- Render the not‑yet‑printed suffix of the transcript and append it to
normal scrollback.
- Advance the high‑water mark.
- Suspend the process.
- On resume (`fg`):
- Re‑enter the TUI mode (alt screen + input modes).
- Clear the viewport region and fully redraw from in‑memory transcript
and state.
This gives predictable behavior across terminals without trying to
maintain scrollback live.
### Exit
- On exit:
- Render any remaining unprinted cells once and write them to stdout.
- Add an extra blank line after the final Codex history cell before
printing token usage, so the
transcript and usage info are visually separated.
- If you never suspended, exit prints the entire transcript exactly
once.
- If you suspended one or more times, exit prints only the cells
appended after the last suspend.
---
## Configuration: Suspend Printing
This PR also adds configuration to control **when** we print history:
- New TUI config option to gate printing on suspend:
- At minimum:
- `print_on_suspend = true` – current behavior: print new history at
each suspend *and* on exit.
- `print_on_suspend = false` – only print on exit.
- Default is tuned to preserve current behavior, but this can be
revisited based on feedback.
- The config is respected in the suspend path:
- If disabled, suspend only restores terminal modes and stops rendering
but does not print new
history.
- Exit still prints the full not‑yet‑printed suffix once.
This keeps the core viewport logic agnostic to preference, while letting
users who care about
quiet scrollback opt out of suspend printing.
---
## Tradeoffs
What we gain:
- A single authoritative history model (the in‑memory transcript).
- Deterministic viewport rendering independent of terminal quirks.
- Suspend/exit flows that:
- Print each logical history cell exactly once.
- Work across resizes and different terminals.
- Interact cleanly with alt screen and raw‑mode toggling.
- Consistent, content‑anchored scrolling, selection, and copy.
- Streaming messages that reflow correctly with the viewport width.
What we accept:
- Scrollback may contain older cells wrapped differently than newer
ones.
- Streaming responses appear in scrollback as a sequence of blocks
corresponding to their streaming
structure, not as a single retroactively reflowed paragraph.
- We do not attempt to rewrite or reflow already‑printed scrollback.
For deeper rationale and diagrams, see
`docs/tui_viewport_and_history.md` and
`codex-rs/tui/streaming_wrapping_design.md`.
---
## Still to Do Before This PR Is Ready
These are scoped to this PR (not long‑term future work):
- [ ] **Streaming wrapping polish**
- Double‑check all streaming paths use display‑time wrapping only.
- Ensure tests cover resizing after streaming has started.
- [ ] **Suspend printing config**
- Finalize config shape and default (keep existing behavior vs opt‑out).
- Wire config through TUI startup and document it in the appropriate
config docs.
- [x] **Bottom pane positioning**
- Ensure the bottom pane is pegged high when there’s no history and
smoothly moves down as the
transcript fills, matching the current behavior across startup and
resume.
- [x] **Transcript mouse scrolling**
- Re‑enable wheel‑based transcript scrolling on top of the new scroll
model.
- Make sure mouse scroll does not get confused with “alternate scroll”
modes from terminals.
- [x] **Mouse selection vs streaming**
- When selection is active, stop auto‑scrolling on streaming so the
selection remains stable on
the selected content.
- Ensure that when streaming continues after selection is cleared,
“follow latest output” mode
resumes correctly.
- [ ] **Auto‑scroll during drag**
- While the user is dragging a selection, auto‑scroll when the cursor is
at/near the top or bottom
of the transcript viewport to allow selecting beyond the current visible
window.
- [ ] **Feature flag / rollout**
- Investigate gating the new viewport/history behavior behind a feature
flag for initial rollout,
so we can fall back to the old behavior if needed during early testing.
- [ ] **Before/after videos**
- Capture short clips showing:
- Scrolling (mouse + keys).
- Selection and copy.
- Streaming behavior under resize.
- Suspend/resume and exit printing.
- Use these to validate UX and share context in the PR discussion.
We want to rely on server-side auto-compaction instead of having the
client trigger context compaction manually. This API was stubbed as a
placeholder and never implemented.
This PR makes sure that inline comment is preserved for mcp server
config and arbitrary key/value setPath config.
---------
Co-authored-by: celia-oai <celia@openai.com>
## Summary
Restores ability to detect when Codex is installed globally via **Bun**,
which was broken by c3e4f920b4. Fixes
#8003.
Instead of relying on `npm_config_user_agent` (which is only set when
running via `bunx` or `bun run`), this adds a path-based check to see if
the CLI wrapper is located in Bun's global installation directory.
## Regression Context
Commit `c3e4f920b4e965085164d6ee0249a873ef96da77` removed the
`BUN_INSTALL` environment variable checks to prevent false positives.
However, this caused false negatives for genuine Bun global installs
because `detectPackageManager()` defaults to NPM when no signal is
found.
## Changes
- Updated `codex-cli/bin/codex.js` to check if `__dirname` contains
`.bun/install/global` (handles both POSIX and Windows paths).
## Verification
Verified by performing a global install of the patched CLI (v0.69.0 to
trigger the update prompt):
1. Packed the CLI using `npm pack` in `codex-cli/` to create a release
tarball.
2. Installed globally via Bun: `bun install -g
$(pwd)/openai-codex-0.0.0-dev.tgz`.
3. Ran `codex`, confirmed it detected Bun (banner showed `bun install -g
@openai/codex`), selected "Update now", and verified it correctly
spawned `bun install -g` instead of `npm`.
4. Confirmed the upgrade completed successfully using Bun.
<img width="1038" height="813" alt="verifying installation via bun"
src="https://github.com/user-attachments/assets/00c9301a-18f1-4440-aa95-82ccffba896c"
/>
5. Verified installations via npm are unaffected.
<img width="2090" height="842" alt="verifying installation via npm"
src="https://github.com/user-attachments/assets/ccb3e031-b85c-4bbe-bac7-23b087c5b844"
/>
## Problem
When generating JSON schemas on Windows, the `codex app-server
generate-json-schema` command fails with a filename error:
```text
Error: Failed to write JSON schema for Option<()>
Caused by:
0: Failed to write .\Option<()>.json
1: The filename, directory name, or volume label syntax is incorrect. (os error 123)
```
This occurs because Windows doesn't allow certain characters in
filenames, specifically the angle brackets **<>** used in the
**Option<()>** type name.
## Root Cause
The schema generation process attempts to create individual JSON files
for each schema definition, including `Option<()>`. However, the
characters `<` and `>` are invalid in Windows filenames, causing the
file creation to fail.
## Solution
The fix extends the existing `IGNORED_DEFINITIONS` constant (which was
already being used in the **bundle generation**) to also skip
`Option<()>` when generating individual JSON schema files. This
maintains consistency with the existing behavior where `Option<()>` is
excluded from the bundled schema.
---
close#7479
This PR updates ghost snapshotting to avoid capturing oversized
untracked artifacts while keeping undo safe. Snapshot creation now
builds a temporary index from `git status --porcelain=2 -z`, writes a
tree and detached commit without touching refs, and records any ignored
large files/dirs in the snapshot report. Undo uses that metadata to
preserve large local artifacts while still cleaning up new transient
files.
Human TL;DR - in some situations, pasting/rapidly inputting text will
currently cause `?` characters to be stripped from the input message
content, and display the key bindings helper. For instance, writing
"Where is X defined? Can we do Y?" results in "Where is X defined Can we
do Y" being added to the message draft area. This is mildly annoying.
The fix was a simple one line addition. Added a test, ran linters, and
all looks good to me. I didn't create an issue to link to in this PR - I
had submitted this bug as a report a while ago but can't seem to find it
now. Let me know if it's an absolute must for the PR to be accepted.
I have read the CLA Document and I hereby sign the CLA
Below is Codex's summary.
---
# `?` characters toggling shortcuts / being dropped
## Symptom
On Termux (and potentially other terminal environments), composing text
in the native input field and sending it to the TTY can cause:
- The shortcuts overlay to appear (as if `?` was pressed on an empty
prompt), and
- All of the literal `?` characters in the text to be **missing** from
the composer input,
even when `?` is not the first character.
This typically happens when the composer was previously empty and the
terminal delivers the text as a rapid sequence of key events rather than
a single bracketed paste event.
## Root cause
The TUI has two relevant behaviors:
1. **Shortcut toggle on `?` when empty**
- `ChatComposer::handle_shortcut_overlay_key` treats a plain `?` press
as a toggle between the shortcut summary and the full shortcut overlay,
but only when the composer is empty.
- When it toggles, it consumes the key event (so `?` is *not* inserted
into the text input).
2. **“Paste burst” buffering for fast key streams**
- The TUI uses a heuristic to detect “paste-like” input bursts even when
the terminal doesn’t send an explicit paste event.
- During that burst detection, characters can be buffered (and the text
area can remain empty temporarily) while the system decides whether to
treat the stream as paste-like input.
In Termux’s “send composed text all at once” mode, the input often
arrives as a very fast stream of `KeyCode::Char(...)` events. While that
stream is being buffered as a burst, the visible textarea can still be
empty. If a `?` arrives during this window, it matches “empty composer”
and is interpreted as “toggle shortcuts” instead of “insert literal
`?`”, so the `?` is dropped.
## Fix
Make the `?` toggle conditional on not being in any paste-burst
transient state.
Implementation:
- `ChatComposer::handle_shortcut_overlay_key` now checks
`!self.is_in_paste_burst()` in addition to `self.is_empty()` before
toggling.
- This ensures that when input is arriving as a fast burst (including
the “pending first char” case), `?` is treated as normal text input
rather than a UI toggle.
## Test coverage
Added a test that simulates a Termux-like fast stream:
- Sends `h i ? t h e r e` as immediate successive `KeyEvent::Char`
events (no delays).
- Asserts that a paste burst is active and the textarea is still empty
while buffering.
- Flushes the burst and verifies:
- The final text contains the literal `?` (`"hi?there"`), and
- The footer mode is not `ShortcutOverlay`.
## Notes
This fix intentionally keeps the existing UX:
- `?` still toggles shortcuts when the composer is genuinely empty and
the user is not in the middle of entering text.
- `?` typed while composing content (including IME/native-input fast
streams) remains literal.
Bumps [lru](https://github.com/jeromefroe/lru-rs) from 0.12.5 to 0.16.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md">lru's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/jeromefroe/lru-rs/tree/0.16.2">v0.16.2</a> -
2025-10-14</h2>
<ul>
<li>Upgrade hashbrown dependency to 0.16.0.</li>
</ul>
<h2><a
href="https://github.com/jeromefroe/lru-rs/tree/0.16.1">v0.16.1</a> -
2025-09-08</h2>
<ul>
<li>Fix <code>Clone</code> for unbounded cache.</li>
</ul>
<h2><a
href="https://github.com/jeromefroe/lru-rs/tree/0.16.0">v0.16.0</a> -
2025-07-02</h2>
<ul>
<li>Implement <code>Clone</code> for caches with custom hashers.</li>
</ul>
<h2><a
href="https://github.com/jeromefroe/lru-rs/tree/0.15.0">v0.15.0</a> -
2025-06-26</h2>
<ul>
<li>Return bool from <code>promote</code> and <code>demote</code> to
indicate whether key was found.</li>
</ul>
<h2><a
href="https://github.com/jeromefroe/lru-rs/tree/0.14.0">v0.14.0</a> -
2025-04-12</h2>
<ul>
<li>Use <code>NonZeroUsize::MAX</code> instead of <code>unwrap()</code>,
and update MSRV to 1.70.0.</li>
</ul>
<h2><a
href="https://github.com/jeromefroe/lru-rs/tree/0.13.0">v0.13.0</a> -
2025-01-27</h2>
<ul>
<li>Add <code>peek_mru</code> and <code>pop_mru</code> methods, upgrade
dependency on <code>hashbrown</code> to 0.15.2, and update MSRV to
1.65.0.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c1f843ded0"><code>c1f843d</code></a>
Merge pull request <a
href="https://redirect.github.com/jeromefroe/lru-rs/issues/223">#223</a>
from jeromefroe/jerome/prepare-0-16-2-release</li>
<li><a
href="fc4f30953e"><code>fc4f309</code></a>
Prepare 0.16.2 release</li>
<li><a
href="e91ea2bd85"><code>e91ea2b</code></a>
Merge pull request <a
href="https://redirect.github.com/jeromefroe/lru-rs/issues/222">#222</a>
from torokati44/hashbrown-0.16</li>
<li><a
href="90d05feff3"><code>90d05fe</code></a>
Update hashbrown to 0.16</li>
<li><a
href="c699209232"><code>c699209</code></a>
Merge pull request <a
href="https://redirect.github.com/jeromefroe/lru-rs/issues/220">#220</a>
from jeromefroe/jerome/prepare-0-16-1-release</li>
<li><a
href="2bd8207030"><code>2bd8207</code></a>
Prepare 0.16.1 release</li>
<li><a
href="1b21bf1c59"><code>1b21bf1</code></a>
Merge pull request <a
href="https://redirect.github.com/jeromefroe/lru-rs/issues/219">#219</a>
from wqfish/bk</li>
<li><a
href="3ec42b6369"><code>3ec42b6</code></a>
Fix clone implementation for unbounded cache</li>
<li><a
href="e2e3e47c33"><code>e2e3e47</code></a>
Merge pull request <a
href="https://redirect.github.com/jeromefroe/lru-rs/issues/218">#218</a>
from jeromefroe/jerome/prepare-0-16-0-release</li>
<li><a
href="17fe4f328a"><code>17fe4f3</code></a>
Prepare 0.16.0 release</li>
<li>Additional commits viewable in <a
href="https://github.com/jeromefroe/lru-rs/compare/0.12.5...0.16.2">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot 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 [sentry](https://github.com/getsentry/sentry-rust) from 0.34.0 to
0.46.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/getsentry/sentry-rust/releases">sentry's
releases</a>.</em></p>
<blockquote>
<h2>0.46.0</h2>
<h3>Breaking changes</h3>
<ul>
<li>Removed the <code>ClientOptions</code> struct's
<code>trim_backtraces</code> and <code>extra_border_frames</code> fields
(<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/925">#925</a>).
<ul>
<li>These fields configured backtrace trimming, which is being removed
in this release.</li>
</ul>
</li>
</ul>
<h3>Improvements</h3>
<ul>
<li>Removed backtrace trimming to align the Rust SDK with the general
principle that Sentry SDKs should only truncate telemetry data when
needed to comply with <a
href="https://develop.sentry.dev/sdk/data-model/envelopes/#size-limits">documented
size limits</a> (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/925">#925</a>).
This change ensures that as much data as possible remains available for
debugging.
<ul>
<li>If you notice any new issues being created for existing errors after
this change, please open an issue on <a
href="https://github.com/getsentry/sentry-rust/issues/new/choose">GitHub</a>.</li>
</ul>
</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>fix: adjust sentry.origin for log integration (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/919">#919</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a></li>
</ul>
<h2>0.45.0</h2>
<h3>Breaking changes</h3>
<ul>
<li>Add custom variant to <code>AttachmentType</code> that holds an
arbitrary String. (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/916">#916</a>)</li>
</ul>
<h2>0.44.0</h2>
<h3>Breaking changes</h3>
<ul>
<li>feat(log): support combined LogFilters and RecordMappings (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/914">#914</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a>
<ul>
<li>Breaking change: <code>sentry::integrations::log::LogFilter</code>
has been changed to a <code>bitflags</code> struct.</li>
<li>It's now possible to map a <code>log</code> record to multiple items
in Sentry by combining multiple log filters in the filter, e.g.
<code>log::Level::ERROR => LogFilter::Event |
LogFilter::Log</code>.</li>
<li>If using a custom <code>mapper</code> instead, it's possible to
return a
<code>Vec<sentry::integrations::log::RecordMapping></code> to map
a <code>log</code> record to multiple items in Sentry.</li>
</ul>
</li>
</ul>
<h3>Behavioral changes</h3>
<ul>
<li>ref(log): send logs by default when logs feature flag is enabled (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/915">#915</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a>
<ul>
<li>If the <code>logs</code> feature flag is enabled, the default Sentry
<code>log</code> logger now sends logs for all events at or above
INFO.</li>
</ul>
</li>
<li>ref(logs): enable logs by default if logs feature flag is used (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/910">#910</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a>
<ul>
<li>This changes the default value of
<code>sentry::ClientOptions::enable_logs</code> to
<code>true</code>.</li>
<li>This simplifies the setup of Sentry structured logs by requiring
users to just add the <code>log</code> feature flag to the
<code>sentry</code> dependency to opt-in to sending logs.</li>
<li>When the <code>log</code> feature flag is enabled, the
<code>tracing</code> and <code>log</code> integrations will send
structured logs to Sentry for all logs/events at or above INFO level by
default.</li>
</ul>
</li>
</ul>
<h2>0.43.0</h2>
<h3>Breaking changes</h3>
<ul>
<li>ref(tracing): rework tracing to Sentry span name/op conversion (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/887">#887</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a>
<ul>
<li>The <code>tracing</code> integration now uses the tracing span name
as the Sentry span name by default.</li>
<li>Before this change, the span name would be set based on the
<code>tracing</code> span target
(<code><module>::<function></code> when using the
<code>tracing::instrument</code> macro).</li>
<li>The <code>tracing</code> integration now uses <code><span
target>::<span name></code> as the default Sentry span op (i.e.
<code><module>::<function></code> when using
<code>tracing::instrument</code>).</li>
<li>Before this change, the span op would be set based on the
<code>tracing</code> span name.</li>
<li>Read below to learn how to customize the span name and op.</li>
<li>When upgrading, please ensure to adapt any queries, metrics or
dashboards to use the new span names/ops.</li>
</ul>
</li>
<li>ref(tracing): use standard code attributes (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/899">#899</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a>
<ul>
<li>Logs now carry the attributes <code>code.module.name</code>,
<code>code.file.path</code> and <code>code.line.number</code>
standardized in OTEL to surface the respective information, in contrast
with the previously sent <code>tracing.module_path</code>,
<code>tracing.file</code> and <code>tracing.line</code>.</li>
</ul>
</li>
<li>fix(actix): capture only server errors (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/877">#877</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/getsentry/sentry-rust/blob/master/CHANGELOG.md">sentry's
changelog</a>.</em></p>
<blockquote>
<h2>0.46.0</h2>
<h3>Breaking changes</h3>
<ul>
<li>Removed the <code>ClientOptions</code> struct's
<code>trim_backtraces</code> and <code>extra_border_frames</code> fields
(<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/925">#925</a>).
<ul>
<li>These fields configured backtrace trimming, which is being removed
in this release.</li>
</ul>
</li>
</ul>
<h3>Improvements</h3>
<ul>
<li>Removed backtrace trimming to align the Rust SDK with the general
principle that Sentry SDKs should only truncate telemetry data when
needed to comply with <a
href="https://develop.sentry.dev/sdk/data-model/envelopes/#size-limits">documented
size limits</a> (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/925">#925</a>).
This change ensures that as much data as possible remains available for
debugging.
<ul>
<li>If you notice any new issues being created for existing errors after
this change, please open an issue on <a
href="https://github.com/getsentry/sentry-rust/issues/new/choose">GitHub</a>.</li>
</ul>
</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>fix: adjust sentry.origin for log integration (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/919">#919</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a></li>
</ul>
<h2>0.45.0</h2>
<h3>Breaking changes</h3>
<ul>
<li>Add custom variant to <code>AttachmentType</code> that holds an
arbitrary String. (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/916">#916</a>)</li>
</ul>
<h2>0.44.0</h2>
<h3>Breaking changes</h3>
<ul>
<li>feat(log): support combined LogFilters and RecordMappings (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/914">#914</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a>
<ul>
<li>Breaking change: <code>sentry::integrations::log::LogFilter</code>
has been changed to a <code>bitflags</code> struct.</li>
<li>It's now possible to map a <code>log</code> record to multiple items
in Sentry by combining multiple log filters in the filter, e.g.
<code>log::Level::ERROR => LogFilter::Event |
LogFilter::Log</code>.</li>
<li>If using a custom <code>mapper</code> instead, it's possible to
return a
<code>Vec<sentry::integrations::log::RecordMapping></code> to map
a <code>log</code> record to multiple items in Sentry.</li>
</ul>
</li>
</ul>
<h3>Behavioral changes</h3>
<ul>
<li>ref(log): send logs by default when logs feature flag is enabled (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/915">#915</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a>
<ul>
<li>If the <code>logs</code> feature flag is enabled, the default Sentry
<code>log</code> logger now sends logs for all events at or above
INFO.</li>
</ul>
</li>
<li>ref(logs): enable logs by default if logs feature flag is used (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/910">#910</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a>
<ul>
<li>This changes the default value of
<code>sentry::ClientOptions::enable_logs</code> to
<code>true</code>.</li>
<li>This simplifies the setup of Sentry structured logs by requiring
users to just add the <code>log</code> feature flag to the
<code>sentry</code> dependency to opt-in to sending logs.</li>
<li>When the <code>log</code> feature flag is enabled, the
<code>tracing</code> and <code>log</code> integrations will send
structured logs to Sentry for all logs/events at or above INFO level by
default.</li>
</ul>
</li>
</ul>
<h2>0.43.0</h2>
<h3>Breaking changes</h3>
<ul>
<li>ref(tracing): rework tracing to Sentry span name/op conversion (<a
href="https://redirect.github.com/getsentry/sentry-rust/pull/887">#887</a>)
by <a href="https://github.com/lcian"><code>@lcian</code></a>
<ul>
<li>The <code>tracing</code> integration now uses the tracing span name
as the Sentry span name by default.</li>
<li>Before this change, the span name would be set based on the
<code>tracing</code> span target
(<code><module>::<function></code> when using the
<code>tracing::instrument</code> macro).</li>
<li>The <code>tracing</code> integration now uses <code><span
target>::<span name></code> as the default Sentry span op (i.e.
<code><module>::<function></code> when using
<code>tracing::instrument</code>).</li>
<li>Before this change, the span op would be set based on the
<code>tracing</code> span name.</li>
<li>Read below to learn how to customize the span name and op.</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8d82bfde59"><code>8d82bfd</code></a>
release: 0.46.0</li>
<li><a
href="9525735e5c"><code>9525735</code></a>
feat(backtrace): Stop truncating backtraces (<a
href="https://redirect.github.com/getsentry/sentry-rust/issues/925">#925</a>)</li>
<li><a
href="a57b91c5c8"><code>a57b91c</code></a>
ref: Fix new Clippy lints (<a
href="https://redirect.github.com/getsentry/sentry-rust/issues/935">#935</a>)</li>
<li><a
href="57595753d6"><code>5759575</code></a>
meta: Update cargo metadata (<a
href="https://redirect.github.com/getsentry/sentry-rust/issues/927">#927</a>)</li>
<li><a
href="77193f81e4"><code>77193f8</code></a>
chore: X handle update (<a
href="https://redirect.github.com/getsentry/sentry-rust/issues/926">#926</a>)</li>
<li><a
href="ca232686f4"><code>ca23268</code></a>
chore(ci): Migrate danger workflow from v2 to v3 (<a
href="https://redirect.github.com/getsentry/sentry-rust/issues/918">#918</a>)</li>
<li><a
href="2edf6d7a54"><code>2edf6d7</code></a>
fix: adjust sentry.origin for log integration (<a
href="https://redirect.github.com/getsentry/sentry-rust/issues/919">#919</a>)</li>
<li><a
href="6412048910"><code>6412048</code></a>
Merge branch 'release/0.45.0'</li>
<li><a
href="aa6d85b90f"><code>aa6d85b</code></a>
release: 0.45.0</li>
<li><a
href="b99eb46bcf"><code>b99eb46</code></a>
feat(types): Add custom variant to <code>AttachmentType</code> (<a
href="https://redirect.github.com/getsentry/sentry-rust/issues/916">#916</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/getsentry/sentry-rust/compare/0.34.0...0.46.0">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 [actions/cache](https://github.com/actions/cache) from 4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/cache/releases">actions/cache's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<blockquote>
<p>[!IMPORTANT]
<strong><code>actions/cache@v5</code> runs on the Node.js 24 runtime and
requires a minimum Actions Runner version of
<code>2.327.1</code>.</strong></p>
<p>If you are using self-hosted runners, ensure they are updated before
upgrading.</p>
</blockquote>
<hr />
<h2>What's Changed</h2>
<ul>
<li>Upgrade to use node24 by <a
href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1630">actions/cache#1630</a></li>
<li>Prepare v5.0.0 release by <a
href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1684">actions/cache#1684</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/cache/compare/v4.3.0...v5.0.0">https://github.com/actions/cache/compare/v4.3.0...v5.0.0</a></p>
<h2>v4.3.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Add note on runner versions by <a
href="https://github.com/GhadimiR"><code>@GhadimiR</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1642">actions/cache#1642</a></li>
<li>Prepare <code>v4.3.0</code> release by <a
href="https://github.com/Link"><code>@Link</code></a>- in <a
href="https://redirect.github.com/actions/cache/pull/1655">actions/cache#1655</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/GhadimiR"><code>@GhadimiR</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/cache/pull/1642">actions/cache#1642</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/cache/compare/v4...v4.3.0">https://github.com/actions/cache/compare/v4...v4.3.0</a></p>
<h2>v4.2.4</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1620">actions/cache#1620</a></li>
<li>Upgrade <code>@actions/cache</code> to <code>4.0.5</code> and move
<code>@protobuf-ts/plugin</code> to dev depdencies by <a
href="https://github.com/Link"><code>@Link</code></a>- in <a
href="https://redirect.github.com/actions/cache/pull/1634">actions/cache#1634</a></li>
<li>Prepare release <code>4.2.4</code> by <a
href="https://github.com/Link"><code>@Link</code></a>- in <a
href="https://redirect.github.com/actions/cache/pull/1636">actions/cache#1636</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/nebuk89"><code>@nebuk89</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/cache/pull/1620">actions/cache#1620</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/cache/compare/v4...v4.2.4">https://github.com/actions/cache/compare/v4...v4.2.4</a></p>
<h2>v4.2.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Update to use <code>@actions/cache</code> 4.0.3 package &
prepare for new release by <a
href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1577">actions/cache#1577</a>
(SAS tokens for cache entries are now masked in debug logs)</li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/cache/pull/1577">actions/cache#1577</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/cache/compare/v4.2.2...v4.2.3">https://github.com/actions/cache/compare/v4.2.2...v4.2.3</a></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/cache/blob/main/RELEASES.md">actions/cache's
changelog</a>.</em></p>
<blockquote>
<h1>Releases</h1>
<h2>Changelog</h2>
<h3>5.0.1</h3>
<ul>
<li>Update <code>@azure/storage-blob</code> to <code>^12.29.1</code> via
<code>@actions/cache@5.0.1</code> <a
href="https://redirect.github.com/actions/cache/pull/1685">#1685</a></li>
</ul>
<h3>5.0.0</h3>
<blockquote>
<p>[!IMPORTANT]
<code>actions/cache@v5</code> runs on the Node.js 24 runtime and
requires a minimum Actions Runner version of <code>2.327.1</code>.
If you are using self-hosted runners, ensure they are updated before
upgrading.</p>
</blockquote>
<h3>4.3.0</h3>
<ul>
<li>Bump <code>@actions/cache</code> to <a
href="https://redirect.github.com/actions/toolkit/pull/2132">v4.1.0</a></li>
</ul>
<h3>4.2.4</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.5</li>
</ul>
<h3>4.2.3</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.3 (obfuscates SAS token in
debug logs for cache entries)</li>
</ul>
<h3>4.2.2</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.2</li>
</ul>
<h3>4.2.1</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.1</li>
</ul>
<h3>4.2.0</h3>
<p>TLDR; The cache backend service has been rewritten from the ground up
for improved performance and reliability. <a
href="https://github.com/actions/cache">actions/cache</a> now integrates
with the new cache service (v2) APIs.</p>
<p>The new service will gradually roll out as of <strong>February 1st,
2025</strong>. The legacy service will also be sunset on the same date.
Changes in these release are <strong>fully backward
compatible</strong>.</p>
<p><strong>We are deprecating some versions of this action</strong>. We
recommend upgrading to version <code>v4</code> or <code>v3</code> as
soon as possible before <strong>February 1st, 2025.</strong> (Upgrade
instructions below).</p>
<p>If you are using pinned SHAs, please use the SHAs of versions
<code>v4.2.0</code> or <code>v3.4.0</code></p>
<p>If you do not upgrade, all workflow runs using any of the deprecated
<a href="https://github.com/actions/cache">actions/cache</a> will
fail.</p>
<p>Upgrading to the recommended versions will not break your
workflows.</p>
<h3>4.1.2</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9255dc7a25"><code>9255dc7</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/cache/issues/1686">#1686</a>
from actions/cache-v5.0.1-release</li>
<li><a
href="8ff5423e8b"><code>8ff5423</code></a>
chore: release v5.0.1</li>
<li><a
href="9233019a15"><code>9233019</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/cache/issues/1685">#1685</a>
from salmanmkc/node24-storage-blob-fix</li>
<li><a
href="b975f2bb84"><code>b975f2b</code></a>
fix: add peer property to package-lock.json for dependencies</li>
<li><a
href="d0a0e18134"><code>d0a0e18</code></a>
fix: update license files for <code>@actions/cache</code>,
fast-xml-parser, and strnum</li>
<li><a
href="74de208dcf"><code>74de208</code></a>
fix: update <code>@actions/cache</code> to ^5.0.1 for Node.js 24
punycode fix</li>
<li><a
href="ac7f1152ea"><code>ac7f115</code></a>
peer</li>
<li><a
href="b0f846b50b"><code>b0f846b</code></a>
fix: update <code>@actions/cache</code> with storage-blob fix for
Node.js 24 punycode depr...</li>
<li><a
href="a783357455"><code>a783357</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/cache/issues/1684">#1684</a>
from actions/prepare-cache-v5-release</li>
<li><a
href="3bb0d78750"><code>3bb0d78</code></a>
docs: highlight v5 runner requirement in releases</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/cache/compare/v4...v5">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>
Normally, all tool calls within a saved session should have a response,
but there are legitimate reasons for the response to be missing. This
can occur if the user canceled the call or there was an error of some
sort during the rollout. We shouldn't panic in this case.
This is a partial fix for #7990
Bumps [socket2](https://github.com/rust-lang/socket2) from 0.6.0 to
0.6.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/socket2/blob/master/CHANGELOG.md">socket2's
changelog</a>.</em></p>
<blockquote>
<h1>0.6.1</h1>
<h2>Added</h2>
<ul>
<li>Added support for Windows Registered I/O (RIO)
(<a
href="https://redirect.github.com/rust-lang/socket2/pull/604">rust-lang/socket2#604</a>).</li>
<li>Added support for <code>TCP_NOTSENT_LOWAT</code> on Linux via
<code>Socket::(set_)tcp_notsent_lowat</code>
(<a
href="https://redirect.github.com/rust-lang/socket2/pull/611">rust-lang/socket2#611</a>).</li>
<li>Added support for <code>SO_BUSY_POLL</code> on Linux via
<code>Socket::set_busy_poll</code>
(<a
href="https://redirect.github.com/rust-lang/socket2/pull/607">rust-lang/socket2#607</a>).</li>
<li><code>SockFilter::new</code> is now a const function
(<a
href="https://redirect.github.com/rust-lang/socket2/pull/609">rust-lang/socket2#609</a>).</li>
</ul>
<h2>Changed</h2>
<ul>
<li>Updated the windows-sys dependency to version 0.60
(<a
href="https://redirect.github.com/rust-lang/socket2/pull/605">rust-lang/socket2#605</a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d0ba3d39a6"><code>d0ba3d3</code></a>
Release v0.6.1</li>
<li><a
href="3a8b7edda3"><code>3a8b7ed</code></a>
Add example to create <code>SockAddr</code> from
<code>libc::sockaddr_storage</code> (<a
href="https://redirect.github.com/rust-lang/socket2/issues/615">#615</a>)</li>
<li><a
href="b54e2e6dbf"><code>b54e2e6</code></a>
Disable armv7-sony-vita-newlibeabihf CI check</li>
<li><a
href="2d4a2f7b3b"><code>2d4a2f7</code></a>
Update feature <code>doc_auto_cfg</code> to <code>doc_cfg</code></li>
<li><a
href="11aa1029f2"><code>11aa102</code></a>
Add missing components when installing Rust in CI</li>
<li><a
href="528ba2b0da"><code>528ba2b</code></a>
Add TCP_NOTSENT_LOWAT socketopt support</li>
<li><a
href="1fdd2938c1"><code>1fdd293</code></a>
Correct rename in CHANGELOG.md (<a
href="https://redirect.github.com/rust-lang/socket2/issues/610">#610</a>)</li>
<li><a
href="600ff0d246"><code>600ff0d</code></a>
Add support for Windows Registered I/O</li>
<li><a
href="f0836965a1"><code>f083696</code></a>
Allow <code>SockFilter::new</code> in const contexts</li>
<li><a
href="15ade5100c"><code>15ade51</code></a>
Refactor for cargo fmt</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-lang/socket2/compare/v0.6.0...v0.6.1">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot 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
[actions/upload-artifact](https://github.com/actions/upload-artifact)
from 5 to 6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/upload-artifact/releases">actions/upload-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.0</h2>
<h2>v6 - What's new</h2>
<blockquote>
<p>[!IMPORTANT]
actions/upload-artifact@v6 now runs on Node.js 24 (<code>runs.using:
node24</code>) and requires a minimum Actions Runner version of 2.327.1.
If you are using self-hosted runners, ensure they are updated before
upgrading.</p>
</blockquote>
<h3>Node.js 24</h3>
<p>This release updates the runtime to Node.js 24. v5 had preliminary
support for Node.js 24, however this action was by default still running
on Node.js 20. Now this action by default will run on Node.js 24.</p>
<h2>What's Changed</h2>
<ul>
<li>Upload Artifact Node 24 support by <a
href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/upload-artifact/pull/719">actions/upload-artifact#719</a></li>
<li>fix: update <code>@actions/artifact</code> for Node.js 24 punycode
deprecation by <a
href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/upload-artifact/pull/744">actions/upload-artifact#744</a></li>
<li>prepare release v6.0.0 for Node.js 24 support by <a
href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/upload-artifact/pull/745">actions/upload-artifact#745</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0">https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b7c566a772"><code>b7c566a</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/upload-artifact/issues/745">#745</a>
from actions/upload-artifact-v6-release</li>
<li><a
href="e516bc8500"><code>e516bc8</code></a>
docs: correct description of Node.js 24 support in README</li>
<li><a
href="ddc45ed9bc"><code>ddc45ed</code></a>
docs: update README to correct action name for Node.js 24 support</li>
<li><a
href="615b319bd2"><code>615b319</code></a>
chore: release v6.0.0 for Node.js 24 support</li>
<li><a
href="017748b48f"><code>017748b</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/upload-artifact/issues/744">#744</a>
from actions/fix-storage-blob</li>
<li><a
href="38d4c7997f"><code>38d4c79</code></a>
chore: rebuild dist</li>
<li><a
href="7d27270e0c"><code>7d27270</code></a>
chore: add missing license cache files for <code>@actions/core</code>,
<code>@actions/io</code>, and mi...</li>
<li><a
href="5f643d3c94"><code>5f643d3</code></a>
chore: update license files for <code>@actions/artifact</code><a
href="https://github.com/5"><code>@5</code></a>.0.1 dependencies</li>
<li><a
href="1df1684032"><code>1df1684</code></a>
chore: update package-lock.json with <code>@actions/artifact</code><a
href="https://github.com/5"><code>@5</code></a>.0.1</li>
<li><a
href="b5b1a91840"><code>b5b1a91</code></a>
fix: update <code>@actions/artifact</code> to ^5.0.0 for Node.js 24
punycode fix</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/upload-artifact/compare/v5...v6">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>
## Notes
Skills are behind the experimental `skills` feature flag (disabled by
default), but the skills guide didn't explain how to turn them on.
- Add an explicit enable section to `docs/skills.md` (config +
`--enable`)
- Add the skills flag to `docs/config.md` and `docs/example-config.md`
- Document the `/skills` slash command
refactor the way we load and manage skills:
1. Move skill discovery/caching into SkillsManager and reuse it across
sessions.
2. Add the skills/list API (Op::ListSkills/SkillsListResponse) to fetch
skills for one or more cwds. Also update app-server for VSCE/App;
3. Trigger skills/list during session startup so UIs preload skills and
handle errors immediately.
The existing version of `shell-tool-mcp/README.md` was not written in a
way that was meant to be consumed by end-users. This is now fixed.
Added `codex-rs/exec-server/README.md` for the more technical bits.
Codex identified this as the cause of a reported hang:
https://github.com/openai/codex/issues/7822. Apparently, the wrapping
algorithm we're using has known issues and bad worst-case behaviors when
OptimalFit is used on certain strings. It recommended switching to
FirstFit instead.
Drop the AGENTS.md rule that forbids unsigned ints. The blanket guidance
causes unnecessary complexity in cases where values are naturally
unsigned, leading to extra clamping/conversion code instead of using
checked or saturating arithmetic where needed.
Changes the `writable_roots` field of the `WorkspaceWrite` variant of
the `SandboxPolicy` enum from `Vec<PathBuf>` to `Vec<AbsolutePathBuf>`.
This is helpful because now callers can be sure the value is an absolute
path rather than a relative one. (Though when using an absolute path in
a Seatbelt config policy, we still have to _canonicalize_ it first.)
Because `writable_roots` can be read from a config file, it is important
that we are able to resolve relative paths properly using the parent
folder of the config file as the base path.
## Problem
The introduction of `notify_sandbox_state_change()` in #7112 caused a
regression where the blocking call in `Session::new()` waits for all MCP
servers to fully initialize before returning. This prevents the TUI
event loop from starting, resulting in `McpStartupUpdateEvent` messages
being emitted but never consumed or displayed. As a result, the app
appears to hang during startup, and users do not see the expected
"Booting MCP server: {name}" status line.
Issue: [#7827](https://github.com/openai/codex/issues/7827)
## Solution
This change moves sandbox state notification into each MCP server's
background initialization task. The notification is sent immediately
after the server transitions to the Ready state. This approach:
- Avoids blocking `Session::new()`, allowing the TUI event loop to start
promptly.
- Ensures each MCP server receives its sandbox state before handling any
tool calls.
- Restores the display of "Booting MCP server" status lines during
startup.
## Key Changes
- Added `ManagedClient::notify_sandbox_state()` method.
- Passed sandbox_state to `McpConnectionManager::initialize()`.
- Sends sandbox state notification in the background task after the
server reaches Ready status.
- Removed blocking notify_sandbox_state_change() methods.
- Added a chatwidget snapshot test for the "Booting MCP server" status
line.
## Regression Details
Regression was bisected to #7112, which introduced the blocking
behavior.
---------
Co-authored-by: Michael Bolin <bolinfest@gmail.com>
Co-authored-by: Michael Bolin <mbolin@openai.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.
The elevated sandbox ships two exes
* one for elevated setup of the sandbox
* one to actually run commands under the sandbox user.
This PR adds them to the windows signing step
Previous to this PR, we used a hand-rolled PowerShell parser in
`windows_safe_commands.rs` to take a `&str` of PowerShell script see if
it is equivalent to a list of `execvp(3)` invocations, and if so, we
then test each using `is_safe_powershell_command()` to determine if the
overall command is safe:
6e6338aa87/codex-rs/core/src/command_safety/windows_safe_commands.rs (L89-L98)
Unfortunately, our PowerShell parser did not recognize `@(...)` as a
special construct, so it was treated as an ordinary token. This meant
that the following would erroneously be considered "safe:"
```powershell
ls @(calc.exe)
```
The fix introduced in this PR is to do something comparable what we do
for Bash/Zsh, which is to use a "proper" parser to derive the list of
`execvp(3)` calls. For Bash/Zsh, we rely on
https://crates.io/crates/tree-sitter-bash, but there does not appear to
be a crate of comparable quality for parsing PowerShell statically
(https://github.com/airbus-cert/tree-sitter-powershell/ is the best
thing I found).
Instead, in this PR, we use a PowerShell script to parse the input
PowerShell program to produce the AST.
This PR changes the length validation for SKILL.md `name` and
`description` fields so they use character counts rather than byte
counts. Aligned character limits to other harnesses.
This addresses #7730.
I am trying to tighten up some of our logic around PowerShell over in
https://github.com/openai/codex/pull/7607 and it would be helpful to be
more precise about `pwsh.exe` versus `powershell.exe`, as they do not
accept the exact same input language.
To that end, this PR introduces utilities for detecting each on the
system. I think we also want to update `get_user_shell_path()` to return
PowerShell instead of `None` on Windows, but we'll consider that in a
separate PR since it may require more testing.
## Changes
- Update config docs and example config comments to state that "xhigh"
is supported on gpt-5.2 as well as gpt-5.1-codex-max
- Adjust the FAQ model-support section to reflect broader xhigh
availability
# 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.
We decided that `*.rules` is a more fitting (and concise) file extension
than `*.codexpolicy`, so we are changing the file extension for the
"execpolicy" effort. We are also changing the subfolder of `$CODEX_HOME`
from `policy` to `rules` to match.
This PR updates the in-repo docs and we will update the public docs once
the next CLI release goes out.
Locally, I created `~/.codex/rules/default.rules` with the following
contents:
```
prefix_rule(pattern=["gh", "pr", "view"])
```
And then I asked Codex to run:
```
gh pr view 7888 --json title,body,comments
```
and it was able to!
## Summary
- update CLI OAuth guidance to reference `features.rmcp_client` instead
of the deprecated experimental flag
- keep login/logout help text consistent with the new feature flag
## Testing
- `cargo test -p codex-cli`
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_693b3e0bf27c832cb66d585847a552ab)
### What
Don't treat `q` as a special quit character on the API key paste page in
the onboarding flow.
This addresses #7413, where pasting API keys with `q` would cause codex
to quit on Windows.
### Test Plan
Tested on Windows and MacOS.
Let's see if this `sleep()` call is good enough to fix the test
flakiness we currently see in CI. It will take me some time to upstream
a proper fix, and I would prefer not to disable this test in the
interim.
Our previous design of cancellation endpoint is not idempotent, which
caused a bunch of flaky tests. Make app server just returned a not_found
status instead of throwing an error if the login id is not found. Keep
V1 endpoint behavior the same.
When I originally introduced `accept_elicitation_for_prompt_rule()` in
https://github.com/openai/codex/pull/7617, it worked for me locally
because I had run `codex-rs/exec-server/tests/suite/bash` once myself,
which had the side-effect of installing the corresponding DotSlash
artifact.
In CI, I added explicit logic to do this as part of
`.github/workflows/rust-ci.yml`, which meant the test also passed in CI,
but this logic should have been done as part of the test so that it
would work locally for devs who had not installed the DotSlash artifact
for `codex-rs/exec-server/tests/suite/bash` before. This PR updates the
test to do this (and deletes the setup logic from `rust-ci.yml`),
creating a new `DOTSLASH_CACHE` in a temp directory so that this is
handled independently for each test.
While here, also added a check to ensure that the `codex` binary has
been built prior to running the test, as we have to ensure it is
symlinked as `codex-linux-sandbox` on Linux in order for the integration
test to work on that platform.
This caused some conversations to not appear when they otherwise should.
Prior to this change, `thread/list`/`list_conversations_common` would:
- Fetch N conversations from `RolloutRecorder::list_conversations`
- Then it would filter those (like by the provided `model_providers`)
- This would make it potentially return less than N items.
With this change:
- `list_conversations_common` now continues fetching more conversations
from `RolloutRecorder::list_conversations` until it "fills up" the
`requested_page_size`.
- Ultimately this means that clients can rely on getting eg 20
conversations if they request 20 conversations.
Introduce a full codex-tui source snapshot under the new codex-tui2
crate so viewport work can be replayed in isolation.
This change copies the entire codex-rs/tui/src tree into
codex-rs/tui2/src in one atomic step, rather than piecemeal, to keep
future diffs vs the original viewport bookmark easy to reason about.
The goal is for codex-tui2 to render identically to the existing TUI
behind the `features.tui2` flag while we gradually port the
viewport/history commits from the joshka/viewport bookmark onto this
forked tree.
While on this baseline change, we also ran the codex-tui2 snapshot test
suite and accepted all insta snapshots for the new crate, so the
snapshot files now use the codex-tui2 naming scheme and encode the
unmodified legacy TUI behavior. This keeps later viewport commits
focused on intentional behavior changes (and their snapshots) rather
than on mechanical snapshot renames.
1. Skills load once in core at session start; the cached outcome is
reused across core and surfaced to TUI via SessionConfigured.
2. TUI detects explicit skill selections, and core injects the matching
SKILL.md content into the turn when a selected skill is present.
## Slash Command popup issue
#7659
When recalling history, the
composer(`codex_tui::bottom_pane::chat_composer`) restores the previous
prompt text (which may start with `/`) and then calls
`sync_command_popup`. The logic in `sync_command_popup` treats any first
line that starts with `/` and has the caret inside the initial `/name`
token as an active slash command name:
```rust
let is_editing_slash_command_name = if first_line.starts_with('/') && caret_on_first_line {
let token_end = first_line
.char_indices()
.find(|(_, c)| c.is_whitespace())
.map(|(i, _)| i)
.unwrap_or(first_line.len());
cursor <= token_end
} else {
false
};
```
This detection does not distinguish between an actual interactive slash
command being typed and a normal historical prompt that happens to begin
with `/`. As a result, after history recall, the restored prompt like `/
test` is interpreted as an "editing command name" context and the
slash-command popup is (re)activated. Once `active_popup` is
`ActivePopup::Command`, subsequent `Up` key presses are handled by
`handle_key_event_with_slash_popup` instead of
`handle_key_event_without_popup`, so they no longer trigger
`history.navigate_up(...)` and the session prompt history cannot be
scrolled.
- Make Config.model optional and centralize default-selection logic in
ModelsManager, including a default_model helper (with
codex-auto-balanced when available) so sessions now carry an explicit
chosen model separate from the base config.
- Resolve `model` once in `core` and `tui` from config. Then store the
state of it on other structs.
- Move refreshing models to be before resolving the default model
Make sure that config writes preserve comments and order of configs by
utilizing the ConfigEditsBuilder in core.
Tested by running a real example and made sure that nothing in the
config file changes other than the configs to edit.
## What?
Upgrades @modelcontextprotocol/sdk from ^1.20.2 to ^1.24.0 in the
TypeScript SDK's devDependencies.
## Why?
Related to #7737 - keeping development dependencies up to date with the
latest MCP SDK version that includes the fix for CVE-2025-66414.
Note: This change does not address the CVE for Codex users, as the MCP
SDK is only in devDependencies here. The actual MCP integration that
would be affected by the CVE is in the Rust codebase.
## How?
• Updated dependency version in sdk/typescript/package.json
• Ran pnpm install to update lockfile
• Fixed formatting (added missing newline in package.json)
## Related Issue
Related to #7737
## Test Status
⚠️ After this upgrade, 2 additional tests timeout (1 test was already
failing on main):
• tests/run.test.ts: "sends previous items when run is called twice"
• tests/run.test.ts: "resumes thread by id"
• tests/runStreamed.test.ts: "sends previous items when runStreamed is
called twice"
Marking as draft to investigate test timeouts. Maintainer guidance would
be appreciated.
Co-authored-by: HalfonA <amit@miggo.io>
helpful in the future if we want more granularity for requesting
escalated permissions:
e.g when running in readonly sandbox, model can request to escalate to a
sandbox that allows writes
- updating helpers, refactoring some functions that will be used in the
elevated sandbox
- better logging
- better and faster handling of ACL checks/writes
- No functional change—legacy restricted-token sandbox
remains the only path.
### Summary
* Added `mcpServer/oauthLogin` in app server for supporting in session
MCP server login
* Added `McpServerOauthLoginParams` and `McpServerOauthLoginResponse` to
support above method with response returning the auth URL for consumer
to open browser or display accordingly.
* Added `McpServerOauthLoginCompletedNotification` which the app server
would emit on MCP server login success or failure (i.e. timeout).
* Refactored rmcp-client oath_login to have the ability on starting a
auth server which the codex_message_processor uses for in-session auth.
This PR attempts to solve two problems by introducing a
`AbsolutePathBuf` type with a special deserializer:
- `AbsolutePathBuf` attempts to be a generally useful abstraction, as it
ensures, by constructing, that it represents a value that is an
absolute, normalized path, which is a stronger guarantee than an
arbitrary `PathBuf`.
- Values in `config.toml` that can be either an absolute or relative
path should be resolved against the folder containing the `config.toml`
in the relative path case. This PR makes this easy to support: the main
cost is ensuring `AbsolutePathBufGuard` is used inside
`deserialize_config_toml_with_base()`.
While `AbsolutePathBufGuard` may seem slightly distasteful because it
relies on thread-local storage, this seems much cleaner to me than using
than my various experiments with
https://docs.rs/serde/latest/serde/de/trait.DeserializeSeed.html.
Further, since the `deserialize()` method from the `Deserialize` trait
is not async, we do not really have to worry about the deserialization
work being spread across multiple threads in a way that would interfere
with `AbsolutePathBufGuard`.
To start, this PR introduces the use of `AbsolutePathBuf` in
`OtelTlsConfig`. Note how this simplifies `otel_provider.rs` because it
no longer requires `settings.codex_home` to be threaded through.
Furthermore, this sets us up better for a world where multiple
`config.toml` files from different folders could be loaded and then
merged together, as the absolutifying of the paths must be done against
the correct parent folder.
Introduce a new codex-tui2 crate that re-exports the existing
interactive TUI surface and delegates run_main directly to codex-tui.
This keeps behavior identical while giving tui2 its own crate for future
viewport work.
Wire the codex CLI to select the frontend via the tui2 feature flag.
When the merged CLI overrides include features.tui2=true (e.g. via
--enable tui2), interactive runs are routed through
codex_tui2::run_main; otherwise they continue to use the original
codex_tui::run_main.
Register Feature::Tui2 in the core feature registry and add the tui2
crate and dependency entries so the new frontend builds alongside the
existing TUI.
This is a stub that only wires up the feature flag for this.
<img width="619" height="364" alt="image"
src="https://github.com/user-attachments/assets/4893f030-932f-471e-a443-63fe6b5d8ed9"
/>
## Summary
Support "j" and "k" keys as aliases for "down" and "up" so vim users
feel loved. Only support these keys when the selection is not
searchable.
## Testing
- env -u NO_COLOR TERM=xterm-256color cargo test -p codex-tui
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_693771b53bc8833088669060dfac2083)
Fix for #7459
## What
Since codex errors out for unsupported images, stop attempting to
base64/attach them and instead emit a clear placeholder when the file
isn’t a supported image MIME.
## Why
Local uploads for unsupported formats (e.g., SVG/GIF/etc.) were
dead-ending after decode failures because of the 400 retry loop. Users
now get an explicit “cannot attach … unsupported image format …”
response.
## How
Replace the fallback read/encode path with MIME detection that bails out
for non-image or unsupported image types, returning a consistent
placeholder. Unreadable and invalid images still produce their existing
error placeholders.
This changes our default Landlock policy to allow `sendmsg(2)` and
`recvmsg(2)` syscalls. We believe these were originally denied out of an
abundance of caution, but given that `send(2)` nor `recv(2)` are allowed
today [which provide comparable capability to the `*msg` equivalents],
we do not believe allowing them grants any privileges beyond what we
already allow.
Rather than using the syscall as the security boundary, preventing
access to the potentially hazardous file descriptor in the first place
seems like the right layer of defense.
In particular, this makes it possible for `shell-tool-mcp` to run on
Linux when using a read-only sandbox for the Bash process, as
demonstrated by `accept_elicitation_for_prompt_rule()` now succeeding in
CI.
Fixes#7759:
- Drop the stale `rmcp` entry from `codex-rs/default.nix`’s
`cargoLock.outputHashes` since the crate now comes from crates.io and no
longer needs a git hash.
- Add the missing hash for the filedescriptor-0.8.3 git dependency (from
`pakrym/wezterm`) so `buildRustPackage` can vendor it.
## What
Fix PageUp/PageDown behaviour in the Ctrl+T transcript overlay so that
paging is continuous and reversible, and add tests to lock in the
expected behaviour.
## Why
Today, paging in the transcript overlay uses the raw viewport height
instead of the effective content height after layout. Because the
overlay reserves some rows for chrome (header/footer), this can cause:
- PageDown to skip transcript lines between pages.
- PageUp/PageDown not to “round-trip” cleanly (PageDown then PageUp does
not always return to the same set of visible lines).
This shows up when inspecting longer transcripts via Ctrl+T; see #7356
for context.
## How
- Add a dedicated `PagerView::page_step` helper that computes the page
size from the last rendered content height and falls back to
`content_area(viewport_area).height` when that is not yet available.
- Use `page_step(...)` for both PageUp and PageDown (including SPACE) so
the scroll step always matches the actual content area height, not the
full viewport height.
- Add a focused test
`transcript_overlay_paging_is_continuous_and_round_trips` that:
- Renders a synthetic transcript with numbered `line-NN` rows.
- Asserts that successive PageDown operations show continuous line
numbers (no gaps).
- Asserts that PageDown+PageUp and PageUp+PageDown round-trip correctly
from non-edge offsets.
The change is limited to `codex-rs/tui/src/pager_overlay.rs` and only
affects the transcript overlay paging semantics.
## Related issue
- #7356
## Testing
On Windows 11, using PowerShell 7 in the repo root:
```powershell
cargo test
cargo clippy --tests
cargo fmt -- --config imports_granularity=Item
```
- All tests passed.
- `cargo clippy --tests` reported some pre-existing warnings that are
unrelated to this change; no new lints were introduced in the modified
code.
---------
Signed-off-by: muyuanjin <24222808+muyuanjin@users.noreply.github.com>
Co-authored-by: Eric Traut <etraut@openai.com>
We received a bug report that Codex CLI crashes when an env var contains
a non-ASCII character, or more specifically, cannot be decoded as UTF-8:
```shell
$ RUST_BACKTRACE=full RÖDBURK=1 codex
thread '<unnamed>' panicked at library/std/src/env.rs:162:57:
called `Result::unwrap()` on an `Err` value: "RÃ\xB6DBURK"
stack backtrace:
0: 0x101905c18 - __mh_execute_header
1: 0x1012bd76c - __mh_execute_header
2: 0x1019050e4 - __mh_execute_header
3: 0x101905ad8 - __mh_execute_header
4: 0x101905874 - __mh_execute_header
5: 0x101904f38 - __mh_execute_header
6: 0x1019347bc - __mh_execute_header
7: 0x10193472c - __mh_execute_header
8: 0x101937884 - __mh_execute_header
9: 0x101b3bcd0 - __mh_execute_header
10: 0x101b3c0bc - __mh_execute_header
11: 0x101927a20 - __mh_execute_header
12: 0x1005c58d8 - __mh_execute_header
thread '<unnamed>' panicked at library/core/src/panicking.rs:225:5:
panic in a function that cannot unwind
stack backtrace:
0: 0x101905c18 - __mh_execute_header
1: 0x1012bd76c - __mh_execute_header
2: 0x1019050e4 - __mh_execute_header
3: 0x101905ad8 - __mh_execute_header
4: 0x101905874 - __mh_execute_header
5: 0x101904f38 - __mh_execute_header
6: 0x101934794 - __mh_execute_header
7: 0x10193472c - __mh_execute_header
8: 0x101937884 - __mh_execute_header
9: 0x101b3c144 - __mh_execute_header
10: 0x101b3c1a0 - __mh_execute_header
11: 0x101b3c158 - __mh_execute_header
12: 0x1005c5ef8 - __mh_execute_header
thread caused non-unwinding panic. aborting.
```
I discovered I could reproduce this on a release build, but not a dev
build, so between that and the unhelpful stack trace, my mind went to
the pre-`main()` logic we run in prod builds. Sure enough, we were
operating on `std::env::vars()` instead of `std::env::vars_os()`, which
is why the non-UTF-8 environment variable was causing an issue.
This PR updates the logic to use `std::env::vars_os()` and adds a unit
test.
And to be extra sure, I also verified the fix works with a local release
build:
```
$ cargo build --bin codex --release
$ RÖDBURK=1 ./target/release/codex --version
codex-cli 0.0.0
```
This is a step towards removing the need to know `model` when
constructing config. We firstly don't need to know `model_info` and just
respect if the user has already set it. Next step, we don't need to know
`model` unless the user explicitly set it in `config.toml`
## Summary
- restore the previous status header when a non-error event arrives
after a stream retry
- add a regression test to ensure the reconnect banner clears once
streaming resumes
## Testing
- cargo fmt -- --config imports_granularity=Item
- cargo clippy --fix --all-features --tests --allow-dirty -p codex-tui
- NO_COLOR=0 cargo test -p codex-tui *(fails: vt100 color assertion
tests expect colored cells but the environment returns Default colors
even with NO_COLOR cleared and TERM/COLORTERM set)*
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_69337f8c77508329b3ea85134d4a7ac7)
# 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
Extend Ctrl+n/Ctrl+p navigation support to selection popups (model
picker, approval mode, etc.)
This is a follow-up to #7530, which added Ctrl+n/Ctrl+p navigation to
the textarea.
The same keybindings were missing from `ListSelectionView`, causing
inconsistent behavior
when navigating selection popups.
## Related
- #7530 - feat(tui): map Ctrl-P/N to arrow navigation in textarea
## Changes
- Added Ctrl+n as alternative to Down arrow in selection popups
- Added Ctrl+p as alternative to Up arrow in selection popups
- Added unit tests for the new keybindings
## Test Plan
- [x] `cargo test -p codex-tui list_selection_view` - all tests pass
- [x] Manual testing: verified Ctrl+n/p navigation works in model
selection popup
---------
Co-authored-by: Eric Traut <etraut@openai.com>
### Summary
Linux codesigning with sigstore and test run output at
https://github.com/openai/codex/actions/runs/19994328162?pr=7662.
Sigstore is one of the few ways for codesigning for linux platform.
Linux is open sourced and therefore binary/dist validation comes with
the build itself instead of a central authority like Windows or Mac.
Alternative here is to use GPG which again a public key included with
the bundle for validation. Advantage with Sigstore is that we do not
have to create a private key for signing but rather with[ keyless
signing](https://docs.sigstore.dev/cosign/signing/overview/).
This should be sufficient for us at this point and if we want to we can
support GPG in the future.
Currently, we only show the “don’t ask again for commands that start
with…” option when a command is immediately flagged as needing approval.
However, there is another case where we ask for approval: When a command
is initially auto-approved to run within sandbox, but it fails to run
inside sandbox, we would like to attempt to retry running outside of
sandbox. This will require a prompt to the user.
This PR addresses this latter case
Bumps [wildmatch](https://github.com/becheran/wildmatch) from 2.5.0 to
2.6.1.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ca6568be7e"><code>ca6568b</code></a>
chore: Release wildmatch version 2.6.1</li>
<li><a
href="513c5ab967"><code>513c5ab</code></a>
docs: fix broken links</li>
<li><a
href="fe47b5f750"><code>fe47b5f</code></a>
chore: use latest mlc version</li>
<li><a
href="4d05f9f3d1"><code>4d05f9f</code></a>
Merge pull request <a
href="https://redirect.github.com/becheran/wildmatch/issues/30">#30</a>
from arifd/patch-1</li>
<li><a
href="26114f73de"><code>26114f7</code></a>
unify example pattern used in WildMatchPattern examples</li>
<li><a
href="32c36f5113"><code>32c36f5</code></a>
chore: Release wildmatch version 2.6.0</li>
<li><a
href="4777964a65"><code>4777964</code></a>
Merge pull request <a
href="https://redirect.github.com/becheran/wildmatch/issues/29">#29</a>
from arifd/prevent-ambiguous-same-single-multi-wildcard</li>
<li><a
href="3a5bf1b4f6"><code>3a5bf1b</code></a>
prevent ambiguous same single multi wildcard</li>
<li>See full diff in <a
href="https://github.com/becheran/wildmatch/compare/v2.5.0...v2.6.1">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot 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 [ts-rs](https://github.com/Aleph-Alpha/ts-rs) from 11.0.1 to
11.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Aleph-Alpha/ts-rs/releases">ts-rs's
releases</a>.</em></p>
<blockquote>
<h2>v11.1.0</h2>
<p>Today, we're happy to publish a small follow-up to v11.0.1!</p>
<p>This release fixes a nasty build failure when using the
<code>format</code> feature.
<strong>Note:</strong> For those that use the <code>format</code>
feature, this release bumps the MSRV to 1.88. We'd have preferred to do
this in a major release, but felt this was acceptable since the build
was broken by one of the dependencies anyway.</p>
<h1>New features</h1>
<h2>TypeScript enums with <code>#[ts(repr(enum))</code></h2>
<p><code>#[ts(repr(enum))</code> instructs ts-rs to generate an
<code>enum</code>, instead of a <code>type</code> for your rust
enum.</p>
<pre lang="rust"><code>#[derive(TS)]
#[ts(repr(enum))]
enum Role {
User,
Admin,
}
// will generate `export enum Role { "User", "Admin"
}`
</code></pre>
<p>Discriminants are preserved, and you can use the variant's name as
discriminant instead using <code>#[ts(repr(enum = name))]</code></p>
<h2><code>#[ts(optional_fields)]</code> in enums</h2>
<p>The <code>#[ts(optional_fields)]</code> attribute can now be applied
directly to enums, or even to individual enum variants.</p>
<h2>Control over file extensions in imports</h2>
<p>Normally, we generate <code>import { Type } from
"file"</code> statements. In some scenarios though, it might
be necessary to use a <code>.ts</code> or even <code>.js</code>
extension instead.<br />
This is now possible by setting the <code>TS_RS_IMPORT_EXTENSION</code>
environment variable.</p>
<blockquote>
<p>Note: With the introduction of this feature, we deprecate the
<code>import-esm</code> cargo feature. It will be removed in a future
major release.</p>
</blockquote>
<h2>Full changelog</h2>
<ul>
<li>Regression: <code>#[ts(optional)]</code> with
<code>#[ts(type)]</code> by <a
href="https://github.com/NyxCode"><code>@NyxCode</code></a> in <a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/416">Aleph-Alpha/ts-rs#416</a></li>
<li>release v11.0.1 by <a
href="https://github.com/NyxCode"><code>@NyxCode</code></a> in <a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/417">Aleph-Alpha/ts-rs#417</a></li>
<li>Make <code>rename_all</code> compatible with tuple and unit structs
as a no-op attribute by <a
href="https://github.com/gustavo-shigueo"><code>@gustavo-shigueo</code></a>
in <a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/422">Aleph-Alpha/ts-rs#422</a></li>
<li>Replace <code>import-esm</code> with
<code>TS_RS_IMPORT_EXTENSION</code> by <a
href="https://github.com/gustavo-shigueo"><code>@gustavo-shigueo</code></a>
in <a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/423">Aleph-Alpha/ts-rs#423</a></li>
<li>Updated chrono Duration emitted type by <a
href="https://github.com/fxf8"><code>@fxf8</code></a> in <a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/434">Aleph-Alpha/ts-rs#434</a></li>
<li>Add optional_fields to enum by <a
href="https://github.com/gustavo-shigueo"><code>@gustavo-shigueo</code></a>
in <a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/432">Aleph-Alpha/ts-rs#432</a></li>
<li>Add <code>#[ts(repr(enum)]</code> attribute by <a
href="https://github.com/gustavo-shigueo"><code>@gustavo-shigueo</code></a>
in <a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/425">Aleph-Alpha/ts-rs#425</a></li>
<li>Fix build with <code>format</code> feature by <a
href="https://github.com/gustavo-shigueo"><code>@gustavo-shigueo</code></a>
in <a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/438">Aleph-Alpha/ts-rs#438</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/fxf8"><code>@fxf8</code></a> made their
first contribution in <a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/434">Aleph-Alpha/ts-rs#434</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Aleph-Alpha/ts-rs/blob/main/CHANGELOG.md">ts-rs's
changelog</a>.</em></p>
<blockquote>
<h1>11.1.0</h1>
<h3>Features</h3>
<ul>
<li>Add <code>#[ts(repr(enum))]</code> attribute (<a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/425">#425</a>)</li>
<li>Add support for <code>#[ts(optional_fields)]</code> in enums and
enum variants (<a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/432">#432</a>)</li>
<li>Deprecate <code>import-esm</code> cargo feature in favour of
<code>RS_RS_IMPORT_EXTENSION</code> (<a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/423">#423</a>)</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Fix bindings for <code>chrono::Duration</code> (<a
href="https://redirect.github.com/Aleph-Alpha/ts-rs/pull/434">#434</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/Aleph-Alpha/ts-rs/commits/v11.1.0">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>
Issue #7661 revealed that users are confused by deprecation warnings
like:
> `tools.web_search` is deprecated. Use `web_search_request` instead.
This message misleadingly suggests renaming the config key from
`web_search` to `web_search_request`, when the actual required change is
to **move and rename the configuration from the `[tools]` section to the
`[features]` section**.
This PR clarifies the warning messages and documentation to make it
clear that deprecated `[tools]` configurations should be moved to
`[features]`. Changes made:
- Updated deprecation warning format in `codex-rs/core/src/codex.rs:520`
to include `[features].` prefix
- Updated corresponding test expectations in
`codex-rs/core/tests/suite/deprecation_notice.rs:39`
- Improved documentation in `docs/config.md` to clarify upfront that
`[tools]` options are deprecated in favor of `[features]`
…alid (#7668)
The `otel` exporter example in `docs/config.md` is misleading and will
cause
the configuration parser to fail if copied verbatim.
Summary
-------
The example uses a TOML inline table but spreads the inline-table braces
across multiple lines. TOML inline tables must be contained on a single
line
(`key = { a = 1, b = 2 }`); placing newlines inside the braces triggers
a
parse error in most TOML parsers and prevents Codex from starting.
Reproduction
------------
1. Paste the snippet below into `~/.codex/config.toml` (or your project
config).
2. Run `codex` (or the command that loads the config).
3. The process will fail to start with a TOML parse error similar to:
```text
Error loading config.toml: TOML parse error at line 55, column 27
|
55 | exporter = { otlp-http = {
| ^
newlines are unsupported in inline tables, expected nothing
```
Problematic snippet (as currently shown in the docs)
---------------------------------------------------
```toml
[otel]
exporter = { otlp-http = {
endpoint = "https://otel.example.com/v1/logs",
protocol = "binary",
headers = { "x-otlp-api-key" = "${OTLP_TOKEN}" }
}}
```
Recommended fixes
------------------
```toml
[otel.exporter."otlp-http"]
endpoint = "https://otel.example.com/v1/logs"
protocol = "binary"
[otel.exporter."otlp-http".headers]
"x-otlp-api-key" = "${OTLP_TOKEN}"
```
Or, keep an inline table but write it on one line (valid but less
readable):
```toml
[otel]
exporter = { "otlp-http" = { endpoint = "https://otel.example.com/v1/logs", protocol = "binary", headers = { "x-otlp-api-key" = "${OTLP_TOKEN}" } } }
```
# 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.
This PR introduces integration tests that run
[codex-shell-tool-mcp](https://www.npmjs.com/package/@openai/codex-shell-tool-mcp)
as a user would. Note that this requires running our fork of Bash, so we
introduce a [DotSlash](https://dotslash-cli.com/) file for `bash` so
that we can run the integration tests on multiple platforms without
having to check the binaries into the repository. (As noted in the
DotSlash file, it is slightly more heavyweight than necessary, which may
be worth addressing as disk space in CI is limited:
https://github.com/openai/codex/pull/7678.)
To start, this PR adds two tests:
- `list_tools()` makes the `list_tools` request to the MCP server and
verifies we get the expected response
- `accept_elicitation_for_prompt_rule()` defines a `prefix_rule()` with
`decision="prompt"` and verifies the elicitation flow works as expected
Though the `accept_elicitation_for_prompt_rule()` test **only works on
Linux**, as this PR reveals that there are currently issues when running
the Bash fork in a read-only sandbox on Linux. This will have to be
fixed in a follow-up PR.
Incidentally, getting this test run to correctly on macOS also requires
a recent fix we made to `brew` that hasn't hit a mainline release yet,
so getting CI green in this PR required
https://github.com/openai/codex/pull/7680.
When I put up https://github.com/openai/codex/pull/7617 for review,
initially I started seeing failures on the `ubuntu-24.04` runner used
for Rust test runs for the `x86_64-unknown-linux-gnu` architecture. Chat
suggested a number of things that could be removed to save space, which
seems to help.
Update install and contributing guides to use the root justfile helpers
(`just fmt`, `just fix -p <crate>`, and targeted tests) instead of the
older cargo fmt/clippy/test instructions that have been in place since
459363e17b. This matches the justfile relocation to the repo root in
952d6c946 and the current lint/test workflow for CI (see
`.github/workflows/rust-ci.yml`).
Previous to this change, large `EscalateRequest` payloads exceeded the
kernel send buffer, causing our single `sendmsg(2)` call (with attached
FDs) to be split and retried without proper control handling; this led
to `EINVAL`/broken pipe in the
`handle_escalate_session_respects_run_in_sandbox_decision()` test when
using an `env` with large contents.
**Before:** `AsyncSocket::send_with_fds()` called `send_json_message()`,
which called `send_message_bytes()`, which made one `socket.sendmsg()`
call followed by additional `socket.send()` calls, as necessary:
2e4a402521/codex-rs/exec-server/src/posix/socket.rs (L198-L209)
**After:** `AsyncSocket::send_with_fds()` now calls
`send_stream_frame()`, which calls `send_stream_chunk()` one or more
times. Each call to `send_stream_chunk()` calls `socket.sendmsg()`.
In the previous implementation, the subsequent `socket.send()` writes
had no control information associated with them, whereas in the new
`send_stream_chunk()` implementation, a fresh `MsgHdr` (using
`with_control()`, as appropriate) is created for `socket.sendmsg()` each
time.
Additionally, with this PR, stream sending attaches `SCM_RIGHTS` only on
the first chunk, and omits control data when there are no FDs, allowing
oversized payloads to deliver correctly while preserving FD limits and
error checks.
This fixes two issues with the OTEL HTTP exporter:
1. **Runtime panic with async reqwest client**
The `opentelemetry_sdk` `BatchLogProcessor` spawns a dedicated OS thread
that uses `futures_executor::block_on()` rather than tokio's runtime.
When the async reqwest client's timeout mechanism calls
`tokio::time::sleep()`, it panics with "there is no reactor running,
must be called from the context of a Tokio 1.x runtime".
The fix is to use `reqwest::blocking::Client` instead, which doesn't
depend on tokio for timeouts. However, the blocking client creates its
own internal tokio runtime during construction, which would panic if
built from within an async context. We wrap the construction in
`tokio::task::block_in_place()` to handle this.
2. **mTLS certificate handling**
The HTTP client wasn't properly configured for mTLS, matching the fixes
previously done for the model provider client:
- Added `.tls_built_in_root_certs(false)` when using a custom CA
certificate to ensure only our CA is trusted
- Added `.https_only(true)` when using client identity
- Added `rustls-tls` feature to ensure rustls is used (required for
`Identity::from_pem()` to work correctly)
## Summary
This PR is heavily based on #4017, which contains the core logic for the
fix. To reduce the risk, we are first introducing it only on windows. We
can then expand to wsl / other environments as needed, and then tackle
net new files.
## Testing
- [x] added unit tests in apply-patch
- [x] add integration tests to apply_patch_cli.rs
---------
Co-authored-by: Chase Naples <Cnaples79@gmail.com>
## Summary
- move the workspace justfile to the repository root for easier
discovery
- set the just working directory to codex-rs so existing recipes still
run in the Rust workspace
## Testing
- not run (not requested)
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_69334db473108329b0cc253b7fd8218e)
- This PR wires `with_remote_overrides` and make the
`construct_model_families` an async function
- Moves getting model family a level above to keep the function `sync`
- Updates the tests to local, offline, and `sync` helper for model
families
- Introduce `with_remote_overrides` and update
`refresh_available_models`
- Put `auth_manager` instead of `auth_mode` on `models_manager`
- Remove `ShellType` and `ReasoningLevel` to use already existing
structs
## Summary
This PR introduces an End to End test suite for apply-patch, so we can
easily validate behavior against other implementations as well.
## Testing
- [x] These are tests
The caller should decide whether wrapping the policy in `Arc<RwLock>` is
necessary. This should make https://github.com/openai/codex/pull/7609 a
bit smoother.
- `exec_policy_for()` -> `load_exec_policy_for_features()`
- introduce `load_exec_policy()` that does not take `Features` as an arg
- both return `Result<Policy, ExecPolicyError>` instead of
Result<Arc<RwLock<Policy>>, ExecPolicyError>`
This simplifies the tests as they have no need for `Arc<RwLock>`.
**Summary**
- Shortcut toggle using `?` in `handle_shortcut_overlay_key` fails to
trigger on some platforms (notably Windows). Current match requires
`KeyCode::Char('?')` with `KeyModifiers::NONE`. Some terminals set
`SHIFT` when producing `?` (since it is typically `Shift + /`), so the
strict `NONE` check prevents toggling.
**Impact**
- On Windows consoles/terminals, pressing `?` with an empty composer
often does nothing, leading to inconsistent UX compared to macOS/Linux.
**Root Cause**
- Crossterm/terminal backends report modifiers inconsistently across
platforms. Generating `?` may include `SHIFT`. The code enforces
`modifiers == NONE`, so valid `?` presses with `SHIFT` are ignored.
AltGr keyboards may also surface as `ALT`.
**Repro Steps**
- Open the TUI, ensure the composer is empty.
- Press `?`.
- Expected: Shortcut overlay toggles.
- Actual (Windows frequently): No toggle occurs.
**Fix Options**
- Option 1 (preferred): Accept `?` regardless of `SHIFT`, but reject
`CONTROL` and `ALT`.
- Rationale: Keeps behavior consistent across platforms with minimal
code change.
- Example change:
- Before: matching `KeyModifiers::NONE` only.
- After: allow `SHIFT`, disallow `CONTROL | ALT`.
- Suggested condition:
```rust
let toggles = matches!(key_event.code, KeyCode::Char('?'))
&& !key_event.modifiers.intersects(KeyModifiers::CONTROL |
KeyModifiers::ALT)
&& self.is_empty();
```
- Option 2: Platform-specific handling (Windows vs non-Windows).
- Implement two variants or conditional branches using `#[cfg(target_os
= "windows")]`.
- On Windows, accept `?` with `SHIFT`; on other platforms, retain
current behavior.
- Trade-off: Higher maintenance burden and code divergence for limited
benefit.
---
close#5495
VSCE renders `codex/event/stream_error` (automatically retried, e.g.
`"Reconnecting... 1/n"`) and `codex/event/error` (terminal errors)
differently, so add `will_retry` on ErrorNotification to indicate this.
- Inline response recording during streaming: `run_turn` now records
items as they arrive instead of building a `ProcessedResponseItem` list
and post‑processing via `process_items`.
- Simplify turn handling: `handle_output_item_done` returns the
follow‑up signal + optional tool future; `needs_follow_up` is set only
there, and in‑flight tool futures are drained once at the end (errors
logged, no extra state writes).
- Flattened stream loop: removed `process_items` indirection and the
extra output queue
- - Tests: relaxed `tool_parallelism::tool_results_grouped` to allow any
completion order while still requiring matching call/output IDs.
## Related issues:
- https://github.com/openai/codex/issues/3939
- https://github.com/openai/codex/issues/2292
- https://github.com/openai/codex/issues/7528 (After correction
https://github.com/openai/codex/pull/3990)
**Area:** `codex-cli` (image handling / clipboard & file uploads)
**Platforms affected:** WSL (Ubuntu on Windows 10/11). No behavior
change on native Linux/macOS/Windows.
## Summary
This PR fixes image pasting and file uploads when running `codex-cli`
inside WSL. Previously, image operations failed silently or with
permission errors because paths weren't properly mapped between Windows
and WSL filesystems.
## Visual Result
<img width="1118" height="798" alt="image"
src="https://github.com/user-attachments/assets/14e10bc4-6b71-4d1f-b2a6-52c0a67dd069"
/>
## Last Rust-Cli
<img width="1175" height="859" alt="image"
src="https://github.com/user-attachments/assets/7ef41e29-9118-42c9-903c-7116d21e1751"
/>
## Root cause
The CLI assumed native Linux/Windows environments and didn't handle the
WSL↔Windows boundary:
- Used Linux paths for files that lived on the Windows host
- Missing path normalization between Windows (`C:\...`) and WSL
(`/mnt/c/...`)
- Clipboard access failed under WSL
### Why `Ctrl+V` doesn't work in WSL terminals
Most WSL terminal emulators (Windows Terminal, ConEmu, etc.) intercept
`Ctrl+V` at the terminal level to paste text from the Windows clipboard.
This keypress never reaches the CLI application itself, so our clipboard
image handler never gets triggered. Users need `Ctrl+Alt+V`.
## Changes
### WSL detection & path mapping
- Detects WSL by checking `/proc/sys/kernel/osrelease` and the
`WSL_INTEROP` env var
- Maps Windows drive paths to WSL mount paths (`C:\...` → `/mnt/c/...`)
### Clipboard fallback for WSL
- When clipboard access fails under WSL, falls back to PowerShell to
extract images from the Windows clipboard
- Saves to a temp file and maps the path back to WSL
### UI improvements
- Shows `Ctrl+Alt+V` hint on WSL (many terminals intercept plain
`Ctrl+V`)
- Better error messages for unreadable images
## Performance
- Negligible overhead. The fallback adds a single FS copy to a temp file
only when needed.
- Direct streaming remains the default.
## Files changed
- `protocol/src/lib.rs` – Added platform detection module
- `protocol/src/models.rs` – Added WSL path mapping for local images
- `protocol/src/platform.rs` – New module with WSL detection utilities
- `tui/src/bottom_pane/chat_composer.rs` – Added base64 data URL support
and WSL path mapping
- `tui/src/bottom_pane/footer.rs` – WSL-aware keyboard shortcuts
- `tui/src/clipboard_paste.rs` – PowerShell clipboard fallback
## How to reproduce the original bug (pre-fix)
1. Run `codex-cli` inside WSL2 on Windows.
2. Paste an image from the Windows clipboard or drag an image from
`C:\...` into the terminal.
3. Observe that the image is not attached (silent failure) or an error
is logged; no artifact reaches the tool.
## How to verify the fix
1. Build this branch and run `codex-cli` inside WSL2.
2. Paste from clipboard and drag from both Windows and WSL paths.
3. Confirm that the image appears in the tool and the CLI shows a single
concise info line (no warning unless fallback was used).
I’m happy to adjust paths, naming, or split helpers into a separate
module if you prefer.
## How to try this branch
If you want to try this before it’s merged, you can use my Git branch:
Repository: https://github.com/Waxime64/codex.git
Branch: `wsl-image-2`
1. Start WSL on your Windows machine.
2. Clone the repository and switch to the branch:
```bash
git clone https://github.com/Waxime64/codex.git
cd codex
git checkout wsl-image-2
# then go into the Rust workspace root, e.g.:
cd codex-rs
3. Build the TUI binary:
cargo build -p codex-tui --bin codex-tui --release
4. Install the binary:
sudo install -m 0755 target/release/codex-tui /usr/local/bin/codex
5. From the project directory where you want to use Codex, start it
with:
cd /path/to/your/project
/usr/local/bin/codex
On WSL, use CTRL+ALT+V to paste an image from the Windows clipboard into
the chat.
**Change**: Seatbelt now allows file-ioctl on /dev/ttys[0-9]+ even
without the sandbox extension so pre-created PTYs remain interactive
(Python REPL, shells).
**Risk**: A seatbelted process that already holds a PTY fd (including
one it shouldn’t) could issue tty ioctls like TIOCSTI or termios changes
on that fd. This doesn’t allow opening new PTYs or reading/writing them;
it only broadens ioctl capability on existing fds.
**Why acceptable**: We already hand the child its PTY for interactive
use; restoring ioctls is required for isatty() and prompts to work. The
attack requires being given or inheriting a sensitive PTY fd; by design
we don’t hand untrusted processes other users’ PTYs (we don't hand them
any PTYs actually), so the practical exposure is limited to the PTY
intentionally allocated for the session.
**Validation**:
Running
```
start a python interpreter and keep it running
```
Followed by:
* `calculate 1+1 using it` -> works as expected
* `Use this Python session to run the command just fix in
/Users/jif/code/codex/codex-rs` -> does not work as expected
## Updating the `execpolicy` TUI flow
In the TUI, when going through the command approval flow, codex will now
ask the user if they would like to whitelist the FIRST unmatched command
among a chain of commands.
For example, let's say the agent wants to run `apple | pear` with an
empty `execpolicy`
Neither apple nor pear will match to an `execpolicy` rule. Thus, when
prompting the user, codex tui will ask the user if they would like to
whitelist `apple`.
If the agent wants to run `apple | pear` again, they would be prompted
again because pear is still unknown. when prompted, the user will now be
asked if they'd like to whitelist `pear`.
Here's a demo video of this flow:
https://github.com/user-attachments/assets/fd160717-f6cb-46b0-9f4a-f0a974d4e710
This PR also removed the `allow for this session` option from the TUI.
## Refactor of the `execpolicy` crate
To illustrate why we need this refactor, consider an agent attempting to
run `apple | rm -rf ./`. Suppose `apple` is allowed by `execpolicy`.
Before this PR, `execpolicy` would consider `apple` and `pear` and only
render one rule match: `Allow`. We would skip any heuristics checks on
`rm -rf ./` and immediately approve `apple | rm -rf ./` to run.
To fix this, we now thread a `fallback` evaluation function into
`execpolicy` that runs when no `execpolicy` rules match a given command.
In our example, we would run `fallback` on `rm -rf ./` and prevent
`apple | rm -rf ./` from being run without approval.
this PR enables TUI to approve commands and add their prefixes to an
allowlist:
<img width="708" height="605" alt="Screenshot 2025-11-21 at 4 18 07 PM"
src="https://github.com/user-attachments/assets/56a19893-4553-4770-a881-becf79eeda32"
/>
note: we only show the option to whitelist the command when
1) command is not multi-part (e.g `git add -A && git commit -m 'hello
world'`)
2) command is not already matched by an existing rule
## Summary
- render tooltip content through the markdown renderer and prepend a
bold Tip label
- wrap tooltips at the available width using the indent’s measured width
before adding the indent
## Testing
- `/root/.cargo/bin/just fmt`
- `RUSTFLAGS="--cfg tokio_unstable" TOKIO_UNSTABLE=1
/root/.cargo/bin/just fix -p codex-tui` *(fails: codex-tui tests
reference tokio::time::advance/start_paused gated behind the tokio
test-util feature)*
- `RUSTFLAGS="--cfg tokio_unstable" TOKIO_UNSTABLE=1 cargo test -p
codex-tui` *(fails: codex-tui tests reference
tokio::time::advance/start_paused gated behind the tokio test-util
feature)*
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_693081406050832c9772ae9fa5dd77ca)
When we are writing to config using `config/value/write` or
`config/batchWrite`, it always require a `config/read` before it right
now in order to get the correct file path to write to. make this
optional so we read from the default user config file if this is not
passed in.
This PR moves `ModelsFamily` to `openai_models`. It also propagates
`ModelsManager` to session services and use it to drive model family. We
also make `derive_default_model_family` private because it's a step
towards what we want: one place that gives model configuration.
This is a second step at having one source of truth for models
information and config: `ModelsManager`.
Next steps would be to remove `ModelsFamily` from config. That's massive
because it's being used in 41 occasions mostly pre launching `codex`.
Also, we need to make `find_family_for_model` private. It's also big
because it's being used in 21 occasions ~ all tests.
- This PR treats the `ModelsManager` like `AuthManager` and propagate it
into the tui, replacing the `builtin_model_presets`
- We are also decreasing the visibility of `builtin_model_presets`
based on https://github.com/openai/codex/pull/7552
What
-----
- Fix the Ctrl+T transcript overlay so that very long exec output lines
are soft‑wrapped to the viewport width instead of being rendered as a
single truncated row.
- Add a regression test to `TranscriptOverlay` to ensure long exec
outputs are rendered on multiple lines in the overlay.
Why
----
- Previously, the transcript overlay rendered extremely long single exec
lines as one on‑screen row and simply cut them off at the right edge,
with no horizontal scrolling.
- This made it impossible to inspect the full content of long tool/exec
outputs in the transcript view, even though the main TUI view already
wrapped those lines.
- Fixes#7454.
How
----
- Update `ExecCell::transcript_lines` to wrap exec output lines using
the existing `RtOptions`/`word_wrap_line` helpers so that transcript
rendering is width‑aware.
- Reuse the existing line utilities to expand the wrapped `Line` values
into the transcript overlay, preserving styling while respecting the
current viewport width.
- Add `transcript_overlay_wraps_long_exec_output_lines` test in
`pager_overlay.rs` that constructs a long single‑line exec output,
renders the transcript overlay into a small buffer, and asserts that the
long marker string spans multiple rendered lines.
I noticed that `features: Features` was defined on `struct
SessionConfiguration`, which is commonly owned by `SessionState`, which
is in turn owned by `Session`.
Though I do not believe that `Features` should be allowed to be modified
over the course of a session (if the feature state is not invariant, it
makes it harder to reason about), which argues that it should live on
`Session` rather than `SessionState` or `SessionConfiguration`.
This PR moves `Features` to `Session` and updates all call sites. It
appears the only place we were mutating `Features` was:
- in tests
- the sub-agent config for a review task:
3ef76ff29d/codex-rs/core/src/tasks/review.rs (L86-L89)
Note this change also means it is no longer an `async` call to check the
state of a feature, eliminating the possibility of a
[TOCTTOU](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use)
error between checking the state of a feature and acting on it:
3ef76ff29d/codex-rs/core/src/codex.rs (L1069-L1076)
- Treat Ctrl-P/N (and their C0 fallbacks) the same as Up/Down so cursor
movement matches popup/history behavior and control bytes never land in
the buffer
Fixes#7529
Signed-off-by: Aofei Sheng <aofei@aofeisheng.com>
What
- Limit the TUI "user shell" output panel by the number of visible
screen lines rather than by the number of logical lines.
- Apply middle truncation after wrapping, so a few extremely long lines
cannot expand into hundreds of visible lines.
- Add a regression test to guard this behavior.
Why
When the `ExecCommandSource::UserShell` tool returns a small number of
very long logical lines, the TUI wraps those lines into many visual
lines. The existing truncation logic applied
`USER_SHELL_TOOL_CALL_MAX_LINES` to the number of logical lines *before*
wrapping.
As a result, a command like:
- `Ran bash -lc "grep -R --line-number 'maskAssetId' ."`
or a synthetic command that prints a single ~50,000‑character line, can
produce hundreds of screen lines and effectively flood the viewport. The
intended middle truncation for user shell output does not take effect in
this scenario.
How
- In `codex-rs/tui/src/exec_cell/render.rs`, change the `ExecCell`
rendering path for `ExecCommandSource::UserShell` so that:
- Each logical line from `CommandOutput::aggregated_output` is first
wrapped via `word_wrap_line` into multiple screen lines using the
appropriate `RtOptions` and width from the `EXEC_DISPLAY_LAYOUT`
configuration.
- `truncate_lines_middle` is then applied to the wrapped screen lines,
with `USER_SHELL_TOOL_CALL_MAX_LINES` as the limit. This means the limit
is enforced on visible screen lines, not logical lines.
- The existing layout struct (`ExecDisplayLayout`) continues to provide
`output_max_lines`, so user shell output is subject to both
`USER_SHELL_TOOL_CALL_MAX_LINES` and the layout-specific
`output_max_lines` constraint.
- Keep using `USER_SHELL_TOOL_CALL_MAX_LINES` as the cap, but interpret
it as a per‑tool‑call limit on screen lines.
- Add a regression test `user_shell_output_is_limited_by_screen_lines`
in `codex-rs/tui/src/exec_cell/render.rs` that:
- Constructs two extremely long logical lines containing a short marker
(`"Z"`), so each wrapped screen line still contains the marker.
- Wraps them at a narrow width to generate many screen lines.
- Asserts that the unbounded wrapped output would exceed
`USER_SHELL_TOOL_CALL_MAX_LINES` screen lines.
- Renders an `ExecCell` for `ExecCommandSource::UserShell` at the same
width and counts rendered lines containing the marker.
- Asserts `output_screen_lines <= USER_SHELL_TOOL_CALL_MAX_LINES`,
guarding against regressions where truncation happens before wrapping.
This change keeps user shell output readable while ensuring it cannot
flood the TUI, even when the tool emits a few extremely long lines.
Tests
- `cargo test -p codex-tui`
Issue
- Fixes#7447
- Introduce `openai_models` in `/core`
- Move `PRESETS` under it
- Move `ModelPreset`, `ModelUpgrade`, `ReasoningEffortPreset`,
`ReasoningEffortPreset`, and `ReasoningEffortPreset` to `protocol`
- Introduce `Op::ListModels` and `EventMsg::AvailableModels`
Next steps:
- migrate `app-server` and `tui` to use the introduced Operation
No integration test for now because it would make them flaky. Tracking
it in my todos to add some once we have a clock based system for
integration tests
This item is sent as a turn notification instead: `turn/plan/updated`,
similar to Turn diffs (which is `turn/diff/updated`).
We treat these concepts as ephemeral compared to Items which are usually
persisted.
### Summary
Added `mcp/servers/list` which is equivalent to `/mcp` slash command in
CLI for response. This will be used in VSCE MCP settings to show log in
status, available tools etc.
If an image can't be read by the API, it will poison the entire history,
preventing any new turn on the conversation.
This detect such cases and replace the image by a placeholder
Closes#3404
## Summary
On windows, ctrl+v does not work for the same reason that cmd+v does not
work on macos. This PR adds alt/option+v detection, which allows windows
users to paste images from the clipboard using.
We could swap between just ctrl on mac and just alt on windows, but this
felt simpler - I don't feel strongly about it.
Note that this will NOT address image pasting in WSL environments, due
to issues with WSL <> Windows clipboards. I'm planning to address that
in a separate PR since it will likely warrant some discussion.
## Testing
- [x] Tested locally on a Mac and Windows laptop
# Unified Exec Shell Selection on Windows
## Problem
reference issue #7466
The `unified_exec` handler currently deserializes model-provided tool
calls into the `ExecCommandArgs` struct:
```rust
#[derive(Debug, Deserialize)]
struct ExecCommandArgs {
cmd: String,
#[serde(default)]
workdir: Option<String>,
#[serde(default = "default_shell")]
shell: String,
#[serde(default = "default_login")]
login: bool,
#[serde(default = "default_exec_yield_time_ms")]
yield_time_ms: u64,
#[serde(default)]
max_output_tokens: Option<usize>,
#[serde(default)]
with_escalated_permissions: Option<bool>,
#[serde(default)]
justification: Option<String>,
}
```
The `shell` field uses a hard-coded default:
```rust
fn default_shell() -> String {
"/bin/bash".to_string()
}
```
When the model returns a tool call JSON that only contains `cmd` (which
is the common case), Serde fills in `shell` with this default value.
Later, `get_command` uses that value as if it were a model-provided
shell path:
```rust
fn get_command(args: &ExecCommandArgs) -> Vec<String> {
let shell = get_shell_by_model_provided_path(&PathBuf::from(args.shell.clone()));
shell.derive_exec_args(&args.cmd, args.login)
}
```
On Unix, this usually resolves to `/bin/bash` and works as expected.
However, on Windows this behavior is problematic:
- The hard-coded `"/bin/bash"` is not a valid Windows path.
- `get_shell_by_model_provided_path` treats this as a model-specified
shell, and tries to resolve it (e.g. via `which::which("bash")`), which
may or may not exist and may not behave as intended.
- In practice, this leads to commands being executed under a non-default
or non-existent shell on Windows (for example, WSL bash), instead of the
expected Windows PowerShell or `cmd.exe`.
The core of the issue is that **"model did not specify `shell`" is
currently interpreted as "the model explicitly requested `/bin/bash`"**,
which is both Unix-specific and wrong on Windows.
## Proposed Solution
Instead of hard-coding `"/bin/bash"` into `ExecCommandArgs`, we should
distinguish between:
1. **The model explicitly specifying a shell**, e.g.:
```json
{
"cmd": "echo hello",
"shell": "pwsh"
}
```
In this case, we *do* want to respect the model’s choice and use
`get_shell_by_model_provided_path`.
2. **The model omitting the `shell` field entirely**, e.g.:
```json
{
"cmd": "echo hello"
}
```
In this case, we should *not* assume `/bin/bash`. Instead, we should use
`default_user_shell()` and let the platform decide.
To express this distinction, we can:
1. Change `shell` to be optional in `ExecCommandArgs`:
```rust
#[derive(Debug, Deserialize)]
struct ExecCommandArgs {
cmd: String,
#[serde(default)]
workdir: Option<String>,
#[serde(default)]
shell: Option<String>,
#[serde(default = "default_login")]
login: bool,
#[serde(default = "default_exec_yield_time_ms")]
yield_time_ms: u64,
#[serde(default)]
max_output_tokens: Option<usize>,
#[serde(default)]
with_escalated_permissions: Option<bool>,
#[serde(default)]
justification: Option<String>,
}
```
Here, the absence of `shell` in the JSON is represented as `shell:
None`, rather than a hard-coded string value.
When running `npx @openai/codex-shell-tool-mcp`, the old code derived
`__dirname` from `process.argv[1]`, which points to npx’s transient
wrapper script in
`~/.npm/_npx/134d0fb7e1a27652/node_modules/.bin/codex-shell-tool-mcp`.
That made `vendorRoot` resolve to `<npx cache>/vendor`, so the startup
checks failed with "Required binary missing" because it looked for
`codex-execve-wrapper` in the wrong place.
By relying on the real module `__dirname` and `path.resolve(__dirname,
"..", "vendor")`, the package now anchors to its installed location
under `node_modules/@openai/codex-shell-tool-mcp/`, so the bundled
binaries are found and npx launches correctly.
Fix issue #7430
Generate unique numbered placeholders for multiple large pastes of the
same length so deleting one no longer removes the others.
Signed-off-by: Joshua <joshua1s@protonmail.com>
I find it helpful to easily verify which version is running.
Tested:
```shell
~/code/codex3/codex-rs/exec-server$ cargo run --bin codex-exec-mcp-server -- --help
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.19s
Running `/Users/mbolin/code/codex3/codex-rs/target/debug/codex-exec-mcp-server --help`
Usage: codex-exec-mcp-server [OPTIONS]
Options:
--execve <EXECVE_WRAPPER> Executable to delegate execve(2) calls to in Bash
--bash <BASH_PATH> Path to Bash that has been patched to support execve() wrapping
-h, --help Print help
-V, --version Print version
~/code/codex3/codex-rs/exec-server$ cargo run --bin codex-exec-mcp-server -- --version
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `/Users/mbolin/code/codex3/codex-rs/target/debug/codex-exec-mcp-server --version`
codex-exec-server 0.0.0
```
Pull FrameRequester out of tui.rs into its own module and make a
FrameScheduler struct. This is effectively an Actor/Handler approach
(see https://ryhl.io/blog/actors-with-tokio/). Adds tests and docs.
Small refactor of pending_viewport_area logic.
I think this might help with https://github.com/openai/codex/pull/7033
because `create_approval_requirement_for_command()` will soon need
access to `Session.state`, which is a `tokio::sync::Mutex` that needs to
be accessed via `async`.
This PR honors the `history.max_bytes` configuration parameter by
trimming `history.jsonl` whenever it grows past the configured limit.
While appending new entries we retain the newest record, drop the oldest
lines to stay within the byte budget, and serialize the compacted file
back to disk under the same lock to keep writers safe.
The problem with using `serde(flatten)` on Turn status is that it
conditionally serializes the `error` field, which is not the pattern we
want in API v2 where all fields on an object should always be returned.
```
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct Turn {
pub id: String,
/// Only populated on a `thread/resume` response.
/// For all other responses and notifications returning a Turn,
/// the items field will be an empty list.
pub items: Vec<ThreadItem>,
#[serde(flatten)]
pub status: TurnStatus,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(tag = "status", rename_all = "camelCase")]
#[ts(tag = "status", export_to = "v2/")]
pub enum TurnStatus {
Completed,
Interrupted,
Failed { error: TurnError },
InProgress,
}
```
serializes to:
```
{
"id": "turn-123",
"items": [],
"status": "completed"
}
{
"id": "turn-123",
"items": [],
"status": "failed",
"error": {
"message": "Tool timeout",
"codexErrorInfo": null
}
}
```
Instead we want:
```
{
"id": "turn-123",
"items": [],
"status": "completed",
"error": null
}
{
"id": "turn-123",
"items": [],
"status": "failed",
"error": {
"message": "Tool timeout",
"codexErrorInfo": null
}
}
```
this PR
- adds a helper function to amend `.codexpolicy` files with new prefix
rules
- adds a utility to `Policy` allowing prefix rules to be added to
existing `Policy` structs
both additions will be helpful as we thread codexpolicy into the TUI
workflow
# Ghost snapshot ignores
This PR should close#7067, #7395, #7405.
Prior to this change the ghost snapshot task ran `git status
--ignored=matching` so the report picked up literally every ignored
file. When a directory only contained entries matched by patterns such
as `dozens/*.txt`, `/test123/generated/*.html`, or `/wp-includes/*`, Git
still enumerated them and the large-untracked-dir detection treated the
parent directory as “large,” even though everything inside was
intentionally ignored.
By removing `--ignored=matching` we only capture true untracked paths
now, so those patterns stay out of the snapshot report and no longer
trigger the “large untracked directories” warning.
---------
Signed-off-by: lionelchg <lionel.cheng@hotmail.fr>
Co-authored-by: lionelchg <lionel.cheng@hotmail.fr>
Add a warning message as a user turn to the model if the model does not
behave as expected (here, for example, if the model opens too many
`unified_exec` sessions)
This change prototypes support for Skills with the CLI. This is an
**experimental** feature for internal testing.
---------
Co-authored-by: Gav Verma <gverma@openai.com>
Fixes#3939Fixes#2803
## Summary
- convert Windows clipboard file paths into their `/mnt/<drive>`
equivalents when running inside WSL so pasted images resolve correctly
- add WSL detection helpers and share them with unit tests to cover both
native Windows and WSL clipboard normalization cases
- improve the test suite by exercising Windows path handling plus a
dedicated WSL conversion scenario and keeping the code path guarded by
targeted cfgs
## Testing
- just fmt
- cargo test -p codex-tui
- cargo clippy -p codex-tui --tests
- just fix -p codex-tui
## Screenshots
_Codex TUI screenshot:_
<img width="1880" height="848" alt="describe this copied image"
src="https://github.com/user-attachments/assets/c620d43c-f45c-451e-8893-e56ae85a5eea"
/>
_GitHub docs directory screenshot:_
<img width="1064" height="478" alt="image-copied"
src="https://github.com/user-attachments/assets/eb5eef6c-eb43-45a0-8bfe-25c35bcae753"
/>
Co-authored-by: Eric Traut <etraut@openai.com>
- Add portable history log id helper to support inode-like tracking on
Unix and creation time on Windows
- Refactor history metadata and lookup to share code paths and allow
nonzero log ids across platforms
- Add coverage for lookup stability after appends
Fix the issue #7416 that the codex-cli produce an error "MCP startup
failure on missing 'type' field" in the startup.
- Cause: serde in `convert_to_rmcp`
(`codex-rs/rmcp-client/src/utils.rs`) failed because no `r#type` value
was provided
- Fix: set a default `r#type` value in the corresponding structs
## Summary
Adds the missing `xhigh` reasoning level everywhere it should have been
documented, and makes clear it only works with `gpt-5.1-codex-max`.
## Changes
* `docs/config.md`
* Add `xhigh` to the official list of reasoning levels with a note that
`xhigh` is exclusive to Codex Max.
* `docs/example-config.md`
* Update the example comment adding `xhigh` as a valid option but only
for Codex Max.
* `docs/faq.md`
* Update the model recommendation to `GPT-5.1 Codex Max`.
* Mention that users can choose `high` or the newly documented `xhigh`
level when using Codex Max.
Output hash for `rmcp-0.9.0` was missing from the nix package, (i.e.
`error: No hash was found while vendoring the git dependency
rmcp-0.9.0.`) blocking the build.
Fixes#7333
This is a small bug fix.
This PR fixes an inconsistency in `recent_commits` where `limit == 0`
still returns 1 commit due to the use of `limit.max(1)` when
constructing the `git log -n` argument.
Expected behavior: requesting 0 commits should return an empty list.
This PR:
- returns an empty `Vec` when `limit == 0`
- adds a test for `recent_commits(limit == 0)` that fails before the
change and passes afterwards
- maintains existing behavior for `limit > 0`
This aligns behavior with API expectations and avoids downstream
consumers misinterpreting the repository as having commit history when
`limit == 0` is used to explicitly request none.
Happy to adjust if the current behavior is intentional.
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 3.14.0 to
3.16.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/jonasbb/serde_with/releases">serde_with's
releases</a>.</em></p>
<blockquote>
<h2>serde_with v3.16.1</h2>
<h3>Fixed</h3>
<ul>
<li>Fix <code>JsonSchemaAs</code> of <code>SetPreventDuplicates</code>
and <code>SetLastValueWins</code>. (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/906">#906</a>,
<a
href="https://redirect.github.com/jonasbb/serde_with/issues/907">#907</a>)</li>
</ul>
<h2>serde_with v3.16.0</h2>
<h3>Added</h3>
<ul>
<li>Added support for <code>smallvec</code> v1 under the
<code>smallvec_1</code> feature flag by <a
href="https://github.com/isharma228"><code>@isharma228</code></a> (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/895">#895</a>)</li>
<li>Add <code>JsonSchemaAs</code> implementation for
<code>json::JsonString</code> by <a
href="https://github.com/yogevm15"><code>@yogevm15</code></a> (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/901">#901</a>)</li>
</ul>
<h2>serde_with v3.15.1</h2>
<h3>Fixed</h3>
<ul>
<li>Fix building of the documentation by updating references to use
<code>serde_core</code>.</li>
</ul>
<h2>serde_with v3.15.0</h2>
<h3>Added</h3>
<ul>
<li>
<p>Added error inspection to <code>VecSkipError</code> and
<code>MapSkipError</code> by <a
href="https://github.com/michelhe"><code>@michelhe</code></a> (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/878">#878</a>)
This allows interacting with the previously hidden error, for example
for logging.
Checkout the newly added example to both types.</p>
</li>
<li>
<p>Allow documenting the types generated by <code>serde_conv!</code>.
The <code>serde_conv!</code> macro now acceps outer attributes before
the optional visibility modifier.
This allow adding doc comments in the shape of <code>#[doc =
"..."]</code> or any other attributes, such as lint
modifiers.</p>
<pre lang="rust"><code>serde_conv!(
#[doc = "Serialize bools as string"]
#[allow(dead_code)]
pub BoolAsString,
bool,
|x: &bool| ::std::string::ToString::to_string(x),
|x: ::std::string::String| x.parse()
);
</code></pre>
</li>
<li>
<p>Add support for <code>hashbrown</code> v0.16 (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/877">#877</a>)</p>
<p>This extends the existing support for <code>hashbrown</code> v0.14
and v0.15 to the newly released version.</p>
</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Bump MSRV to 1.76, since that is required for <code>toml</code>
dev-dependency.</li>
</ul>
<h2>serde_with v3.14.1</h2>
<h3>Fixed</h3>
<ul>
<li>Show macro expansion in the docs.rs generated rustdoc.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8513323fda"><code>8513323</code></a>
Bump version to 3.16.1 (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/908">#908</a>)</li>
<li><a
href="5392bbe75e"><code>5392bbe</code></a>
Bump version to 3.16.1</li>
<li><a
href="1e54f1cd38"><code>1e54f1c</code></a>
Fix duplicate schema set definitions for schemars 0.8, 0.9, and 1.0 (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/907">#907</a>)</li>
<li><a
href="0650180645"><code>0650180</code></a>
Fix duplicate schema set definitions for schemars 0.8, 0.9, and 1.0</li>
<li><a
href="41d1033438"><code>41d1033</code></a>
Fix test conditions for schemars tests to include "hex"
feature</li>
<li><a
href="2eed58af05"><code>2eed58a</code></a>
Bump the github-actions group across 1 directory with 2 updates (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/905">#905</a>)</li>
<li><a
href="ed040f2330"><code>ed040f2</code></a>
Bump the github-actions group across 1 directory with 2 updates</li>
<li><a
href="fa2129b1b9"><code>fa2129b</code></a>
Bump ron from 0.11.0 to 0.12.0 (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/904">#904</a>)</li>
<li><a
href="b55cb99757"><code>b55cb99</code></a>
Bump ron from 0.11.0 to 0.12.0</li>
<li><a
href="066b9d4019"><code>066b9d4</code></a>
Bump version to 3.16.0 (<a
href="https://redirect.github.com/jonasbb/serde_with/issues/903">#903</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/jonasbb/serde_with/compare/v3.14.0...v3.16.1">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot 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>
Co-authored-by: Eric Traut <etraut@openai.com>
Bumps [arboard](https://github.com/1Password/arboard) from 3.6.0 to
3.6.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/1Password/arboard/releases">arboard's
releases</a>.</em></p>
<blockquote>
<h2>v3.6.1</h2>
<p>This release focuses on improving compatibility with data in the real
world and bug fixes. It also includes a new <code>Set</code> API for
working with file paths via drag-and-drop interfaces across Linux,
macOS, and Windows.</p>
<p>This release also marks the start of exclusively publishing
changelogs via GitHub Releases. The old <code>CHANGELOG.md</code> has
been removed due to maintenance overhead and duplication. <a
href="https://github.com/1Password/arboard/releases/tag/v3.6.0">v3.6.0</a>
is the last revision to include this file.</p>
<h3>Added</h3>
<ul>
<li>Add support for pasting lists of files via
<code>Set::file_list</code> interface by <a
href="https://github.com/Gae24"><code>@Gae24</code></a> in <a
href="https://redirect.github.com/1Password/arboard/pull/181">1Password/arboard#181</a></li>
<li>Support <code>windows-sys</code> 0.60 in <code>arboard</code>'s
allowed version range by <a
href="https://github.com/complexspaces"><code>@complexspaces</code></a>
in <a
href="https://redirect.github.com/1Password/arboard/pull/201">1Password/arboard#201</a></li>
</ul>
<h3>Changed</h3>
<ul>
<li>Fix grammar and typos by <a
href="https://github.com/complexspaces"><code>@complexspaces</code></a>
and <a href="https://github.com/gagath"><code>@gagath</code></a> in <a
href="https://redirect.github.com/1Password/arboard/pull/194">1Password/arboard#194</a>
and <a
href="https://redirect.github.com/1Password/arboard/pull/196">1Password/arboard#196</a></li>
<li>Prefer PNG when pasting images on Windows by <a
href="https://github.com/wcassels"><code>@wcassels</code></a> in <a
href="https://redirect.github.com/1Password/arboard/pull/198">1Password/arboard#198</a>
<ul>
<li>Note: This change greatly increases compatibility for
"complicated" images that contain alpha values and/or
transparent pixels. Support for transparency in <code>BITMAP</code>
formats is ill-defined and inconsistently implemented in the wild, but
is consistent in <code>PNG</code>. Most applications loading images onto
the clipboard include <code>PNG</code>-encoded data already.</li>
</ul>
</li>
<li>Bitmap images pasted on Windows now use the <code>image</code> crate
instead of a homegrown internal parser.
<ul>
<li>This <strong>should not</strong> regress any existing Bitmap use
cases and instead will provide more consistent and robust parsing. If
you notice something now broken, please open an issue!</li>
</ul>
</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Remove silent dropping of file paths when non-UTF8 was mixed in on
Linux by <a href="https://github.com/Gae24"><code>@Gae24</code></a> in
<a
href="https://redirect.github.com/1Password/arboard/pull/197">1Password/arboard#197</a></li>
<li>Fix parsing of 24-bit bitmaps on Windows by <a
href="https://github.com/wcassels"><code>@wcassels</code></a> in <a
href="https://redirect.github.com/1Password/arboard/pull/198">1Password/arboard#198</a>
<ul>
<li>Example: Images with transparency copied by Firefox are now handled
correctly, among others.</li>
</ul>
</li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/gagath"><code>@gagath</code></a> made
their first contribution in <a
href="https://redirect.github.com/1Password/arboard/pull/196">1Password/arboard#196</a></li>
<li><a href="https://github.com/wcassels"><code>@wcassels</code></a>
made their first contribution in <a
href="https://redirect.github.com/1Password/arboard/pull/198">1Password/arboard#198</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/1Password/arboard/compare/v3.6.0...v3.6.1">https://github.com/1Password/arboard/compare/v3.6.0...v3.6.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a3750c79a5"><code>a3750c7</code></a>
Release 3.6.1</li>
<li><a
href="edcce2cd6b"><code>edcce2c</code></a>
Remove CHANGELOG.md in favor of GitHub releases</li>
<li><a
href="26a96a6199"><code>26a96a6</code></a>
Bump windows-sys semver range to support 0.60.x</li>
<li><a
href="7bdd1c1175"><code>7bdd1c1</code></a>
Update errno for windows-sys 0.60 flexibility</li>
<li><a
href="55c0b260c4"><code>55c0b26</code></a>
read/write_unaligned rather than using manual field offsets</li>
<li><a
href="ff15a093d6"><code>ff15a09</code></a>
Return conversionFailure instead of adhoc errors</li>
<li><a
href="16ef18113f"><code>16ef181</code></a>
Implement fetching PNG on Windows and prefer over DIB when
available</li>
<li><a
href="a3c64f9a93"><code>a3c64f9</code></a>
Add a couple of end-to-end DIBV5 tests</li>
<li><a
href="e6008eaa91"><code>e6008ea</code></a>
Use image for reading DIB and try to make it do the right thing for
32-bit BI...</li>
<li><a
href="17ef05ce13"><code>17ef05c</code></a>
add <code>file_list</code> to <code>Set</code> interface (<a
href="https://redirect.github.com/1Password/arboard/issues/181">#181</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/1Password/arboard/compare/v3.6.0...v3.6.1">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot 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>
Title: Improve rollout session initialization error messages
Issue: https://github.com/openai/codex/issues/7283
What: add targeted mapping for rollout/session initialization errors so
users get actionable messages when Codex cannot access session files.
Why: session creation previously returned a generic internal error,
hiding permissions/FS issues and making support harder.
How:
- Added rollout::error::map_session_init_error to translate the more
common io::Error kinds into user-facing hints (permission, missing dir,
file blocking, corruption). Others are passed through directly with
`CodexErr::Fatal`.
- Reused the mapper in Codex session creation to preserve root causes
instead of returning InternalAgentDied.
This PR is a modified version of [a
PR](https://github.com/openai/codex/pull/7316) submitted by @yydrowz3.
* Removes a redundant `experimental_sandbox_command_assessment` flag
* Moves `mcp_oauth_credentials_store` from the `[features]` table, where
it doesn't belong
This PR is a documentation only one which:
- addresses the #7231 by adding a paragraph in `docs/getting-started.md`
in the tips category to encourage users to load everything needed in
their environment
- corrects link referencing in `docs/platform-sandboxing.md` so that the
page link opens at the right section
- removes the explicit heading IDs like {#my-id} in `docs/advanced.md`
which are not supported by GitHub and are **not** rendered in the UI:
<img width="1198" height="849" alt="Screenshot 2025-11-26 at 16 25 31"
src="https://github.com/user-attachments/assets/308d33c3-81d3-4785-a6c1-e9377e6d3ea6"
/>
This caused the following links in `README.md` to not work in `main` but
to work in this branch (you can test by going to
https://github.com/openai/codex/blob/docs/getting-started-enhancement/README.md)
- the MCP link goes straight to the correct section now:
```markdown
- [**Advanced**](./docs/advanced.md)
- [Tracing / verbose logging](./docs/advanced.md#tracing--verbose-logging)
- [Model Context Protocol (MCP)](./docs/advanced.md#model-context-protocol-mcp)
```
---------
Signed-off-by: lionel-oai <lionel@openai.com>
Signed-off-by: lionelchg <lionel.cheng@hotmail.fr>
Co-authored-by: lionelchg <lionel.cheng@hotmail.fr>
Recent model updates caused the experimental "sandbox tool assessment"
to time out most of the time leaving the user without any risk
assessment or tool summary. This change explicitly sets the reasoning
effort to medium and bumps the timeout.
This change has no effect if the user hasn't enabled the
`experimental_sandbox_command_assessment` feature flag.
the `/approvals` popup fails to recognize that the CLI is in
WorkspaceWrite mode if that policy has extra bits, like `writable_roots`
etc.
This change matches the policy, ignoring additional config aspects.
This is the V2 version of `EventMsg::TurnDiff`.
I decided to expose this as a `turn/*` notification as opposed to an
Item to make it more explicit that the diff is accumulated throughout a
turn (every `apply_patch` call updates the running diff). Also, I don't
think it's worth persisting this diff as an Item because it can always
be recomputed from the actual `FileChange` Items.
Add `thread_id` and `turn_id` to `item/started`, `item/completed`, and
`error` notifications. Otherwise the client will have a hard time
knowing which thread & turn (if multiple threads are running in
parallel) a new item/error is for.
Also add `thread_id` to `turn/started` and `turn/completed`.
Add a new endpoint that allows us to test multi-turn behavior.
Tested with running:
```
RUST_LOG=codex_app_server=debug CODEX_BIN=target/debug/codex \
cargo run -p codex-app-server-test-client -- \
send-follow-up-v2 "hello" "and now a follow-up question"
```
### **Summary of Changes**
**What?**
Fix for slash commands (e.g., /prompts:code-review) not being recognized
when large content (>3000 chars) is pasted.
[Bug Report](https://github.com/openai/codex/issues/7047)
**Why?**
With large pastes, slash commands were ignored, so custom prompts
weren't expanded and were submitted as literal text.
**How?**
Refactored the early return block in handle_key_event_without_popup
(lines 957-968).
Instead of returning early after replacing placeholders, the code now
replaces placeholders in the textarea and continues to the normal
submission flow.
This reuses the existing slash command detection and custom prompt
expansion logic (lines 981-1047), avoiding duplication.
**Changes:**
Modified codex-rs/tui/src/bottom_pane/chat_composer.rs: refactored early
return block to continue to normal flow instead of returning immediately
Added test: custom_prompt_with_large_paste_expands_correctly to verify
the fix
**Code Quality:**
No lint warnings
Code follows existing patterns and reuses existing logic
Atomic change focused on the bug fix
Bumps [webbrowser](https://github.com/amodm/webbrowser-rs) from 1.0.5 to
1.0.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/amodm/webbrowser-rs/releases">webbrowser's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.6</h2>
<h3>Fixed</h3>
<ul>
<li>Windows: fix browser opening when unicode characters exist in path.
See PR <a
href="https://redirect.github.com/amodm/webbrowser-rs/issues/108">#108</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/amodm/webbrowser-rs/blob/main/CHANGELOG.md">webbrowser's
changelog</a>.</em></p>
<blockquote>
<h2>[1.0.6] - 2025-10-15 <!-- raw HTML omitted --><!-- raw HTML omitted
--></h2>
<h3>Fixed</h3>
<ul>
<li>Windows: fix browser opening when unicode characters exist in path.
See PR <a
href="https://redirect.github.com/amodm/webbrowser-rs/issues/108">#108</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="659622914a"><code>6596229</code></a>
Release v1.0.6 [skip ci]</li>
<li><a
href="44908ca5f3"><code>44908ca</code></a>
ios: fix lint for objc2 invocation #build-ios</li>
<li><a
href="b76a217a07"><code>b76a217</code></a>
Merge branch 'Nodeigi-fix/107'</li>
<li><a
href="ee2b1cdf2e"><code>ee2b1cd</code></a>
fix opening a browser that is located in a path that contains unicode
characters</li>
<li><a
href="061e65e6b8"><code>061e65e</code></a>
ios: fix lints</li>
<li><a
href="85dd4a37fc"><code>85dd4a3</code></a>
macos: fix lints</li>
<li>See full diff in <a
href="https://github.com/amodm/webbrowser-rs/compare/v1.0.5...v1.0.6">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>
Co-authored-by: Eric Traut <etraut@openai.com>
Bumps [toml_edit](https://github.com/toml-rs/toml) from 0.23.4 to
0.23.5.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="4695fb02fc"><code>4695fb0</code></a>
chore: Release</li>
<li><a
href="6a77ed71cf"><code>6a77ed7</code></a>
docs: Update changelog</li>
<li><a
href="c1e8197964"><code>c1e8197</code></a>
refactor: Switch serde dependency to serde_core (<a
href="https://redirect.github.com/toml-rs/toml/issues/1036">#1036</a>)</li>
<li><a
href="d85d6cd61c"><code>d85d6cd</code></a>
refactor: Switch serde dependency to serde_core</li>
<li>See full diff in <a
href="https://github.com/toml-rs/toml/compare/v0.23.4...v0.23.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>
Bumps [regex](https://github.com/rust-lang/regex) from 1.11.1 to 1.12.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/regex/blob/master/CHANGELOG.md">regex's
changelog</a>.</em></p>
<blockquote>
<h1>1.12.2 (2025-10-13)</h1>
<p>This release fixes a <code>cargo doc</code> breakage on nightly when
<code>--cfg docsrs</code> is
enabled. This caused documentation to fail to build on docs.rs.</p>
<p>Bug fixes:</p>
<ul>
<li>[BUG <a
href="https://redirect.github.com/rust-lang/regex/issues/1305">#1305</a>](<a
href="https://redirect.github.com/rust-lang/regex/issues/1305">rust-lang/regex#1305</a>):
Switches the <code>doc_auto_cfg</code> feature to <code>doc_cfg</code>
on nightly for docs.rs builds.</li>
</ul>
<h1>1.12.1 (2025-10-10)</h1>
<p>This release makes a bug fix in the new
<code>regex::Captures::get_match</code> API
introduced in <code>1.12.0</code>. There was an oversight with the
lifetime parameter
for the <code>Match</code> returned. This is technically a breaking
change, but given
that it was caught almost immediately and I've yanked the
<code>1.12.0</code> release,
I think this is fine.</p>
<h1>1.12.0 (2025-10-10)</h1>
<p>This release contains a smattering of bug fixes, a fix for excessive
memory
consumption in some cases and a new
<code>regex::Captures::get_match</code> API.</p>
<p>Improvements:</p>
<ul>
<li>[FEATURE <a
href="https://redirect.github.com/rust-lang/regex/issues/1146">#1146</a>](<a
href="https://redirect.github.com/rust-lang/regex/issues/1146">rust-lang/regex#1146</a>):
Add <code>Capture::get_match</code> for returning the overall match
without <code>unwrap()</code>.</li>
</ul>
<p>Bug fixes:</p>
<ul>
<li>[BUG <a
href="https://redirect.github.com/rust-lang/regex/issues/1083">#1083</a>](<a
href="https://redirect.github.com/rust-lang/regex/issues/1083">rust-lang/regex#1083</a>):
Fixes a panic in the lazy DFA (can only occur for especially large
regexes).</li>
<li>[BUG <a
href="https://redirect.github.com/rust-lang/regex/issues/1116">#1116</a>](<a
href="https://redirect.github.com/rust-lang/regex/issues/1116">rust-lang/regex#1116</a>):
Fixes a memory usage regression for large regexes (introduced in
<code>regex 1.9</code>).</li>
<li>[BUG <a
href="https://redirect.github.com/rust-lang/regex/issues/1195">#1195</a>](<a
href="https://redirect.github.com/rust-lang/regex/issues/1195">rust-lang/regex#1195</a>):
Fix universal start states in sparse DFA.</li>
<li>[BUG <a
href="https://redirect.github.com/rust-lang/regex/issues/1295">#1295</a>](<a
href="https://redirect.github.com/rust-lang/regex/pull/1295">rust-lang/regex#1295</a>):
Fixes a panic when deserializing a corrupted dense DFA.</li>
<li><a
href="8f5d9479d0">BUG
8f5d9479</a>:
Make <code>regex_automata::meta::Regex::find</code> consistently return
<code>None</code> when
<code>WhichCaptures::None</code> is used.</li>
</ul>
<h1>1.11.3 (2025-09-25)</h1>
<p>This is a small patch release with an improvement in memory usage in
some
cases.</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5ea3eb1e95"><code>5ea3eb1</code></a>
1.12.2</li>
<li><a
href="ab0b07171b"><code>ab0b071</code></a>
regex-automata-0.4.13</li>
<li><a
href="691d51457d"><code>691d514</code></a>
regex-syntax-0.8.8</li>
<li><a
href="1dd9077779"><code>1dd9077</code></a>
docs: swap <code>doc_auto_cfg</code> with <code>doc_cfg</code></li>
<li><a
href="0089034cb3"><code>0089034</code></a>
regex-cli-0.2.3</li>
<li><a
href="140f8949da"><code>140f894</code></a>
regex-lite-0.1.8</li>
<li><a
href="27d6d65263"><code>27d6d65</code></a>
1.12.1</li>
<li><a
href="85398ad500"><code>85398ad</code></a>
changelog: 1.12.1</li>
<li><a
href="764efbd305"><code>764efbd</code></a>
api: tweak the lifetime of <code>Captures::get_match</code></li>
<li><a
href="ee6aa55e01"><code>ee6aa55</code></a>
rure-0.2.4</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-lang/regex/compare/1.11.1...1.12.2">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot 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>
clean up the code for scanning for world writable directories
One path (selecting a sandbox mode from /approvals) was using an
incorrect method that did not use the new method of creating deny aces
to prevent writing to those directories. Now all paths are the same.
- The total token used returned from the api doesn't account for the
reasoning items before the assistant message
- Account for those for auto compaction
- Add the encrypted reasoning effort in the common tests utils
- Add a test to make sure it works as expected
This introduces a new feature to Codex when it operates as an MCP
_client_ where if an MCP _server_ replies that it has an entry named
`"codex/sandbox-state"` in its _server capabilities_, then Codex will
send it an MCP notification with the following structure:
```json
{
"method": "codex/sandbox-state/update",
"params": {
"sandboxPolicy": {
"type": "workspace-write",
"network-access": false,
"exclude-tmpdir-env-var": false
"exclude-slash-tmp": false
},
"codexLinuxSandboxExe": null,
"sandboxCwd": "/Users/mbolin/code/codex2"
}
}
```
or with whatever values are appropriate for the initial `sandboxPolicy`.
**NOTE:** Codex _should_ continue to send the MCP server notifications
of the same format if these things change over the lifetime of the
thread, but that isn't wired up yet.
The result is that `shell-tool-mcp` can consume these values so that
when it calls `codex_core::exec::process_exec_tool_call()` in
`codex-rs/exec-server/src/posix/escalate_server.rs`, it is now sure to
call it with the correct values (whereas previously we relied on
hardcoded values).
While I would argue this is a supported use case within the MCP
protocol, the `rmcp` crate that we are using today does not support
custom notifications. As such, I had to patch it and I submitted it for
review, so hopefully it will be accepted in some form:
https://github.com/modelcontextprotocol/rust-sdk/pull/556
To test out this change from end-to-end:
- I ran `cargo build` in `~/code/codex2/codex-rs/exec-server`
- I built the fork of Bash in `~/code/bash/bash`
- I added the following to my `~/.codex/config.toml`:
```toml
# Use with `codex --disable shell_tool`.
[mcp_servers.execshell]
args = ["--bash", "/Users/mbolin/code/bash/bash"]
command = "/Users/mbolin/code/codex2/codex-rs/target/debug/codex-exec-mcp-server"
```
- From `~/code/codex2/codex-rs`, I ran `just codex --disable shell_tool`
- When the TUI started up, I verified that the sandbox mode is
`workspace-write`
- I ran `/mcp` to verify that the shell tool from the MCP is there:
<img width="1387" height="1400" alt="image"
src="https://github.com/user-attachments/assets/1a8addcc-5005-4e16-b59f-95cfd06fd4ab"
/>
- Then I asked it:
> what is the output of `gh issue list`
because this should be auto-approved with our existing dummy policy:
af63e6eccc/codex-rs/exec-server/src/posix.rs (L157-L164)
And it worked:
<img width="1387" height="1400" alt="image"
src="https://github.com/user-attachments/assets/7568d2f7-80da-4d68-86d0-c265a6f5e6c1"
/>
`process_exec_tool_call()` was taking `SandboxType` as a param, but in
practice, the only place it was constructed was in
`codex_message_processor.rs` where it was derived from the other
`sandbox_policy` param, so this PR inlines the logic that decides the
`SandboxType` into `process_exec_tool_call()`.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/7122).
* #7112
* __->__ #7122
Piping to codex fails to do anything useful and locks up the process.
We currently check for stdout, but not stdin
```
❯ echo foo|just c
cargo run --bin codex -- "$@"
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
Running `target/debug/codex`
Error: stdin is not a terminal
error: Recipe `codex` failed on line 10 with exit code 1
```
The unified exec tool has a `login` option that defaults to `true`:
3bdcbc7292/codex-rs/core/src/tools/handlers/unified_exec.rs (L35-L36)
This updates the `ExecParams` for `shell-tool-mcp` to support the same
parameter. Note it is declared as `Option<bool>` to ensure it is marked
optional in the generated JSON schema.
## Summary
When incrementing the minor version, we should reset patch to 0, rather
than keeping it.
## Testing
- [x] tested locally with dry_run and `get_latest_release_version`
mocked out
---------
Co-authored-by: Michael Bolin <mbolin@openai.com>
https://github.com/openai/codex/pull/7005 introduced a new part of the
release process that added multiple files named `bash` in the `dist/`
folder used as the basis of the GitHub Release. I believe that all file
names in a GitHub Release have to be unique, which is why the recent
release build failed:
https://github.com/openai/codex/actions/runs/19577669780/job/56070183504
Based on the output of the **List** step, I believe these are the
appropriate artifacts to delete as a quick fix.
Add a `Declined` status for when we request an approval from the user
and the user declines. This allows us to distinguish from commands that
actually ran, but failed.
This behaves similarly to apply_patch / FileChange, which does the same
thing.
This adds a GitHub workflow for building a new npm module we are
experimenting with that contains an MCP server for running Bash
commands. The new workflow, `shell-tool-mcp`, is a dependency of the
general `release` workflow so that we continue to use one version number
for all artifacts across the project in one GitHub release.
`.github/workflows/shell-tool-mcp.yml` is the primary workflow
introduced by this PR, which does the following:
- builds the `codex-exec-mcp-server` and `codex-execve-wrapper`
executables for both arm64 and x64 versions of Mac and Linux (preferring
the MUSL version for Linux)
- builds Bash (dynamically linked) for a [comically] large number of
platforms (both x64 and arm64 for most) with a small patch specified by
`shell-tool-mcp/patches/bash-exec-wrapper.patch`:
- `debian-11`
- `debian-12`
- `ubuntu-20.04`
- `ubuntu-22.04`
- `ubuntu-24.04`
- `centos-9`
- `macos-13` (x64 only)
- `macos-14` (arm64 only)
- `macos-15` (arm64 only)
- builds the TypeScript for the [new] Node module declared in the
`shell-tool-mcp/` folder, which creates `bin/mcp-server.js`
- adds all of the native binaries to `shell-tool-mcp/vendor/` folder;
`bin/mcp-server.js` does a runtime check to determine which ones to
execute
- uses `npm pack` to create the `.tgz` for the module
- if `publish: true` is set, invokes the `npm publish` call with the
`.tgz`
The justification for building Bash for so many different operating
systems is because, since it is dynamically linked, we want to increase
our confidence that the version we build is compatible with the glibc
whatever OS we end up running on. (Note this is less of a concern with
`codex-exec-mcp-server` and `codex-execve-wrapper` on Linux, as they are
statically linked.)
This PR also introduces the code for the npm module in `shell-tool-mcp/`
(the proposed module name is `@openai/codex-shell-tool-mcp`). Initially,
I intended the module to be a single file of vanilla JavaScript (like
[`codex-cli/bin/codex.js`](ab5972d447/codex-cli/bin/codex.js)),
but some of the logic seemed a bit tricky, so I decided to port it to
TypeScript and add unit tests.
`shell-tool-mcp/src/index.ts` defines the `main()` function for the
module, which performs runtime checks to determine the clang triple to
find the path to the Rust executables within the `vendor/` folder
(`resolveTargetTriple()`). It uses a combination of `readOsRelease()`
and `resolveBashPath()` to determine the correct Bash executable to run
in the environment. Ultimately, it spawns a command like the following:
```
codex-exec-mcp-server \
--execve codex-execve-wrapper \
--bash custom-bash "$@"
```
Note `.github/workflows/shell-tool-mcp-ci.yml` defines a fairly standard
CI job for the module (`format`/`build`/`test`).
To test this PR, I pushed this branch to my personal fork of Codex and
ran the CI job there:
https://github.com/bolinfest/codex/actions/runs/19564311320
Admittedly, the graph looks a bit wild now:
<img width="5115" height="2969" alt="Screenshot 2025-11-20 at 11 44
58 PM"
src="https://github.com/user-attachments/assets/cc5ef306-efc1-4ed7-a137-5347e394f393"
/>
But when it finished, I was able to download `codex-shell-tool-mcp-npm`
from the **Artifacts** for the workflow in an empty temp directory,
unzip the `.zip` and then the `.tgz` inside it, followed by `xattr -rc
.` to remove the quarantine bits. Then I ran:
```shell
npx @modelcontextprotocol/inspector node /private/tmp/foobar4/package/bin/mcp-server.js
```
which launched the MCP Inspector and I was able to use it as expected!
This bodes well that this should work once the package is published to
npm:
```shell
npx @modelcontextprotocol/inspector npx @openai/codex-shell-tool-mcp
```
Also, to verify the package contains what I expect:
```shell
/tmp/foobar4/package$ tree
.
├── bin
│ └── mcp-server.js
├── package.json
├── README.md
└── vendor
├── aarch64-apple-darwin
│ ├── bash
│ │ ├── macos-14
│ │ │ └── bash
│ │ └── macos-15
│ │ └── bash
│ ├── codex-exec-mcp-server
│ └── codex-execve-wrapper
├── aarch64-unknown-linux-musl
│ ├── bash
│ │ ├── centos-9
│ │ │ └── bash
│ │ ├── debian-11
│ │ │ └── bash
│ │ ├── debian-12
│ │ │ └── bash
│ │ ├── ubuntu-20.04
│ │ │ └── bash
│ │ ├── ubuntu-22.04
│ │ │ └── bash
│ │ └── ubuntu-24.04
│ │ └── bash
│ ├── codex-exec-mcp-server
│ └── codex-execve-wrapper
├── x86_64-apple-darwin
│ ├── bash
│ │ └── macos-13
│ │ └── bash
│ ├── codex-exec-mcp-server
│ └── codex-execve-wrapper
└── x86_64-unknown-linux-musl
├── bash
│ ├── centos-9
│ │ └── bash
│ ├── debian-11
│ │ └── bash
│ ├── debian-12
│ │ └── bash
│ ├── ubuntu-20.04
│ │ └── bash
│ ├── ubuntu-22.04
│ │ └── bash
│ └── ubuntu-24.04
│ └── bash
├── codex-exec-mcp-server
└── codex-execve-wrapper
26 directories, 26 files
```
It stopped working (found zero duplicates) starting three days ago when
the model was switched from `gpt-5` to `gpt-5.1`. I'm not sure why it
stopped working. This is an attempt to get it working again by using the
default model for the codex action (which is presumably
`gpt-5.1-codex-max`).
### Summary
After #7022, we no longer need this warning. We should also clean up the
schema for the notification, but this is a quick fix to just stop the
behavior in the VSCE
## Testing
- [x] Ran locally
Previously, we were running into an issue where we would run the `shell`
tool call with a timeout of 10s, but it fired an elicitation asking for
user approval, the time the user took to respond to the elicitation was
counted agains the 10s timeout, so the `shell` tool call would fail with
a timeout error unless the user is very fast!
This PR addresses this issue by introducing a "stopwatch" abstraction
that is used to manage the timeout. The idea is:
- `Stopwatch::new()` is called with the _real_ timeout of the `shell`
tool call.
- `process_exec_tool_call()` is called with the `Cancellation` variant
of `ExecExpiration` because it should not manage its own timeout in this
case
- the `Stopwatch` expiration is wired up to the `cancel_rx` passed to
`process_exec_tool_call()`
- when an elicitation for the `shell` tool call is received, the
`Stopwatch` pauses
- because it is possible for multiple elicitations to arrive
concurrently, it keeps track of the number of "active pauses" and does
not resume until that counter goes down to zero
I verified that I can test the MCP server using
`@modelcontextprotocol/inspector` and specify `git status` as the
`command` with a timeout of 500ms and that the elicitation pops up and I
have all the time in the world to respond whereas previous to this PR,
that would not have been possible.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/6973).
* #7005
* __->__ #6973
* #6972
This updates `ExecParams` so that instead of taking `timeout_ms:
Option<u64>`, it now takes a more general cancellation mechanism,
`ExecExpiration`, which is an enum that includes a
`Cancellation(tokio_util::sync::CancellationToken)` variant.
If the cancellation token is fired, then `process_exec_tool_call()`
returns in the same way as if a timeout was exceeded.
This is necessary so that in #6973, we can manage the timeout logic
external to the `process_exec_tool_call()` because we want to "suspend"
the timeout when an elicitation from a human user is pending.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/6972).
* #7005
* #6973
* __->__ #6972
- Use /bin/sh instead of /bin/bash on FreeBSD/OpenBSD in the process
group timeout test to avoid command-not-found failures.
- Accept /usr/local/bin/bash as a valid SHELL path to match common
FreeBSD installations.
- Switch the shell serialization duration test to /bin/sh for improved
portability across Unix platforms.
With this change, `cargo test -p codex-core --lib` runs and passes on
FreeBSD.
Our Restricted Token contains 3 SIDs (Logon, Everyone, {WorkspaceWrite
Capability || ReadOnly Capability})
because it must include Everyone, that left us vulnerable to directories
that allow writes to Everyone. Even though those directories do not have
ACEs that enable our capability SIDs to write to them, they could still
be written to even in ReadOnly mode, or even in WorkspaceWrite mode if
they are outside of a writable root.
A solution to this is to explicitly add *Deny* ACEs to these
directories, always for the ReadOnly Capability SID, and for the
WorkspaceWrite SID if the directory is outside of a workspace root.
Under a restricted token, Windows always checks Deny ACEs before Allow
ACEs so even though our restricted token would allow a write to these
directories due to the Everyone SID, it fails first because of the Deny
ACE on the capability SID
second attempt to fix this test after
https://github.com/openai/codex/pull/6884. I think this flakiness is
happening because yield_time is too small for a 10,000 step loop in
python.
adding execpolicycheck tool onto codex cli
this is useful for validating policies (can be multiple) against
commands.
it will also surface errors in policy syntax:
<img width="1150" height="281" alt="Screenshot 2025-11-19 at 12 46
21 PM"
src="https://github.com/user-attachments/assets/8f99b403-564c-4172-acc9-6574a8d13dc3"
/>
this PR also changes output format when there's no match in the CLI.
instead of returning the raw string `noMatch`, we return
`{"noMatch":{}}`
this PR is a rewrite of: https://github.com/openai/codex/pull/6932 (due
to the numerous merge conflicts present in the original PR)
---------
Co-authored-by: Michael Bolin <mbolin@openai.com>
setting user shell timeout to an unreasonably high value since there
isn't an easy way to have a command run without timeouts
currently, user shell commands timeout is 10 seconds
## Summary
- TUI feedback note now only links to the bug-report template when the
category is bug/bad result.
- Good result/other feedback shows a thank-you+thread ID instead of
funneling people to file a bug.
- Added a helper + unit test so future changes keep the behavior
consistent.
## Testing
- just fmt
- just fix -p codex-tui
- cargo test -p codex-tui
Fixes#6839
### Description
- codex exec --json resume --last "<prompt>" bailed out because clap
treated the prompt as SESSION_ID. I removed the conflicts_with flag and
reinterpret that positional as a prompt when
--last is set, so the flow now keeps working in JSON mode.
(codex-rs/exec/src/cli.rs:84-104, codex-rs/exec/src/lib.rs:75-130)
- Added a regression test that exercises resume --last in JSON mode to
ensure the prompt is accepted and the rollout file is updated.
(codex-rs/exec/tests/suite/resume.rs:126-178)
### Testing
- just fmt
- cargo test -p codex-exec
- just fix -p codex-exec
- cargo test -p codex-exec
#6717
Signed-off-by: Dmitri Khokhlov <dkhokhlov@cribl.io>
## Summary
- add an explicit `override_usage` string to `AddArgs` so clap prints
`<NAME>` before the command/url choice, matching the actual parser and
docs
### Before
Usage: codex mcp add [OPTIONS] <COMMAND|--url <URL>> <NAME>
### After
Usage: codex mcp add [OPTIONS] <NAME> [--url <URL> | -- <COMMAND>...]
---------
Signed-off-by: kyuheon-kr <kyuheon.kr@gmail.com>
## 🐛 Problem
Users running commands with non-ASCII characters (like Russian text
"пример") in Windows/WSL environments experience garbled text in
VSCode's shell preview window, with Unicode replacement characters (�)
appearing instead of the actual text.
**Issue**: https://github.com/openai/codex/issues/6178
## 🔧 Root Cause
The issue was in `StreamOutput<Vec<u8>>::from_utf8_lossy()` method in
`codex-rs/core/src/exec.rs`, which used `String::from_utf8_lossy()` to
convert shell output bytes to strings. This function immediately
replaces any invalid UTF-8 byte sequences with replacement characters,
without attempting to decode using other common encodings.
In Windows/WSL environments, shell output often uses encodings like:
- Windows-1252 (common Windows encoding)
- Latin-1/ISO-8859-1 (extended ASCII)
## 🛠️ Solution
Replaced the simple `String::from_utf8_lossy()` call with intelligent
encoding detection via a new `bytes_to_string_smart()` function that
tries multiple encoding strategies:
1. **UTF-8** (fast path for valid UTF-8)
2. **Windows-1252** (handles Windows-specific characters in 0x80-0x9F
range)
3. **Latin-1** (fallback for extended ASCII)
4. **Lossy UTF-8** (final fallback, same as before)
## 📁 Changes
### New Files
- `codex-rs/core/src/text_encoding.rs` - Smart encoding detection module
- `codex-rs/core/tests/suite/text_encoding_fix.rs` - Integration tests
### Modified Files
- `codex-rs/core/src/lib.rs` - Added text_encoding module
- `codex-rs/core/src/exec.rs` - Updated StreamOutput::from_utf8_lossy()
- `codex-rs/core/tests/suite/mod.rs` - Registered new test module
## ✅ Testing
- **5 unit tests** covering UTF-8, Windows-1252, Latin-1, and fallback
scenarios
- **2 integration tests** simulating the exact Issue #6178 scenario
- **Demonstrates improvement** over the previous
`String::from_utf8_lossy()` approach
All tests pass:
```bash
cargo test -p codex-core text_encoding
cargo test -p codex-core test_shell_output_encoding_issue_6178
```
## 🎯 Impact
- ✅ **Eliminates garbled text** in VSCode shell preview for non-ASCII
content
- ✅ **Supports Windows/WSL environments** with proper encoding detection
- ✅ **Zero performance impact** for UTF-8 text (fast path)
- ✅ **Backward compatible** - UTF-8 content works exactly as before
- ✅ **Handles edge cases** with robust fallback mechanism
## 🧪 Test Scenarios
The fix has been tested with:
- Russian text ("пример")
- Windows-1252 quotation marks (""test")
- Latin-1 accented characters ("café")
- Mixed encoding content
- Invalid byte sequences (graceful fallback)
## 📋 Checklist
- [X] Addresses the reported issue
- [X] Includes comprehensive tests
- [X] Maintains backward compatibility
- [X] Follows project coding conventions
- [X] No breaking changes
---------
Co-authored-by: Josh McKinney <joshka@openai.com>
This PR adds support for a new feature flag `tui.animations`. By
default, the TUI uses animations in its welcome screen, "working"
spinners, and "shimmer" effects. This animations can interfere with
screen readers, so it's good to provide a way to disable them.
This change is inspired by [a
PR](https://github.com/openai/codex/pull/4014) contributed by @Orinks.
That PR has faltered a bit, but I think the core idea is sound. This
version incorporates feedback from @aibrahim-oai. In particular:
1. It uses a feature flag (`tui.animations`) rather than the unqualified
CLI key `no-animations`. Feature flags are the preferred way to expose
boolean switches. They are also exposed via CLI command switches.
2. It includes more complete documentation.
3. It disables a few animations that the other PR omitted.
Thread through an `exit_notify` tokio `Notify` through to the
`UnifiedExecSession` so that we can return early if the command
terminates before `yield_time_ms`.
As Codex review correctly pointed out below 🙌 we also need a
`exit_signaled` flag so that commands which finish before we start
waiting can also exit early.
Since the default `yield_time_ms` is now 10s, this means that we don't
have to wait 10s for trivial commands like ls, sed, etc (which are the
majority of agent commands 😅)
---------
Co-authored-by: jif-oai <jif@openai.com>
This PR adds the API V2 version of the apply_patch approval flow, which
centers around `ThreadItem::FileChange`.
This PR wires the new RPC (`item/fileChange/requestApproval`, V2 only)
and related events (`item/started`, `item/completed` for
`ThreadItem::FileChange`, which are emitted in both V1 and V2) through
the app-server
protocol. The new approval RPC is only sent when the user initiates a
turn with the new `turn/start` API so we don't break backwards
compatibility with VSCE.
Similar to https://github.com/openai/codex/pull/6758, the approach I
took was to make as few changes to the Codex core as possible,
leveraging existing `EventMsg` core events, and translating those in
app-server. I did have to add a few additional fields to
`EventMsg::PatchApplyBegin` and `EventMsg::PatchApplyEnd`, but those
were fairly lightweight.
However, the `EventMsg`s emitted by core are the following:
```
1) Auto-approved (no request for approval)
- EventMsg::PatchApplyBegin
- EventMsg::PatchApplyEnd
2) Approved by user
- EventMsg::ApplyPatchApprovalRequest
- EventMsg::PatchApplyBegin
- EventMsg::PatchApplyEnd
3) Declined by user
- EventMsg::ApplyPatchApprovalRequest
- EventMsg::PatchApplyBegin
- EventMsg::PatchApplyEnd
```
For a request triggering an approval, this would result in:
```
item/fileChange/requestApproval
item/started
item/completed
```
which is different from the `ThreadItem::CommandExecution` flow
introduced in https://github.com/openai/codex/pull/6758, which does the
below and is preferable:
```
item/started
item/commandExecution/requestApproval
item/completed
```
To fix this, we leverage `TurnSummaryStore` on codex_message_processor
to store a little bit of state, allowing us to fire `item/started` and
`item/fileChange/requestApproval` whenever we receive the underlying
`EventMsg::ApplyPatchApprovalRequest`, and no-oping when we receive the
`EventMsg::PatchApplyBegin` later.
This is much less invasive than modifying the order of EventMsg within
core (I tried).
The resulting payloads:
```
{
"method": "item/started",
"params": {
"item": {
"changes": [
{
"diff": "Hello from Codex!\n",
"kind": "add",
"path": "/Users/owen/repos/codex/codex-rs/APPROVAL_DEMO.txt"
}
],
"id": "call_Nxnwj7B3YXigfV6Mwh03d686",
"status": "inProgress",
"type": "fileChange"
}
}
}
```
```
{
"id": 0,
"method": "item/fileChange/requestApproval",
"params": {
"grantRoot": null,
"itemId": "call_Nxnwj7B3YXigfV6Mwh03d686",
"reason": null,
"threadId": "019a9e11-8295-7883-a283-779e06502c6f",
"turnId": "1"
}
}
```
```
{
"id": 0,
"result": {
"decision": "accept"
}
}
```
```
{
"method": "item/completed",
"params": {
"item": {
"changes": [
{
"diff": "Hello from Codex!\n",
"kind": "add",
"path": "/Users/owen/repos/codex/codex-rs/APPROVAL_DEMO.txt"
}
],
"id": "call_Nxnwj7B3YXigfV6Mwh03d686",
"status": "completed",
"type": "fileChange"
}
}
}
```
This reverts commit c2ec477d93.
# 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.
This PR threads execpolicy2 into codex-core.
activated via feature flag: exec_policy (on by default)
reads and parses all .codexpolicy files in `codex_home/codex`
refactored tool runtime API to integrate execpolicy logic
---------
Co-authored-by: Michael Bolin <mbolin@openai.com>
This PR reorganizes things slightly so that:
- Instead of a single multitool executable, `codex-exec-server`, we now
have two executables:
- `codex-exec-mcp-server` to launch the MCP server
- `codex-execve-wrapper` is the `execve(2)` wrapper to use with the
`BASH_EXEC_WRAPPER` environment variable
- `BASH_EXEC_WRAPPER` must be a single executable: it cannot be a
command string composed of an executable with args (i.e., it no longer
adds the `escalate` subcommand, as before)
- `codex-exec-mcp-server` takes `--bash` and `--execve` as options.
Though if `--execve` is not specified, the MCP server will check the
directory containing `std::env::current_exe()` and attempt to use the
file named `codex-execve-wrapper` within it. In development, this works
out since these executables are side-by-side in the `target/debug`
folder.
With respect to testing, this also fixes an important bug in
`dummy_exec_policy()`, as I was using `ends_with()` as if it applied to
a `String`, but in this case, it is used with a `&Path`, so the
semantics are slightly different.
Putting this all together, I was able to test this by running the
following:
```
~/code/codex/codex-rs$ npx @modelcontextprotocol/inspector \
./target/debug/codex-exec-mcp-server --bash ~/code/bash/bash
```
If I try to run `git status` in `/Users/mbolin/code/codex` via the
`shell` tool from the MCP server:
<img width="1589" height="1335" alt="image"
src="https://github.com/user-attachments/assets/9db6aea8-7fbc-4675-8b1f-ec446685d6c4"
/>
then I get prompted with the following elicitation, as expected:
<img width="1589" height="1335" alt="image"
src="https://github.com/user-attachments/assets/21b68fe0-494d-4562-9bad-0ddc55fc846d"
/>
Though a current limitation is that the `shell` tool defaults to a
timeout of 10s, which means I only have 10s to respond to the
elicitation. Ideally, the time spent waiting for a response from a human
should not count against the timeout for the command execution. I will
address this in a subsequent PR.
---
Note `~/code/bash/bash` was created by doing:
```
cd ~/code
git clone https://github.com/bminor/bash
cd bash
git checkout a8a1c2fac029404d3f42cd39f5a20f24b6e4fe4b
<apply the patch below>
./configure
make
```
The patch:
```
diff --git a/execute_cmd.c b/execute_cmd.c
index 070f5119..d20ad2b9 100644
--- a/execute_cmd.c
+++ b/execute_cmd.c
@@ -6129,6 +6129,19 @@ shell_execve (char *command, char **args, char **env)
char sample[HASH_BANG_BUFSIZ];
size_t larray;
+ char* exec_wrapper = getenv("BASH_EXEC_WRAPPER");
+ if (exec_wrapper && *exec_wrapper && !whitespace (*exec_wrapper))
+ {
+ char *orig_command = command;
+
+ larray = strvec_len (args);
+
+ memmove (args + 2, args, (++larray) * sizeof (char *));
+ args[0] = exec_wrapper;
+ args[1] = orig_command;
+ command = exec_wrapper;
+ }
+
```
This closes#6748 by implementing fallback to
`model_family.default_reasoning_effort` in `reasoning_effort` display of
`/status` when no `model_reasoning_effort` is set in the configuration.
## common/src/config_summary.rs
- `create_config_summary_entries` now fills the "reasoning effort" entry
with the explicit `config.model_reasoning_effort` when present and falls
back to `config.model_family.default_reasoning_effort` when it is
`None`, instead of emitting the literal string `none`.
- This ensures downstream consumers such as `tui/src/status/helpers.rs`
continue to work unchanged while automatically picking up model-family
defaults when the user has not selected a reasoning effort.
## tui/src/status/helpers.rs / core/src/model_family.rs
`ModelFamily::default_reasoning_effort` metadata is set to `medium` for
both `gpt-5*-codex` and `gpt-5` models following the default behaviour
of the API and recommendation of the codebase:
- per https://platform.openai.com/docs/api-reference/responses/create
`gpt-5` defaults to `medium` reasoning when no preset is passed
- there is no mention of the preset for `gpt-5.1-codex` in the API docs
but `medium` is the default setting for `gpt-5.1-codex` as per
`codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap`
---------
Signed-off-by: lionelchg <lionel.cheng@hotmail.fr>
Co-authored-by: Eric Traut <etraut@openai.com>
## Summary
- show live review token usage while `/review` runs and restore the main
session indicator afterward
- add regression coverage for the footer behavior
## Testing
- just fmt
- cargo test -p codex-tui
Fixes#5604
---------
Signed-off-by: Fahad <fahad@2doapp.com>
Fix world-writable audit false positives by expanding generic
permissions with MapGenericMask and then checking only concrete write
bits. The earlier check looked for FILE_GENERIC_WRITE/generic masks
directly, which shares bits with read permissions and could flag an
Everyone read ACE as writable.
This PR introduces an extra layer of abstraction to prepare us for the
migration to execpolicy2:
- introduces a new trait, `EscalationPolicy`, whose `determine_action()`
method is responsible for producing the `EscalateAction`
- the existing `ExecPolicy` typedef is changed to return an intermediate
`ExecPolicyOutcome` instead of `EscalateAction`
- the default implementation of `EscalationPolicy`,
`McpEscalationPolicy`, composes `ExecPolicy`
- the `ExecPolicyOutcome` includes `codex_execpolicy2::Decision`, which
has a `Prompt` variant
- when `McpEscalationPolicy` gets `Decision::Prompt` back from
`ExecPolicy`, it prompts the user via an MCP elicitation and maps the
result into an `ElicitationAction`
- now that the end user can reply to an elicitation with `Decline` or
`Cancel`, we introduce a new variant, `EscalateAction::Deny`, which the
client handles by returning exit code `1` without running anything
Note the way the elicitation is created is still not quite right, but I
will fix that once we have things running end-to-end for real in a
follow-up PR.
Expand the rate-limit cache/TUI: store credit snapshots alongside
primary and secondary windows, render “Credits” when the backend reports
they exist (unlimited vs rounded integer balances)
This PR allows clients to render historical messages when resuming a
thread via `thread/resume` by reading from the list of `EventMsg`
payloads loaded from the rollout, and then transforming them into Turns
and ThreadItems to be returned on the `Thread` object.
This is implemented by leveraging `SessionConfiguredNotification` which
returns this list of `EventMsg` objects when resuming a conversation,
and then applying a stateful `ThreadHistoryBuilder` that parses from
this EventMsg log and transforms it into Turns and ThreadItems.
Note that we only persist a subset of `EventMsg`s in a rollout as
defined in `policy.rs`, so we lose fidelity whenever we resume a thread
compared to when we streamed the thread's turns originally. However,
this behavior is at parity with the legacy API.
## Summary
Setting `/approvals` before the start of a conversation was not updating
the environment_context for a conversation. Not sure exactly when this
problem was introduced, but this should reduce model confusion
dramatically.
## Testing
- [x] Added unit test to reproduce bug, confirmed fix with update
- [x] Tested locally
## Summary
On app-server startup, detect whether the experimental sandbox is
enabled, and send a notification .
**Note**
New conversations will not respect the feature because we [ignore cli
overrides in
NewConversation](a75321a64c/codex-rs/app-server/src/codex_message_processor.rs (L1237-L1252)).
However, this should be okay, since we don't actually use config for
this, we use a [global
variable](87cce88f48/codex-rs/core/src/safety.rs (L105-L110)).
We should carefully unwind this setup at some point.
## Testing
- [ ] In progress: testing locally
---------
Co-authored-by: jif-oai <jif@openai.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.
Instead of returning structured out and then re-formatting it into
freeform, return the freeform output from shell_command tool.
Keep `shell` as the default tool for GPT-5.
# 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.
New strings:
1. Approval mode picker just says "Select Approval Mode"
1. Updated "Auto" to "Agent"
1. When you select "Agent", you get "Agent mode on Windows uses an
experimental sandbox to limit network and filesystem access. [Learn
more]"
1. Updated world-writable warning to "The Windows sandbox cannot protect
writes to folders that are writable by Everyone. Consider removing write
access for Everyone from the following folders: {folders}"
---------
Co-authored-by: iceweasel-oai <iceweasel@openai.com>
This adds the following fields to `ThreadStartResponse` and
`ThreadResumeResponse`:
```rust
pub model: String,
pub model_provider: String,
pub cwd: PathBuf,
pub approval_policy: AskForApproval,
pub sandbox: SandboxPolicy,
pub reasoning_effort: Option<ReasoningEffort>,
```
This is important because these fields are optional in
`ThreadStartParams` and `ThreadResumeParams`, so the caller needs to be
able to determine what values were ultimately used to start/resume the
conversation. (Though note that any of these could be changed later
between turns in the conversation.)
Though to get this information reliably, it must be read from the
internal `SessionConfiguredEvent` that is created in response to the
start of a conversation. Because `SessionConfiguredEvent` (as defined in
`codex-rs/protocol/src/protocol.rs`) did not have all of these fields, a
number of them had to be added as part of this PR.
Because `SessionConfiguredEvent` is referenced in many tests, test
instances of `SessionConfiguredEvent` had to be updated, as well, which
is why this PR touches so many files.
similar to logic in
`codex/codex-rs/exec/src/event_processor_with_jsonl_output.rs`.
translation of v1 -> v2 events:
`codex/event/task_complete` -> `turn/completed`
`codex/event/turn_aborted` -> `turn/completed` with `interrupted` status
`codex/event/error` -> `turn/completed` with `error` status
this PR also makes `items` field in `Turn` optional. For now, we only
populate it when we resume a thread, and leave it as None for all other
places until we properly rewrite core to keep track of items.
tested using the codex app server client. example new event:
```
< {
< "method": "turn/completed",
< "params": {
< "turn": {
< "id": "0",
< "items": [],
< "status": "interrupted"
< }
< }
< }
```
By default, show only sessions that shared a cwd with the current cwd.
`--all` shows all sessions in all cwds. Also, show the branch name from
the rollout metadata.
<img width="1091" height="638" alt="Screenshot 2025-11-04 at 3 30 47 PM"
src="https://github.com/user-attachments/assets/aae90308-6115-455f-aff7-22da5f1d9681"
/>
This PR fixes the `release_event_does_not_change_selection` test so it
doesn't cause an extra `config.toml` to be emitted in the sources when
running the tests locally. Prior to this fix, I needed to delete this
file every time I ran the tests to prevent it from showing up as an
uncommitted source file.
The `generated_ts_has_no_optional_nullable_fields` test was occasionally
failing on slow CI nodes because of a timeout. This change reduces the
work done by the test. It adds some "options" for the `generate_ts`
function so it can skip work that's not needed for the test.
# 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.
- This PR is to make it on path for truncating by tokens. This path will
be initially used by unified exec and context manager (responsible for
MCP calls mainly).
- We are exposing new config `calls_output_max_tokens`
- Use `tokens` as the main budget unit but truncate based on the model
family by Introducing `TruncationPolicy`.
- Introduce `truncate_text` as a router for truncation based on the
mode.
In next PRs:
- remove truncate_with_line_bytes_budget
- Add the ability to the model to override the token budget.
- Local-shell tool responses were always tagged as
`ExecCommandSource::UserShell` because handler would call
`run_exec_like` with `is_user_shell_cmd` set to true.
- Treat `ToolPayload::LocalShell` the same as other model generated
shell tool calls by deleting `is_user_shell_cmd` from `run_exec_like`
(since actual user shell commands follow a separate code path)
## Summary
Enables shell_command for windows users, and starts adding some basic
command parsing here, to at least remove powershell prefixes. We'll
follow this up with command parsing but I wanted to land this change
separately with some basic UX.
**NOTE**: This implementation parses bash and powershell on both
platforms. In theory this is possible, since you can use git bash on
windows or powershell on linux. In practice, this may not be worth the
complexity of supporting, so I don't feel strongly about the current
approach vs. platform-specific branching.
## Testing
- [x] Added a bunch of tests
- [x] Ran on both windows and os x
## Summary
Similar to #6545, this PR updates the shell_serialization test suite to
cover the various `shell` tool invocations we have. Note that this does
not cover unified_exec, which has its own suite of tests. This should
provide some test coverage for when we eventually consolidate
serialization logic.
## Testing
- [x] These are tests
## Summary
- update documentation, example configs, and automation defaults to
reference gpt-5.1 / gpt-5.1-codex
- bump the CLI and core configuration defaults, model presets, and error
messaging to the new models while keeping the model-family/tool coverage
for legacy slugs
- refresh tests, fixtures, and TUI snapshots so they expect the upgraded
defaults
## Testing
- `cargo test -p codex-core
config::tests::test_precedence_fixture_with_gpt5_profile`
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6916c5b3c2b08321ace04ee38604fc6b)
- enabling execpolicy2 parser to parse multiple policy files to build a
combined `Policy` (useful if codex detects many `.codexpolicy` files)
- adding functionality to `Policy` to allow evaluation of multiple cmds
at once (useful when we have chained commands)
This PR adds the API V2 version of the command‑execution approval flow
for the shell tool.
This PR wires the new RPC (`item/commandExecution/requestApproval`, V2
only) and related events (`item/started`, `item/completed`, and
`item/commandExecution/delta`, which are emitted in both V1 and V2)
through the app-server
protocol. The new approval RPC is only sent when the user initiates a
turn with the new `turn/start` API so we don't break backwards
compatibility with VSCE.
The approach I took was to make as few changes to the Codex core as
possible, leveraging existing `EventMsg` core events, and translating
those in app-server. I did have to add additional fields to
`EventMsg::ExecCommandEndEvent` to capture the command's input so that
app-server can statelessly transform these events to a
`ThreadItem::CommandExecution` item for the `item/completed` event.
Once we stabilize the API and it's complete enough for our partners, we
can work on migrating the core to be aware of command execution items as
a first-class concept.
**Note**: We'll need followup work to make sure these APIs work for the
unified exec tool, but will wait til that's stable and landed before
doing a pass on app-server.
Example payloads below:
```
{
"method": "item/started",
"params": {
"item": {
"aggregatedOutput": null,
"command": "/bin/zsh -lc 'touch /tmp/should-trigger-approval'",
"cwd": "/Users/owen/repos/codex/codex-rs",
"durationMs": null,
"exitCode": null,
"id": "call_lNWWsbXl1e47qNaYjFRs0dyU",
"parsedCmd": [
{
"cmd": "touch /tmp/should-trigger-approval",
"type": "unknown"
}
],
"status": "inProgress",
"type": "commandExecution"
}
}
}
```
```
{
"id": 0,
"method": "item/commandExecution/requestApproval",
"params": {
"itemId": "call_lNWWsbXl1e47qNaYjFRs0dyU",
"parsedCmd": [
{
"cmd": "touch /tmp/should-trigger-approval",
"type": "unknown"
}
],
"reason": "Need to create file in /tmp which is outside workspace sandbox",
"risk": null,
"threadId": "019a93e8-0a52-7fe3-9808-b6bc40c0989a",
"turnId": "1"
}
}
```
```
{
"id": 0,
"result": {
"acceptSettings": {
"forSession": false
},
"decision": "accept"
}
}
```
```
{
"params": {
"item": {
"aggregatedOutput": null,
"command": "/bin/zsh -lc 'touch /tmp/should-trigger-approval'",
"cwd": "/Users/owen/repos/codex/codex-rs",
"durationMs": 224,
"exitCode": 0,
"id": "call_lNWWsbXl1e47qNaYjFRs0dyU",
"parsedCmd": [
{
"cmd": "touch /tmp/should-trigger-approval",
"type": "unknown"
}
],
"status": "completed",
"type": "commandExecution"
}
}
}
```
The `cap_sid` file contains the IDs of the two custom SIDs that the
Windows sandbox creates/manages to implement read-only and
workspace-write sandbox policies.
It previously lived in `<cwd>/.codex` which means that the sandbox could
write to it, which could degrade the efficacy of the sandbox. This
change moves it to `~/.codex/` (or wherever `CODEX_HOME` points to) so
that it is outside the workspace.
This PR fixes keyboard handling for the Right Alt (aka "Alt-Gr") key on
Windows. This key appears on keyboards in Central and Eastern Europe.
Codex has effectively never worked for Windows users in these regions
because the code didn't properly handle this key, which is used for
typing common symbols like `\` and `@`.
A few days ago, I merged a [community-authored
PR](https://github.com/openai/codex/pull/6720) that supplied a partial
fix for this issue. Upon closer inspect, that PR was 1) too broad (not
scoped to Windows only) and 2) incomplete (didn't fix all relevant code
paths, so paste was still broken).
This improvement is based on another [community-provided
PR](https://github.com/openai/codex/pull/3241) by @marektomas-cz. He
submitted it back in September and later closed it because it didn't
receive any attention.
This fix addresses the following bugs: #5922, #3046, #3092, #3519,
#5684, #5843.
`--disable shell_tool` disables the built-in shell tool. This is useful
for MCP-only operation.
---------
Co-authored-by: Michael Bolin <mbolin@openai.com>
## Overview
Adds LM Studio OSS support. Closes#1883
### Changes
This PR enhances the behavior of `--oss` flag to support LM Studio as a
provider. Additionally, it introduces a new flag`--local-provider` which
can take in `lmstudio` or `ollama` as values if the user wants to
explicitly choose which one to use.
If no provider is specified `codex --oss` will auto-select the provider
based on whichever is running.
#### Additional enhancements
The default can be set using `oss-provider` in config like:
```
oss_provider = "lmstudio"
```
For non-interactive users, they will need to either provide the provider
as an arg or have it in their `config.toml`
### Notes
For best performance, [set the default context
length](https://lmstudio.ai/docs/app/advanced/per-model) for gpt-oss to
the maximum your machine can support
---------
Co-authored-by: Matt Clayton <matt@lmstudio.ai>
Co-authored-by: Eric Traut <etraut@openai.com>
We're running into quite a bit of drag maintaining this test, since
every time we add fields to an EventMsg that happened to be dumped into
the `binary-size-log.jsonl` fixture, this test starts to fail. The fix
is usually to either manually update the `binary-size-log.jsonl` fixture
file, or update the `upgrade_event_payload_for_tests` function to map
the data in that file into something workable.
Eason says it's fine to delete this test, so let's just delete it
## Summary
- Introduces the `codex-execpolicy2` crate.
- This PR covers only the prefix-rule subset of the planned execpolicy
v2 language; a richer language will follow.
## Policy
- Policy language centers on `prefix_rule(pattern=[...], decision?,
match?, not_match?)`, where `pattern` is an ordered list of tokens; any
element may be a list to denote alternatives. `decision` defaults to
`allow`; valid values are `allow`, `prompt`, and `forbidden`. `match` /
`not_match` hold example commands that are tokenized and validated at
load time (think of these as unit tests).
## Policy shapes
- Prefix rules use Starlark syntax:
```starlark
prefix_rule(
pattern = ["cmd", ["alt1", "alt2"]], # ordered tokens; list entries denote alternatives
decision = "prompt", # allow | prompt | forbidden; defaults to allow
match = [["cmd", "alt1"]], # examples that must match this rule (enforced at compile time)
not_match = [["cmd", "oops"]], # examples that must not match this rule (enforced at compile time)
)
```
## Response shapes
- Match:
```json
{
"match": {
"decision": "allow|prompt|forbidden",
"matchedRules": [
{
"prefixRuleMatch": {
"matchedPrefix": ["<token>", "..."],
"decision": "allow|prompt|forbidden"
}
}
]
}
}
```
- No match:
```json
"noMatch"
```
- `matchedRules` lists every rule whose prefix matched the command;
`matchedPrefix` is the exact prefix that matched.
- The effective `decision` is the strictest severity across all matches
(`forbidden` > `prompt` > `allow`).
---------
Co-authored-by: Michael Bolin <mbolin@openai.com>
## Summary
Builds on FreeBSD and OpenBSD were failing due to globally enabled
Linux-specific keyring features and hardening code paths not gated by
OS. This PR scopes keyring native backends to the
appropriate targets, disables default features at the workspace root,
and adds a BSD-specific hardening function. Linux/macOS/Windows behavior
remains unchanged, while FreeBSD/OpenBSD
now build and run with a supported backend.
## Key Changes
- Keyring features:
- Disable keyring default features at the workspace root to avoid
pulling Linux backends on non-Linux.
- Move native backend features into target-specific sections in the
affected crates:
- Linux: linux-native-async-persistent
- macOS: apple-native
- Windows: windows-native
- FreeBSD/OpenBSD: sync-secret-service
- Process hardening:
- Add pre_main_hardening_bsd() for FreeBSD/OpenBSD, applying:
- Set RLIMIT_CORE to 0
- Clear LD_* environment variables
- Simplify process-hardening Cargo deps to unconditional libc (avoid
conflicting OS fragments).
- No changes to CODEX_SANDBOX_* behavior.
## Rationale
- Previously, enabling keyring native backends globally pulled
Linux-only features on BSD, causing build errors.
- Hardening logic was tailored for Linux/macOS; BSD builds lacked a
gated path with equivalent safeguards.
- Target-scoped features and BSD hardening make the crates portable
across these OSes without affecting existing behavior elsewhere.
## Impact by Platform
- Linux: No functional change; backends now selected via target cfg.
- macOS: No functional change; explicit apple-native mapping.
- Windows: No functional change; explicit windows-native mapping.
- FreeBSD/OpenBSD: Builds succeed using sync-secret-service; BSD
hardening applied during startup.
## Testing
- Verified compilation across affected crates with target-specific
features.
- Smoke-checked that Linux/macOS/Windows feature sets remain identical
functionally after scoping.
- On BSD, confirmed keyring resolves to sync-secret-service and
hardening compiles.
## Risks / Compatibility
- Minimal risk: only feature scoping and OS-gated additions.
- No public API changes in the crates; runtime behavior on non-BSD
platforms is preserved.
- On BSD, the new hardening clears LD_*; this is consistent with
security posture on other Unix platforms.
## Reviewer Notes
- Pay attention to target-specific sections for keyring in the affected
Cargo.toml files.
- Confirm pre_main_hardening_bsd() mirrors the safe subset of
Linux/macOS hardening without introducing Linux-only calls.
- Confirm no references to CODEX_SANDBOX_ENV_VAR or
CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR were added/modified.
## Checklist
- Disable keyring default features at workspace root.
- Target-specific keyring features mapped per OS
(Linux/macOS/Windows/BSD).
- Add BSD hardening (RLIMIT_CORE=0, clear LD_*).
- Simplify process-hardening dependencies to unconditional libc.
- No changes to sandbox env var code.
- Formatting and linting: just fmt + just fix -p for changed crates.
- Project tests pass for changed crates; broader suite unchanged.
---------
Co-authored-by: celia-oai <celia@openai.com>
## Summary
Fixes streaming issue where Claude models return only 1-4 characters
instead of full responses when used through certain API
providers/proxies.
## Environment
- **OS**: Windows
- **Models affected**: Claude models (e.g., claude-haiku-4-5-20251001)
- **API Provider**: AAAI API proxy (https://api.aaai.vip/v1)
- **Working models**: GLM, Google models work correctly
## Problem
When using Claude models in both TUI and exec modes, only 1-4 characters
are displayed despite the backend receiving the full response. Debug
logs revealed that some API providers send SSE chunks with an empty
string finish_reason during active streaming, rather than null or
omitting the field entirely.
The current code treats any non-null finish_reason as a termination
signal, causing the stream to exit prematurely after the first chunk.
The problematic chunks contain finish_reason with an empty string
instead of null.
## Solution
Fix empty finish_reason handling in chat_completions.rs by adding a
check to only process non-empty finish_reason values. This ensures empty
strings are ignored and streaming continues normally.
## Testing
- Tested on Windows with Claude Haiku model via AAAI API proxy
- Full responses now received and displayed correctly in both TUI and
exec modes
- Other models (GLM, Google) continue to work as expected
- No regression in existing functionality
## Impact
- Improves compatibility with API providers that send empty
finish_reason during streaming
- Enables Claude models to work correctly in Windows environment
- No breaking changes to existing functionality
## Related Issues
This fix resolves the issue where Claude models appeared to return
incomplete responses. The root cause was identified as a compatibility
issue in parsing SSE responses from certain API providers/proxies,
rather than a model-specific problem. This change improves overall
robustness when working with various API endpoints.
---------
Co-authored-by: Eric Traut <etraut@openai.com>
### Summary
- Treat AltGr chords (Ctrl+Alt) as literal character input in the Codex
TUI textarea so Windows terminals that report
backslash and other characters via AltGr insert correctly.
- Add regression test altgr_ctrl_alt_char_inserts_literal to ensure
Ctrl+Alt char events append the character and
advance the cursor.
### Motivation
On US/UK keyboard layouts, backslash is produced by a plain key, so
Ctrl+Alt handling is never exercised and the
bug isn’t visible. On many non‑US layouts (e.g., German), backslash and
other symbols require AltGr, which terminals
report as Ctrl+Alt+<char>. Our textarea previously filtered these chords
like navigation bindings, so AltGr input was
dropped on affected layouts. This change treats AltGr chords as literal
input so backslash and similar symbols work on
Windows terminals.
This fixes multiple reported Issues where the \ symbol got cut off.
Like:
C:\Users\Admin
became
C:UsersAdmin
Co-authored-by: Eric Traut <etraut@openai.com>
## What?
Fixes MCP server initialization failures on Windows when using
script-based tools like `npx`, `pnpm`, and `yarn` that rely on
`.cmd`/`.bat` files rather than `.exe` binaries.
Fixes#2945
## Why?
Windows users encounter "program not found" errors when configuring MCP
servers with commands like `npx` in their `~/.codex/config.toml`. This
happens because:
- Tools like `npx` are batch scripts (`npx.cmd`) on Windows, not
executable binaries
- Rust's `std::process::Command` bypasses the shell and cannot execute
these scripts directly
- The Windows shell normally handles this by checking `PATHEXT` for
executable extensions
Without this fix, Windows users must specify full paths or add `.cmd`
extensions manually, which breaks cross-platform compatibility.
## How?
Added platform-specific program resolution using the `which` crate to
find the correct executable path:
- **Windows**: Resolves programs through PATH/PATHEXT to find
`.cmd`/`.bat` scripts
- **Unix**: Returns the program unchanged (no-op, as Unix handles
scripts natively)
### Changes
- Added `which = "6"` dependency to `mcp-client/Cargo.toml`
- Implemented `program_resolver` module in `mcp_client.rs` with
platform-specific resolution
- Added comprehensive tests for both Windows and Unix behavior
### Testing
Added platform-specific tests to verify:
- Unix systems execute scripts without extensions
- Windows fails without proper extensions
- Windows succeeds with explicit extensions
- Cross-platform resolution enables successful execution
**Tested on:**
- Windows 11 (NT 10.0.26100.0 x64)
- PowerShell 5.1 & 7+, CMD, Git Bash
- MCP servers: playwright, context7, supabase
- WSL (verified no regression)
**Local checks passed:**
```bash
cargo test && cargo clippy --tests && cargo fmt -- --config imports_granularity=Item
```
### Results
**Before:**
```
🖐 MCP client for `playwright` failed to start: program not found
```
**After:**
```
🖐 MCP client for `playwright` failed to start: request timed out
```
Windows users can now use simple commands like `npx` in their config
without specifying full paths or extensions. The timeout issue is a
separate concern that will be addressed in a follow-up PR.
---------
Co-authored-by: Eric Traut <etraut@openai.com>
The Custom Prompts documentation (docs/prompts.md) was incomplete for
named arguments:
1. **Documentation for custom prompts was incomplete** - named argument
usage was mentioned briefly but lacked comprehensive canonical examples
showing proper syntax and behavior.
2. **Fixed by adding canonical, tested syntax and examples:**
- Example 1: Basic named arguments with TICKET_ID and TICKET_TITLE
- Example 2: Mixed positional and named arguments with FILE and FOCUS
- Example 3: Using positional arguments
- Example 4: Updated draftpr example to use proper $FEATURE_NAME syntax
- Added clear usage examples showing KEY=value syntax
- Added expanded prompt examples showing the result
- Documented error handling and validation requirements
3. **Added Implementation Reference section** that references the
relevant feature implementation from the codebase (PRs #4470 and #4474
for initial implementation, #5332 and #5403 for clarifications).
This addresses issue #5039 by providing complete, accurate documentation
for named argument usage in custom prompts.
---------
Co-authored-by: Eric Traut <etraut@openai.com>
This PR does the following:
- Add compact prefix to the summary
- Change the compaction prompt
- Allow multiple compaction for long running tasks
- Filter out summary messages on the following compaction
Considerations:
- Filtering out the summary message isn't the most clean
- Theoretically, we can end up in infinite compaction loop if the user
messages > compaction limit . However, that's not possible in today's
code because we have hard cap on user messages.
- We need to address having multiple user messages because it confuses
the model.
Testing:
- Making sure that after compact we always end up with one user message
(task) and one summary, even on multiple compaction.
Fixes#4940Fixes#4892
When selecting "No, ask me to approve edits and commands" during
onboarding, the code wasn't applying the correct approval policy,
causing Codex to block all write operations instead of requesting
approval.
This PR fixes the issue by persisting the "DontTrust" decision in
config.toml as `trust_level = "untrusted"` and handling it in the
sandbox and approval policy logic, so Codex correctly asks for approval
before making changes.
## Before (bug)
<img width="709" height="500" alt="bef"
src="https://github.com/user-attachments/assets/5aced26d-d810-4754-879a-89d9e4e0073b"
/>
## After (fixed)
<img width="713" height="359" alt="aft"
src="https://github.com/user-attachments/assets/9887bbcb-a9a5-4e54-8e76-9125a782226b"
/>
---------
Co-authored-by: Eric Traut <etraut@openai.com>
For better caching performance all output items should be rendered in
the order they were produced before all new input items (for example,
all function_call before all function_call_output).
For app-server development it's been helpful to be able to trigger some
test flows end-to-end and print the JSON-RPC messages sent between
client and server.
## Summary
- add an `env` option for the TypeScript Codex client and plumb it into
`CodexExec` so the CLI can run without inheriting `process.env`
- extend the test spy to capture spawn environments, add coverage for
the new option, and document how to use it
## Testing
- `pnpm test` *(fails: corepack cannot download pnpm because outbound
network access is blocked in the sandbox)*
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6916b2d7c7548322a72d61d91a2dac85)
## Summary
- default the `tui.notifications` setting to enabled so desktop
notifications work out of the box
- update configuration tests and documentation to reflect the new
default
## Testing
- `cargo test -p codex-core` *(fails:
`exec::tests::kill_child_process_group_kills_grandchildren_on_timeout`
is flaky in this sandbox because the spawned grandchild process stays
alive)*
- `cargo test -p codex-core
exec::tests::kill_child_process_group_kills_grandchildren_on_timeout`
*(fails: same sandbox limitation as above)*
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_69166f811144832c9e8aaf8ee2642373)
## Summary
- add `TestCodex::submit_turn_with_policies` and extend the response
helpers with reusable tool-call utilities
- update the grep_files, read_file, list_dir, shell_serialization, and
tools suites to rely on the shared helpers instead of local copies
- make the list_dir helper return `anyhow::Result` so clippy no longer
warns about `expect`
## Testing
- `just fix -p codex-core`
- `cargo test -p codex-core --test all
suite::grep_files::grep_files_tool_collects_matches`
- `cargo test -p codex-core
suite::grep_files::grep_files_tool_collects_matches -- --ignored`
(filter requests ignored tests so nothing runs, but the build stays
clean)
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_69112d53abac83219813cab4d7cb6446)
## Summary
- replace the bespoke network check in the chat completion payload and
SSE tests with the existing `skip_if_no_network!` helper so they follow
the same gating convention as the rest of the suite
## Testing
- `just fmt`
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_69112d4cb9f08321ba773e8ccf39778e)
A partner is consuming our generated JSON schema bundle for app-server
and identified a few issues:
- not all polymorphic / one-of types have a type descriminator
- `"$ref": "#/definitions/v2/SandboxPolicy"` is missing
- "Option<>" is an invalid schema name, and also unnecessary
This PR:
- adds the type descriminator to the various types that are missing it
except for `SessionSource` and `SubAgentSource` because they are
serialized to disk (adding this would break backwards compat for
resume), and they should not be necessary to consume for an integration
with app-server.
- removes the special handling in `export.rs` of various types like
SandboxPolicy, which turned out to be unnecessary and incorrect
- filters out `Option<>` which was auto-generated for request params
that don't need a body
For context, we currently pull in wayyy more types than we need through
the `EventMsg` god object which we are **not** planning to expose in API
v2 (this is how I suspect `SessionSource` and `SubAgentSource` are being
pulled in). But until we have all the necessary v2 notifications in
place that will allow us to remove `EventMsg`, we will keep exporting it
for now.
core event to app server event mapping:
1. `codex/event/reasoning_content_delta` ->
`item/reasoning/summaryTextDelta`.
2. `codex/event/reasoning_raw_content_delta` ->
`item/reasoning/textDelta`
3. `codex/event/agent_message_content_delta` →
`item/agentMessage/delta`.
4. `codex/event/agent_reasoning_section_break` ->
`item/reasoning/summaryPartAdded`.
Also added a change in core to pass down content index, summary index
and item id from events.
Tested with the `git checkout owen/app_server_test_client && cargo run
-p codex-app-server-test-client -- send-message-v2 "hello"` and verified
that new events are emitted correctly.
## Summary
Consolidates our apply_patch tests into one suite, and ensures each test
case tests the various ways the harness supports apply_patch:
1. Freeform custom tool call
2. JSON function tool
3. Simple shell call
4. Heredoc shell call
There are a few test cases that are specific to a particular variant,
I've left those alone.
## Testing
- [x] This adds a significant number of tests
## Summary
Adds support for specifying additional directories in the TypeScript SDK
through a new `additionalDirectories` option in `ThreadOptions`.
## Changes
- Added `additionalDirectories` parameter to `ThreadOptions` interface
- Updated `CodexExec` to accept and pass through additional directories
via the `--config` flag for `sandbox_workspace_write.writable_roots`
- Added comprehensive test coverage for the new functionality
## Test plan
- Added test case that verifies `additionalDirectories` is correctly
passed as repeated flags
- Existing tests continue to pass
---------
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
Adds AbortSignal support to the TypeScript SDK for canceling thread
execution using AbortController.
## Changes
- Add `signal?: AbortSignal` property to `TurnOptions` type
- Pass signal through Thread class methods to exec layer
- Add signal parameter to `CodexExecArgs`
- Leverage Node.js native `spawn()` signal support for automatic
cancellation
- Add comprehensive test coverage (6 tests covering all abort scenarios)
## Implementation
The implementation uses Node.js's built-in AbortSignal support in
`spawn()` (available since Node v15, SDK requires >=18), which
automatically handles:
- Checking if already aborted before starting
- Killing the child process when abort is triggered
- Emitting appropriate error events
- All cleanup operations
This is a one-line change to the core implementation (`signal:
args.signal` passed to spawn), making it simple, reliable, and
maintainable.
## Usage Example
```typescript
import { Codex } from '@openai/codex-sdk';
const codex = new Codex({ apiKey: 'your-api-key' });
const thread = codex.startThread();
// Create AbortController
const controller = new AbortController();
// Run with abort signal
const resultPromise = thread.run("Your prompt here", {
signal: controller.signal
});
// Cancel anytime
controller.abort('User requested cancellation');
```
## Testing
All tests pass (23 total across SDK):
- ✅ Aborts when signal is already aborted (both run and runStreamed)
- ✅ Aborts during execution/iteration
- ✅ Completes normally when not aborted
- ✅ Backward compatible (signal is optional)
Tests verified to fail correctly when signal support is removed (no
false positives).
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: pakrym-oai <pakrym@openai.com>
Previously the popup measured rows using the full content width while
the renderer drew them with 2 columns of padding, so at certain widths
the layout allocated too little vertical space and hid the third option.
Now both desired_height and render call a shared helper that subtracts
the padding before measuring, so the height we reserve always matches
what we draw and the menu doesn't drops entries.
https://github.com/user-attachments/assets/59058fd9-1e34-4325-b5fe-fc888dfcb6bc
We've received many reports of codex hanging when calling certain tools.
[Here](https://github.com/openai/codex/issues/3204) is one example. This
is likely a major cause. The problem occurs when
`consume_truncated_output` waits for `stdout` and `stderr` to be closed
once the child process terminates. This normally works fine, but it
doesn't handle the case where the child has spawned grandchild processes
that inherits `stdout` and `stderr`.
The fix was originally written by @md-oai in [this
PR](https://github.com/openai/codex/pull/1852), which has gone stale.
I've copied the original fix (which looks sound to me) and added an
integration test to prevent future regressions.
- Introducing a screen to inform users of model changes.
- Config name is being passed to be able to reuse this component in the
future for future models
This updates `thread/resume` to be at parity with v1's
`ResumeConversationParams`. Turns out history is useful for codex cloud
and path is useful for the VSCode extension. And config overrides are
always useful.
This PR addresses https://github.com/openai/codex/issues/6360. The root
problem is that the TUI was directly loading the `auth.json` file to
access the auth information. It should instead be using the AuthManager,
which records the current auth information. The `auth.json` file can be
overwritten at any time by other instances of the CLI or extension, so
its information can be out of sync with the current instance. The
`/status` command should always report the auth information associated
with the current instance.
An alternative fix for this bug was submitted by @chojs23 in [this
PR](https://github.com/openai/codex/pull/6495). That approach was only a
partial fix.
This is important to ensure that this:
```
codex --enable unified_exec
```
and this:
```
codex --config features.unified_exec=true
```
are equivalent. Also that when it is passed programmatically:
807e2c27f0/codex-rs/app-server-protocol/src/protocol/v1.rs (L55)
then this should work for `config`:
```json
{"features": {"shell_command_tool": true}}
```
though I believe also this:
```json
{"features.shell_command_tool": true}
```
This adds support for a new variant of the shell tool behind a flag. To
test, run `codex` with `--enable shell_command_tool`, which will
register the tool with Codex under the name `shell_command` that accepts
the following shape:
```python
{
command: str
workdir: str | None,
timeout_ms: int | None,
with_escalated_permissions: bool | None,
justification: str | None,
}
```
This is comparable to the existing tool registered under
`shell`/`container.exec`. The primary difference is that it accepts
`command` as a `str` instead of a `str[]`. The `shell_command` tool
executes by running `execvp(["bash", "-lc", command])`, though the exact
arguments to `execvp(3)` depend on the user's default shell.
The hypothesis is that this will simplify things for the model. For
example, on Windows, instead of generating:
```json
{"command": ["pwsh.exe", "-NoLogo", "-Command", "ls -Name"]}
```
The model could simply generate:
```json
{"command": "ls -Name"}
```
As part of this change, I extracted some logic out of `user_shell.rs` as
`Shell::derive_exec_args()` so that it can be reused in
`codex-rs/core/src/tools/handlers/shell.rs`. Note the original code
generated exec arg lists like:
```javascript
["bash", "-lc", command]
["zsh", "-lc", command]
["pwsh.exe", "-NoProfile", "-Command", command]
```
Using `-l` for Bash and Zsh, but then specifying `-NoProfile` for
PowerShell seemed inconsistent to me, so I changed this in the new
implementation while also adding a `use_login_shell: bool` option to
make this explicit. If we decide to add a `login: bool` to
`ShellCommandToolCallParams` like we have for unified exec:
807e2c27f0/codex-rs/core/src/tools/handlers/unified_exec.rs (L33-L34)
Then this should make it straightforward to support.
This PR fixes#6522 by correcting the comment for `full-auto` in both
`codex-rs/exec/src/cli.rs` and `codex-rs/tui/src/cli.rs` from `-a
on-failure` to `-a on-request` to make it coherent with
`codex-rs/tui/src/lib.rs:97-105`:
```rust
pub async fn run_main(
mut cli: Cli,
codex_linux_sandbox_exe: Option<PathBuf>,
) -> std::io::Result<AppExitInfo> {
let (sandbox_mode, approval_policy) = if cli.full_auto {
(
Some(SandboxMode::WorkspaceWrite),
Some(AskForApproval::OnRequest),
)
```
Running `just codex --help` or `just codex exec --help` should now yield
the correct description of `full-auto` CLI argument.
Signed-off-by: lionelchg <lionel.cheng@hotmail.fr>
This one should be quite straightforward, as it's just a translation of
TurnItem events we already emit to ThreadItem that app-server exposes to
customers.
To test, cp my change to owen/app_server_test_client and do the
following:
```
cargo build -p codex-cli
RUST_LOG=codex_app_server=info CODEX_BIN=target/debug/codex cargo run -p codex-app-server-test-client -- send-message-v2 "hello"
```
example event before (still kept there for backward compatibility):
```
{
< "method": "codex/event/item_completed",
< "params": {
< "conversationId": "019a74cc-fad9-7ab3-83a3-f42827b7b074",
< "id": "0",
< "msg": {
< "item": {
< "Reasoning": {
< "id": "rs_03d183492e07e20a016913a936eb8c81a1a7671a103fee8afc",
< "raw_content": [],
< "summary_text": [
< "Hey! What would you like to work on? I can explore the repo, run specific tests, or implement a change. Let's keep it short and straightforward. There's no need for a lengthy introduction or elaborate planning, just a friendly greeting and an open offer to help. I want to make sure the user feels welcomed and understood right from the start. It's all about keeping the tone friendly and concise!"
< ]
< }
< },
< "thread_id": "019a74cc-fad9-7ab3-83a3-f42827b7b074",
< "turn_id": "0",
< "type": "item_completed"
< }
< }
< }
```
after (v2):
```
< {
< "method": "item/completed",
< "params": {
< "item": {
< "id": "rs_03d183492e07e20a016913a936eb8c81a1a7671a103fee8afc",
< "text": "Hey! What would you like to work on? I can explore the repo, run specific tests, or implement a change. Let's keep it short and straightforward. There's no need for a lengthy introduction or elaborate planning, just a friendly greeting and an open offer to help. I want to make sure the user feels welcomed and understood right from the start. It's all about keeping the tone friendly and concise!",
< "type": "reasoning"
< }
< }
< }
```
Unified exec isn't working on Linux because we don't provide the correct
arg0.
The library we use for pty management doesn't allow setting arg0
separately from executable. Use the same aliasing strategy we use for
`apply_patch` for `codex-linux-sandbox`.
Use `#[ctor]` hack to dispatch codex-linux-sandbox calls.
Addresses https://github.com/openai/codex/issues/6450
This PR is to unlock future WinGet installation. WinGet struggles to
create command aliases when installing from nested ZIPs on some clients,
so adding raw Windows x64/Arm64 executables lets the manifest use
InstallerType: portable with direct EXEs, which reliably registers the
codex alias. This makes “winget install → codex” work out of the box
without PATH changes.
---------
Co-authored-by: Michael Bolin <mbolin@openai.com>
We already do this for notification definitions and it's really nice.
Verified there are no changes to actual exported files by diff'ing
before and after this change.
- Moved the unix-only suspend/resume logic into a dedicated job_control
module housing SuspendContext, replacing scattered cfg-gated fields and
helpers in tui.rs.
- Tui now holds a single suspend_context (Arc-backed) instead of
multiple atomics, and the event stream uses it directly for Ctrl-Z
handling.
- Added detailed docs around the suspend/resume flow, cursor tracking,
and the Arc/atomic ownership model for the 'static event stream.
- Renamed the process-level SIGTSTP helper to suspend_process and the
cursor tracker to set_cursor_y to better reflect their roles.
This adds a debugging tool for analyzing why certain commands fail to
execute under the sandbox.
Example output:
```
$ codex debug seatbelt --log-denials bash -lc "(echo foo > ~/foo.txt)"
bash: /Users/nornagon/foo.txt: Operation not permitted
=== Sandbox denials ===
(bash) file-write-data /dev/tty
(bash) file-write-data /dev/ttys001
(bash) sysctl-read kern.ngroups
(bash) file-write-create /Users/nornagon/foo.txt
```
It operates by:
1. spawning `log stream` to watch system logs, and
2. tracking all descendant PIDs using kqueue + proc_listchildpids.
this is a "best-effort" technique, as `log stream` may drop logs(?), and
kqueue + proc_listchildpids isn't atomic and can end up missing very
short-lived processes. But it works well enough in my testing to be
useful :)
## Summary
- log and surface clipboard failures instead of silently ignoring them
when `Ctrl+V` pastes an image (`paste_image_to_temp_png()` now feeds an
error history cell)
- enable `arboard`’s `wayland-data-control` feature so native Wayland
sessions can deliver image data without XWayland
- keep the success path unchanged: valid images still attach and show
the `[image …]` placeholder as before
Fixes#4818
---------
Co-authored-by: Eric Traut <etraut@openai.com>
Co-authored-by: Jeremy Rose <172423086+nornagon-openai@users.noreply.github.com>
## Summary
- add a `hide_rate_limit_model_nudge` notice flag plus config edit
plumbing so the rate limit reminder preference is persisted and
documented
- extend the chat widget prompt with a "never show again" option, and
wire new app events so selecting it hides future nudges immediately and
writes the config
- add unit coverage and refresh the snapshot for the three-option prompt
## Testing
- `just fmt`
- `just fix -p codex-tui`
- `just fix -p codex-core`
- `cargo test -p codex-tui`
- `cargo test -p codex-core` *(fails at
`exec::tests::kill_child_process_group_kills_grandchildren_on_timeout`:
grandchild process still alive)*
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6910d7f407748321b2661fc355416994)
Add a `codex generate-json-schema` command for generating a JSON schema
bundle of app-server types, analogous to the existing `codex
generate-ts` command for Typescript.
The CLA action is designed to automatically lock a PR when it is closed.
This preserves the CLA agreement statements, preventing the contributor
from deleting them after the fact. However, this action is currently
locking PRs that are closed without merging. I'd like to keep such PRs
open so the contributor can respond with additional comments. I'm
currently manually unlocking PRs that I close, but I'd like to eliminate
this manual step.
I recently fixed a bug in [this
PR](https://github.com/openai/codex/pull/6285) that prevented Ctrl+C
from dismissing the login menu in the TUI and leaving the user unauthed.
A [user pointed out](https://github.com/openai/codex/issues/6418) that
this makes Ctrl+C can no longer be used to exit the app. This PR changes
the behavior so we exit the app rather than ignoring the Ctrl+C.
Update `codex generate-ts` to use the TS export code from
`app-server-protocol/src/export.rs`.
I realized there were two duplicate implementations of Typescript export
code:
- `app-server-protocol/src/export.rs`
- the `codex-protocol-ts` crate
The `codex-protocol-ts` crate that `codex generate-ts` uses is out of
date now since it doesn't handle the V2 namespace from:
https://github.com/openai/codex/pull/6212.
This is a simplified version of [a
PR](https://github.com/openai/codex/pull/6134) supplied by a community
member.
It updates the docs to reflect a recent config deprecation.
The CLI help text and inline comments incorrectly stated that -c
key=value flag parses values as JSON, when the implementation actually
uses TOML parsing via parse_toml_value(). This caused confusion when
users attempted to configure MCP servers using JSON syntax based on the
documentation.
Changes:
- Updated help text to correctly state TOML parsing instead of JSON
Fixes#4531
Bumps [zeroize](https://github.com/RustCrypto/utils) from 1.8.1 to
1.8.2.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c100874101"><code>c100874</code></a>
zeroize v1.8.2 (<a
href="https://redirect.github.com/RustCrypto/utils/issues/1229">#1229</a>)</li>
<li><a
href="3940ccbebd"><code>3940ccb</code></a>
Switch from <code>doc_auto_cfg</code> to <code>doc_cfg</code> (<a
href="https://redirect.github.com/RustCrypto/utils/issues/1228">#1228</a>)</li>
<li><a
href="c68a5204b2"><code>c68a520</code></a>
Fix Nightly warnings (<a
href="https://redirect.github.com/RustCrypto/utils/issues/1080">#1080</a>)</li>
<li><a
href="b15cc6c1cd"><code>b15cc6c</code></a>
cargo: point <code>repository</code> metadata to clonable URLs (<a
href="https://redirect.github.com/RustCrypto/utils/issues/1079">#1079</a>)</li>
<li><a
href="3db6690f7b"><code>3db6690</code></a>
zeroize: fix <code>homepage</code>/<code>repository</code> in Cargo.toml
(<a
href="https://redirect.github.com/RustCrypto/utils/issues/1076">#1076</a>)</li>
<li>See full diff in <a
href="https://github.com/RustCrypto/utils/compare/zeroize-v1.8.1...zeroize-v1.8.2">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot 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
[taiki-e/install-action](https://github.com/taiki-e/install-action) from
2.60.0 to 2.62.49.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/releases">taiki-e/install-action's
releases</a>.</em></p>
<blockquote>
<h2>2.62.49</h2>
<ul>
<li>
<p>Update <code>cargo-binstall@latest</code> to 1.15.11.</p>
</li>
<li>
<p>Update <code>cargo-auditable@latest</code> to 0.7.2.</p>
</li>
<li>
<p>Update <code>vacuum@latest</code> to 0.20.2.</p>
</li>
</ul>
<h2>2.62.48</h2>
<ul>
<li>
<p>Update <code>mise@latest</code> to 2025.11.3.</p>
</li>
<li>
<p>Update <code>cargo-audit@latest</code> to 0.22.0.</p>
</li>
<li>
<p>Update <code>vacuum@latest</code> to 0.20.1.</p>
</li>
<li>
<p>Update <code>uv@latest</code> to 0.9.8.</p>
</li>
<li>
<p>Update <code>cargo-udeps@latest</code> to 0.1.60.</p>
</li>
<li>
<p>Update <code>zizmor@latest</code> to 1.16.3.</p>
</li>
</ul>
<h2>2.62.47</h2>
<ul>
<li>
<p>Update <code>vacuum@latest</code> to 0.20.0.</p>
</li>
<li>
<p>Update <code>cargo-nextest@latest</code> to 0.9.111.</p>
</li>
<li>
<p>Update <code>cargo-shear@latest</code> to 1.6.2.</p>
</li>
</ul>
<h2>2.62.46</h2>
<ul>
<li>
<p>Update <code>vacuum@latest</code> to 0.19.5.</p>
</li>
<li>
<p>Update <code>syft@latest</code> to 1.37.0.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2025.11.2.</p>
</li>
<li>
<p>Update <code>knope@latest</code> to 0.21.5.</p>
</li>
</ul>
<h2>2.62.45</h2>
<ul>
<li>
<p>Update <code>zizmor@latest</code> to 1.16.2.</p>
</li>
<li>
<p>Update <code>cargo-binstall@latest</code> to 1.15.10.</p>
</li>
<li>
<p>Update <code>ubi@latest</code> to 0.8.4.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2025.11.1.</p>
</li>
<li>
<p>Update <code>cargo-semver-checks@latest</code> to 0.45.0.</p>
</li>
</ul>
<h2>2.62.44</h2>
<ul>
<li>Update <code>mise@latest</code> to 2025.11.0.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md">taiki-e/install-action's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<p>All notable changes to this project will be documented in this
file.</p>
<p>This project adheres to <a href="https://semver.org">Semantic
Versioning</a>.</p>
<!-- raw HTML omitted -->
<h2>[Unreleased]</h2>
<h2>[2.62.49] - 2025-11-09</h2>
<ul>
<li>
<p>Update <code>cargo-binstall@latest</code> to 1.15.11.</p>
</li>
<li>
<p>Update <code>cargo-auditable@latest</code> to 0.7.2.</p>
</li>
<li>
<p>Update <code>vacuum@latest</code> to 0.20.2.</p>
</li>
</ul>
<h2>[2.62.48] - 2025-11-08</h2>
<ul>
<li>
<p>Update <code>mise@latest</code> to 2025.11.3.</p>
</li>
<li>
<p>Update <code>cargo-audit@latest</code> to 0.22.0.</p>
</li>
<li>
<p>Update <code>vacuum@latest</code> to 0.20.1.</p>
</li>
<li>
<p>Update <code>uv@latest</code> to 0.9.8.</p>
</li>
<li>
<p>Update <code>cargo-udeps@latest</code> to 0.1.60.</p>
</li>
<li>
<p>Update <code>zizmor@latest</code> to 1.16.3.</p>
</li>
</ul>
<h2>[2.62.47] - 2025-11-05</h2>
<ul>
<li>
<p>Update <code>vacuum@latest</code> to 0.20.0.</p>
</li>
<li>
<p>Update <code>cargo-nextest@latest</code> to 0.9.111.</p>
</li>
<li>
<p>Update <code>cargo-shear@latest</code> to 1.6.2.</p>
</li>
</ul>
<h2>[2.62.46] - 2025-11-04</h2>
<ul>
<li>
<p>Update <code>vacuum@latest</code> to 0.19.5.</p>
</li>
<li>
<p>Update <code>syft@latest</code> to 1.37.0.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2025.11.2.</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="44c6d64aa6"><code>44c6d64</code></a>
Release 2.62.49</li>
<li><a
href="3a701df4c2"><code>3a701df</code></a>
Update <code>cargo-binstall@latest</code> to 1.15.11</li>
<li><a
href="4242e04eb8"><code>4242e04</code></a>
Update <code>cargo-auditable@latest</code> to 0.7.2</li>
<li><a
href="3df5533ef8"><code>3df5533</code></a>
Update <code>vacuum@latest</code> to 0.20.2</li>
<li><a
href="e797ba6a25"><code>e797ba6</code></a>
Release 2.62.48</li>
<li><a
href="bcf91e02ac"><code>bcf91e0</code></a>
Update <code>mise@latest</code> to 2025.11.3</li>
<li><a
href="e78113b60c"><code>e78113b</code></a>
Update <code>cargo-audit@latest</code> to 0.22.0</li>
<li><a
href="0ef486444e"><code>0ef4864</code></a>
Update <code>vacuum@latest</code> to 0.20.1</li>
<li><a
href="5eda7b1985"><code>5eda7b1</code></a>
Update <code>uv@latest</code> to 0.9.8</li>
<li><a
href="3853a413e6"><code>3853a41</code></a>
Update <code>cargo-udeps@latest</code> to 0.1.60</li>
<li>Additional commits viewable in <a
href="0c5db7f7f8...44c6d64aa6">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>
On Windows, `npm` by itself does not resolve under std::process::Command
which does not consider PATHEXT to resolve it to `npm.cmd` in the PATH.
By running the npm upgrade command via cmd.exe we get proper path
semantics so it actually works.
## Problem
`codex cloud` always instantiated `AuthManager` with `File` mode,
ignoring the user's actual `cli_auth_credentials_store` setting. This
caused users with `cli_auth_credentials_store = "keyring"` (or `"auto"`)
to see "Not signed in" errors even when they had valid credentials
stored in the system keyring.
## Root cause
The code called `Config::load_from_base_config_with_overrides()` with an
empty `ConfigToml::default()`, which always returned `File` as the
default store mode instead of loading the actual user configuration.
## Solution
- **Added `util::load_cli_auth_manager()` helper**
Properly loads user config via
`load_config_as_toml_with_cli_overrides()` and extracts the
`cli_auth_credentials_store` setting before creating `AuthManager`.
- **Updated callers**
- `init_backend()` - used when starting cloud tasks UI
- `build_chatgpt_headers()` - used for API requests
## Testing
- ✅ `just fmt`
- ✅ `just fix -p codex-cloud-tasks`
- ✅ `cargo test -p codex-cloud-tasks`
## Files changed
- `codex-rs/cloud-tasks/src/lib.rs`
- `codex-rs/cloud-tasks/src/util.rs`
## Verification
Users with keyring-based auth can now run `codex cloud` successfully
without "Not signed in" errors.
---------
Co-authored-by: Eric Traut <etraut@openai.com>
Co-authored-by: celia-oai <celia@openai.com>
The TypeScript SDK's README incorrectly claimed that runStreamed() emits
"file diffs". However, the FileChangeItem type only contains metadata
(path, kind, status) without actual diff content.
Updated line 36 to accurately describe the SDK as providing "file change
notifications" instead of "file diffs" to match the actual
implementation in items.ts.
Fixes#5850
3 improvements:
1. show up to 3 actual paths that are world-writable
2. do the scan/warning for Read-Only mode too, because it also applies
there
3. remove the "Cancel" option since it doesn't always apply (like on
startup)
Shows single-key shortcuts (y, a, n) next to approval options to make
them more discoverable. Previously these shortcuts worked but were
hidden, making the feature hard to discover.
Changes:
- "Yes, proceed" now shows "y" shortcut
- "Yes, and don't ask again" now shows "a" shortcut
- "No, and tell Codex..." continues to show "esc" shortcut
This improves UX by surfacing the quick keyboard shortcuts that were
already functional but undiscoverable in the UI.
---
Update:
added parentheses for better visual clarity
<img width="1540" height="486" alt="CleanShot 2025-11-05 at 11 47 07@2x"
src="https://github.com/user-attachments/assets/f951c34a-9ec8-4b81-b151-7b2ccba94658"
/>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Eric Traut <etraut@openai.com>
1. scan many more directories since it's much faster than the original
implementation
2. limit overall scan time to 2s
3. skip some directories that are noisy - ApplicationData, Installer,
etc.
## Summary
- launch shell tool processes in their own process group so Codex owns
the full tree
- on timeout or ctrl-c, send SIGKILL to the process group before
terminating the tracked child
- document that the default shell/unified_exec timeout remains 1000 ms
## Original Bug
Long-lived shell tool commands hang indefinitely because the timeout
handler only terminated the direct child process; any grandchildren it
spawned kept running and held the PTY open, preventing Codex from
regaining control.
## Repro Original Bug
Install next.js and run `next dev` (which is a long-running shell
process with children). On openai:main, it will cause the agent to
permanently get stuck here until human intervention. On this branch,
this command will be terminated successfully after timeout_ms which will
unblock the agent. This is a critical fix for unmonitored / lightly
monitored agents that don't have immediate human observation to unblock
them.
---------
Co-authored-by: Michael Bolin <mbolin@openai.com>
Co-authored-by: Michael Bolin <bolinfest@gmail.com>
### Motivation
When Codex is launched from a region where Cloudflare blocks access (for
example, Russia), the CLI currently dumps Cloudflare’s entire HTML error
page. This isn’t actionable and makes it hard for users to understand
what happened. We want to detect the Cloudflare block and show a
concise, user-friendly explanation instead.
### What Changed
- Added CLOUDFLARE_BLOCKED_MESSAGE and a friendly_message() helper to
UnexpectedResponseError. Whenever we see a 403 whose body contains the
Cloudflare block notice, we now emit a single-line message (Access
blocked by Cloudflare…) while preserving the HTTP status and request id.
All other responses keep the original behaviour.
- Added two focused unit tests:
- unexpected_status_cloudflare_html_is_simplified ensures the Cloudflare
HTML case yields the friendly message.
- unexpected_status_non_html_is_unchanged confirms plain-text 403s still
return the raw body.
### Testing
- cargo build -p codex-cli
- cargo test -p codex-core
- just fix -p codex-core
- cargo test --all-features
---------
Co-authored-by: Eric Traut <etraut@openai.com>
Removes flush logic that was leftover to test against ratatui's flush
Cleaned up the flush logic so it's a bit more intent revealing.
DrawCommand now owns the Cells that it draws as this works around a
borrow checker problem.
When running under WSL, the update command could receive Windows-style
absolute paths (e.g., `C:\...`) and pass them to Linux processes
unchanged, which fails because WSL expects those paths in
`/mnt/<drive>/...` form.
This patch adds a tiny helper in the CLI (`cli/src/wsl_paths.rs`) that:
- Detects WSL (`WSL_DISTRO_NAME` or `"microsoft"` in `/proc/version`)
- Converts `X:\...` → `/mnt/x/...`
`run_update_action` now normalizes the package-manager command and
arguments under WSL before spawning.
Non-WSL platforms are unaffected.
Includes small unit tests for the converter.
**Fixes:** #6086, #6084
Co-authored-by: Eric Traut <etraut@openai.com>
## Summary
This PR adds two new optional boolean fields to `ThreadOptions` in the
TypeScript SDK:
- **`networkAccess`**: Enables network access in the sandbox by setting
`sandbox_workspace_write.network_access` config
- **`webSearch`**: Enables the web search tool by setting
`tools.web_search` config
These options map to existing Codex configuration options and are
properly threaded through the SDK layers:
1. `ThreadOptions` (threadOptions.ts) - User-facing API
2. `CodexExecArgs` (exec.ts) - Internal execution args
3. CLI flags via `--config` in the `codex exec` command
## Changes
- `sdk/typescript/src/threadOptions.ts`: Added `networkAccess` and
`webSearch` fields to `ThreadOptions` type
- `sdk/typescript/src/exec.ts`: Added fields to `CodexExecArgs` and CLI
flag generation
- `sdk/typescript/src/thread.ts`: Pass options through to exec layer
## Test Plan
- [x] Build succeeds (`pnpm build`)
- [x] Linter passes (`pnpm lint`)
- [x] Type definitions are properly exported
- [ ] Manual testing with sample code (to be done by reviewer)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Historically, running `create_github_release --publish-release` would
always publish a new release from latest `main`, which isn't always the
best idea. We should really publish an alpha, let it bake, and then
promote it.
This PR introduces a new flag, `--promote-alpha`, which does exactly
that. It also works with `--dry-run`, so you can sanity check the commit
it will use as the base commit for the new release before running it for
real.
```shell
$ ./codex-rs/scripts/create_github_release --dry-run --promote-alpha 0.56.0-alpha.2
Publishing version 0.56.0
Running gh api GET /repos/openai/codex/git/refs/tags/rust-v0.56.0-alpha.2
Running gh api GET /repos/openai/codex/git/tags/7d4ef77bc35b011aa0c76c5cbe6cd7d3e53f1dfe
Running gh api GET /repos/openai/codex/compare/main...8b49211e67d3c863df5ecc13fc5f88516a20fa69
Would publish version 0.56.0 using base commit 62474a30e8 derived from rust-v0.56.0-alpha.2.
```
Add the following fields to Thread:
```
pub preview: String,
pub model_provider: String,
pub created_at: i64,
```
Will prob need another PR once this lands:
https://github.com/openai/codex/pull/6337
This PR does two things:
1. add a new function in core that maps the core-internal plan type to
the external plan type;
2. implement account/read that get account status (v2 of
`getAuthStatus`).
Fixes https://github.com/openai/codex/issues/5485.
Fixed rename hunks so `apply_patch` resolves the destination path using
the verifier’s effective cwd, ensuring patches that run under `cd
<worktree> && apply_patch` stay inside the worktree.
Added a regression test
(`test_apply_patch_resolves_move_path_with_effective_cwd`) that
reproduced the old behavior (dest path resolved in the main repo) and
now passes.
Related to https://github.com/openai/codex/issues/5483.
Co-authored-by: Eric Traut <etraut@openai.com>
This PR updates the AI prompt used for the workflow that adds automated
labels to incoming issues. I've been updating and refining the list of
labels as I work through the issue backlog, and the old prompt was
becoming somewhat outdated.
This PR makes an "insufficient quota" error fatal so we don't attempt to
retry it multiple times in the agent loop.
We have multiple bug reports from users about intermittent retry
behaviors, and this could explain some of them. With this change, we'll
eliminate the retries and surface a clear error message.
The PR is a nearly identical copy of [this
PR](https://github.com/openai/codex/pull/4837) contributed by
@abimaelmartell. The original PR has gone stale. Rather than wait for
the contributor to resolve merge conflicts, I wanted to get this change
in.
This allows `gh api` to work in the workspace-write sandbox w/ network
enabled. Without this we see e.g.
```
$ codex debug seatbelt --full-auto gh api repos/openai/codex/pulls --paginate -X GET -F state=all
Get "https://api.github.com/repos/openai/codex/pulls?per_page=100&state=all": tls: failed to verify certificate: x509: OSStatus -26276
```
Some PRs are being submitted without reference to existing bug reports
or feature requests. This updates the PR template and contributing
guidelines to request that all PRs from the community contain such a
link. This provides additional context and helps prioritize, track, and
assess PRs.
Show a warning when Auto Sandbox mode becomes enabled, if we detect
Everyone-writable directories, since they cannot be protected by the
current implementation of the Sandbox.
This PR also includes changes to how we detect Everyone-writable to be
*much* faster
Implements:
```
turn/start
turn/interrupt
```
along with their integration tests. These are relatively light wrappers
around the existing core logic, and changes to core logic are minimal.
However, an improvement made for developer ergonomics:
- `turn/start` replaces both `SendUserMessage` (no turn overrides) and
`SendUserTurn` (can override model, approval policy, etc.)
turns out the ToC was including itself when generating, which messed up
comparisons and sometimes made the file rewrite endlessly.
also fixed the slice so `<!-- End ToC -->` doesn’t get duplicated when
we insert the new ToC.
should behave nicely now - no extra rewrites, no doubled markers.
Co-authored-by: Eric Traut <etraut@openai.com>
We currently allow the user to dismiss the login menu via Ctrl+C. This
leaves them in a bad state where they're not auth'ed but have an input
prompt. In the extension, this isn't a problem because we don't allow
the user to dismiss the login screen.
Testing: I confirmed that Ctrl+C no longer dismisses the login menu.
This is an alternative (simpler) fix for a [community
PR](https://github.com/openai/codex/pull/3234).
This PR implements `account/login/start` and `account/login/completed`.
Instead of having separate endpoints for login with chatgpt and api, we
have a single enum handling different login methods. For sync auth
methods like sign in with api key, we still send a `completed`
notification back to be compatible with the async login flow.
I'm seeing two tests fail intermittently in CI. This PR attempts to
address (or at least mitigate) the flakiness.
* summarize_context_three_requests_and_instructions - The test snapshots
server.received_requests() immediately after observing TaskComplete.
Because the OpenAI /v1/responses call is streamed, the HTTP request can
still be draining when that event fires, so wiremock occasionally
reports only two captured requests. Fix is to wait for async activity to
complete.
* archive_conversation_moves_rollout_into_archived_directory - times out
on a slow CI run. Mitigation is to increase timeout value from 10s to
20s.
Implements:
```
thread/list
thread/start
thread/resume
thread/archive
```
along with their integration tests. These are relatively light wrappers
around the existing core logic, and changes to core logic are minimal.
However, an improvement made for developer ergonomics:
- `thread/start` and `thread/resume` automatically attaches a
conversation listener internally, so clients don't have to make a
separate `AddConversationListener` call like they do today.
For consistency, also updated `model/list` and `feedback/upload` (naming
conventions, list API params).
Currently, when the access token expires, we attempt to use the refresh
token to acquire a new access token. This works most of the time.
However, there are situations where the refresh token is expired,
exhausted (already used to perform a refresh), or revoked. In those
cases, the current logic treats the error as transient and attempts to
retry it repeatedly.
This PR changes the token refresh logic to differentiate between
permanent and transient errors. It also changes callers to treat the
permanent errors as fatal rather than retrying them. And it provides
better error messages to users so they understand how to address the
problem. These error messages should also help us further understand why
we're seeing examples of refresh token exhaustion.
Here is the error message in the CLI. The same text appears within the
extension.
<img width="863" height="38" alt="image"
src="https://github.com/user-attachments/assets/7ffc0d08-ebf0-4900-b9a9-265064202f4f"
/>
I also correct the spelling of "Re-connecting", which shouldn't have a
hyphen in it.
Testing: I manually tested these code paths by adding temporary code to
programmatically cause my refresh token to be exhausted (by calling the
token refresh endpoint in a tight loop more than 50 times). I then
simulated an access token expiration, which caused the token refresh
logic to be invoked. I confirmed that the updated logic properly handled
the error condition.
Note: We earlier discussed the idea of forcefully logging out the user
at the point where token refresh failed. I made several attempts to do
this, and all of them resulted in a bad UX. It's important to surface
this error to users in a way that explains the problem and tells them
that they need to log in again. We also previously discussed deleting
the auth.json file when this condition is detected. That also creates
problems because it effectively changes the auth status from logged in
to logged out, and this causes odd failures and inconsistent UX. I think
it's therefore better not to delete auth.json in this case. If the user
closes the CLI or VSCE and starts it again, we properly detect that the
access token is expired and the refresh token is "dead", and we force
the user to go through the login flow at that time.
This should address aspects of #6191, #5679, and #5505
This is just a refactor of `conversation_history` file by breaking it up
into multiple smaller ones with helper. This refactor will help us move
more functionality related to context management here. in a clean way.
- introduce RenderableItem to support both owned and borrowed children
in composite Renderables
- refactor some of our gnarlier manual layouts, BottomPane and
ChatWidget, to use ColumnRenderable
- Renderable and friends now handle cursor_pos()
## Summary
- Adds `ModelReasoningEffort` type to TypeScript SDK with values:
`minimal`, `low`, `medium`, `high`
- Adds `modelReasoningEffort` option to `ThreadOptions`
- Forwards the option to the codex CLI via `--config
model_reasoning_effort="<value>"`
- Includes test coverage for the new option
## Changes
- `sdk/typescript/src/threadOptions.ts`: Define `ModelReasoningEffort`
type and add to `ThreadOptions`
- `sdk/typescript/src/index.ts`: Export `ModelReasoningEffort` type
- `sdk/typescript/src/exec.ts`: Forward `modelReasoningEffort` to CLI as
config flag
- `sdk/typescript/src/thread.ts`: Pass option through to exec (+ debug
logging)
- `sdk/typescript/tests/run.test.ts`: Add test for
`modelReasoningEffort` flag forwarding
---------
Co-authored-by: Eric Traut <etraut@openai.com>
Previously it was not possible for codex to run commands as the init
process (pid 1) in linux. Commands run in containers tend to see their
own pid as 1. See https://github.com/openai/codex/issues/4198
This pr implements the solution mentioned in that issue.
Co-authored-by: Eric Traut <etraut@openai.com>
Previously, the `nix build .#default` command fails due to a missing
output hash in the `./codex-rs/default.nix` for `crossterm-0.28.1`:
```
error: No hash was found while vendoring the git dependency crossterm-0.28.1. You can add
a hash through the `outputHashes` argument of `importCargoLock`:
outputHashes = {
"crossterm-0.28.1" = "<hash>";
};
If you use `buildRustPackage`, you can add this attribute to the `cargoLock`
attribute set.
```
This PR adds the missing hash:
```diff
cargoLock.outputHashes = {
"ratatui-0.29.0" = "sha256-HBvT5c8GsiCxMffNjJGLmHnvG77A6cqEL+1ARurBXho=";
+ "crossterm-0.28.1" = "sha256-6qCtfSMuXACKFb9ATID39XyFDIEMFDmbx6SSmNe+728=";
};
```
With this change, `nix build .#default` succeeds:
```
> nix build .#default --max-jobs 1 --cores 2
warning: Git tree '/home/lukas/r/github.com/lukasl-dev/codex' is dirty
[1/0/1 built] building codex-rs-0.1.0 (buildPhase)[1/0/1 built] building codex-rs-0.1.0 (buildP[1/0/1 built] building codex-rs-0.1.0 (buildPhase): [1/0/1 built] building codex-rs-0.1.0 (b[1/0/1 built] building codex-rs-0.1.0 (buildPhase): Compi[1/0/1 built] building codex-rs-0.1
> ./result/bin/codex
You are running Codex in /home/lukas/r/github.com/lukasl-dev/codex
Since this folder is version controlled, you may wish to allow Codex to work in this folder without asking for approval.
...
```
**Typescript and JSON schema exports**
While working on Thread/Turn/Items type definitions, I realize we will
run into name conflicts between v1 and v2 APIs (e.g. `RateLimitWindow`
which won't be reusable since v1 uses `RateLimitWindow` from `protocol/`
which uses snake_case, but we want to expose camelCase everywhere, so
we'll define a V2 version of that struct that serializes as camelCase).
To set us up for a clean and isolated v2 API, generate types into a
`v2/` namespace for both typescript and JSON schema.
- TypeScript: v2 types emit under `out_dir/v2/*.ts`, and root index.ts
now re-exports them via `export * as v2 from "./v2"`;.
- JSON Schemas: v2 definitions bundle under `#/definitions/v2/*` rather
than the root.
The location for the original types (v1 and types pulled from
`protocol/` and other core crates) haven't changed and are still at the
root. This is for backwards compatibility: no breaking changes to
existing usages of v1 APIs and types.
**Notifications**
While working on export.rs, I:
- refactored server/client notifications with macros (like we already do
for methods) so they also get exported (I noticed they weren't being
exported at all).
- removed the hardcoded list of types to export as JSON schema by
leveraging the existing macros instead
- and took a stab at API V2 notifications. These aren't wired up yet,
and I expect to iterate on these this week.
The deprecation message is currently a bit confusing. Users may not
understand what is `[features].x`. I updated the docs and the
deprecation message for more guidance.
---------
Co-authored-by: Gabriel Peal <gpeal@users.noreply.github.com>
## Summary
musl 1.2.5 includes [several fixes to DNS over
TCP](https://www.openwall.com/lists/musl/2024/03/01/2), which appears to
be the root cause of #6116.
This approach is a bit janky, but according to codex:
> On the Ubuntu 24.04 runners we use, apt-cache policy musl-tools shows
only the distro build (1.2.4-2ubuntu2)"
We should build with this version and confirm.
## Testing
- [ ] TODO: test and see if this fixes Azure issues
V2 for `account/updated` and `account/logout` for app server. correspond
to old `authStatusChange` and `LogoutChatGpt` respectively. Followup PRs
will make other v2 endpoints call `account/updated` instead of
`authStatusChange` too.
## Problem
The `is_api_message` function in `conversation_history.rs` had a
misalignment between its documentation and implementation:
- **Comment stated**: "Anything that is not a system message or
'reasoning' message is considered an API message"
- **Code behavior**: Was returning `true` for `ResponseItem::Reasoning`,
meaning reasoning messages were incorrectly treated as API messages
This inconsistency could lead to reasoning messages being persisted in
conversation history when they should be filtered out.
## Root Cause
Investigation revealed that reasoning messages are explicitly excluded
throughout the codebase:
1. **Chat completions API** (lines 267-272 in `chat_completions.rs`)
omits reasoning from conversation history:
```rust
ResponseItem::Reasoning { .. } | ResponseItem::Other => {
// Omit these items from the conversation history.
continue;
}
```
2. **Existing tests** like `drops_reasoning_when_last_role_is_user` and
`ignores_reasoning_before_last_user` validate that reasoning should be
excluded from API payloads
## Solution
Fixed the `is_api_message` function to align with its documentation and
the rest of the codebase:
```rust
// Before: Reasoning was incorrectly returning true
ResponseItem::Reasoning { .. } | ResponseItem::WebSearchCall { .. } => true,
// After: Reasoning correctly returns false
ResponseItem::WebSearchCall { .. } => true,
ResponseItem::Reasoning { .. } | ResponseItem::Other => false,
```
## Testing
- Enhanced existing test to verify reasoning messages are properly
filtered out
- All 264 core tests pass, including 8 chat completions tests that
validate reasoning behavior
- No regressions introduced
This ensures reasoning messages are consistently excluded from API
message processing across the entire codebase.
I have read the CLA Document and I hereby sign the CLA
Closes#4452
This fixes a usability issue where users with symlinked folders in their
working directory couldn't search those files using the `@` file search
feature.
## Rationale
The "bug" was in the file search implementation in
`codex-rs/file-search/src/lib.rs`. The `WalkBuilder` was using default
settings which don't follow symlinks, causing two related issues:
1. Partial search results: The `@` search would find symlinked
directories but couldn't find files inside them
2. Inconsistent behavior: Users expect symlinked folders to behave like
regular folders in search results.
## Root cause
The `ignore` crate's `WalkBuilder` defaults to `.follow_links(false)`
[[source](9802945e63/crates/ignore/src/walk.rs (L532))],
so when traversing the file system, it would:
- Detect symlinked directories as directory entries
- But not traverse into them to index their contents
- The `get_file_path` function would then filter out actual directories,
leaving only the symlinked folder itself as a result
Fix: Added `.follow_links(true)` to the `WalkBuilder` configuration,
making the file search follow symlinks and index their contents just
like regular directories.
This change maintains backward compatibility since symlink following is
generally expected behavior for file search tools, and it aligns with
how users expect the `@` search feature to work.
Co-authored-by: Eric Traut <etraut@openai.com>
I was missing an example config.toml, and following docs/config.md alone
was slower. I had GPT-5 scan the codebase for every accepted config key,
check the defaults, and generate a single example config.toml with
annotations. It lists all keys Codex reads from TOML, sets each to its
effective default where it exists, leaves optional ones commented, and
adds short comments on purpose and valid values. This should make
onboarding faster and reduce configuration errors. I can rename it to
config.example.toml or move it under docs/ if you prefer.
This fixes bug #6121.
The `input_messages` field passed to the notify handler is currently
empty because the logic is incorrectly including the OutputText rather
than InputText. I've fixed that and added proper filtering to remove
messages associated with AGENTS.md and other context injected by the
harness.
Testing: I wrote a notify handler and verified that the user prompt is
correctly passed through to the handler.
## Summary
- replace the word part enum with a simple `is_word_separator` helper
- keep word-boundary logic aligned with the helper and punctuation-aware
behavior
- extend forward/backward deletion tests to cover whitespace around
separators
## Testing
- just fix -p codex-tui
- cargo test -p codex-tui
------
https://chatgpt.com/codex/tasks/task_i_68f91c71d838832ca2a3c4f0ec1b55d4
This value is used to determine whether mid-turn compaction is required.
Reasoning items are only excluded between turns (and soon will start to
be preserved even across turns) so it's incorrect to subtract
reasoning_output_tokens mid term.
This will result in higher values reported between turns but we are also
looking into preserving reasoning items for the entire conversation to
improve performance and caching.
Error message for attempting to OAuth with a remote RCP is incorrect and
misleading. The correct config is
```
[features]
rmcp_client = true
```
Co-authored-by: Eric Traut <etraut@openai.com>
This pull request adds a new documentation section to explain the
available slash commands in Codex. The update introduces a clear
overview and a reference table for built-in commands, making it easier
for users to understand and utilize these features.
Documentation updates:
* Added a new section to `docs/slash_commands.md` describing what slash
commands are and listing all built-in commands with their purposes in a
formatted table.
Hi OpenAI Codex team, currently "Visit chatgpt.com/codex/settings/usage
for up-to-date information on rate limits and credits" message in status
card and error messages. For now, without the "https://" prefix, the
link cannot be clicked directly from most terminals or chat interfaces.
<img width="636" height="127" alt="Screenshot 2025-11-02 at 22 47 06"
src="https://github.com/user-attachments/assets/5ea11e8b-fb74-451c-85dc-f4d492b2678b"
/>
---
The fix is intent to improve this issue:
- It makes the link clickable in terminals that support it, hence better
accessibility
- It follows standard URL formatting practices
- It maintains consistency with other links in the application (like the
existing "https://openai.com/chatgpt/pricing" links)
Thank you!
Addresses issue https://github.com/openai/codex/issues/3582 where an
"archive conversation" command in the extension fails on Windows.
The problem is that the `archive_conversation` api server call is not
canonicalizing the path to the rollout path when performing its check to
verify that the rollout path is in the sessions directory. This causes
it to fail 100% of the time on Windows.
Testing: I was able to repro the error on Windows 100% prior to this
change. After the change, I'm no longer able to repro.
When I enable `experimental_sandbox_command_assessment`, I get an
incorrect deprecation warning: "experimental_sandbox_command_assessment
is deprecated. Use experimental_sandbox_command_assessment instead."
This PR fixes this error.
* Removed sandbox risk categories; feedback indicates that these are not
that useful and "less is more"
* Tweaked the assessment prompt to generate terser answers
* Fixed bug in orchestrator that prevents this feature from being
exposed in the extension
Fixes#4161
Currently Codex uses a regex to parse the "Please try again in 1.898s"
OpenAI-style rate limit message, so that it can wait the correct
duration before retrying. Azure OpenAI returns a different error that
looks like "Rate limit exceeded. Try again in 35 seconds."
This PR extends the regex and parsing code to match in a more fuzzy
manner, handling anything matching the pattern "try again in
\<duration>\<unit>".
## Summary
This PR fixes a broken self-referencing link in the contributing
documentation.
## Changes
- Removed the phrase 'Following the [development
setup](#development-workflow) instructions above' from the Development
workflow section
- The link referenced a non-existent section and the phrase didn't make
logical sense in context
## Before
The text referenced 'development setup instructions above' but:
1. No section called 'development setup' exists
2. There were no instructions 'above' that point
3. The link pointed to the same section it was in
## After
Simplified to: 'Ensure your change is free of lint warnings and test
failures.'
## Type
Documentation fix
I have read the CLA Document and I hereby sign the CLA
Co-authored-by: Ritesh Chauhan <sagar.chauhn11@gmail.com>
## Summary
Can never have enough tests on this code path - checking that json
inside a shell call is deserialized correctly.
## Tests
- [x] These are tests 😎
I finished reading “Getting Started,” but couldn’t find the
“Configuration” section in the README. After following the link, I
realized “Configuration” is in a separate file, so I updated the README
accordingly.
# 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.
Co-authored-by: Eric Traut <etraut@openai.com>
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.10.0 to
2.11.4.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md">indexmap's
changelog</a>.</em></p>
<blockquote>
<h2>2.11.4 (2025-09-18)</h2>
<ul>
<li>Updated the <code>hashbrown</code> dependency to a range allowing
0.15 or 0.16.</li>
</ul>
<h2>2.11.3 (2025-09-15)</h2>
<ul>
<li>Make the minimum <code>serde</code> version only apply when
"serde" is enabled.</li>
</ul>
<h2>2.11.2 (2025-09-15)</h2>
<ul>
<li>Switched the "serde" feature to depend on
<code>serde_core</code>, improving build
parallelism in cases where other dependents have enabled
"serde/derive".</li>
</ul>
<h2>2.11.1 (2025-09-08)</h2>
<ul>
<li>Added a <code>get_key_value_mut</code> method to
<code>IndexMap</code>.</li>
<li>Removed the unnecessary <code>Ord</code> bound on
<code>insert_sorted_by</code> methods.</li>
</ul>
<h2>2.11.0 (2025-08-22)</h2>
<ul>
<li>Added <code>insert_sorted_by</code> and
<code>insert_sorted_by_key</code> methods to <code>IndexMap</code>,
<code>IndexSet</code>, and <code>VacantEntry</code>, like customizable
versions of <code>insert_sorted</code>.</li>
<li>Added <code>is_sorted</code>, <code>is_sorted_by</code>, and
<code>is_sorted_by_key</code> methods to
<code>IndexMap</code> and <code>IndexSet</code>, as well as their
<code>Slice</code> counterparts.</li>
<li>Added <code>sort_by_key</code> and <code>sort_unstable_by_key</code>
methods to <code>IndexMap</code> and
<code>IndexSet</code>, as well as parallel counterparts.</li>
<li>Added <code>replace_index</code> methods to <code>IndexMap</code>,
<code>IndexSet</code>, and <code>VacantEntry</code>
to replace the key (or set value) at a given index.</li>
<li>Added optional <code>sval</code> serialization support.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="03f9e58626"><code>03f9e58</code></a>
Merge pull request <a
href="https://redirect.github.com/indexmap-rs/indexmap/issues/418">#418</a>
from a1phyr/hashbrown_0.16</li>
<li><a
href="ee6080d480"><code>ee6080d</code></a>
Release 2.11.4</li>
<li><a
href="a7da8f181e"><code>a7da8f1</code></a>
Use a range for hashbrown</li>
<li><a
href="0cd5aefb44"><code>0cd5aef</code></a>
Update <code>hashbrown</code> to 0.16</li>
<li><a
href="fd5c819daf"><code>fd5c819</code></a>
Merge pull request <a
href="https://redirect.github.com/indexmap-rs/indexmap/issues/417">#417</a>
from cuviper/release-2.11.3</li>
<li><a
href="9321145e1f"><code>9321145</code></a>
Release 2.11.3</li>
<li><a
href="7b485688c2"><code>7b48568</code></a>
Merge pull request <a
href="https://redirect.github.com/indexmap-rs/indexmap/issues/416">#416</a>
from cuviper/release-2.11.2</li>
<li><a
href="49ce7fa471"><code>49ce7fa</code></a>
Release 2.11.2</li>
<li><a
href="58fd834804"><code>58fd834</code></a>
Merge pull request <a
href="https://redirect.github.com/indexmap-rs/indexmap/issues/414">#414</a>
from DaniPopes/serde_core</li>
<li><a
href="5dc1d6ab31"><code>5dc1d6a</code></a>
Depend on <code>serde_core</code> instead of <code>serde</code></li>
<li>Additional commits viewable in <a
href="https://github.com/indexmap-rs/indexmap/compare/2.10.0...2.11.4">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>
Co-authored-by: Eric Traut <etraut@openai.com>
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.99 to
1.0.100.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/anyhow/releases">anyhow's
releases</a>.</em></p>
<blockquote>
<h2>1.0.100</h2>
<ul>
<li>Teach clippy to lint formatting arguments in <code>bail!</code>,
<code>ensure!</code>, <code>anyhow!</code> (<a
href="https://redirect.github.com/dtolnay/anyhow/issues/426">#426</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="18c2598afa"><code>18c2598</code></a>
Release 1.0.100</li>
<li><a
href="f2719888cb"><code>f271988</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/anyhow/issues/426">#426</a>
from dtolnay/clippyfmt</li>
<li><a
href="52f2115a1f"><code>52f2115</code></a>
Mark macros with clippy::format_args</li>
<li><a
href="da5fd9d5a3"><code>da5fd9d</code></a>
Raise minimum tested compiler to rust 1.76</li>
<li><a
href="211e4092b7"><code>211e409</code></a>
Opt in to generate-macro-expansion when building on docs.rs</li>
<li><a
href="b48fc02c32"><code>b48fc02</code></a>
Enforce trybuild >= 1.0.108</li>
<li><a
href="d5f59fbd45"><code>d5f59fb</code></a>
Update ui test suite to nightly-2025-09-07</li>
<li><a
href="238415d25b"><code>238415d</code></a>
Update ui test suite to nightly-2025-08-24</li>
<li><a
href="3bab0709a3"><code>3bab070</code></a>
Update actions/checkout@v4 -> v5</li>
<li><a
href="42492546e3"><code>4249254</code></a>
Order cap-lints flag in the same order as thiserror build script</li>
<li>See full diff in <a
href="https://github.com/dtolnay/anyhow/compare/1.0.99...1.0.100">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>
Co-authored-by: Eric Traut <etraut@openai.com>
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 2.0.16 to
2.0.17.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/thiserror/releases">thiserror's
releases</a>.</em></p>
<blockquote>
<h2>2.0.17</h2>
<ul>
<li>Use differently named __private module per patch release (<a
href="https://redirect.github.com/dtolnay/thiserror/issues/434">#434</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="72ae716e6d"><code>72ae716</code></a>
Release 2.0.17</li>
<li><a
href="599fdce83a"><code>599fdce</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/thiserror/issues/434">#434</a>
from dtolnay/private</li>
<li><a
href="9ec05f6b38"><code>9ec05f6</code></a>
Use differently named __private module per patch release</li>
<li><a
href="d2c492b549"><code>d2c492b</code></a>
Raise minimum tested compiler to rust 1.76</li>
<li><a
href="fc3ab9501d"><code>fc3ab95</code></a>
Opt in to generate-macro-expansion when building on docs.rs</li>
<li><a
href="819fe29dbb"><code>819fe29</code></a>
Update ui test suite to nightly-2025-09-12</li>
<li><a
href="259f48c549"><code>259f48c</code></a>
Enforce trybuild >= 1.0.108</li>
<li><a
href="470e6a681c"><code>470e6a6</code></a>
Update ui test suite to nightly-2025-08-24</li>
<li><a
href="544e191e6e"><code>544e191</code></a>
Update actions/checkout@v4 -> v5</li>
<li><a
href="cbc1ebad3e"><code>cbc1eba</code></a>
Delete duplicate cap-lints flag from build script</li>
<li>See full diff in <a
href="https://github.com/dtolnay/thiserror/compare/2.0.16...2.0.17">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
You can trigger a rebase of this PR 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>
> **Note**
> Automatic rebases have been disabled on this pull request as it has
been open for over 30 days.
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eric Traut <etraut@openai.com>
## What?
Fixed error handling in `insert_history_lines_to_writer` where all
terminal operations were silently ignoring errors via `.ok()`.
## Why?
Silent I/O failures could leave the terminal in an inconsistent state
(e.g., scroll region not reset) with no way to debug. This violates Rust
error handling best practices.
## How?
- Changed function signature to return `io::Result<()>`
- Replaced all `.ok()` calls with `?` operator to propagate errors
- Added `tracing::warn!` in wrapper function for backward compatibility
- Updated 15 test call sites to handle Result with `.expect()`
## Testing
- ✅ Pass all tests
## Type of Change
- [x] Bug fix (non-breaking change)
---------
Signed-off-by: Huaiwu Li <lhwzds@gmail.com>
Co-authored-by: Eric Traut <etraut@openai.com>
we are seeing [reports](https://github.com/openai/codex/issues/6004) of
users having verbosity in their config.toml and facing issues.
gpt-5-codex doesn't accept other values rather than medium for
verbosity.
Fixes a Markdown parsing issue where a list item used `*` without a
following space (`*Line ranges ...`). Per CommonMark, a space after the
list marker is required. Updated to `* Line ranges ...` so the guideline
renders as a standalone bullet. This change improves readability and
prevents mis-parsing in renderers.
Co-authored-by: Eric Traut <etraut@openai.com>
## Summary
- add the `/exit` slash command alongside `/quit` and reuse shared exit
handling
- refactor the chat widget to funnel quit, exit, logout, and shutdown
flows through a common `request_exit` helper
- add focused unit tests that confirm both `/quit` and `/exit` send an
`ExitRequest`
## Testing
- `just fmt`
- `just fix -p codex-tui`
- `cargo test -p codex-tui`
------
https://chatgpt.com/codex/tasks/task_i_6903d5a8f47c8321bf180f031f2fa330
- Added the new codex-windows-sandbox crate that builds both a library
entry point (run_windows_sandbox_capture) and a CLI executable to launch
commands inside a Windows restricted-token sandbox, including ACL
management, capability SID provisioning, network lockdown, and output
capture
(windows-sandbox-rs/src/lib.rs:167, windows-sandbox-rs/src/main.rs:54).
- Introduced the experimental WindowsSandbox feature flag and wiring so
Windows builds can opt into the sandbox:
SandboxType::WindowsRestrictedToken, the in-process execution path, and
platform sandbox selection now honor the flag (core/src/features.rs:47,
core/src/config.rs:1224, core/src/safety.rs:19,
core/src/sandboxing/mod.rs:69, core/src/exec.rs:79,
core/src/exec.rs:172).
- Updated workspace metadata to include the new crate and its
Windows-specific dependencies so the core crate can link against it
(codex-rs/
Cargo.toml:91, core/Cargo.toml:86).
- Added a PowerShell bootstrap script that installs the Windows
toolchain, required CLI utilities, and builds the workspace to ease
development
on the platform (scripts/setup-windows.ps1:1).
- Landed a Python smoke-test suite that exercises
read-only/workspace-write policies, ACL behavior, and network denial for
the Windows sandbox
binary (windows-sandbox-rs/sandbox_smoketests.py:1).
# Summary
This PR is related to the Issue #3978 and contains a fix to the seatbelt
profile for macOS that allows to run java/jdk tooling from the sandbox.
I have found that the included change is the minimum change to make it
run on my machine.
There is a unit test added by codex when making this fix. I wonder if it
is useful since you need java installed on the target machine for it to
be relevant. I can remove it it is better.
Fixes#3978
There's still some debate about whether we want to expose
`tools.view_image` or `feature.view_image` so those are left unchanged
for now, but this old `include_view_image_tool` config is good-to-go.
Also updated the doc to reflect that `view_image` tool is now by default
true.
Pull request template, minimal:
---
### **What?**
Minor change (low-hanging fruit).
### **Why?**
To improve code quality or documentation with minimal risk and effort.
### **How?**
Edited directly via VSCode Editor.
---
**Checklist (pre-PR):**
* [x] I have read the CLA Document and hereby sign the CLA.
* [x] I reviewed the “Contributing” markdown file for this project.
*This template meets standard external (non-OpenAI) PR requirements and
signals compliance for maintainers.*
Co-authored-by: Eric Traut <etraut@openai.com>
We had this annotation everywhere in app-server APIs which made it so
that fields get serialized as `field?: T`, meaning if the field as
`None` we would omit the field in the payload. Removing this annotation
changes it so that we return `field: T | null` instead, which makes
codex app-server's API more aligned with the convention of public OpenAI
APIs like Responses.
Separately, remove the `#[ts(optional_fields = nullable)]` annotations
that were recently added which made all the TS types become `field?: T |
null` which is not great since clients need to handle undefined and
null.
I think generally it'll be best to have optional types be either:
- `field: T | null` (preferred, aligned with public OpenAI APIs)
- `field?: T` where we have to, such as types generated from the MCP
schema:
https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-06-18/schema.ts
(see changes to `mcp-types/`)
I updated @etraut-openai's unit test to check that all generated TS
types are one or the other, not both (so will error if we have a type
that has `field?: T | null`). I don't think there's currently a good use
case for that - but we can always revisit.
## Summary
Duplicates the tests in `apply_patch_cli.rs`, but tests the freeform
apply_patch tool as opposed to the function call path. The good news is
that all the tests pass with zero logical tests, with the exception of
the heredoc, which doesn't really make sense in the freeform tool
context anyway.
@jif-oai since you wrote the original tests in #5557, I'd love your
opinion on the right way to DRY these test cases between the two. Happy
to set up a more sophisticated harness, but didn't want to go down the
rabbit hole until we agreed on the right pattern
## Testing
- [x] These are tests
## Summary
- add a debug-only `/rollout` slash command that prints the rollout file
path or reports when none is known
- surface the new command in the slash command metadata and cover it
with unit tests
<img width="539" height="99" alt="image"
src="https://github.com/user-attachments/assets/688e1334-8a06-4576-abb8-ada33b458661"
/>
## Summary
- re-enable the TypeScript SDK test that verifies local images are
forwarded to `codex exec`
## Testing
- `pnpm test` *(fails: unable to download pnpm 10.8.1 because external
network access is blocked in the sandbox)*
------
https://chatgpt.com/codex/tasks/task_i_690289cb861083209fd006867e2adfb1
Adds AgentMessageContentDelta, ReasoningContentDelta,
ReasoningRawContentDelta item streaming events while maintaining
compatibility for old events.
---------
Co-authored-by: Owen Lin <owen@openai.com>
In this PR, I am exploring migrating task kind to an invocation of
Codex. The main reason would be getting rid off multiple
`ConversationHistory` state and streamlining our context/history
management.
This approach depends on opening a channel between the sub-codex and
codex. This channel is responsible for forwarding `interactive`
(`approvals`) and `non-interactive` events. The `task` is responsible
for handling those events.
This opens the door for implementing `codex as a tool`, replacing
`compact` and `review`, and potentially subagents.
One consideration is this code is very similar to `app-server` specially
in the approval part. If in the future we wanted an interactive
`sub-codex` we should consider using `codex-mcp`
The goal is to have a single place where we actually write files
In a follow-up PR, will move everything config related in a dedicated
module and move the helpers in a dedicated file
We currently have nested enums when sending raw response items in the
app-server protocol. This makes downstream schemas confusing because we
need to embed `type`-discriminated enums within each other.
This PR adds a small wrapper around the response item so we can keep the
schemas separate
This PR addresses a current hole in the TypeScript code generation for
the API server protocol. Fields that are marked as "Optional<>" in the
Rust code are serialized such that the value is omitted when it is
deserialized — appearing as `undefined`, but the TS type indicates
(incorrectly) that it is always defined but possibly `null`. This can
lead to subtle errors that the TypeScript compiler doesn't catch. The
fix is to include the `#[ts(optional_fields = nullable)]` macro for all
protocol structs that contain one or more `Optional<>` fields.
This PR also includes a new test that validates that all TS protocol
code containing "| null" in its type is marked optional ("?") to catch
cases where `#[ts(optional_fields = nullable)]` is omitted.
feature: Add "!cmd" user shell execution
This change lets users run local shell commands directly from the TUI by
prefixing their input with ! (e.g. !ls). Output is truncated to keep the
exec cell usable, and Ctrl-C cleanly
interrupts long-running commands (e.g. !sleep 10000).
**Summary of changes**
- Route Op::RunUserShellCommand through a dedicated UserShellCommandTask
(core/src/tasks/user_shell.rs), keeping the task logic out of codex.rs.
- Reuse the existing tool router: the task constructs a ToolCall for the
local_shell tool and relies on ShellHandler, so no manual MCP tool
lookup is required.
- Emit exec lifecycle events (ExecCommandBegin/ExecCommandEnd) so the
TUI can show command metadata, live output, and exit status.
**End-to-end flow**
**TUI handling**
1. ChatWidget::submit_user_message (TUI) intercepts messages starting
with !.
2. Non-empty commands dispatch Op::RunUserShellCommand { command };
empty commands surface a help hint.
3. No UserInput items are created, so nothing is enqueued for the model.
**Core submission loop**
4. The submission loop routes the op to handlers::run_user_shell_command
(core/src/codex.rs).
5. A fresh TurnContext is created and Session::spawn_user_shell_command
enqueues UserShellCommandTask.
**Task execution**
6. UserShellCommandTask::run emits TaskStartedEvent, formats the
command, and prepares a ToolCall targeting local_shell.
7. ToolCallRuntime::handle_tool_call dispatches to ShellHandler.
**Shell tool runtime**
8. ShellHandler::run_exec_like launches the process via the unified exec
runtime, honoring sandbox and shell policies, and emits
ExecCommandBegin/End.
9. Stdout/stderr are captured for the UI, but the task does not turn the
resulting ToolOutput into a model response.
**Completion**
10. After ExecCommandEnd, the task finishes without an assistant
message; the session marks it complete and the exec cell displays the
final output.
**Conversation context**
- The command and its output never enter the conversation history or the
model prompt; the flow is local-only.
- Only exec/task events are emitted for UI rendering.
**Demo video**
https://github.com/user-attachments/assets/fcd114b0-4304-4448-a367-a04c43e0b996
Found that the VS Code Codex extension throws “Error starting
conversation” when initializing a conversation with Git for Windows’
bash on PATH.
Debugging showed the bash-detection logic did not return as expected;
this change makes it reliable in that scenario.
Possibly related to issue #2841.
# Preserve PATH precedence & fix Windows MCP env propagation
## Problem & intent
Preserve user PATH precedence and reduce Windows setup friction for MCP
servers by avoiding PATH reordering and ensuring Windows child processes
receive essential env vars.
- Addresses: #4180#5225#2945#3245#3385#2892#3310#3457#4370
- Supersedes: #4182, #3866, #3828 (overlapping/inferior once this
merges)
- Notes: #2626 / #2646 are the original PATH-mutation sources being
corrected.
---
## Before / After
**Before**
- PATH was **prepended** with an `apply_patch` helper dir (Rust + Node
wrapper), reordering tools and breaking virtualenvs/shims on
macOS/Linux.
- On Windows, MCP servers missed core env vars and often failed to start
without explicit per-server env blocks.
**After**
- Helper dir is **appended** to PATH (preserves user/tool precedence).
- Windows MCP child env now includes common core variables and mirrors
`PATH` → `Path`, so typical CLIs/plugins work **without** per-server env
blocks.
---
## Scope of change
### `codex-rs/arg0/src/lib.rs`
- Append temp/helper dir to `PATH` instead of prepending.
### `codex-cli/bin/codex.js`
- Mirror the same append behavior for the Node wrapper.
### `codex-rs/rmcp-client/src/utils.rs`
- Expand Windows `DEFAULT_ENV_VARS` (e.g., `COMSPEC`, `SYSTEMROOT`,
`PROGRAMFILES*`, `APPDATA`, etc.).
- Mirror `PATH` → `Path` for Windows child processes.
- Small unit test; conditional `mut` + `clippy` cleanup.
---
## Security effects
No broadened privileges. Only environment propagation for well-known
Windows keys on stdio MCP child processes. No sandbox policy changes and
no network additions.
---
## Testing evidence
**Static**
- `cargo fmt`
- `cargo clippy -p codex-arg0 -D warnings` → **clean**
- `cargo clippy -p codex-rmcp-client -D warnings` → **clean**
- `cargo test -p codex-rmcp-client` → **13 passed**
**Manual**
- Local verification on Windows PowerShell 5/7 and WSL (no `unused_mut`
warnings on non-Windows targets).
---
## Checklist
- [x] Append (not prepend) helper dir to PATH in Rust and Node wrappers
- [x] Windows MCP child inherits core env vars; `PATH` mirrored to
`Path`
- [x] `cargo fmt` / `clippy` clean across touched crates
- [x] Unit tests updated/passing where applicable
- [x] Cross-platform behavior preserved (macOS/Linux PATH precedence
intact)
This PR adds an option to app server to allow conversation summaries to
be fetched from just the conversation id rather than rollout path for
convenience at the cost of some latency to discover the rollout path.
This convenience is non-trivial as it allows app servers to simply
maintain conversation ids rather than rollout paths and the associated
platform (Windows) handling associated with storing and encoding them
correctly.
Follow-up to https://github.com/openai/codex/pull/5063
Refined the app-server export pipeline so JSON Schema variants and
discriminator fields are annotated with descriptive, stable titles
before writing the bundle. This eliminates anonymous enum names in the
generated Pydantic models (goodbye Type7) while keeping downstream
tooling simple. Added shared helpers to derive titles and literals, and
reused them across the traversal logic for clarity. Running just fix -p
codex-app-server-protocol, just fmt, and cargo test -p
codex-app-server-protocol validates the change.
solves: https://github.com/openai/codex/issues/5675
Block non-image uploads in the view_image workflow. We now confirm the
file’s MIME is image/* before building the data URL; otherwise we emit a
“unsupported MIME type” error to the model. This stops the agent from
sending application/json blobs that the Responses API rejects with 400s.
<img width="409" height="556" alt="Screenshot 2025-10-28 at 1 15 10 PM"
src="https://github.com/user-attachments/assets/a92199e8-2769-4b1d-8e33-92d9238c90fe"
/>
Addresses https://github.com/openai/codex/issues/5773
Testing: I tested that images work (regardless of order that they are
associated with the task prompt) in both the CLI and Extension. Also
verified that conversations in CLI and extension with images can be
resumed.
This fixes an issue where messages sent during the final response stream
would seem to disappear, because the "queued messages" UI wasn't shown
during streaming.
There's a lot of visual noise in app-server's integration tests due to
the number of `.expect("<some_msg>")` lines which are largely redundant
/ not very useful. Clean them up by using `anyhow::Result` + `?`
consistently.
Replaces the existing pattern of:
```
let codex_home = TempDir::new().expect("create temp dir");
create_config_toml(codex_home.path()).expect("write config.toml");
let mut mcp = McpProcess::new(codex_home.path())
.await
.expect("spawn mcp process");
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize())
.await
.expect("initialize timeout")
.expect("initialize request");
```
With:
```
let codex_home = TempDir::new()?;
create_config_toml(codex_home.path())?;
let mut mcp = McpProcess::new(codex_home.path()).await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
```
This PR is a follow-up to #5591. It allows users to choose which auth
storage mode they want by using the new
`cli_auth_credentials_store_mode` config.
## Summary
- Coerce Windows `workspace-write` configs back to read-only, surface
the forced downgrade in the approvals popup,
and funnel users toward WSL or Full Access.
- Add WSL installation instructions to the Auto preset on Windows while
keeping the preset available for other
platforms.
- Skip the trust-on-first-run prompt on native Windows so new folders
remain read-only without additional
confirmation.
- Expose a structured sandbox policy resolution from config to flag
Windows downgrades and adjust tests (core,
exec, TUI) to reflect the new behavior; provide a Windows-only approvals
snapshot.
## Testing
- cargo fmt
- cargo test -p codex-core
config::tests::add_dir_override_extends_workspace_writable_roots
- cargo test -p codex-exec
suite::resume::exec_resume_preserves_cli_configuration_overrides
- cargo test -p codex-tui
chatwidget::tests::approvals_selection_popup_snapshot
- cargo test -p codex-tui
approvals_popup_includes_wsl_note_for_auto_mode
- cargo test -p codex-tui windows_skips_trust_prompt
- just fix -p codex-core
- just fix -p codex-tui
fixing drag/drop photos bug in codex
state of the world before:
sometimes, when you drag screenshots into codex, the image does not
properly render into context. instead, the file name is shown in
quotation marks.
https://github.com/user-attachments/assets/3c0e540a-505c-4ec0-b634-e9add6a73119
the screenshot is not actually included in agent context. the agent
needs to manually call the view_image tool to see the screenshot. this
can be unreliable especially if the image is part of a longer prompt and
is dependent on the agent going out of its way to view the image.
state of the world after:
https://github.com/user-attachments/assets/5f2b7bf7-8a3f-4708-85f3-d68a017bfd97
now, images will always be directly embedded into chat context
## Technical Details
- MacOS sends screenshot paths with a narrow no‑break space right before
the “AM/PM” suffix, which used to trigger our non‑ASCII fallback in the
paste burst detector.
- That fallback flushed the partially buffered paste immediately, so the
path arrived in two separate `handle_paste` calls (quoted prefix +
`PM.png'`). The split string could not be normalized to a real path, so
we showed the quoted filename instead of embedding the image.
- We now append non‑ASCII characters into the burst buffer when a burst
is already active. Finder’s payload stays intact, the path normalizes,
and the image attaches automatically.
- When no burst is active (e.g. during IME typing), non‑ASCII characters
still bypass the buffer so text entry remains responsive.
It's pretty amazing we have gotten here without the ability for the
model to see image content from MCP tool calls.
This PR builds off of 4391 and fixes#4819. I would like @KKcorps to get
adequete credit here but I also want to get this fix in ASAP so I gave
him a week to update it and haven't gotten a response so I'm going to
take it across the finish line.
This test highlights how absured the current situation is. I asked the
model to read this image using the Chrome MCP
<img width="2378" height="674" alt="image"
src="https://github.com/user-attachments/assets/9ef52608-72a2-4423-9f5e-7ae36b2b56e0"
/>
After this change, it correctly outputs:
> Captured the page: image dhows a dark terminal-style UI labeled
`OpenAI Codex (v0.0.0)` with prompt `model: gpt-5-codex medium` and
working directory `/codex/codex-rs`
(and more)
Before this change, it said:
> Took the full-page screenshot you asked for. It shows a long,
horizontally repeating pattern of stylized people in orange, light-blue,
and mustard clothing, holding hands in alternating poses against a white
background. No text or other graphics-just rows of flat illustration
stretching off to the right.
Without this change, the Figma, Playwright, Chrome, and other visual MCP
servers are pretty much entirely useless.
I tested this change with the openai respones api as well as a third
party completions api
Makes sense to move this struct to `app-server-protocol/` since we want
to serialize as camelCase, but we don't for structs defined in
`protocol/`
It was:
```
export type Account = { "type": "ApiKey", api_key: string, } | { "type": "chatgpt", email: string | null, plan_type: PlanType, };
```
But we want:
```
export type Account = { "type": "apiKey", apiKey: string, } | { "type": "chatgpt", email: string | null, planType: PlanType, };
```
move the truncation logic to conversation history to use on any tool
output. This will help us in avoiding edge cases while truncating the
tool calls and mcp calls.
Follow-up PR to #5569. Add Keyring Support for Auth Storage in Codex CLI
as well as a hybrid mode (default to persisting in keychain but fall
back to file when unavailable.)
It also refactors out the keyringstore implementation from rmcp-client
[here](https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/oauth.rs)
to a new keyring-store crate.
There will be a follow-up that picks the right credential mode depending
on the config, instead of hardcoding `AuthCredentialsStoreMode::File`.
This PR introduces a new `Auth Storage` abstraction layer that takes
care of read, write, and load of auth tokens based on the
AuthCredentialsStoreMode. It is similar to how we handle MCP client
oauth
[here](https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/oauth.rs).
Instead of reading and writing directly from disk for auth tokens, Codex
CLI workflows now should instead use this auth storage using the public
helper functions.
This PR is just a refactor of the current code so the behavior stays the
same. We will add support for keyring and hybrid mode in follow-up PRs.
I have read the CLA Document and I hereby sign the CLA
This PR does the following:
1. Changes `try_refresh_token` to handle the case where the endpoint
returns a response without an `id_token`. The OpenID spec indicates that
this field is optional and clients should not assume it's present.
2. Changes the `attempt_stream_responses` to propagate token refresh
errors rather than silently ignoring them.
3. Fixes a typo in a couple of error messages (unrelated to the above,
but something I noticed in passing) - "reconnect" should be spelled
without a hyphen.
This PR does not implement the additional suggestion from @pakrym-oai
that we should sign out when receiving `refresh_token_expired` from the
refresh endpoint. Leaving this as a follow-on because I'm undecided on
whether this should be implemented in `try_refresh_token` or its
callers.
This adds an RPC to the app server to the the `ConversationSummary` via
a rollout path. Now that the VS Code extension supports showing the
Codex UI in an editor panel where the URI of the panel maps to the
rollout file, we need to be able to get the `ConversationSummary` from
the rollout file directly.
An AppServer client should be able to use any (`model_provider`, `model`) in the user's config. `NewConversationParams` already supported specifying the `model`, but this PR expands it to support `model_provider`, as well.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/5793).
* #5803
* __->__ #5793
Because conversations that use the Responses API can have encrypted
reasoning messages, trying to resume a conversation with a different
provider could lead to confusing "failed to decrypt" errors. (This is
reproducible by starting a conversation using ChatGPT login and resuming
it as a conversation that uses OpenAI models via Azure.)
This changes `ListConversationsParams` to take a `model_providers:
Option<Vec<String>>` and adds `model_provider` on each
`ConversationSummary` it returns so these cases can be disambiguated.
Note this ended up making changes to
`codex-rs/core/src/rollout/tests.rs` because it had a number of cases
where it expected `Some` for the value of `next_cursor`, but the list of
rollouts was complete, so according to this docstring:
bcd64c7e72/codex-rs/app-server-protocol/src/protocol.rs (L334-L337)
If there are no more items to return, then `next_cursor` should be
`None`. This PR updates that logic.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/5658).
* #5803
* #5793
* __->__ #5658
Revert #5642 because this generates:
```
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type GetAccountResponse = Account | null;
```
But `Account` is unknown.
The unique use of `#[ts(export)]` on `GetAccountResponse` is also
suspicious as are the changes to
`codex-rs/app-server-protocol/src/export.rs` since the existing system
has worked fine for quite some time.
Though a pure backout of #5642 puts things in a state where, as the PR
noted, the following does not work:
```
cargo run -p codex-app-server-protocol --bin export -- --out DIR
```
So in addition to the backout, this PR adds:
```rust
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
pub struct GetAccountResponse {
pub account: Account,
}
```
and changes `GetAccount.response` as follows:
```diff
- response: Option<Account>,
+ response: GetAccountResponse,
```
making it consistent with other types.
With this change, I verified that both of the following work:
```
just codex generate-ts --out /tmp/somewhere
cargo run -p codex-app-server-protocol --bin export -- --out /tmp/somewhere-else
```
The generated TypeScript is as follows:
```typescript
// GetAccountResponse.ts
import type { Account } from "./Account";
export type GetAccountResponse = { account: Account, };
```
and
```typescript
// Account.ts
import type { PlanType } from "./PlanType";
export type Account = { "type": "ApiKey", api_key: string, } | { "type": "chatgpt", email: string | null, plan_type: PlanType, };
```
Though while the inconsistency between `"type": "ApiKey"` and `"type":
"chatgpt"` is quite concerning, I'm not sure if that format is ever
written to disk in any case, but @owenlin0, I would recommend looking
into that.
Also, it appears that the types in `codex-rs/protocol/src/account.rs`
are used exclusively by the `app-server-protocol` crate, so perhaps they
should just be moved there?
Currently, `approval_policy` is supported in profiles, but
`sandbox_mode` is not. This PR adds support for `sandbox_mode`.
Note: a fix for this was submitted in [this
PR](https://github.com/openai/codex/pull/2397), but the underlying code
has changed significantly since then.
This addresses issue #3034
This PR fixes a test that is sporadically failing in CI.
The problem is that two unit tests (the older `login_and_cancel_chatgpt`
and a recently added
`login_chatgpt_includes_forced_workspace_query_param`) exercise code
paths that start the login server. The server binds to a hard-coded
localhost port number, so attempts to start more than one server at the
same time will fail. If these two tests happen to run concurrently, one
of them will fail.
To fix this, I've added a simple mutex. We can use this same mutex for
future tests that use the same pattern.
This PR adds support for a model-based summary and risk assessment for
commands that violate the sandbox policy and require user approval. This
aids the user in evaluating whether the command should be approved.
The feature works by taking a failed command and passing it back to the
model and asking it to summarize the command, give it a risk level (low,
medium, high) and a risk category (e.g. "data deletion" or "data
exfiltration"). It uses a new conversation thread so the context in the
existing thread doesn't influence the answer. If the call to the model
fails or takes longer than 5 seconds, it falls back to the current
behavior.
For now, this is an experimental feature and is gated by a config key
`experimental_sandbox_command_assessment`.
Here is a screen shot of the approval prompt showing the risk assessment
and summary.
<img width="723" height="282" alt="image"
src="https://github.com/user-attachments/assets/4597dd7c-d5a0-4e9f-9d13-414bd082fd6b"
/>
The API schema export is currently broken:
```
> cargo run -p codex-app-server-protocol --bin export -- --out DIR
Error: this type cannot be exported
```
This PR fixes the error message so we get more info:
```
> cargo run -p codex-app-server-protocol --bin export -- --out DIR
Error: failed to export client responses: dependency core::option::Option<codex_protocol::account::Account> cannot be exported
```
And fixes the root cause which is the `account/read` response.
## Summary
- wrap the default reqwest::Client inside a new
CodexHttpClient/CodexRequestBuilder pair and log the HTTP method, URL,
and status for each request
- update the auth/model/provider plumbing to use the new builder helpers
so headers and bearer auth continue to be applied consistently
- add the shared `http` dependency that backs the header conversion
helpers
## Testing
- `CODEX_SANDBOX=seatbelt CODEX_SANDBOX_NETWORK_DISABLED=1 cargo test -p
codex-core`
- `CODEX_SANDBOX=seatbelt CODEX_SANDBOX_NETWORK_DISABLED=1 cargo test -p
codex-chatgpt`
- `CODEX_SANDBOX=seatbelt CODEX_SANDBOX_NETWORK_DISABLED=1 cargo test -p
codex-tui`
------
https://chatgpt.com/codex/tasks/task_i_68fa5038c17483208b1148661c5873be
1. I have seen too many reports of people hitting startup timeout errors
and thinking Codex is broken. Hopefully this will help people
self-serve. We may also want to consider raising the timeout to ~15s.
2. Make it more clear what PAT is (personal access token) in the GitHub
error
<img width="2378" height="674" alt="CleanShot 2025-10-23 at 22 05 06"
src="https://github.com/user-attachments/assets/d148ce1d-ade3-4511-84a4-c164aefdb5c5"
/>
I want to centralize input processing and management to
`ConversationHistory`. This would need `ConversationHistory` to have
access to `token_info` (i.e. preventing adding a big input to the
history). Besides, it makes more sense to have it on
`ConversationHistory` than `state`.
Walk the sessions tree instead of using file_search so gitignored
CODEX_HOME directories can resume sessions. Add a regression test that
covers a .gitignore'd sessions directory.
Fixes#5247Fixes#5412
---------
Co-authored-by: Owen Lin <owen@openai.com>
Currently we collect all all turn items in a vector, then we add it to
the history on success. This result in losing those items on errors
including aborting `ctrl+c`.
This PR:
- Adds the ability for the tool call to handle cancellation
- bubble the turn items up to where we are recording this info
Admittedly, this logic is an ad-hoc logic that doesn't handle a lot of
error edge cases. The right thing to do is recording to the history on
the spot as `items`/`tool calls output` come. However, this isn't
possible because of having different `task_kind` that has different
`conversation_histories`. The `try_run_turn` has no idea what thread are
we using. We cannot also pass an `arc` to the `conversation_histories`
because it's a private element of `state`.
That's said, `abort` is the most common case and we should cover it
until we remove `task kind`
This shows the aggregated (stdout + stderr) buffer regardless of exit
code.
Many commands output useful / relevant info on stdout when returning a
non-zero exit code, or the same on stderr when returning an exit code of
0. Often, useful info is present on both stdout AND stderr. Also, the
model sees both. So it is confusing to see commands listed as "(no
output)" that in fact do have output, just on the stream that doesn't
match the exit status, or to see some sort of trivial output like "Tests
failed" but lacking any information about the actual failure.
As such, always display the aggregated output in the display. Transcript
mode remains unchanged as it was already displaying the text that the
model sees, which seems correct for transcript mode.
These are the schema definitions for the new JSON-RPC APIs associated
with accounts. These are not wired up to business logic yet and will
currently throw an internal error indicating these are unimplemented.
- ensure paste burst flush preserves ASCII characters before IME commits
- add regression test covering digit followed by Japanese text
submission
Fixesopenai/codex#4356
Co-authored-by: Josh McKinney <joshka@openai.com>
Codex will now send an `account/rateLimits/updated` notification
whenever the user's rate limits are updated.
This is implemented by just transforming the existing TokenCount event.
We are doing some ad-hoc logic while dealing with conversation history.
Ideally, we shouldn't mutate `vec[responseitem]` manually at all and
should depend on `ConversationHistory` for those changes.
Those changes are:
- Adding input to the history
- Removing items from the history
- Correcting history
I am also adding some `error` logs for cases we shouldn't ideally face.
For example, we shouldn't be missing `toolcalls` or `outputs`. We
shouldn't hit `ContextWindowExceeded` while performing `compact`
This refactor will give us granular control over our context management.
I haven't heard of any issues with the studio rmcp client so let's
remove the legacy one and default to the new one.
Any code changes are moving code from the adapter inline but there
should be no meaningful functionality changes.
1. Adds AgentMessage, Reasoning, WebSearch items.
2. Switches the ResponseItem parsing to use new items and then also emit
3. Removes user-item kind and filters out "special" (environment) user
items when returning to clients.
## What
- Add the `--cask` flag to the Homebrew update command for Codex.
## Why
- `brew upgrade codex` alone does not update the cask, so users were not
getting the right upgrade instructions.
## How
- Update `UpdateAction::BrewUpgrade` in `codex-rs/tui/src/updates.rs` to
use `upgrade --cask codex`.
## Testing
- [x] cargo test -p codex-tui
Co-authored-by: Thibault Sottiaux <tibo@openai.com>
While we do not want to encourage users to hardcode secrets in their
`config.toml` file, it should be possible to pass an API key
programmatically. For example, when using `codex app-server`, it is
possible to pass a "bag of configuration" as part of the
`NewConversationParams`:
682d05512f/codex-rs/app-server-protocol/src/protocol.rs (L248-L251)
When using `codex app-server`, it's not practical to change env vars of
the `codex app-server` process on the fly (which is how we usually read
API key values), so this helps with that.
## Summary
- make the plan tool available by default by removing the feature flag
and always registering the handler
- drop plan-tool CLI and API toggles across the exec, TUI, MCP server,
and app server code paths
- update tests and configs to reflect the always-on plan tool and guard
workspace restriction tests against env leakage
## Testing
Manually tested the extension.
------
https://chatgpt.com/codex/tasks/task_i_68f67a3ff2d083209562a773f814c1f9
This #[serial] approach is not ideal. I am tracking a separate issue to
create an injectable env var provider but I want to fix these tests
first.
Fixes#5447
Today `sub_id` is an ID of a single incoming Codex Op submition. We then
associate all events triggered by this operation using the same
`sub_id`.
At the same time we are also creating a TurnContext per submission and
we'd like to start associating some events (item added/item completed)
with an entire turn instead of just the operation that started it.
Using turn context when sending events give us flexibility to change
notification scheme.
Expose the session cwd in the notify payload and update docs so scripts
and extensions receive the real project path; users get accurate
project-aware notifications in CLI and VS Code.
Fixes#5387
Because the GitHub MCP is one of the most popular MCPs and it
confusingly doesn't support OAuth, we should make it more clear how to
make it work so people don't think Codex is broken.
Without proper `zsh -lc` parsing, we lose some things like proper
command parsing, turn diff tracking, safe command checks, and other
things we expect from raw or `bash -lc` commands.
Some MCP servers expose a lot of tools. In those cases, it is reasonable
to allow/denylist tools for Codex to use so it doesn't get overwhelmed
with too many tools.
The new configuration options available in the `mcp_server` toml table
are:
* `enabled_tools`
* `disabled_tools`
Fixes#4796
Adds a `GET account/rateLimits/read` API to app-server. This calls the
codex backend to fetch the user's current rate limits.
This would be helpful in checking rate limits without having to send a
message.
For calling the codex backend usage API, I generated the types and
manually copied the relevant ones into `codex-backend-openapi-types`.
It'll be nice to extend our internal openapi generator to support Rust
so we don't have to run these manual steps.
# 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.
We don't instruct the model to use citations, so it never emits them.
Further, ratatui [doesn't currently support rendering links into the
terminal with OSC 8](https://github.com/ratatui/ratatui/issues/1028), so
even if we did parse citations, we can't correctly render them.
So, remove all the code related to rendering them.
Adds a new ItemStarted event and delivers UserMessage as the first item
type (more to come).
Renames `InputItem` to `UserInput` considering we're using the `Item`
suffix for actual items.
The backend will be returning unix timestamps (seconds since epoch)
instead of RFC 3339 strings. This will make it more ergonomic for
developers to integrate against - no string parsing.
Add shared helper to format warnings when add-dir is incompatible with
the sandbox. Surface the warning in the TUI entrypoint and document the
limitation for add-dir.
Add annotations and an export script that let us generate app-server
protocol types as typescript and JSONSchema.
The script itself is a bit hacky because we need to manually label some
of the types. Unfortunately it seems that enum variants don't get good
names by default and end up with something like `EventMsg1`,
`EventMsg2`, etc. I'm not an expert in this by any means, but since this
is only run manually and we already need to enumerate the types required
to describe the protocol, it didn't seem that much worse. An ideal
solution here would be to have some kind of root that we could generate
schemas for in one go, but I'm not sure if that's compatible with how we
generate the protocol today.
Extends shell wrapper stripping in TUI to handle `zsh -lc` in addition
to `bash -lc`.
Currently, Linux users (and macOS users with zsh profiles) see cluttered
command headers like `• Ran zsh -lc "echo hello"` instead of `• Ran echo
hello`. This happens because `codex-rs/tui/src/exec_command.rs` only
checks for literal `"bash"`, ignoring `zsh` and absolute paths like
`/usr/bin/zsh`.
**Changes:**
- Added `is_login_shell_with_lc` helper that extracts shell basename and
matches against `bash` or `zsh`
- Updated pattern matching to use the helper instead of hardcoded check
- Added test coverage for zsh and absolute paths (`/usr/bin/zsh`,
`/bin/bash`)
**Testing:**
```bash
cd codex-rs
cargo test strip_bash_lc_and_escape -p codex-tui
```
All 4 test cases pass (bash, zsh, and absolute paths for both).
Closes#4201
Extend `run` and `runStreamed` input to be either a `string` or
structured input. A structured input is an array of text parts and/or
image paths, which will then be fed to the CLI through the `--image`
argument. Text parts are combined with double newlines. For instance:
```ts
const turn = await thread.run([
{ type: "text", text: "Describe these screenshots" },
{ type: "local_image", path: "./ui.png" },
{ type: "local_image", path: "./diagram.jpg" },
{ type: "text", text: "Thanks!" },
]);
```
Ends up launching the CLI with:
```
codex exec --image foo.png --image bar.png "Describe these screenshots\n\nThanks!"
```
The complete `Input` type for both function now is:
```ts
export type UserInput =
| {
type: "text";
text: string;
}
| {
type: "local_image";
path: string;
};
export type Input = string | UserInput[];
```
This brings the Codex SDK closer to feature parity with the CLI.
Adresses #5280 .
This should make it more clear that specific tools come from MCP
servers.
#4806 requested that we add the server name but we already do that.
Fixes#4806
Tightened the docs so the sandbox guide matches reality, noted the new
tools.view_image toggle next to web search, and linked the README to the
getting-started guide which now owns the familiar tips (backtrack, --cd,
--add-dir, etc.).
Updated the configuration guide so it matches the current CLI behavior.
Clarified the platform-specific default model, explained how custom
model-providers interact with bundled ones, refreshed the streamable
HTTP/MCP section with accurate guidance on the RMCP client and OAuth
flag, and removed stale keys from the reference table.
Update FAQ, improve general structure for config, add more links across
the sections in the documentation, remove out of date and duplicate
content and better explain certain concepts such as approvals and
sandboxing.
Add a `--add-dir` CLI flag so sessions can use extra writable roots in
addition to the ones specified in the config file. These are ephemerally
added during the session only.
Fixes#3303Fixes#2797
The goal of this change:
1. Unify user input and user turn implementation.
2. Have a single place where turn/session setting overrides are applied.
3. Have a single place where turn context is created.
4. Create TurnContext only for actual turn and have a separate structure
for current session settings (reuse ConfigureSession)
Expand the custom prompts documentation and link it from other guides. Show saved prompt metadata in the slash-command popup, with tests covering description fallbacks.
Exit when a requested resume session is missing after restoring the
terminal and print a helpful message instructing users how to resume
existing sessions.
Partially addresses #5247.
# What
Updates the install command in the changelog template (`cliff.toml`)
from
```
npm install -g codex@version
```
to
```
npm install -g @openai/codex@<version>
```
# Why
The current command is incorrect, it tries installs the old “codex”
static site generator rather than the OpenAI Codex CLI.
# How
Edited only the header string in `cliff.toml` to point to
@openai/codex@<version>. No changelog regeneration or other files
touched.
Fixes#2059
Co-authored-by: Thibault Sottiaux <tibo@openai.com>
Fixes#4870#4717#3260#4431#2718#4898#5036
- Fix the chat composer “phantom space” bug that appeared when
backspacing CJK (and other double-width) characters after the composer
got a uniform background in 43b63ccae89c….
- Pull diff_buffers’s clear-to-end logic forward to iterate by display
width, so wide graphemes are counted correctly when computing the
trailing column.
- Keep modifier-aware detection so styled cells are still flushed, and
add a regression test (diff_buffers_clear_to_end_starts_after_wide_char)
that covers the CJK deletion scenario.
---------
Co-authored-by: Josh McKinney <joshka@openai.com>
This change ensures that we store the absolute time instead of relative
offsets of when the primary and secondary rate limits will reset.
Previously these got recalculated relative to current time, which leads
to the displayed reset times to change over time, including after doing
a codex resume.
For previously changed sessions, this will cause the reset times to not
show due to this being a breaking change:
<img width="524" height="55" alt="Screenshot 2025-10-17 at 5 14 18 PM"
src="https://github.com/user-attachments/assets/53ebd43e-da25-4fef-9c47-94a529d40265"
/>
Fixes https://github.com/openai/codex/issues/4761
I dropped the build of the old cli from the flake, where the default.nix
already seemed to removed in a previous iterations. Then I updated
flake.nix and codex-rs expression to be able to build again (see
individual commits for details).
Tested by running the following builds:
```
$ nix build .#packages.x86_64-linux.codex-rs
$ nix build .#packages.aarch64-darwin.codex-cli
```
---------
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
`ParsedCommand::Read` has a `name` field that attempts to identify the
name of the file being read, but the file may not be in the `cwd` in
which the command is invoked as demonstrated by this existing unit test:
0139f6780c/codex-rs/core/src/parse_command.rs (L250-L260)
As you can see, `tui/Cargo.toml` is the relative path to the file being
read.
This PR introduces a new `path: PathBuf` field to `ParsedCommand::Read`
that attempts to capture this information. 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.
This should make it easier for clients to provide UI for a "read file"
event that corresponds to the command execution.
This makes stdio mcp servers more flexible by allowing users to specify
the cwd to run the server command from and adding additional environment
variables to be passed through to the server.
Example config using the test server in this repo:
```toml
[mcp_servers.test_stdio]
cwd = "/Users/<user>/code/codex/codex-rs"
command = "cargo"
args = ["run", "--bin", "test_stdio_server"]
env_vars = ["MCP_TEST_VALUE"]
```
@bolinfest I know you hate these env var tests but let's roll with this
for now. I may take a stab at the env guard + serial macro at some
point.
This adds two new config fields to streamable http mcp servers:
`http_headers`: a map of key to value
`env_http_headers` a map of key to env var which will be resolved at
request time
All headers will be passed to all MCP requests to that server just like
authorization headers.
There is a test ensuring that headers are not passed to other servers.
Fixes#5180
## Summary
When using the trusted state during tui startup, we created a new
WorkspaceWrite policy without checking the config.toml for a
`sandbox_workspace_write` field. This would result in us setting the
sandbox_mode as workspace-write, but ignoring the field if the user had
set `sandbox_workspace_write` without also setting `sandbox_mode` in the
config.toml. This PR adds support for respecting
`sandbox_workspace_write` setting in config.toml in the trusted
directory flow, and adds tests to cover this case.
## Testing
- [x] Added unit tests
Also: fixed the contents of the `APPLE_CERTIFICATE_P12` and
`APPLE_CERTIFICATE_PASSWORD` secrets, so the code-signing step will use
the right certificate now.
## Summary
- add a kill buffer to the text area and wire Ctrl+Y to yank it
- capture text from Ctrl+W, Ctrl+U, and Ctrl+K operations into the kill
buffer
- add regression coverage ensuring the last kill can be yanked back
Fixes#5017
------
https://chatgpt.com/codex/tasks/task_i_68e95bf06c48832cbf3d2ba8fa2035d2
This adds `parsed_cmd: Vec<ParsedCommand>` to `ExecApprovalRequestEvent`
in the core protocol (`protocol/src/protocol.rs`), which is also what
this field is named on `ExecCommandBeginEvent`. Honestly, I don't love
the name (it sounds like a single command, but it is actually a list of
them), but I don't want to get distracted by a naming discussion right
now.
This also adds `parsed_cmd` to `ExecCommandApprovalParams` in
`codex-rs/app-server-protocol/src/protocol.rs`, so it will be available
via `codex app-server`, as well.
For consistency, I also updated `ExecApprovalElicitRequestParams` in
`codex-rs/mcp-server/src/exec_approval.rs` to include this field under
the name `codex_parsed_cmd`, as that struct already has a number of
special `codex_*` fields. Note this is the code for when Codex is used
as an MCP _server_ and therefore has to conform to the official spec for
an MCP elicitation type.
Note these two types were identical, so it seems clear to standardize on the one in `codex_protocol` and eliminate the `Into` stuff.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/5218).
* #5222
* __->__ #5218
1. If Codex detects that a `codex mcp add -url …` server supports oauth,
it will auto-initiate the login flow.
2. If the TUI starts and a MCP server supports oauth but isn't logged
in, it will give the user an explicit warning telling them to log in.
Tightened the CLI integration tests to stop relying on wall-clock
sleeps—new fs watcher helper waits for session files instead of timing
out, and SSE mocks/fixtures make the flows deterministic.
keep a 1 cell margin at the right edge of the screen in the composer
(and in the user message in history).
this lets us print clear-to-EOL 1 char before the end of the line in
history, so that resizing the terminal will keep the background color
(at least in iterm/terminal.app). it also stops the cursor in the
textarea from floating off the right edge.
---------
Co-authored-by: joshka-oai <joshka@openai.com>
Add proper feature flag instead of having custom flags for everything.
This is just for experimental/wip part of the code
It can be used through CLI:
```bash
codex --enable unified_exec --disable view_image_tool
```
Or in the `config.toml`
```toml
# Global toggles applied to every profile unless overridden.
[features]
apply_patch_freeform = true
view_image_tool = false
```
Follow-up:
In a following PR, the goal is to have a default have `bundles` of
features that we can associate to a model
Refactor trust_directory to use ColumnRenderable & friends, thus
correcting wrapping behavior at small widths. Also introduce
RowRenderable with fixed-width rows.
- fixed wrapping in trust_directory
- changed selector cursor to match other list item selections
- allow y/n to work as well as 1/2
- fixed key_hint to be standard
before:
<img width="661" height="550" alt="Screenshot 2025-10-09 at 9 50 36 AM"
src="https://github.com/user-attachments/assets/e01627aa-bee4-4e25-8eca-5575c43f05bf"
/>
after:
<img width="661" height="550" alt="Screenshot 2025-10-09 at 9 51 31 AM"
src="https://github.com/user-attachments/assets/cb816cbd-7609-4c83-b62f-b4dba392d79a"
/>
# 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.
This adds a queryable auth status for MCP servers which is useful:
1. To determine whether a streamable HTTP server supports auth or not
based on whether or not it supports RFC 8414-3.2
2. Allow us to build a better user experience on top of MCP status
Instead of querying all 256 terminal colors on startup, which was slow
in some terminals, hardcode the default xterm palette.
Additionally, tweak the shimmer so that it blends between default_fg and
default_bg, instead of "dark gray" (according to the terminal) and pure
white (regardless of terminal theme).
Clear the history cursor before checking for duplicate submissions so
sending the same message twice exits history mode. This prevents Up/Down
from staying stuck in history browsing after duplicate sends.
1. You can now add streamable http servers via the CLI
2. As part of this, I'm also changing the existing bearer_token plain
text config field with ane env var
```
mcp add github --url https://api.githubcopilot.com/mcp/ --bearer-token-env-var=GITHUB_PAT
```
This lets users/companies explicitly choose whether to force/disallow
the keyring/fallback file storage for mcp credentials.
People who develop with Codex will want to use this until we sign
binaries or else each ad-hoc debug builds will require keychain access
on every build. I don't love this and am open to other ideas for how to
handle that.
```toml
mcp_oauth_credentials_store = "auto"
mcp_oauth_credentials_store = "file"
mcp_oauth_credentials_store = "keyrung"
```
Defaults to `auto`
# 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.
## Summary
- ensure the TypeScript SDK sets CODEX_INTERNAL_ORIGINATOR_OVERRIDE to
codex_sdk_ts when spawning the Codex CLI
- extend the responses proxy test helper to capture request headers for
assertions
- add coverage that verifies Codex threads launched from the TypeScript
SDK send the codex_sdk_ts originator header
## Testing
- Not Run (not requested)
------
https://chatgpt.com/codex/tasks/task_i_68e561b125248320a487f129093d16e7
We use to put the review prompt in the first user message as well to
bypass statsig overrides, but now that's been resolved and instructions
are being respected, so we're duplicating the review instructions.
this fixes an issue where text lines with long words would sometimes
overflow.
- the default penalties for the OptimalFit algorithm allow overflowing
in some cases. this seems insane to me, and i considered just banning
the OptimalFit algorithm by disabling the 'smawk' feature on textwrap,
but decided to keep it and just bump the overflow penalty to ~infinity
since optimal fit does sometimes produce nicer wrappings. it's not clear
this is worth it, though, and maybe we should just dump the optimal fit
algorithm completely.
- user history messages weren't rendering with the same wrap algorithm
as used in the composer, which sometimes resulted in wrapping messages
differently in the history vs. in the composer.
Before this change:
```
tamird@L03G26TD12 codex-rs % codex
zsh: do you wish to see all 3864 possibilities (1285 lines)?
```
After this change:
```
tamird@L03G26TD12 codex-rs % codex
app-server -- [experimental] Run the app server
apply a -- Apply the latest diff produced by Codex agent as a `git apply` to your local working tree
cloud -- [EXPERIMENTAL] Browse tasks from Codex Cloud and apply changes locally
completion -- Generate shell completion scripts
debug -- Internal debugging commands
exec e -- Run Codex non-interactively
generate-ts -- Internal: generate TypeScript protocol bindings
help -- Print this message or the help of the given subcommand(s)
login -- Manage login
logout -- Remove stored authentication credentials
mcp -- [experimental] Run Codex as an MCP server and manage MCP servers
mcp-server -- [experimental] Run the Codex MCP server (stdio transport)
responses-api-proxy -- Internal: run the responses API proxy
resume -- Resume a previous interactive session (picker by default; use --last to continue the most recent)
```
when exiting a session that was started with `codex resume`, the note
about how to resume again wasn't being printed.
thanks @aibrahim-oai for pointing out this issue!
`http_config.auth_header` automatically added `Bearer `. By adding it
ourselves, we were sending `Bearer Bearer <token>`.
I confirmed that the GitHub MCP initialization 400s before and works
now.
I also optimized the oauth flow to not check the keyring if you
explicitly pass in a bearer token.
# 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.
## Summary
- add a `codex sandbox` subcommand with macOS and Linux targets while
keeping the legacy `codex debug` aliases
- update documentation to highlight the new sandbox entrypoints and
point existing references to the new command
- clarify the core README about the linux sandbox helper alias
## Testing
- just fmt
- just fix -p codex-cli
- cargo test -p codex-cli
------
https://chatgpt.com/codex/tasks/task_i_68e2e00ca1e8832d8bff53aa0b50b49e
## Summary
- replace manual wiremock SSE mounts in the compact suite with the
shared response helpers
- simplify the exec auth_env integration test by using the
mount_sse_once_match helper
- rely on mount_sse_sequence plus server request collection to replace
the bespoke SeqResponder utility in tests
## Testing
- just fmt
------
https://chatgpt.com/codex/tasks/task_i_68e2e238f2a88320a337f0b9e4098093
## Summary
- expand the TypeScript SDK README with streaming, architecture, and API
docs
- refresh quick start examples and clarify thread management options
## Testing
- Not Run (docs only)
---------
Co-authored-by: pakrym-oai <pakrym@openai.com>
## Summary
- add a reusable `ev_response_created` helper that builds
`response.created` SSE events for integration tests
- update the exec and core integration suites to use the new helper
instead of repeating manual JSON literals
- keep the streaming fixtures consistent by relying on the shared helper
in every touched test
## Testing
- `just fmt`
------
https://chatgpt.com/codex/tasks/task_i_68e1fe885bb883208aafffb94218da61
`codex-responses-api-proxy` is designed so that there should be exactly
one copy of the API key in memory (that is `mlock`'d on UNIX), but in
practice, I was seeing two when I dumped the process data from
`/proc/$PID/mem`.
It appears that `std::io::stdin()` maintains an internal `BufReader`
that we cannot zero out, so this PR changes the implementation on UNIX
so that we use a low-level `read(2)` instead.
Even though it seems like it would be incredibly unlikely, we also make
this logic tolerant of short reads. Either `\n` or `EOF` must be sent to
signal the end of the key written to stdin.
## Summary
- replace manual event polling loops in several core test suites with
the shared wait_for_event helpers
- keep prior assertions intact by using closure captures for stateful
expectations, including plan updates, patch lifecycles, and review flow
checks
- rely on wait_for_event_with_timeout where longer waits are required,
simplifying timeout handling
## Testing
- just fmt
------
https://chatgpt.com/codex/tasks/task_i_68e1d58582d483208febadc5f90dd95e
## Summary
This PR is an alternative approach to #4711, but instead of changing our
storage, parses out shell calls in the client and reserializes them on
the fly before we send them out as part of the request.
What this changes:
1. Adds additional serialization logic when the
ApplyPatchToolType::Freeform is in use.
2. Adds a --custom-apply-patch flag to enable this setting on a
session-by-session basis.
This change is delicate, but is not meant to be permanent. It is meant
to be the first step in a migration:
1. (This PR) Add in-flight serialization with config
2. Update model_family default
3. Update serialization logic to store turn outputs in a structured
format, with logic to serialize based on model_family setting.
4. Remove this rewrite in-flight logic.
## Test Plan
- [x] Additional unit tests added
- [x] Integration tests added
- [x] Tested locally
In the past, we were treating `input exceeded context window` as a
streaming error and retrying on it. Retrying on it has no point because
it won't change the behavior. In this PR, we surface the error to the
client without retry and also send a token count event to indicate that
the context window is full.
<img width="650" height="125" alt="image"
src="https://github.com/user-attachments/assets/c26b1213-4c27-4bfc-90f4-51a270a3efd5"
/>
We truncate the output of exec commands to not blow the context window.
However, some cases we weren't doing that. This caused reports of people
with 76% context window left facing `input exceeded context window`
which is weird.
# 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.
The `experimental_use_rmcp_client` flag is still useful to:
1. Toggle between stdio clients
2. Enable oauth beacuse we want to land
https://github.com/modelcontextprotocol/rust-sdk/pull/469,
https://github.com/openai/codex/pull/4677, and binary signing before we
enable it by default
However, for no-auth http servers, there is only one option so we don't
need the flag and it seems to be working pretty well.
Codex isn’t great yet on Windows outside of WSL, and while we’ve merged
https://github.com/openai/codex/pull/4269 to reduce the repetitive
manual approvals on readonly commands, we’ve noticed that users seem to
have more issues with GPT-5-Codex than with GPT-5 on Windows.
This change makes GPT-5 the default for Windows users while we continue
to improve the CLI harness and model for GPT-5-Codex on Windows.
## Summary
- Factor `load_config_as_toml` into `core::config_loader` so config
loading is reusable across callers.
- Layer `~/.codex/config.toml`, optional `~/.codex/managed_config.toml`,
and macOS managed preferences (base64) with recursive table merging and
scoped threads per source.
## Config Flow
```
Managed prefs (macOS profile: com.openai.codex/config_toml_base64)
▲
│
~/.codex/managed_config.toml │ (optional file-based override)
▲
│
~/.codex/config.toml (user-defined settings)
```
- The loader searches under the resolved `CODEX_HOME` directory
(defaults to `~/.codex`).
- Managed configs let administrators ship fleet-wide overrides via
device profiles which is useful for enforcing certain settings like
sandbox or approval defaults.
- For nested hash tables: overlays merge recursively. Child tables are
merged key-by-key, while scalar or array values replace the prior layer
entirely. This lets admins add or tweak individual fields without
clobbering unrelated user settings.
Apparently we were not running our `pnpm run prettier` check in CI, so
many files that were covered by the existing Prettier check were not
well-formatted.
This updates CI and formats the files.
This PR adds oauth login support to streamable http servers when
`experimental_use_rmcp_client` is enabled.
This PR is large but represents the minimal amount of work required for
this to work. To keep this PR smaller, login can only be done with
`codex mcp login` and `codex mcp logout` but it doesn't appear in `/mcp`
or `codex mcp list` yet. Fingers crossed that this is the last large MCP
PR and that subsequent PRs can be smaller.
Under the hood, credentials are stored using platform credential
managers using the [keyring crate](https://crates.io/crates/keyring).
When the keyring isn't available, it falls back to storing credentials
in `CODEX_HOME/.credentials.json` which is consistent with how other
coding agents handle authentication.
I tested this on macOS, Windows, WSL (ubuntu), and Linux. I wasn't able
to test the dbus store on linux but did verify that the fallback works.
One quirk is that if you have credentials, during development, every
build will have its own ad-hoc binary so the keyring won't recognize the
reader as being the same as the write so it may ask for the user's
password. I may add an override to disable this or allow
users/enterprises to opt-out of the keyring storage if it causes issues.
<img width="5064" height="686" alt="CleanShot 2025-09-30 at 19 31 40"
src="https://github.com/user-attachments/assets/9573f9b4-07f1-4160-83b8-2920db287e2d"
/>
<img width="745" height="486" alt="image"
src="https://github.com/user-attachments/assets/9562649b-ea5f-4f22-ace2-d0cb438b143e"
/>
This issue was due to the fact that the timeout is not always sufficient
to have enough character for truncation + a race between synthetic
timeout and process kill
# 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.
This updates `codex exec` so that, by default, most of the agent's
activity is written to stderr so that only the final agent message is
written to stdout. This makes it easier to pipe `codex exec` into
another tool without extra filtering.
I introduced `#![deny(clippy::print_stdout)]` to help enforce this
change and renamed the `ts_println!()` macro to `ts_msg()` because (1)
it no longer calls `println!()` and (2), `ts_eprintln!()` seemed too
long of a name.
While here, this also adds `-o` as an alias for `--output-last-message`.
Fixes https://github.com/openai/codex/issues/1670
# Tool System Refactor
- Centralizes tool definitions and execution in `core/src/tools/*`:
specs (`spec.rs`), handlers (`handlers/*`), router (`router.rs`),
registry/dispatch (`registry.rs`), and shared context (`context.rs`).
One registry now builds the model-visible tool list and binds handlers.
- Router converts model responses to tool calls; Registry dispatches
with consistent telemetry via `codex-rs/otel` and unified error
handling. Function, Local Shell, MCP, and experimental `unified_exec`
all flow through this path; legacy shell aliases still work.
- Rationale: reduce per‑tool boilerplate, keep spec/handler in sync, and
make adding tools predictable and testable.
Example: `read_file`
- Spec: `core/src/tools/spec.rs` (see `create_read_file_tool`,
registered by `build_specs`).
- Handler: `core/src/tools/handlers/read_file.rs` (absolute `file_path`,
1‑indexed `offset`, `limit`, `L#: ` prefixes, safe truncation).
- E2E test: `core/tests/suite/read_file.rs` validates the tool returns
the requested lines.
## Next steps:
- Decompose `handle_container_exec_with_params`
- Add parallel tool calls
Previously, users could supply their API key directly via:
```shell
codex login --api-key KEY
```
but this has the drawback that `KEY` is more likely to end up in shell
history, can be read from `/proc`, etc.
This PR removes support for `--api-key` and replaces it with
`--with-api-key`, which reads the key from stdin, so either of these are
better options:
```
printenv OPENAI_API_KEY | codex login --with-api-key
codex login --with-api-key < my_key.txt
```
Other CLIs, such as `gh auth login --with-token`, follow the same
practice.
Before when you would enter `/di`, hit tab on `/diff`, and then hit
enter, it would execute `/diff`. But now it's just sending it as a text.
This fixes the issue.
### Summary
* Updated fuzzy search result to include the file name.
* This should not affect CLI usage and the UI there will be addressed in
a separate PR.
### Testing
Tested locally and with the extension.
### Screenshot
<img width="431" height="244" alt="Screenshot 2025-10-02 at 11 08 44 AM"
src="https://github.com/user-attachments/assets/ba2ca299-a81d-4453-9242-1750e945aea2"
/>
---------
Co-authored-by: shijie.rao <shijie.rao@squareup.com>
For MacOS and Linux: copy the output of `uname -mprs`
For Windows: copy the output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in the PowerShell console
- type:textarea
id:actual
attributes:
label:What issue are you seeing?
description:Please include the full error messages and prompts with PII redacted. If possible, please provide text instead of a screenshot.
validations:
required:true
- type:textarea
id:steps
attributes:
label:What steps can reproduce the bug?
description:Explain the bug and provide a code snippet that can reproduce it.
description:Explain the bug and provide a code snippet that can reproduce it. Please include session id, token limit usage, context window usage if applicable.
validations:
required:true
- type:textarea
@@ -44,11 +59,6 @@ body:
attributes:
label:What is the expected behavior?
description:If possible, please provide text instead of a screenshot.
- type:textarea
id:actual
attributes:
label:What do you see instead?
description:If possible, please provide text instead of a screenshot.
label:What version of the VS Code extension are you using?
validations:
required:true
- type:input
id:plan
attributes:
label:What subscription do you have?
validations:
required:true
- type:input
id:ide
attributes:
label:Which IDE are you using?
description:Like `VS Code`, `Cursor`, `Windsurf`, etc.
validations:
required:true
- type:input
id:platform
attributes:
@@ -26,11 +36,18 @@ body:
description:|
For MacOS and Linux: copy the output of `uname -mprs`
For Windows: copy the output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in the PowerShell console
- type:textarea
id:actual
attributes:
label:What issue are you seeing?
description:Please include the full error messages and prompts with PII redacted. If possible, please provide text instead of a screenshot.
validations:
required:true
- type:textarea
id:steps
attributes:
label:What steps can reproduce the bug?
description:Explain the bug and provide a code snippet that can reproduce it.
description:Explain the bug and provide a code snippet that can reproduce it. Please include session id, token limit usage, context window usage if applicable.
validations:
required:true
- type:textarea
@@ -38,11 +55,6 @@ body:
attributes:
label:What is the expected behavior?
description:If possible, please provide text instead of a screenshot.
- type:textarea
id:actual
attributes:
label:What do you see instead?
description:If possible, please provide text instead of a screenshot.
# Cancel previous actions from the same PR or branch except 'main' branch.
# See https://docs.github.com/en/actions/using-jobs/using-concurrency and https://docs.github.com/en/actions/learn-github-actions/contexts for more info.
core.info(`Author ${pr.user.login} has ${permission} access; skipping #${pr.number}`);
continue;
}
stalePrs.push(pr);
}
if (!stalePrs.length) {
core.info("No stale contributor pull requests found.");
return;
}
for (const pr of stalePrs) {
const issue_number = pr.number;
const closeComment = `Closing this pull request because it has had no updates for more than ${DAYS_INACTIVE} days. If you plan to continue working on it, feel free to reopen or open a new PR.`;
if (dryRun) {
core.info(`[dry-run] Would close contributor PR #${issue_number} from ${pr.user.login}`);
continue;
}
await github.rest.issues.createComment({
owner,
repo,
issue_number,
body: closeComment,
});
await github.rest.pulls.update({
owner,
repo,
pull_number: issue_number,
state: "closed",
});
core.info(`Closed contributor PR #${issue_number} from ${pr.user.login}`);
You are an assistant that triages new GitHub issues by identifying potential duplicates.
You will receive the following JSON files located in the current working directory:
- `codex-current-issue.json`: JSON object describing the newly created issue (fields: number, title, body).
- `codex-existing-issues.json`: JSON array of recent issues (each element includes number, title, body, createdAt).
Instructions:
- Compare the current issue against the existing issues to find up to five that appear to describe the same underlying problem or request.
- Focus on the underlying intent and context of each issue—such as reported symptoms, feature requests, reproduction steps, or error messages—rather than relying solely on string similarity or synthetic metrics.
- After your analysis, validate your results in 1-2 lines explaining your decision to return the selected matches.
You are an assistant that reviews GitHub issues for the repository.
Your job is to choose the most appropriate labels for the issue described later in this prompt.
Follow these rules:
- Add one (and only one) of the following three labels to distinguish the type of issue. Default to "bug" if unsure.
1. bug — Reproducible defects in Codex products (CLI, VS Code extension, web, auth).
2. enhancement — Feature requests or usability improvements that ask for new capabilities, better ergonomics, or quality-of-life tweaks.
3. documentation — Updates or corrections needed in docs/README/config references (broken links, missing examples, outdated keys, clarification requests).
- If applicable, add one of the following labels to specify which sub-product or product surface the issue relates to.
1. CLI — the Codex command line interface.
2. extension — VS Code (or other IDE) extension-specific issues.
3. codex-web — Issues targeting the Codex web UI/Cloud experience.
4. github-action — Issues with the Codex GitHub action.
5. iOS — Issues with the Codex iOS app.
- Additionally add zero or more of the following labels that are relevant to the issue content. Prefer a small set of precise labels over many broad ones.
1. windows-os — Bugs or friction specific to Windows environments (always when PowerShell is mentioned, path handling, copy/paste, OS-specific auth or tooling failures).
2. mcp — Topics involving Model Context Protocol servers/clients.
3. mcp-server — Problems related to the codex mcp-server command, where codex runs as an MCP server.
4. azure — Problems or requests tied to Azure OpenAI deployments.
5. model-behavior — Undesirable LLM behavior: forgetting goals, refusing work, hallucinating environment details, quota misreports, or other reasoning/performance anomalies.
6. code-review — Issues related to the code review feature or functionality.
7. auth - Problems related to authentication, login, or access tokens.
8. codex-exec - Problems related to the "codex exec" command or functionality.
9. context-management - Problems related to compaction, context windows, or available context reporting.
10. custom-model - Problems that involve using custom model providers, local models, or OSS models.
11. rate-limits - Problems related to token limits, rate limits, or token usage reporting.
12. sandbox - Issues related to local sandbox environments or tool call approvals to override sandbox restrictions.
13. tool-calls - Problems related to specific tool call invocations including unexpected errors, failures, or hangs.
14. TUI - Problems with the terminal user interface (TUI) including keyboard shortcuts, copy & pasting, menus, or screen update issues.
Issue number: ${{ github.event.issue.number }}
Issue title:
${{ github.event.issue.title }}
Issue body:
${{ github.event.issue.body }}
Repository full name:
${{ github.repository }}
output-schema:|
{
"type": "object",
"properties": {
"labels": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["labels"],
"additionalProperties": false
}
apply-labels:
name:Apply labels from Codex output
@@ -53,12 +106,12 @@ jobs:
exit 0
fi
if ! printf '%s' "$json" | jq -e 'type == "array"' >/dev/null 2>&1; then
echo "Codex output was not a JSON array. Raw output: $json"
if ! printf '%s' "$json" | jq -e 'type == "object" and (.labels | type == "array")' >/dev/null 2>&1; then
echo "Codex output did not include a labels array. Raw output: $json"
@@ -8,11 +8,17 @@ In the codex-rs folder where the rust code lives:
- Never add or modify any code related to `CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR` or `CODEX_SANDBOX_ENV_VAR`.
- You operate in a sandbox where `CODEX_SANDBOX_NETWORK_DISABLED=1` will be set whenever you use the `shell` tool. Any existing code that uses `CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR` was authored with this fact in mind. It is often used to early exit out of tests that the author knew you would not be able to run given your sandbox limitations.
- Similarly, when you spawn a process using Seatbelt (`/usr/bin/sandbox-exec`), `CODEX_SANDBOX=seatbelt` will be set on the child process. Integration tests that want to run Seatbelt themselves cannot be run under Seatbelt, so checks for `CODEX_SANDBOX=seatbelt` are also often used to early exit out of tests, as appropriate.
- 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 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.
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:
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.
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.
- Prefer Stylize helpers: use "text".dim(), .bold(), .cyan(), .italic(), .underlined() instead of manual Style where possible.
- Prefer simple conversions: use "text".into() for spans and vec![…].into() for lines; when inference is ambiguous (e.g., Paragraph::new/Cell::from), use Line::from(spans) or Span::from(text).
- Computed styles: if the Style is computed at runtime, using `Span::styled` is OK (`Span::from(text).set_style(style)` is also acceptable).
@@ -39,6 +46,7 @@ See `codex-rs/tui/styles.md`.
- Compactness: prefer the form that stays on one line after rustfmt; if only one of Line::from(vec![…]) or vec![…].into() avoids wrapping, choose that. If both wrap, pick the one with fewer wrapped lines.
### Text wrapping
- Always use textwrap::wrap to wrap plain strings.
- If you have a ratatui Line and you want to wrap it, use the helpers in tui/src/wrapping.rs, e.g. word_wrap_lines / word_wrap_line.
- If you need to indent wrapped lines, use the initial_indent / subsequent_indent options from RtOptions if you can, rather than writing custom logic.
@@ -60,8 +68,44 @@ This repo uses snapshot tests (via `insta`), especially in `codex-rs/tui`, to va
-`cargo insta accept -p codex-tui`
If you don’t have the tool:
-`cargo install cargo-insta`
### Test assertions
- Tests should use pretty_assertions::assert_eq for clearer diffs. Import this at the top of the test module if it isn't already.
- Prefer deep equals comparisons whenever possible. Perform `assert_eq!()` on entire objects, rather than individual fields.
- Avoid mutating process environment in tests; prefer passing environment-derived flags or dependencies from above.
### Spawning workspace binaries in tests (Cargo vs Bazel)
- Prefer `codex_utils_cargo_bin::cargo_bin("...")` over `assert_cmd::Command::cargo_bin(...)` or `escargot` when tests need to spawn first-party binaries.
- Under Bazel, binaries and resources may live under runfiles; use `codex_utils_cargo_bin::cargo_bin` to resolve absolute paths that remain stable after `chdir`.
- When locating fixture files or test resources under Bazel, avoid `env!("CARGO_MANIFEST_DIR")`. Prefer `codex_utils_cargo_bin::find_resource!` so paths resolve correctly under both Cargo and Bazel runfiles.
### Integration tests (core)
- Prefer the utilities in `core_test_support::responses` when writing end-to-end Codex tests.
- All `mount_sse*` helpers return a `ResponseMock`; hold onto it so you can assert against outbound `/responses` POST bodies.
- Use `ResponseMock::single_request()` when a test should only issue one POST, or `ResponseMock::requests()` to inspect every captured `ResponsesRequest`.
-`ResponsesRequest` exposes helpers (`body_json`, `input`, `function_call_output`, `custom_tool_call_output`, `call_output`, `header`, `path`, `query_param`) so assertions can target structured payloads instead of manual JSON digging.
- Build SSE payloads with the provided `ev_*` constructors and the `sse(...)`.
- Prefer `wait_for_event` over `wait_for_event_with_timeout`.
- Prefer `mount_sse_once` over `mount_sse_once_match` or `mount_sse_sequence`
- Typical pattern:
```rust
let mock = responses::mount_sse_once(&server, responses::sse(vec.
You can also use Codex with an API key, but this requires [additional setup](./docs/authentication.md#usage-based-billing-alternative-use-an-openai-api-key). If you previously used an API key for usage-based billing, see the [migration steps](./docs/authentication.md#migrating-from-usage-based-billing-api-key). If you're having trouble with login, please comment on [this issue](https://github.com/openai/codex/issues/1243).
You can also use Codex with an API key, but this requires [additional setup](https://developers.openai.com/codex/auth#sign-in-with-an-api-key).
### Model Context Protocol (MCP)
## Docs
Codex CLI supports [MCP servers](./docs/advanced.md#model-context-protocol-mcp). Enable by adding an `mcp_servers` section to your `~/.codex/config.toml`.
### Configuration
Codex CLI supports a rich set of configuration options, with preferences stored in `~/.codex/config.toml`. For full configuration options, see [Configuration](./docs/config.md).
You can also install via Homebrew (`brew install codex`) or download a platform-specific release directly from our [GitHub Releases](https://github.com/openai/codex/releases).
You can also install via Homebrew (`brew install --cask codex`) or download a platform-specific release directly from our [GitHub Releases](https://github.com/openai/codex/releases).
## Documentation quickstart
- First run with Codex? Start with [`docs/getting-started.md`](../docs/getting-started.md) (links to the walkthrough for prompts, keyboard shortcuts, and session management).
- Want deeper control? See [`docs/config.md`](../docs/config.md) and [`docs/install.md`](../docs/install.md).
## What's new in the Rust CLI
@@ -23,9 +28,15 @@ Codex supports a rich set of configuration options. Note that the Rust CLI uses
### Model Context Protocol Support
Codex CLI functions as an MCP client that can connect to MCP servers on startup. See the [`mcp_servers`](../docs/config.md#mcp_servers) section in the configuration documentation for details.
#### MCP client
It is still experimental, but you can also launch Codex as an MCP _server_ by running `codex mcp-server`. Use the [`@modelcontextprotocol/inspector`](https://github.com/modelcontextprotocol/inspector) to try it out:
Codex CLI functions as an MCP client that allows the Codex CLI and IDE extension to connect to MCP servers on startup. See the [`configuration documentation`](../docs/config.md#connecting-to-mcp-servers) for details.
#### MCP server (experimental)
Codex can be launched as an MCP _server_ by running `codex mcp-server`. This allows _other_ MCP clients to use Codex as a tool for another agent.
Use the [`@modelcontextprotocol/inspector`](https://github.com/modelcontextprotocol/inspector) to try it out:
@@ -35,45 +46,28 @@ Use `codex mcp` to add/list/get/remove MCP server launchers defined in `config.t
### Notifications
You can enable notifications by configuring a script that is run whenever the agent finishes a turn. The [notify documentation](../docs/config.md#notify) includes a detailed example that explains how to get desktop notifications via [terminal-notifier](https://github.com/julienXX/terminal-notifier) on macOS.
You can enable notifications by configuring a script that is run whenever the agent finishes a turn. The [notify documentation](../docs/config.md#notify) includes a detailed example that explains how to get desktop notifications via [terminal-notifier](https://github.com/julienXX/terminal-notifier) on macOS. When Codex detects that it is running under WSL 2 inside Windows Terminal (`WT_SESSION` is set), the TUI automatically falls back to native Windows toast notifications so approval prompts and completed turns surface even though Windows Terminal does not implement OSC 9.
### `codex exec` to run Codex programmatically/non-interactively
To run Codex non-interactively, run `codex exec PROMPT` (you can also pass the prompt via `stdin`) and Codex will work on your task until it decides that it is done and exits. Output is printed to the terminal directly. You can set the `RUST_LOG` environment variable to see more about what's going on.
### Use `@` for file search
Typing `@` triggers a fuzzy-filename search over the workspace root. Use up/down to select among the results and Tab or Enter to replace the `@` with the selected path. You can use Esc to cancel the search.
### Esc–Esc to edit a previous message
When the chat composer is empty, press Esc to prime “backtrack” mode. Press Esc again to open a transcript preview highlighting the last user message; press Esc repeatedly to step to older user messages. Press Enter to confirm and Codex will fork the conversation from that point, trim the visible transcript accordingly, and pre‑fill the composer with the selected user message so you can edit and resubmit it.
In the transcript preview, the footer shows an `Esc edit prev` hint while editing is active.
### `--cd`/`-C` flag
Sometimes it is not convenient to `cd` to the directory you want Codex to use as the "working root" before running Codex. Fortunately, `codex` supports a `--cd` option so you can specify whatever folder you want. You can confirm that Codex is honoring `--cd` by double-checking the **workdir** it reports in the TUI at the start of a new session.
### Shell completions
Generate shell completion scripts via:
```shell
codex completion bash
codex completion zsh
codex completion fish
```
### Experimenting with the Codex Sandbox
To test to see what happens when a command is run under the sandbox provided by Codex, we provide the following subcommands in Codex CLI:
"Generated TypeScript still includes unions with `undefined` in {undefined_offenders:?}"
);
// If this assertion fails, it means a field was generated as
// "?: T | null" — i.e., both optional (undefined) and nullable (null).
// We only want either "?: T" or ": T | null".
assert!(
optional_nullable_offenders.is_empty(),
"Generated TypeScript has optional fields with nullable types (disallowed '?: T | null'), add #[ts(optional)] to fix:\n{optional_nullable_offenders:?}"
`codex app-server` is the interface Codex uses to power rich interfaces such as the [Codex VS Code extension](https://marketplace.visualstudio.com/items?itemName=openai.chatgpt).
## Table of Contents
- [Protocol](#protocol)
- [Message Schema](#message-schema)
- [Core Primitives](#core-primitives)
- [Lifecycle Overview](#lifecycle-overview)
- [Initialization](#initialization)
- [API Overview](#api-overview)
- [Events](#events)
- [Approvals](#approvals)
- [Skills](#skills)
- [Auth endpoints](#auth-endpoints)
## Protocol
Similar to [MCP](https://modelcontextprotocol.io/), `codex app-server` supports bidirectional communication, streaming JSONL over stdio. The protocol is JSON-RPC 2.0, though the `"jsonrpc":"2.0"` header is omitted.
## Message Schema
Currently, you can dump a TypeScript version of the schema using `codex app-server generate-ts`, or a JSON Schema bundle via `codex app-server generate-json-schema`. Each output is specific to the version of Codex you used to run the command, so the generated artifacts are guaranteed to match that version.
```
codex app-server generate-ts --out DIR
codex app-server generate-json-schema --out DIR
```
## Core Primitives
The API exposes three top level primitives representing an interaction between a user and Codex:
- **Thread**: A conversation between a user and the Codex agent. Each thread contains multiple turns.
- **Turn**: One turn of the conversation, typically starting with a user message and finishing with an agent message. Each turn contains multiple items.
- **Item**: Represents user inputs and agent outputs as part of the turn, persisted and used as the context for future conversations. Example items include user message, agent reasoning, agent message, shell command, file edit, etc.
Use the thread APIs to create, list, or archive conversations. Drive a conversation with turn APIs and stream progress via turn notifications.
## Lifecycle Overview
- Initialize once: Immediately after launching the codex app-server process, send an `initialize` request with your client metadata, then emit an `initialized` notification. Any other request before this handshake gets rejected.
- Start (or resume) a thread: Call `thread/start` to open a fresh conversation. The response returns the thread object and you’ll also get a `thread/started` notification. If you’re continuing an existing conversation, call `thread/resume` with its ID instead. If you want to branch from an existing conversation, call `thread/fork` to create a new thread id with copied history.
- Begin a turn: To send user input, call `turn/start` with the target `threadId` and the user's input. Optional fields let you override model, cwd, sandbox policy, etc. This immediately returns the new turn object and triggers a `turn/started` notification.
- Stream events: After `turn/start`, keep reading JSON-RPC notifications on stdout. You’ll see `item/started`, `item/completed`, deltas like `item/agentMessage/delta`, tool progress, etc. These represent streaming model output plus any side effects (commands, tool calls, reasoning notes).
- Finish the turn: When the model is done (or the turn is interrupted via making the `turn/interrupt` call), the server sends `turn/completed` with the final turn state and token usage.
## Initialization
Clients must send a single `initialize` request before invoking any other method, then acknowledge with an `initialized` notification. The server returns the user agent string it will present to upstream services; subsequent requests issued before initialization receive a `"Not initialized"` error, and repeated `initialize` calls receive an `"Already initialized"` error.
Applications building on top of `codex app-server` should identify themselves via the `clientInfo` parameter.
**Important**: `clientInfo.name` is used to identify the client for the OpenAI Compliance Logs Platform. If
you are developing a new Codex integration that is intended for enterprise use, please contact us to get it
added to a known clients list. For more context: https://chatgpt.com/admin/api-reference#tag/Logs:-Codex
Example (from OpenAI's official VSCode extension):
```json
{
"method":"initialize",
"id":0,
"params":{
"clientInfo":{
"name":"codex_vscode",
"title":"Codex VS Code Extension",
"version":"0.1.0"
}
}
}
```
## API Overview
-`thread/start` — create a new thread; emits `thread/started` and auto-subscribes you to turn/item events for that thread.
-`thread/resume` — reopen an existing thread by id so subsequent `turn/start` calls append to it.
-`thread/fork` — fork an existing thread into a new thread id by copying the stored history; emits `thread/started` and auto-subscribes you to turn/item events for the new thread.
-`thread/list` — page through stored rollouts; supports cursor-based pagination and optional `modelProviders` filtering.
-`thread/loaded/list` — list the thread ids currently loaded in memory.
-`thread/archive` — move a thread’s rollout file into the archived directory; returns `{}` on success.
-`thread/rollback` — drop the last N turns from the agent’s in-memory context and persist a rollback marker in the rollout so future resumes see the pruned history; returns the updated `thread` (with `turns` populated) on success.
-`turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications.
-`turn/interrupt` — request cancellation of an in-flight turn by `(thread_id, turn_id)`; success is an empty `{}` response and the turn finishes with `status: "interrupted"`.
-`review/start` — kick off Codex’s automated reviewer for a thread; responds like `turn/start` and emits `item/started`/`item/completed` notifications with `enteredReviewMode` and `exitedReviewMode` items, plus a final assistant `agentMessage` containing the review.
-`command/exec` — run a single command under the server sandbox without starting a thread/turn (handy for utilities and validation).
-`model/list` — list available models (with reasoning effort options).
-`skills/list` — list skills for one or more `cwd` values (optional `forceReload`).
-`mcpServer/oauth/login` — start an OAuth login for a configured MCP server; returns an `authorization_url` and later emits `mcpServer/oauthLogin/completed` once the browser flow finishes.
-`mcpServerStatus/list` — enumerate configured MCP servers with their tools, resources, resource templates, and auth status; supports cursor+limit pagination.
-`feedback/upload` — submit a feedback report (classification + optional reason/logs and conversation_id); returns the tracking thread id.
-`command/exec` — run a single command under the server sandbox without starting a thread/turn (handy for utilities and validation).
-`config/read` — fetch the effective config on disk after resolving config layering.
-`config/value/write` — write a single config key/value to the user's config.toml on disk.
-`config/batchWrite` — apply multiple config edits atomically to the user's config.toml on disk.
-`configRequirements/read` — fetch the loaded requirements allow-lists from `requirements.toml` and/or MDM (or `null` if none are configured).
### Example: Start or resume a thread
Start a fresh thread when you need a new Codex conversation.
```json
{"method":"thread/start","id":10,"params":{
// Optionally set config settings. If not specified, will use the user's
To continue a stored session, call `thread/resume` with the `thread.id` you previously recorded. The response shape matches `thread/start`, and no additional notifications are emitted:
To branch from a stored session, call `thread/fork` with the `thread.id`. This creates a new thread id and emits a `thread/started` notification for it:
When `nextCursor` is `null`, you’ve reached the final page.
### Example: List loaded threads
`thread/loaded/list` returns thread ids currently loaded in memory. This is useful when you want to check which sessions are active without scanning rollouts on disk.
```json
{"method":"thread/loaded/list","id":21}
{"id":21,"result":{
"data":["thr_123","thr_456"]
}}
```
### Example: Archive a thread
Use `thread/archive` to move the persisted rollout (stored as a JSONL file on disk) into the archived sessions directory.
You can optionally specify config overrides on the new turn. If specified, these settings become the default for subsequent turns on the same thread. `outputSchema` applies only to the current turn.
```json
{"method":"turn/start","id":30,"params":{
"threadId":"thr_123",
"input":[{"type":"text","text":"Run tests"}],
// Below are optional config overrides
"cwd":"/Users/me/project",
"approvalPolicy":"unlessTrusted",
"sandboxPolicy":{
"type":"workspaceWrite",
"writableRoots":["/Users/me/project"],
"networkAccess":true
},
"model":"gpt-5.1-codex",
"effort":"medium",
"summary":"concise",
// Optional JSON Schema to constrain the final assistant message for this turn.
"outputSchema":{
"type":"object",
"properties":{"answer":{"type":"string"}},
"required":["answer"],
"additionalProperties":false
}
}}
{"id":30,"result":{"turn":{
"id":"turn_456",
"status":"inProgress",
"items":[],
"error":null
}}}
```
### Example: Start a turn (invoke a skill)
Invoke a skill explicitly by including `$<skill-name>` in the text input and adding a `skill` input item alongside it.
```json
{"method":"turn/start","id":33,"params":{
"threadId":"thr_123",
"input":[
{"type":"text","text":"$skill-creator Add a new skill for triaging flaky CI and include step-by-step usage."},
You can cancel a running Turn with `turn/interrupt`.
```json
{"method":"turn/interrupt","id":31,"params":{
"threadId":"thr_123",
"turnId":"turn_456"
}}
{"id":31,"result":{}}
```
The server requests cancellations for running subprocesses, then emits a `turn/completed` event with `status: "interrupted"`. Rely on the `turn/completed` to know when Codex-side cleanup is done.
### Example: Request a code review
Use `review/start` to run Codex’s reviewer on the currently checked-out project. The request takes the thread id plus a `target` describing what should be reviewed:
-`{"type":"uncommittedChanges"}` — staged, unstaged, and untracked files.
-`{"type":"baseBranch","branch":"main"}` — diff against the provided branch’s upstream (see prompt for the exact `git merge-base`/`git diff` instructions Codex will run).
-`{"type":"commit","sha":"abc1234","title":"Optional subject"}` — review a specific commit.
-`{"type":"custom","instructions":"Free-form reviewer instructions"}` — fallback prompt equivalent to the legacy manual review request.
-`delivery` (`"inline"` or `"detached"`, default `"inline"`) — where the review runs:
-`"inline"`: run the review as a new turn on the existing thread. The response’s `reviewThreadId` equals the original `threadId`, and no new `thread/started` notification is emitted.
-`"detached"`: fork a new review thread from the parent conversation and run the review there. The response’s `reviewThreadId` is the id of this new review thread, and the server emits a `thread/started` notification for it before streaming review items.
For a detached review, use `"delivery": "detached"`. The response is the same shape, but `reviewThreadId` will be the id of the new review thread (different from the original `threadId`). The server also emits a `thread/started` notification for that new thread before streaming the review turn.
Codex streams the usual `turn/started` notification followed by an `item/started`
with an `enteredReviewMode` item so clients can show progress:
```json
{
"method":"item/started",
"params":{
"item":{
"type":"enteredReviewMode",
"id":"turn_900",
"review":"current changes"
}
}
}
```
When the reviewer finishes, the server emits `item/started` and `item/completed`
containing an `exitedReviewMode` item with the final review text:
The `review` string is plain text that already bundles the overall explanation plus a bullet list for each structured finding (matching `ThreadItem::ExitedReviewMode` in the generated schema). Use this notification to render the reviewer output in your client.
### Example: One-off command execution
Run a standalone command (argv vector) in the server’s sandbox without creating a thread or turn:
```json
{"method":"command/exec","id":32,"params":{
"command":["ls","-la"],
"cwd":"/Users/me/project",// optional; defaults to server cwd
"sandboxPolicy":{"type":"workspaceWrite"},// optional; defaults to user config
"timeoutMs":10000// optional; ms timeout; defaults to server timeout
- For clients that are already sandboxed externally, set `sandboxPolicy` to `{"type":"externalSandbox","networkAccess":"enabled"}` (or omit `networkAccess` to keep it restricted). Codex will not enforce its own sandbox in this mode; it tells the model it has full file-system access and passes the `networkAccess` state through `environment_context`.
Notes:
- Empty `command` arrays are rejected.
-`sandboxPolicy` accepts the same shape used by `turn/start` (e.g., `dangerFullAccess`, `readOnly`, `workspaceWrite` with flags, `externalSandbox` with `networkAccess``restricted|enabled`).
- When omitted, `timeoutMs` falls back to the server default.
## Events
Event notifications are the server-initiated event stream for thread lifecycles, turn lifecycles, and the items within them. After you start or resume a thread, keep reading stdout for `thread/started`, `turn/*`, and `item/*` notifications.
### Turn events
The app-server streams JSON-RPC notifications while a turn is running. Each turn starts with `turn/started` (initial `turn`) and ends with `turn/completed` (final `turn` status). Token usage events stream separately via `thread/tokenUsage/updated`. Clients subscribe to the events they care about, rendering each item incrementally as updates arrive. The per-item lifecycle is always: `item/started` → zero or more item-specific deltas → `item/completed`.
-`turn/started` — `{ turn }` with the turn id, empty `items`, and `status: "inProgress"`.
-`turn/completed` — `{ turn }` where `turn.status` is `completed`, `interrupted`, or `failed`; failures carry `{ error: { message, codexErrorInfo?, additionalDetails? } }`.
-`turn/diff/updated` — `{ threadId, turnId, diff }` represents the up-to-date snapshot of the turn-level unified diff, emitted after every FileChange item. `diff` is the latest aggregated unified diff across every file change in the turn. UIs can render this to show the full "what changed" view without stitching individual `fileChange` items.
-`turn/plan/updated` — `{ turnId, explanation?, plan }` whenever the agent shares or changes its plan; each `plan` entry is `{ step, status }` with `status` in `pending`, `inProgress`, or `completed`.
Today both notifications carry an empty `items` array even when item events were streamed; rely on `item/*` notifications for the canonical item list until this is fixed.
#### Items
`ThreadItem` is the tagged union carried in turn responses and `item/*` notifications. Currently we support events for the following items:
-`userMessage` — `{id, content}` where `content` is a list of user inputs (`text`, `image`, or `localImage`).
-`agentMessage` — `{id, text}` containing the accumulated agent reply.
-`reasoning` — `{id, summary, content}` where `summary` holds streamed reasoning summaries (applicable for most OpenAI models) and `content` holds raw reasoning blocks (applicable for e.g. open source models).
-`commandExecution` — `{id, command, cwd, status, commandActions, aggregatedOutput?, exitCode?, durationMs?}` for sandboxed commands; `status` is `inProgress`, `completed`, `failed`, or `declined`.
-`fileChange` — `{id, changes, status}` describing proposed edits; `changes` list `{path, kind, diff}` and `status` is `inProgress`, `completed`, `failed`, or `declined`.
-`mcpToolCall` — `{id, server, tool, status, arguments, result?, error?}` describing MCP calls; `status` is `inProgress`, `completed`, or `failed`.
-`webSearch` — `{id, query}` for a web search request issued by the agent.
-`imageView` — `{id, path}` emitted when the agent invokes the image viewer tool.
-`enteredReviewMode` — `{id, review}` sent when the reviewer starts; `review` is a short user-facing label such as `"current changes"` or the requested target description.
-`exitedReviewMode` — `{id, review}` emitted when the reviewer finishes; `review` is the full plain-text review (usually, overall notes plus bullet point findings).
-`compacted` - `{threadId, turnId}` when codex compacts the conversation history. This can happen automatically.
All items emit two shared lifecycle events:
-`item/started` — emits the full `item` when a new unit of work begins so the UI can render it immediately; the `item.id` in this payload matches the `itemId` used by deltas.
-`item/completed` — sends the final `item` once that work finishes (e.g., after a tool call or message completes); treat this as the authoritative state.
There are additional item-specific events:
#### agentMessage
-`item/agentMessage/delta` — appends streamed text for the agent message; concatenate `delta` values for the same `itemId` in order to reconstruct the full reply.
#### reasoning
-`item/reasoning/summaryTextDelta` — streams readable reasoning summaries; `summaryIndex` increments when a new summary section opens.
-`item/reasoning/summaryPartAdded` — marks the boundary between reasoning summary sections for an `itemId`; subsequent `summaryTextDelta` entries share the same `summaryIndex`.
-`item/reasoning/textDelta` — streams raw reasoning text (only applicable for e.g. open source models); use `contentIndex` to group deltas that belong together before showing them in the UI.
#### commandExecution
-`item/commandExecution/outputDelta` — streams stdout/stderr for the command; append deltas in order to render live output alongside `aggregatedOutput` in the final item.
Final `commandExecution` items include parsed `commandActions`, `status`, `exitCode`, and `durationMs` so the UI can summarize what ran and whether it succeeded.
#### fileChange
-`item/fileChange/outputDelta` - contains the tool call response of the underlying `apply_patch` tool call.
### Errors
`error` event is emitted whenever the server hits an error mid-turn (for example, upstream model errors or quota limits). Carries the same `{ error: { message, codexErrorInfo?, additionalDetails? } }` payload as `turn.status: "failed"` and may precede that terminal notification.
`codexErrorInfo` maps to the `CodexErrorInfo` enum. Common values:
-`ContextWindowExceeded`
-`UsageLimitExceeded`
-`HttpConnectionFailed { httpStatusCode? }`: upstream HTTP failures including 4xx/5xx
-`ResponseStreamConnectionFailed { httpStatusCode? }`: failure to connect to the response SSE stream
-`ResponseStreamDisconnected { httpStatusCode? }`: disconnect of the response SSE stream in the middle of a turn before completion
When an upstream HTTP status is available (for example, from the Responses API or a provider), it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant.
## Approvals
Certain actions (shell commands or modifying files) may require explicit user approval depending on the user's config. When `turn/start` is used, the app-server drives an approval flow by sending a server-initiated JSON-RPC request to the client. The client must respond to tell Codex whether to proceed. UIs should present these requests inline with the active turn so users can review the proposed command or diff before choosing.
- Requests include `threadId` and `turnId`—use them to scope UI state to the active conversation.
- Respond with a single `{ "decision": "accept" | "decline" }` payload (plus optional `acceptSettings` on command executions). The server resumes or declines the work and ends the item with `item/completed`.
### Command execution approvals
Order of messages:
1.`item/started` — shows the pending `commandExecution` item with `command`, `cwd`, and other fields so you can render the proposed action.
2.`item/commandExecution/requestApproval` (request) — carries the same `itemId`, `threadId`, `turnId`, optionally `reason` or `risk`, plus `parsedCmd` for friendly display.
4.`item/completed` — final `commandExecution` item with `status: "completed" | "failed" | "declined"` and execution output. Render this as the authoritative result.
### File change approvals
Order of messages:
1.`item/started` — emits a `fileChange` item with `changes` (diff chunk summaries) and `status: "inProgress"`. Show the proposed edits and paths to the user.
2.`item/fileChange/requestApproval` (request) — includes `itemId`, `threadId`, `turnId`, and an optional `reason`.
4.`item/completed` — returns the same `fileChange` item with `status` updated to `completed`, `failed`, or `declined` after the patch attempt. Rely on this to show success/failure and finalize the diff state in your UI.
UI guidance for IDEs: surface an approval dialog as soon as the request arrives. The turn will proceed after the server receives a response to the approval request. The terminal `item/completed` notification will be sent with the appropriate status.
## Skills
Invoke a skill by including `$<skill-name>` in the text input. Add a `skill` input item (recommended) so the backend injects full skill instructions instead of relying on the model to resolve the name.
```json
{
"method":"turn/start",
"id":101,
"params":{
"threadId":"thread-1",
"input":[
{"type":"text","text":"$skill-creator Add a new skill for triaging flaky CI."},
If you omit the `skill` item, the model will still parse the `$<skill-name>` marker and try to locate the skill, which can add latency.
Example:
```
$skill-creator Add a new skill for triaging flaky CI and include step-by-step usage.
```
Use `skills/list` to fetch the available skills (optionally scoped by `cwd` and/or with `forceReload`).
```json
{"method":"skills/list","id":25,"params":{
"cwd":"/Users/me/project",
"forceReload":false
}}
{"id":25,"result":{
"skills":[
{"name":"skill-creator","description":"Create or update a Codex skill"}
]
}}
```
## Auth endpoints
The JSON-RPC auth/account surface exposes request/response methods plus server-initiated notifications (no `id`). Use these to determine auth state, start or cancel logins, logout, and inspect ChatGPT rate limits.
### API Overview
-`account/read` — fetch current account info; optionally refresh tokens.
-`account/login/start` — begin login (`apiKey` or `chatgpt`).
-`account/login/completed` (notify) — emitted when a login attempt finishes (success or error).
-`account/login/cancel` — cancel a pending ChatGPT login by `loginId`.
-`mcpServer/oauthLogin/completed` (notify) — emitted after a `mcpServer/oauth/login` flow finishes for a server; payload includes `{ name, success, error? }`.
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.