Agent wouldn't "see" attached images and would instead try to use the
view_file tool:
<img width="1516" height="504" alt="image"
src="https://github.com/user-attachments/assets/68a705bb-f962-4fc1-9087-e932a6859b12"
/>
In this PR, we wrap image content items in XML tags with the name of
each image (now just a numbered name like `[Image #1]`), so that the
model can understand inline image references (based on name). We also
put the image content items above the user message which the model seems
to prefer (maybe it's more used to definitions being before references).
We also tweak the view_file tool description which seemed to help a bit
Results on a simple eval set of images:
Before
<img width="980" height="310" alt="image"
src="https://github.com/user-attachments/assets/ba838651-2565-4684-a12e-81a36641bf86"
/>
After
<img width="918" height="322" alt="image"
src="https://github.com/user-attachments/assets/10a81951-7ee6-415e-a27e-e7a3fd0aee6f"
/>
```json
[
{
"id": "single_describe",
"prompt": "Describe the attached image in one sentence.",
"images": ["image_a.png"]
},
{
"id": "single_color",
"prompt": "What is the dominant color in the image? Answer with a single color word.",
"images": ["image_b.png"]
},
{
"id": "orientation_check",
"prompt": "Is the image portrait or landscape? Answer in one sentence.",
"images": ["image_c.png"]
},
{
"id": "detail_request",
"prompt": "Look closely at the image and call out any small details you notice.",
"images": ["image_d.png"]
},
{
"id": "two_images_compare",
"prompt": "I attached two images. Are they the same or different? Briefly explain.",
"images": ["image_a.png", "image_b.png"]
},
{
"id": "two_images_captions",
"prompt": "Provide a short caption for each image (Image 1, Image 2).",
"images": ["image_c.png", "image_d.png"]
},
{
"id": "multi_image_rank",
"prompt": "Rank the attached images from most colorful to least colorful.",
"images": ["image_a.png", "image_b.png", "image_c.png"]
},
{
"id": "multi_image_choice",
"prompt": "Which image looks more vibrant? Answer with 'Image 1' or 'Image 2'.",
"images": ["image_b.png", "image_d.png"]
}
]
```
As explained in `codex-rs/core/BUILD.bazel`, including the repo's own
`AGENTS.md` is a hack to get some tests passing. We should fix this
properly, but I wanted to put stake in the ground ASAP to get `just
bazel-remote-test` working and then add a job to `bazel.yml` to ensure
it keeps working.
As noted in the comment, this was causing a problem for me locally
because Sapling backed up some files under `.git/sl` named `BUILD.bazel`
and so Bazel tried to parse them.
It's a bit surprising that Bazel does not ignore `.git` out of the box
such that you have to opt-in to considering it rather than opting-out.
Add model provider info to /status if non-default
Enterprises are running Codex and migrating between proxied / API key
auth and SIWC. If you accidentally run Codex with `OPENAI_BASE_URL=...`,
which is surprisingly easy to do, we don't tend to surface this anywhere
and it may lead to breakage. One suggestion was to include this
information in `/status`:
<img width="477" height="157" alt="Screenshot 2026-01-09 at 15 45 34"
src="https://github.com/user-attachments/assets/630ce68f-c856-4a2b-a004-7df2fbe5de93"
/>
### 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)
# 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.
@@ -11,7 +11,6 @@ In the codex-rs folder where the rust code lives:
- Always collapse if statements per https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
- Always inline format! args when possible per https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args
- Use method references over closures when possible per https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
- Do not use unsigned integer even if the number cannot be negative.
- 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.
@@ -75,6 +74,14 @@ If you don’t have the tool:
### 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.
<p align="center"><code>npm i -g @openai/codex</code><br />or <code>brew install --cask codex</code></p>
<p align="center"><strong>Codex CLI</strong> is a coding agent from OpenAI that runs locally on your computer.
</br>
</br>If you want Codex in your code editor (VS Code, Cursor, Windsurf), <a href="https://developers.openai.com/codex/ide">install in your IDE</a>
</br>If you are looking for the <em>cloud-based agent</em> from OpenAI, <strong>Codex Web</strong>, go to <a href="https://chatgpt.com/codex">chatgpt.com/codex</a></p>
If you want Codex in your code editor (VS Code, Cursor, Windsurf), <a href="https://developers.openai.com/codex/ide">install in your IDE.</a>
</br>If you are looking for the <em>cloud-based agent</em> from OpenAI, <strong>Codex Web</strong>, go to <a href="https://chatgpt.com/codex">chatgpt.com/codex</a>.</p>
---
@@ -15,25 +13,19 @@
### Installing and running Codex CLI
Install globally with your preferred package manager. If you use npm:
Install globally with your preferred package manager:
```shell
# Install using npm
npm install -g @openai/codex
```
Alternatively, if you use Homebrew:
```shell
# Install using Homebrew
brew install --cask codex
```
Then simply run `codex` to get started:
```shell
codex
```
If you're running into upgrade issues with Homebrew, see the [FAQ entry on brew upgrade codex](./docs/faq.md#brew-upgrade-codex-isnt-upgrading-me).
Then simply run `codex` to get started.
<details>
<summary>You can also go to the <a href="https://github.com/openai/codex/releases/latest">latest GitHub Release</a> and download the appropriate binary for your platform.</summary>
@@ -53,57 +45,15 @@ Each archive contains a single entry with the platform baked into the name (e.g.
Run `codex` and select **Sign in with ChatGPT**. We recommend signing into your ChatGPT account to use Codex as part of your Plus, Pro, Team, Edu, or Enterprise plan. [Learn more about what's included in your ChatGPT plan](https://help.openai.com/en/articles/11369540-codex-in-chatgpt).
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 can access MCP servers. To configure them, refer to the [config docs](./docs/config.md#mcp_servers).
### 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).
@@ -15,8 +15,8 @@ You can also install via Homebrew (`brew install --cask codex`) or download a pl
## Documentation quickstart
- First run with Codex? Follow the walkthrough in [`docs/getting-started.md`](../docs/getting-started.md) for prompts, keyboard shortcuts, and session management.
-Already shipping with Codex and want deeper control? Jump to [`docs/advanced.md`](../docs/advanced.md) and the configuration reference at [`docs/config.md`](../docs/config.md).
- 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
@@ -30,7 +30,7 @@ Codex supports a rich set of configuration options. Note that the Rust CLI uses
#### MCP client
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#mcp_servers) for details.
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)
@@ -46,7 +46,7 @@ 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
`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). The message schema is currently unstable, but those who wish to build experimental UIs on top of Codex may find it valuable.
`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).
@@ -25,10 +28,20 @@ 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.
- 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.
@@ -37,37 +50,53 @@ codex app-server generate-json-schema --out DIR
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.
Example:
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"}
- Thread - a conversation between the Codex agent and a user. 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.
## Thread & turn endpoints
The JSON-RPC API exposes dedicated methods for managing Codex conversations. Threads store long-lived conversation metadata, and turns store the per-message exchange (input → Codex output, including streamed items). Use the thread APIs to create, list, or archive sessions, then drive the conversation with turn APIs and notifications.
### Quick reference
-`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 a `item/completed` notification with a `codeReview` item when results are ready.
-`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).
### 1) Start or resume a thread
### Example: Start or resume a thread
Start a fresh thread when you need a new Codex conversation.
@@ -98,9 +127,18 @@ To continue a stored session, call `thread/resume` with the `thread.id` you prev
{"id":11,"result":{"thread":{"id":"thr_123",…}}}
```
### 2) List threads (pagination & filters)
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:
### Example: List threads (with pagination & filters)
`thread/list` lets you render a history UI. Pass any combination of:
-`cursor` — opaque string from a prior response; omit for the first page.
-`limit` — server defaults to a reasonable page size if unset.
-`modelProviders` — restrict results to specific providers; unset, null, or an empty array will include all providers.
@@ -123,7 +161,18 @@ Example:
When `nextCursor` is `null`, you’ve reached the final page.
### 3) Archive a thread
### 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.
@@ -134,7 +183,7 @@ Use `thread/archive` to move the persisted rollout (stored as a JSONL file on di
An archived thread will not appear in future calls to `thread/list`.
### 4) Start a turn (send user input)
### Example: Start a turn (send user input)
Turns attach user input (text or images) to a thread and trigger Codex generation. The `input` field is a list of discriminated unions:
@@ -142,7 +191,7 @@ Turns attach user input (text or images) to a thread and trigger Codex generatio
You can optionally specify config overrides on the new turn. If specified, these settings become the default for subsequent turns on the same thread.
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":{
@@ -152,13 +201,20 @@ You can optionally specify config overrides on the new turn. If specified, these
"cwd":"/Users/me/project",
"approvalPolicy":"unlessTrusted",
"sandboxPolicy":{
"mode":"workspaceWrite",
"type":"workspaceWrite",
"writableRoots":["/Users/me/project"],
"networkAccess":true
},
"model":"gpt-5.1-codex",
"effort":"medium",
"summary":"concise"
"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",
@@ -168,7 +224,27 @@ You can optionally specify config overrides on the new turn. If specified, these
}}}
```
### 5) Interrupt an active turn
### 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`.
@@ -182,7 +258,7 @@ You can cancel a running Turn with `turn/interrupt`.
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.
### 6) Request a code review
### 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:
@@ -190,55 +266,239 @@ Use `review/start` to run Codex’s reviewer on the currently checked-out projec
-`{"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.
-`appendToOriginalThread` (bool, default `false`) — when `true`, Codex also records a final assistant-style message with the review summary in the original thread. When `false`, only the `codeReview` item is emitted for the review run and no extra message is added to the original thread.
-`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 the same `codeReview` item id so clients can show progress:
with an `enteredReviewMode` item so clients can show progress:
```json
{"method":"item/started","params":{"item":{
"type":"codeReview",
"id":"turn_900",
"review":"current changes"
}}}
{
"method":"item/started",
"params":{
"item":{
"type":"enteredReviewMode",
"id":"turn_900",
"review":"current changes"
}
}
}
```
When the reviewer finishes, the server emits `item/completed` containing the same
`codeReview` item with the final review text:
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::CodeReview` in the generated schema). Use this notification to render the reviewer output in your client.
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.
### Quick reference
### 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).
@@ -246,15 +506,19 @@ The JSON-RPC auth/account surface exposes request/response methods plus server-i
-`mcpServer/oauthLogin/completed` (notify) — emitted after a `mcpServer/oauth/login` flow finishes for a server; payload includes `{ name, success, error? }`.
- See [“Authentication and authorization” in the config docs](../../docs/config.md#authentication-and-authorization) for configuration knobs.
## Events (work-in-progress)
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` plus token `usage`), and 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`.
#### Thread 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).
- `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.
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.
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.