6511 Commits

Author SHA1 Message Date
Eric Traut
a5040d0b39 tui: split composer attachment and popup state (#22581)
## Why

`ChatComposer` currently owns text editing alongside attachment
bookkeeping and popup lifecycle state, while `BottomPane` still triggers
a couple of popup resyncs after composer methods that already do that
work internally. That blurs the ownership boundary and makes the
composer harder to simplify safely.

This PR is part 1 of a two-part cleanup. It peels off the composer state
that can move cleanly on its own, so the follow-up can tackle the
heavier draft/editing boundary without mixing every concern into one
diff.

## What changed

- Move local and remote image bookkeeping, placeholder relabeling, and
remote-image keyboard selection into `AttachmentState`.
- Move active-popup and popup-dismissal/query bookkeeping into
`PopupState`.
- Update composer and history-search paths to use those state owners
directly.
- Remove redundant `BottomPane` popup synchronization after paste
handling and `insert_str`.

## Part 2

The follow-up PR will finish the cleanup around the remaining composer
boundary: split out the draft/editing-oriented state and footer/status
presentation concerns that still live in `ChatComposer`, then revisit
the leftover `BottomPane` pass-throughs once those ownership lines are
explicit. The goal is for `ChatComposer` to coordinate a few focused
collaborators instead of continuing to be the landing zone for every
input-path concern.

## Verification

Did manual smoke tests.
2026-05-14 09:04:27 -07:00
Shijie Rao
e79e1b42b9 Chore: better published unsigned artifacts (#22649)
This is the exact same change as @bolinfest made but he could not push
because of github action change permission.

## Why

The `rust-release` workflow can now be run manually with
`sign_macos=false` to skip macOS signing, but that path previously
stopped before creating a GitHub Release. That left the unsigned macOS
binaries available only as workflow-run artifacts, which are awkward to
fetch from automation and cannot be retrieved with a simple
unauthenticated `curl`.

For the unsigned path we still should not perform the normal release
side effects: no npm or Python publishing, no WinGet publishing, no
`latest-alpha-cli` branch update, and no promotion to GitHub's latest
release. The goal is only to make the build outputs easy to fetch from
the release page.

## What changed

- Allow the `release` job in `.github/workflows/rust-release.yml` to run
for `workflow_dispatch` runs with `sign_macos=false`.
- For unsigned runs, keep the unsigned macOS artifacts plus the normal
Linux and Windows release artifacts needed for DotSlash, then
create/update the GitHub Release with `make_latest: false`.
- Keep the normal publish/promote paths gated to signed releases:
  - npm staging and publish
  - Python runtime publish
  - WinGet publish
  - `latest-alpha-cli` update
  - developer-site deploy
  - normal DotSlash release files
- Add `.github/dotslash-unsigned-config.json`, which publishes
`*-unsigned` DotSlash files that use unsigned macOS artifacts and the
normal Linux/Windows artifacts.


## What I added
PLEASE READ THIS!!!
I added `codex-command-runner` and `codex-windows-sandbox-setup` entries
to `.github/dotslash-unsigned-config.json` so that with
`sign_macos=false` we would still get the dotslash files for those
artifacts which are necessary for windows builds.
2026-05-14 08:47:21 -07:00
Michael Bolin
01d93fd9fc permissions: canonicalize workspace_roots and danger-full-access names (#22624)
## Why

This is a small precursor to the larger permissions-migration work. Both
the comparison stack in
[#22401](https://github.com/openai/codex/pull/22401) /
[#22402](https://github.com/openai/codex/pull/22402) and the alternate
stack in [#22610](https://github.com/openai/codex/pull/22610) /
[#22611](https://github.com/openai/codex/pull/22611) /
[#22612](https://github.com/openai/codex/pull/22612) are easier to
review if the terminology is already settled underneath them.

Because `:project_roots` and `:danger-no-sandbox` have not shipped as
stable user-facing surface area, carrying them forward as aliases would
just add more migration logic to the later stacks. This PR removes that
ambiguity now so the follow-on work can rely on one spelling for each
built-in concept.

## What Changed

- renamed the config-facing special filesystem key from `:project_roots`
to `:workspace_roots`
- dropped unpublished `:project_roots` parsing support in
`core/src/config/permissions.rs`, so new config only recognizes
`:workspace_roots`
- renamed the built-in full-access permission profile id from
`:danger-no-sandbox` to `:danger-full-access`
- dropped unpublished `:danger-no-sandbox` support entirely, including
the old active-profile canonicalization path, and added explicit
rejection coverage for the legacy id
- introduced shared built-in permission-profile id constants in
`codex-rs/protocol/src/models.rs`
- updated `core`, `app-server`, and `tui` call sites that special-case
built-in profiles to use the shared constants and canonical ids
- updated tests and the Linux sandbox README to use `:workspace_roots` /
`:danger-full-access`

## Verification

I focused verification on the three places this rename can regress:
config parsing, active-profile identity surfaced back out of `core`, and
user/server call sites that special-case built-in profiles.

Targeted checks:

-
`config::tests::default_permissions_can_select_builtin_profile_without_permissions_table`
-
`config::tests::default_permissions_read_only_applies_additional_writable_roots_as_modifications`
-
`config::tests::default_permissions_can_select_builtin_full_access_profile`
- `config::tests::legacy_danger_no_sandbox_is_rejected`
- `workspace_root` filtered `codex-core` tests
-
`request_processors::thread_processor::thread_processor_tests::thread_processor_behavior_tests::requested_permissions_trust_project_uses_permission_profile_intent`
-
`suite::v2::turn_start::turn_start_rejects_invalid_permission_selection_before_starting_turn`
- `status::tests::status_snapshot_shows_auto_review_permissions`
-
`status::tests::status_permissions_full_disk_managed_with_network_is_danger_full_access`
-
`app_server_session::tests::embedded_turn_permissions_use_active_profile_selection`
2026-05-14 08:45:54 -07:00
jif-oai
12bfb57139 Fix turn extension data task plumbing (#22646)
## Summary
- carry the per-turn extension data through RunningTask so abort
handling can rebuild SessionTaskContext
- update stale test ExtensionData::new() callsites to pass the turn id

## Testing
- Not run after PR branch creation; CI will cover.
2026-05-14 16:00:06 +02:00
Chris Bookholt
9ea38136b0 [codex] treat PowerShell stop-parsing forms as unsupported (#22643)
## Summary
- Treat PowerShell stop-parsing token forms as unsupported in the
AST-backed command flattener.
- Add focused regressions at the parser layer and Windows command-safety
layer.

## Why
The command-safety parser lowers PowerShell AST elements into argv-like
words. Stop-parsing syntax preserves a native-command argument shape
that this lowering does not model, so these forms should stay on the
conservative unsupported path.

## Validation
- `cargo fmt --manifest-path codex-rs/Cargo.toml --all --check`
- `cargo test --manifest-path codex-rs/Cargo.toml -p
codex-shell-command`
2026-05-14 06:28:34 -07:00
jif-oai
deedf3b2c4 feat: add layered --profile-v2 config files (#17141)
## Why

`--profile-v2 <name>` gives launchers and runtime entry points a named
profile config without making each profile duplicate the base user
config. The base `$CODEX_HOME/config.toml` still loads first, then
`$CODEX_HOME/<name>.config.toml` layers above it and becomes the active
writable user config for that session.

That keeps shared defaults, plugin/MCP setup, and managed/user
constraints in one place while letting a named profile override only the
pieces that need to differ.

## What Changed

- Added the shared `--profile-v2 <name>` runtime option with validated
plain names, now represented by `ProfileV2Name`.
- Extended config layer state so the base user config and selected
profile config are both `User` layers; APIs expose the active user layer
and merged effective user config.
- Threaded profile selection through runtime entry points: `codex`,
`codex exec`, `codex review`, `codex resume`, `codex fork`, and `codex
debug prompt-input`.
- Made user-facing config writes go to the selected profile file when
active, including TUI/settings persistence, app-server config writes,
and MCP/app tool approval persistence.
- Made plugin, marketplace, MCP, hooks, and config reload paths read
from the merged user config so base and profile layers both participate.
- Updated app-server config layer schemas to mark profile-backed user
layers.

## Limits

`--profile-v2` is still rejected for config-management subcommands such
as feature, MCP, and marketplace edits. Those paths remain tied to the
base `config.toml` until they have explicit profile-selection semantics.

Some adjacent background writes may still update base or global state
rather than the selected profile:

- marketplace auto-upgrade metadata
- automatic MCP dependency installs from skills
- remote plugin sync or uninstall config edits
- personality migration marker/default writes

## Verification

Added targeted coverage for profile name validation, layer
ordering/merging, selected-profile writes, app-server config writes,
session hot reload, plugin config merging, hooks/config fixture updates,
and MCP/app approval persistence.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-05-14 15:16:15 +02:00
jif-oai
17cd321c32 Wire turn item contributors into stream output (#22494)
## Summary
- run registered TurnItemContributor hooks for parsed stream output
items
- plumb the active turn extension store into stream item handling
- preserve existing memory citation parsing as fallback after
contributors run

## Tests
- cargo test -p codex-core stream_events_utils -- --nocapture
- just fmt
- just fix -p codex-core
- git diff --check
2026-05-14 14:48:17 +02:00
jif-oai
6d65686313 feat: make ToolExecutor an async trait (#22560)
## Why

`codex_tools::ToolExecutor` keeps a tool spec attached to its runtime
handler, but extension tools still carried a parallel
`ExtensionToolFuture` / `ExtensionToolExecutor` shape. That made
extension-owned tools look different from host tools even though
routing, registration, and execution need the same abstraction.

This PR makes the shared executor contract directly async and lets
extension tools implement it too, so host tools and extension tools can
move through the same registration path.

## What changed

- Changed `ToolExecutor::handle` to an `async fn` using `async-trait`,
and updated built-in tool handlers to implement the async trait
directly.
- Replaced the bespoke `ExtensionToolFuture` contract with a marker
`ExtensionToolExecutor` over `ToolExecutor<ToolCall, Output =
JsonToolOutput>`, re-exporting `ToolExecutor` from
`codex-extension-api`.
- Updated the memories extension tools to implement the shared executor
trait.
- Split tool-router construction into collected executors plus hosted
model specs, keeping hosted tools like web search and image generation
separate from executable handlers.
- Updated spec/router tests and extension-tool stubs for the new
executor shape.

## Verification

- Not run locally.
2026-05-14 11:23:57 +02:00
Eric Traut
6a225e4005 Defer startup NUX impressions until startup succeeds (#22587)
## Why

This is a follow-up to #22573. This problem was surfaced in a code
review comment that I missed before merging the previous PR.

Fresh-session startup could prepare a model-availability NUX before
`app_server.start_thread(&config)` completed. If thread startup then
failed, the TUI never rendered the tooltip, but
`prepare_startup_tooltip_override(...)` had already persisted one of the
limited impressions.

## What Changed

- Move startup tooltip preparation inside the fresh-thread startup
branch, after `start_thread(...)` succeeds.
- Keep resume/fork paths unchanged.
- Remove the now-redundant
`should_prepare_startup_tooltip_override(...)` helper and its gate test.
2026-05-13 21:03:19 -07:00
xli-oai
9797296564 Relax remote plugin sync gate (#22594)
## Summary
- Allow remote installed-plugin cache refresh to start whenever plugins
are enabled.
- Allow remote installed-plugin bundle sync to start whenever plugins
are enabled.
- Remove the extra local `remote_plugin_enabled` guard from those
background sync paths.

## Context
Server-side installed plugin state and optional bundle URL behavior are
owned by plugin-service `/public/plugins/installed`, so these local sync
paths only need the overall plugin enablement gate.

## Test plan
- `just fmt`
- `cargo test -p codex-core-plugins`
2026-05-14 03:38:30 +00:00
Eric Traut
35451ba79c Simplify TUI startup test coverage (#22573)
## Why

The TUI startup test surface had drifted into expensive, brittle
coverage:

- `tui/tests/suite/no_panic_on_startup.rs` was already ignored as flaky
while still spawning a PTY to exercise malformed exec-policy rules.
- `tui/tests/suite/model_availability_nux.rs` used a seeded session,
cursor-query spoofing, and repeated interrupts to verify a narrow
resume-path invariant.
- `app/tests.rs` had started accumulating unrelated startup and summary
coverage in one flat module even after the surrounding app code was
split into feature modules.

This keeps those behaviors covered while making the tests cheaper to
understand and less likely to rot. It also preserves the malformed-rules
regression from #8803 without requiring a terminal orchestration test.

## What changed

- Replaced the malformed `rules` startup PTY case with a direct
exec-policy loader regression:

[`rules_path_file_returns_read_dir_error`](21b6b5622f/codex-rs/core/src/exec_policy_tests.rs (L264-L284))
- Made the existing fresh-session-only startup tooltip behavior explicit
with

[`should_prepare_startup_tooltip_override`](21b6b5622f/codex-rs/tui/src/app/thread_routing.rs (L1272-L1279)),
then added focused coverage for the resume/fork gate and the persisted
NUX counter.
- Split startup and session-summary coverage out of
`tui/src/app/tests.rs` into dedicated modules so the test layout better
mirrors the current app architecture.
- Converted one single-message goal validation snapshot into semantic
assertions where layout was not the behavior under test.
- Removed the two PTY-heavy suite files that the narrower tests now
supersede.

## Verification

- `cargo test -p codex-core rules_path_file_returns_read_dir_error`
- `cargo test -p codex-tui startup_`
- `cargo test -p codex-tui session_summary_`
- `cargo test -p codex-tui
goal_slash_command_rejects_oversized_objective`
2026-05-13 18:16:54 -07:00
Owen Lin
4e368aa2e9 enable/disable remote control at runtime, not via features (#22578)
## Why
reapplies https://github.com/openai/codex/pull/22386 which was
previously reverted

Also, introduce `remoteControl/enable` and `remoteControl/disable`
app-server APIs to toggle on/off remote control at runtime for a given
running app-server instance.

## What Changed

- Adds experimental v2 RPCs:
  - `remoteControl/enable`
  - `remoteControl/disable`
- Adds `RemoteControlRequestProcessor` and routes the new RPCs through
it instead of `ConfigRequestProcessor`.
- Adds named `RemoteControlHandle::enable`, `disable`, and `status`
methods.
- Makes `remoteControl/enable` return an error when sqlite state DB is
unavailable, while keeping enrollment/websocket failures as async status
updates.
- Adds `AppServerRuntimeOptions.remote_control_enabled` and hidden
`--remote-control` flags for `codex app-server` and `codex-app-server`.
- Updates managed daemon startup to use `codex app-server
--remote-control --listen unix://`.
- Marks `Feature::RemoteControl` as removed and ignores
`[features].remote_control`.
- Updates app-server README entries for the new remote-control methods.
2026-05-14 01:07:46 +00:00
Owen Lin
512f8f8012 Improve remote-control daemon UX (#22562)
## Why

`codex remote-control` manages the app-server daemon with
`remote_control` enabled, but it previously only exposed an implicit
start path. Once started, there was no obvious top-level
`remote-control` command for stopping the daemon; users had to know
about the lower-level `codex app-server daemon stop` command.

The startup failure for missing managed installs was also ambiguous.
`codex remote-control` and daemon bootstrap require the standalone Codex
install under `CODEX_HOME/packages/standalone/current/codex`, but the
old error only said to install Codex first, which is unclear when
another `codex` binary is already on PATH. Now we add an explicit
instruction for how to get the standalone Codex install.

## What changed

- Converts `codex remote-control` into a command group while preserving
bare `codex remote-control` as the existing start behavior.
- Adds `codex remote-control start` as the explicit start path.
- Adds `codex remote-control stop`, which maps to app-server daemon
stop.
- Updates the shared daemon managed-install error to name the missing
standalone path, explain why that install is required, provide the
installer command, and tell users to rerun the command they just tried.

## Verification

- `cargo test -p codex-app-server-daemon`
- `cargo test -p codex-cli`
- `./target/debug/codex remote-control --help`
2026-05-13 18:04:08 -07:00
Dylan Hurd
e33cf9ae28 chore(config) rm experimental_use_freeform_apply_patch (#22565)
## Summary
Get rid of the `experimental_use_freeform_apply_patch` config option,
since it is now encoded in model config. No deprecation message since it
has been experimental this entire time.

## Testing
- [x] Updated unit tests

---------

Co-authored-by: Codex <noreply@openai.com>
2026-05-13 17:52:15 -07:00
David de Regt
53a36fc1c2 fix: Block appserver startup if state db can't be opened (#22580)
All apps must be able to open the db to proceed -- codex is having
issues with manufacturing new installation ids in local mode when the db
can't be opened for race conditions or any other reasons.
2026-05-14 00:50:17 +00:00
Eric Ning
64d8f387f9 Remove connector_openai prefix filtering (#22555)
Remove unnecessary prefix filtering from codex

## Test Plan

Test local cli build + make sure backend returns appropriate apps 

```
cd ~/code/codex/codex-rs
cargo build -p codex-cli --bin codex
./target/debug/codex
```

Appropriate apps show up in my list
2026-05-13 16:59:22 -07:00
Max Burkhardt
4fca5c32da Deprecate issue labeler (#22574) 2026-05-13 19:55:31 -04:00
Shijie Rao
49d1f66c4c Add unsigned macOS release artifacts (#22559)
## Summary
- Upload unsigned macOS release binaries before signing so they remain
available from the workflow run if signing fails
- Add a manual `workflow_dispatch` option, `sign_macos`, defaulting to
`true`
- When `sign_macos=false`, skip macOS signing, signed-name macOS
artifacts, DMGs, npm/DotSlash/PyPI publishing, latest release marking,
and `latest-alpha-cli` updates


## Process
HAVE NOT TESTED YET BUT we should be able to run
```
gh workflow run rust-release.yml \
  -R openai/codex \
  --ref rust-v0.132.0 \
  -f sign_macos=false
```

which will then start the rust-release script with `sign_macos` and
therefore do not codesign mac and also no release afterward.
2026-05-13 16:47:44 -07:00
xl-openai
e3bf0cfc63 [codex] Canonicalize shared workspace plugin IDs (#22564)
## Summary
- Canonicalize private and unlisted workspace shared plugin IDs to
`workspace-shared-with-me`.
- Keep `plugin/list` private/unlisted shared-with-me buckets as UI
grouping only.
- Update share read/list/checkout and cache cleanup coverage for the
canonical namespace.

## Tests
- `cargo test -p codex-app-server --test all
plugin_list_fetches_shared_with_me_kind`
- `cargo test -p codex-app-server --test all
plugin_read_returns_share_context_for_shared_remote_plugin`
- `cargo test -p codex-app-server --test all suite::v2::plugin_share`
- `cargo test -p codex-core-plugins
list_remote_plugin_shares_fetches_created_workspace_plugins`
- `cargo test -p codex-core-plugins
stale_remote_plugin_cleanup_removes_old_shared_with_me_cache_and_keeps_canonical_cache`
- `git diff --check`
2026-05-13 16:29:47 -07:00
Eric Traut
3c3e18c222 Refactor chatwidget orchestration into modules (phase 5) (#22537)
## Why

`chatwidget.rs` is still carrying too many unrelated responsibilities in
one file. #22269 started a five-phase cleanup to move coherent behavior
domains into focused modules while keeping `chatwidget.rs` as the
composition layer. #22407 completed phase 2 by extracting input and
submission flow, #22433 completed phase 3 by extracting protocol,
replay, streaming, and tool lifecycle handling, and #22518 completed
phase 4 by extracting settings, popups, and status surfaces.

This PR is phase 5. It cleans up the remaining constructor and
orchestration code now that the larger behavior domains have moved out,
leaving `chatwidget.rs` much closer to the composition layer the cleanup
was aiming for. This is once again a mechanical movement of existing
functions. No functional changes.

## What Changed

- Added focused modules for widget construction and initial wiring,
session configuration flow, key/composer interaction routing, review
popup orchestration, desktop notification coalescing, and render
composition.
- Moved the remaining constructor, session setup, interaction,
notification, review picker, and rendering helpers out of
`codex-rs/tui/src/chatwidget.rs`.
- Preserved the existing startup/session behavior, keyboard handling,
review picker flow, notification priority behavior, and render
composition while shrinking the central widget module substantially.
- Left `codex-rs/tui/src/chatwidget.rs` as the registration and
composition surface for the extracted behavior modules.

## Cleanup Phases

The five-phase cleanup plan from #22269 is:

1. Phase 1: mechanical helper and state moves. Completed in #22269.
2. Phase 2: extract input and submission flow, including queued user
messages, shell prompt submission, pending steer restoration, and thread
input snapshot/restore behavior. Completed in #22407.
3. Phase 3: extract protocol, replay, streaming, and tool lifecycle
handling, while preserving active-cell grouping, transcript
invalidation, interrupt deferral, and final-message separator behavior.
Completed in #22433.
4. Phase 4: extract settings, popups, and status surfaces, including
model/reasoning/collaboration/personality popups, permission prompts,
rate-limit UI, and connectors helpers. Completed in #22518.
5. Phase 5: clean up the remaining constructor and orchestration code
once the larger behavior domains have moved out, leaving `chatwidget.rs`
as the composition layer. This PR.

## Verification

- `cargo check -p codex-tui`
- `cargo test -p codex-tui chatwidget::tests::popups_and_settings`
- `cargo test -p codex-tui chatwidget::tests::plan_mode`
- `cargo test -p codex-tui chatwidget::tests::review_mode`
- `cargo test -p codex-tui chatwidget::tests::status_and_layout`

`cargo test -p codex-tui` also compiles and begins running, but aborts
in the unchanged app-side test
`app::tests::discard_side_thread_keeps_local_state_when_server_close_fails`
with the same reproducible stack overflow noted in phase 4.
2026-05-13 15:40:53 -07:00
Eric Traut
efdcbba053 Remove resurrected /collab slash command (#22535)
## Summary
`/collab` was intentionally removed in
[#12012](https://github.com/openai/codex/pull/12012), but the
TUI/app-server migration accidentally brought that slash-command path
back. This restores the earlier product decision so the TUI no longer
advertises or dispatches `/collab`. This command was redundant because
it did the same thing as `/plan` but in a less-intuitive way.

## What Changed
- Remove `SlashCommand::Collab` from the TUI slash-command surface.
- Delete the picker and app-event plumbing that only existed to service
`/collab`.
- Remove obsolete TUI test coverage for the deleted picker flow.
2026-05-13 15:40:37 -07:00
jif-oai
e6939e3969 feat: namespace in ext (#22556) 2026-05-14 00:37:48 +02:00
Abhinav
23bb524973 Spill oversized PreToolUse additionalContext (#22529)
# Why

`PreToolUse.additionalContext` became model-visible after #20692, but
the hook-output spilling path from #21069 never picked up that newer
lane. As a result, oversized `PreToolUse` context could bypass the
truncation/spill treatment that already applies to the other hook
outputs Codex forwards to the model.

# What

- Run `PreToolUseOutcome.additional_contexts` through
`maybe_spill_texts(...)`
- Add an integration test proving a large `PreToolUse.additionalContext`
is replaced with a truncated preview plus spill-file pointer, while the
full text is preserved on disk.
2026-05-13 15:21:31 -07:00
Andrey Mishchenko
7c57a59f51 Make multi_agent_v2 wait_agent timeouts configurable (#22528)
## Why

`multi_agent_v2` already allowed configuring the minimum `wait_agent`
timeout, but the default timeout and upper bound were still hard-coded.
That made it hard to tune waits for subagent mailbox activity in
sessions that need either faster wakeups or longer waits, and it meant
the model-visible `wait_agent` schema could not fully reflect the
resolved runtime limits.

## What Changed

- Added `features.multi_agent_v2.max_wait_timeout_ms` and
`features.multi_agent_v2.default_wait_timeout_ms` alongside the existing
`min_wait_timeout_ms` setting.
- Validated all three timeouts in config as `0..=3_600_000`, with
`min_wait_timeout_ms <= default_wait_timeout_ms <= max_wait_timeout_ms`.
- Thread and review session tool config now passes the resolved
min/default/max values into the `wait_agent` tool schema.
- `wait_agent` now uses the configured default when `timeout_ms` is
omitted and rejects explicit values outside the configured min/max range
instead of silently clamping them.
- Updated the generated config schema and config-lock test coverage for
the new fields.
2026-05-13 14:43:06 -07:00
iceweasel-oai
8ae0c837f0 Avoid PowerShell profiles in elevated Windows sandbox (#21400)
## Why

On Windows, elevated sandboxed commands run under a dedicated sandbox
account while `HOME` / `USERPROFILE` can still point at the real user's
profile directory. For PowerShell login shells, that combination can
make the sandbox account try to load the real user's PowerShell profile
script. If the sandbox account's execution policy differs from the real
user's policy, startup can emit profile-loading errors before the
requested command runs.

For this backend, loading the profile is not a faithful user login
shell: it is cross-account profile execution. Treating these PowerShell
invocations as non-login shells avoids that invalid startup path.

## Why This Happens Late

The normal `login` decision is resolved when shell argv is created, but
that point is too early to make this Windows sandbox-specific decision.
At argv creation time we do not yet know the actual sandbox attempt that
will run the command. A turn can include sandboxed and unsandboxed
attempts, and a broad turn-level override would also affect Full Access
commands where the user's profile should remain available.

Instead, this change carries the selected `ShellType` alongside the argv
and applies the `-NoProfile` adjustment in the shell runtimes once the
`SandboxAttempt` is known. That keeps the override scoped to actual
`WindowsRestrictedToken` attempts with `WindowsSandboxLevel::Elevated`.

The runtime uses the selected shell metadata rather than re-detecting
PowerShell from argv. That avoids brittle parsing and covers PowerShell
invocation shapes such as `-EncodedCommand`.

## What Changed

- Carry selected shell metadata through `exec_command` / unified exec
requests and shell tool requests.
- Insert `-NoProfile` for PowerShell commands only when the runtime is
about to execute a sandboxed elevated Windows attempt.
- Add focused unit coverage for elevated Windows PowerShell,
`-EncodedCommand`, existing `-NoProfile`, legacy restricted-token
attempts, unsandboxed attempts, and non-PowerShell commands.

## Verification

- `cargo test -p codex-core disable_powershell_profile_tests`
- `cargo test -p codex-core test_get_command`
- `cargo clippy --fix --tests --allow-dirty --allow-no-vcs -p
codex-core`

A full `cargo test -p codex-core` run was also attempted during
development, but it still hit an unrelated stack overflow in
`agent::control` tests before reaching this area.
2026-05-13 21:37:50 +00:00
sayan-oai
3de4d7f238 clean up instructions (#22543)
rm behavioral steering in tool docs for code mode.
2026-05-13 14:28:57 -07:00
Felipe Coury
9798eb377a feat(cli): add codex doctor diagnostics (#22336)
## Why

Users and support need a single command that captures the local Codex
runtime, configuration, auth, terminal, network, and state shape without
asking the user to know which diagnostic depth to choose first. `codex
doctor` now runs the useful checks by default and makes the detailed
human output the default because the command is usually run when someone
already needs context.

The command also targets concrete support failure modes we have seen
while iterating on the design:

- update-target mismatches like #21956, where the installed package
manager target can differ from the running executable
- terminal and multiplexer issues that depend on `TERM`, tmux/zellij
state, color handling, and TTY metadata
- provider-specific HTTP/WebSocket connectivity, including ChatGPT
WebSocket handshakes and API-key/provider endpoint reachability
- local state/log SQLite integrity problems and large rollout
directories
- feedback reports that need an attached, redacted diagnostic snapshot
without asking the user to run a second command

## What Changed

- Adds `codex doctor` as a grouped CLI diagnostic report with default
detailed output and `--summary` for the compact view.
- Adds stable report sections for Environment, Configuration, Updates,
Connectivity, and Background Server, plus a top Notes block that
promotes anomalies such as available updates, large rollout directories,
optional MCP issues, and mixed auth signals.
- Adds runtime provenance, install consistency, bundled/system search
readiness, terminal/multiplexer metadata, `config.toml` parse status,
auth mode details, sandbox details, feature flag summaries, update
cache/latest-version state, app-server daemon state, SQLite integrity
checks, rollout statistics, and provider-aware network diagnostics.
- Adds ChatGPT WebSocket diagnostics that report the negotiated HTTP
upgrade as `HTTP 101 Switching Protocols` and include timeout, DNS,
auth, and provider context in detailed output.
- Makes reachability provider-aware: API-key OpenAI setups check the API
endpoint, ChatGPT auth checks the ChatGPT path, and custom/AWS/local
providers check configured HTTP endpoints when available.
- Adds structured, redacted JSON output where `checks` is keyed by check
id and `details` is a key/value object for support tooling.
- Integrates doctor with feedback uploads by attaching a best-effort
`codex-doctor-report.json` report and adding derived Sentry tags for
overall status and failing/warning checks.
- Updates the TUI feedback consent copy so users can see that the doctor
report is included when logs/diagnostics are uploaded.
- Updates the CLI bug issue template to ask reporters for `codex doctor
--json` and render pasted reports as JSON.

## Example Output

The examples below are sanitized from local smoke runs with `--no-color`
so the structure is reviewable in plain text.

### `codex doctor`

```text
Codex Doctor v0.0.0 · macos-aarch64

Notes
   ↑ updates      0.130.0 available (current 0.0.0, dismissed 0.128.0)
   ⚠ rollouts     1,526 active files · 2.53 GB on disk
   ⚠ mcp          MCP configuration has optional issues
   ⚠ auth         mixed auth signals: ChatGPT login plus API key env var; HTTP reachability uses API-key mode
─────────────────────────────────────────────────────────────

Environment
  ✓ runtime      local debug build
      version                  0.0.0
      install method           other
      commit                   unknown
      executable               ~/code/codex.fcoury-doct…x-rs/target/debug/codex
  ✓ install      consistent
      context                  other
      managed by               npm: no · bun: no · package root —
      PATH entries (2)         ~/.local/share/mise/installs/node/24/bin/codex
                               ~/.local/share/mise/shims/codex
  ✓ search       ripgrep 15.1.0 (system, `rg`)
  ✓ terminal     Ghostty 1.3.2-main-+b0f827665 · tmux 3.6a · TERM=xterm-256color
      terminal                 Ghostty
      TERM_PROGRAM             ghostty
      terminal version         1.3.2-main-+b0f827665
      TERM                     xterm-256color
      multiplexer              tmux 3.6a
      tmux extended-keys       on
      tmux allow-passthrough   on
      tmux set-clipboard       on
  ✓ state        databases healthy
      CODEX_HOME               ~/.codex (dir)
      state DB                 ~/.codex/state_5.sqlite (file) · integrity ok
      log DB                   ~/.codex/logs_2.sqlite (file) · integrity ok
      active rollouts          1,526 files · 2.53 GB (avg 1.70 MB)
      archived rollouts        8 files · 3.84 MB (avg 491.11 KB)

Configuration
  ✓ config       loaded
      model                    gpt-5.5 · openai
      cwd                      ~/code/codex.fcoury-doctor/codex-rs
      config.toml              ~/.codex/config.toml
      config.toml parse        ok
      MCP servers              1
      feature flags            36 enabled · 7 overridden (full list with --all)
      overrides                code_mode, code_mode_only, memories, chronicle, goals, remote_control, prevent_idle_sleep
  ✓ auth         auth is configured
      auth storage mode        File
      auth file                ~/.codex/auth.json
      auth env vars present    OPENAI_API_KEY
      stored auth mode         chatgpt
      stored API key           false
      stored ChatGPT tokens    true
      stored agent identity    false
  ⚠ mcp          MCP configuration has optional issues — Set the missing MCP env vars or disable the affected server.
      configured servers       1
      disabled servers         0
      streamable_http servers  1
      optional reachability    openaiDeveloperDocs: https://developers.openai.com/mcp (HEAD connect failed; GET connect failed)
  ✓ sandbox      restricted fs + restricted network · approval OnRequest
      approval policy          OnRequest
      filesystem sandbox       restricted
      network sandbox          restricted

Connectivity
  ✓ network      network-related environment looks readable
  ✓ websocket    connected (HTTP 101 Switching Protocols) · 15s timeout
      model provider           openai
      provider name            OpenAI
      wire API                 responses
      supports websockets      true
      connect timeout          15000 ms
      auth mode                chatgpt
      endpoint                 wss://chatgpt.com/backend-api/<redacted>
      DNS                      2 IPv4, 2 IPv6, first IPv6
      handshake result         HTTP 101 Switching Protocols
  ✗ reachability one or more required provider endpoints are unreachable over HTTP — Check proxy, VPN, firewall, DNS, and custom CA configuration.
      reachability mode        API key auth
      openai API               https://api.openai.com/v1 connect failed (required)

Background Server
  ○ app-server   not running (ephemeral mode)

─────────────────────────────────────────────────────────────
11 ok · 1 idle · 4 notes · 1 warn · 1 fail failed

--summary compact output           --all expand truncated lists
--json redacted report
```

### `codex doctor --summary`

```text
Codex Doctor v0.0.0 · macos-aarch64

Notes
   ↑ updates      0.130.0 available (current 0.0.0, dismissed 0.128.0)
   ⚠ rollouts     1,526 active files · 2.53 GB on disk
   ⚠ mcp          MCP configuration has optional issues
   ⚠ auth         mixed auth signals: ChatGPT login plus API key env var; HTTP reachability uses API-key mode
─────────────────────────────────────────────────────────────

Environment
  ✓ runtime      local debug build
  ✓ install      consistent
  ✓ search       ripgrep 15.1.0 (system, `rg`)
  ✓ terminal     Ghostty 1.3.2-main-+b0f827665 · tmux 3.6a · TERM=xterm-256color
  ✓ state        databases healthy

Configuration
  ✓ config       loaded
  ✓ auth         auth is configured
  ⚠ mcp          MCP configuration has optional issues — Set the missing MCP env vars or disable the affected server.
  ✓ sandbox      restricted fs + restricted network · approval OnRequest

Updates
  ✓ updates      update configuration is locally consistent

Connectivity
  ✓ network      network-related environment looks readable
  ✓ websocket    connected (HTTP 101 Switching Protocols) · 15s timeout
  ✗ reachability one or more required provider endpoints are unreachable over HTTP — Check proxy, VPN, firewall, DNS, and custom CA configuration.

Background Server
  ○ app-server   not running (ephemeral mode)

─────────────────────────────────────────────────────────────
11 ok · 1 idle · 4 notes · 1 warn · 1 fail failed

Run codex doctor without --summary for detailed diagnostics.
--all expand truncated lists       --json redacted report
```

### `codex doctor --json` shape

```json
{
  "schema_version": 1,
  "overall_status": "fail",
  "checks": {
    "runtime.provenance": {
      "id": "runtime.provenance",
      "category": "Environment",
      "status": "ok",
      "summary": "local debug build",
      "details": {
        "version": "0.0.0",
        "install method": "other",
        "commit": "unknown"
      }
    },
    "sandbox.helpers": {
      "id": "sandbox.helpers",
      "category": "Configuration",
      "status": "ok",
      "summary": "restricted fs + restricted network · approval OnRequest",
      "details": {
        "approval policy": "OnRequest",
        "filesystem sandbox": "restricted",
        "network sandbox": "restricted"
      }
    }
  }
}
```

### `/feedback` new sentry attachment

<img width="938" height="798" alt="CleanShot 2026-05-13 at 15 36 14"
src="https://github.com/user-attachments/assets/715e62e0-d7b4-4fea-a35a-fd5d5d33c4c0"
/>

### New section in CLI issue template

<img width="1164" height="435" alt="CleanShot 2026-05-13 at 15 47 24"
src="https://github.com/user-attachments/assets/9081dc25-a28c-4afa-8ba1-e299c2b4031d"
/>

## How to Test

1. Run `cargo run --bin codex -- doctor --no-color`.
2. Confirm the detailed report is the default and includes promoted
Notes, grouped sections, terminal details, state DB integrity, rollout
stats, provider reachability, WebSocket diagnostics, and app-server
status.
3. Run `cargo run --bin codex -- doctor --summary --no-color`.
4. Confirm the compact view keeps the same sections and summary counts
but omits detailed key/value rows.
5. Run `cargo run --bin codex -- doctor --json`.
6. Confirm the output is redacted JSON, `checks` is an object keyed by
check id, and each check's `details` is a key/value object.
7. Preview the CLI bug issue template and confirm the `Codex doctor
report` field appears after the terminal field, asks for `codex doctor
--json`, and renders pasted output as JSON.
8. Start a feedback flow that includes logs.
9. Confirm the upload consent copy lists `codex-doctor-report.json`
alongside the log attachments.

Targeted tests:

- `cargo test -p codex-cli doctor`
- `cargo test -p codex-app-server
doctor_report_tags_summarize_status_counts`
- `cargo test -p codex-feedback`
- `cargo test -p codex-tui feedback_view`
- `just argument-comment-lint`
- `git diff --check`
2026-05-13 21:23:19 +00:00
canvrno-oai
5d7e6a2503 [codex] Fix TUI wrapping for external borrowed slices (#21235)
Fixes #20587, reported by @noeljackson.

This prevents the TUI wrapping code from panicking when `textwrap`
returns a borrowed slice that does not point into the original source
text. The fix follows the direction proposed by @misrtjakub in the issue
comment: validate the borrowed slice pointer range first, and fall back
to the existing owned-line mapper when the slice is external.

- Guards borrowed wrapped slices before converting pointer offsets into
byte ranges.
- Reuses the existing owned-line range recovery path for external
borrowed slices.
- Adds coverage for rejecting borrowed slices outside the source text.

End-user testing steps:
- Start Codex in TUI mode under a PTY wrapper that can inject stdin
after startup.
- Inject `\x1b[200~test message\x1b[201~\r` after the TUI is ready.
- Confirm Codex does not panic and the pasted text is handled normally.

Local validation:
- `cargo test -p codex-tui wrapping::tests::`
- `cargo test -p codex-tui -- --skip
status::tests::status_permissions_full_disk_managed_with_network_is_danger_full_access
--skip
status::tests::status_permissions_full_disk_managed_without_network_is_external_sandbox`
2026-05-13 14:19:05 -07:00
canvrno-oai
16592f593d Use plugin/list to get list of plugins for mentions (#22375)
This switches TUI plugin mentions to use app-server `plugin/list` for
plugin inventory and metadata instead of `PluginManager`, while keeping
the same mention-eligibility filters as before.

Same filters as before:
- Only plugins in the current config / cwd scope.
- Only installed and enabled plugins.
- Only plugins that actually expose a capability, meaning at least one
skill, MCP server, or app connector.
- Uses `plugin/list` for the mention names/descriptions
2026-05-13 14:11:10 -07:00
Abhinav
14473c216f Enable plugin hooks by default (#22549)
# Why

Plugin-bundled hooks are already wired through the plugin manager,
session setup, and app-server hook listing paths. Keeping `plugin_hooks`
disabled by default means users still need an explicit feature opt-in
before that existing behavior participates in normal plugin loading.

# What

- mark `plugin_hooks` as stable and enable it by default
- add feature-registry test coverage for the new default/stage pairing

Validation:

- `cargo test -p codex-features`
- `just fmt`
2026-05-13 21:10:28 +00:00
stevenlee-oai
0a2d751fc2 Add callback ids to local MCP OAuth redirects (#20237)
## Summary

- Add a deterministic callback-id path segment to local MCP OAuth
redirect URIs before starting authorization.
- Derive the callback id from the normalized MCP server URL and encode
it as a 12-character URL-safe hash.
- Reuse the existing exact callback-path validation so OAuth completion
only succeeds on the callback path that was sent in the redirect URI.

## Context

Slack thread:
https://openai.slack.com/archives/C087WB3AGCR/p1777480566571699

That thread calls out the OAuth mix-up class of issue for MCP servers.
The connector/App Connect flow already has a callback_id concept that
binds the OAuth callback URL to the MCP app/server identity. Codex
desktop's local MCP OAuth flow was still using a generic local callback
path like `/callback`, so this PR adds the same shape to the shared
local MCP OAuth helper.

## Behavior

Before this change, local MCP OAuth used:

- default local callback URL: `http://127.0.0.1:<port>/callback`
- configured callback URL: `<configured callback URL>` unchanged

After this change, Codex appends a deterministic callback-id segment:

- default local callback URL:
`http://127.0.0.1:<port>/callback/<callback_id>`
- configured callback URL: `<configured callback path>/<callback_id>`

The local callback server already compares the incoming request path
against the path from the redirect URI. By appending the callback id
before both authorization and callback validation, callbacks that arrive
on the old generic path or a mismatched callback-id path are rejected.

The callback id is bound to the MCP endpoint URL, including path and
query, so path-based multi-tenant MCP deployments on the same origin do
not share a callback path. URL fragments are ignored because they are
not sent to the server.

The change lives in `codex-rmcp-client`, so it covers both the normal
desktop MCP OAuth login path and silent/plugin-triggered MCP OAuth login
paths that use the same `perform_oauth_login_*` helpers.

## Scope and non-goals

- This does not change the app-server protocol or desktop webview
request shape.
- This does not implement RFC 9207 `iss` validation; issuer validation
is still useful when providers return `iss`.
- This does not make arbitrary untrusted MCP servers safe to use. It
specifically adds callback URL binding for the local MCP OAuth flow.

## Validation

- `cargo fmt --all`
- `cargo test -p codex-rmcp-client perform_oauth_login`
2026-05-13 13:26:04 -07:00
pakrym-oai
3ac1d15598 Use selected environment cwd for filesystem helpers (#22542)
## Why

`TurnContext::cwd` is deprecated in favor of resolving paths from the
selected turn environment cwd. A few filesystem-oriented paths were
still constructing sandbox context from the legacy cwd and then mutating
it afterward, or resolving local file paths through the deprecated
helper.

## What changed

- Make `TurnContext::file_system_sandbox_context` take the trusted cwd
explicitly.
- Pass the selected turn environment cwd directly from `apply_patch` and
`view_image` call sites.
- Restrict `spawn_agents_on_csv` to exactly one local environment and
resolve input/output CSV paths from that local environment cwd.
- Remove a redundant test setup assignment that only synchronized
deprecated `TurnContext::cwd` with a replaced config.

## Validation

- `cargo test -p codex-core view_image`
- `cargo test -p codex-core
maybe_persist_mcp_tool_approval_writes_project_config_for_project_server`
- `cargo test -p codex-core parse_csv_supports_quotes_and_commas`
- `git diff --check`
2026-05-13 13:18:56 -07:00
starr-openai
d666238b40 Shard Bazel Windows tests across jobs (#22408)
## Summary
- split the single PR-blocking Bazel Windows test leg into four Windows
shard jobs
- preserve the existing required Windows Bazel check name with a
lightweight aggregate gate
- keep Linux/macOS Bazel test jobs and the separate Windows
clippy/release jobs unchanged

## Why
The ordinary PR Windows Bazel test leg was one GitHub Actions job, so
Bazel only had in-job parallelism. This gives that lane real job-level
fanout across separate Windows hosts while keeping the target set
disjoint via stable label hashing.

## Evidence
- final pre-rebase green run: `25774733562`
- Windows shard target counts: `61/212`, `48/212`, `52/212`, `51/212`
- Windows test fanout completed in about 7m29s versus a recent
monolithic median around 22m26s

## Notes
- this is scoped to the Bazel Windows test leg only
- each shard keeps the existing Windows cross-compile/RBE path and
restores the former monolithic Windows test cache
- shard jobs do not upload duplicate repository caches after test work,
keeping cache cleanup off the PR-blocking shard path
- no local validation run; relying on GitHub Actions for the
workflow-shaped check

Co-authored-by: Codex <noreply@openai.com>
2026-05-13 12:46:51 -07:00
Owen Lin
fb7cfc813a fix: prevent codex-backend from stealing originator (#22533)
## Why

Remote control starts by letting `codex-backend` initialize against the
app-server as an infrastructure health/proxy client before the real
remote client connects. App-server initialization also sets the
process-wide `originator` from `client_info.name`, so `codex-backend`
could become the sticky originator for later model/API requests even
after the real client initialized.

## What changed

- Treat `codex-backend` as a non-originating initialize client,
alongside the existing `codex_app_server_daemon` probe client.
- Preserve normal per-connection initialize behavior, including session
metadata and initialize analytics.
- Add regression coverage that verifies `codex-backend` initialize does
not replace the default originator.

## Testing

- `cargo test -p codex-app-server --test all
initialize_codex_backend_does_not_override_originator`
2026-05-13 12:38:34 -07:00
Dylan Hurd
9c691b74d6 chore(config) rm tools.view_image (#22501)
## Summary
It appears this config flag has been broken/a noop for quite some time:
since https://github.com/openai/codex/pull/8850. Let's simplify and get
rid of this.

## Testing
- [x] Updated unit tests
2026-05-13 12:35:37 -07:00
Dylan Hurd
d18a7c982e chore(config) rm Feature::CodexGitCommit (#22412)
## Summary
Removes the unused Feature::CodexGitCommit

## Testing
- [x] tests pass
2026-05-13 12:33:36 -07:00
Alex Daley
a98b57d065 [codex] Reuse Apps MCP path override for plugin-service rollout (#22527)
## Summary
- reuse `apps_mcp_path_override` for the plugin-service rollout,
defaulting enabled boolean overrides to `/ps/mcp` while preserving
explicit configured paths

## Validation
- `just write-config-schema`
- `just fmt`
- `cargo test -p codex-mcp`
- `cargo test -p codex-core apps_mcp_path_override`
- `cargo test -p codex-core
to_mcp_config_preserves_apps_feature_from_config`
- `cargo test -p codex-features`
2026-05-13 19:18:35 +00:00
iceweasel-oai
c9edb26755 windows-sandbox: fail elevated setup when firewall policy is ineffective (#22353)
## Why

Elevated Windows sandbox setup currently assumes that the firewall rules
it writes will take effect. On managed Windows hosts, local firewall
policy changes can be ignored or only partially apply across the active
profiles, which means setup can appear to succeed without providing the
expected network isolation.

## What changed

- Query `INetFwPolicy2::LocalPolicyModifyState` before configuring the
elevated sandbox firewall rules.
- Fail setup when Windows reports that local firewall policy edits are
ineffective or only apply to some current profiles.
- Surface that condition with a dedicated
`helper_firewall_policy_ineffective` setup error code so support and
IT-facing diagnostics can distinguish it from COM access failures.
- Add focused coverage for effective policy, group-policy override, and
partial-profile coverage cases.

## Testing

- `cargo test -p codex-windows-sandbox --bin
codex-windows-sandbox-setup`
2026-05-13 18:43:14 +00:00
Adrian
3d2a0b5517 [codex] Scope Windows sandbox write-root capability SIDs (#21479)
## Summary
- fix by scoping Windows workspace-write capability SIDs to active
effective write roots
- build legacy/elevated tokens from only the active effective write
roots
- align setup/audit deny ACL handling with active root-specific SIDs

## Testing
- just fmt
- git diff --check --cached
- just argument-comment-lint
- cargo check -p codex-windows-sandbox --locked (blocked by libwebrtc ->
libyuv fetch: CONNECT tunnel failed, response 403)
2026-05-13 18:28:52 +00:00
Eric Traut
1ae811ddb2 Refactor chatwidget settings surfaces into modules (phase 4) (#22518)
## Why

`chatwidget.rs` is still carrying too many unrelated responsibilities in
one file. #22269 started a five-phase cleanup to move coherent behavior
domains into focused modules while keeping `chatwidget.rs` as the
composition layer. #22407 completed phase 2 by extracting input and
submission flow, and #22433 completed phase 3 by extracting protocol,
replay, streaming, and tool lifecycle handling.

This PR is phase 4. It keeps moving high-churn UI coordination out of
the central widget by extracting settings, popups, and status surfaces
without changing the visible behavior those flows already provide. This
is once again a mechanical movement of existing functions. No functional
changes.

## What Changed

- Added focused modules for runtime settings/model coordination,
model/reasoning/collaboration popups,
settings/personality/theme/audio/experimental popups, permission
prompts, status setup/output controls, and Windows sandbox prompt flows.
- Moved the remaining rate-limit nudge/status helpers and connectors
popup/loading/update helpers into their existing focused modules.
- Preserved the existing picker flows, approval behavior, status/title
setup previews, rate-limit notices, and connectors/app list behavior
while shrinking `chatwidget.rs` back toward orchestration.
- Left `codex-rs/tui/src/chatwidget.rs` as the registration and
composition surface for these extracted behaviors.

## Cleanup Phases

The five-phase cleanup plan from #22269 is:

1. Phase 1: mechanical helper and state moves. Completed in #22269.
2. Phase 2: extract input and submission flow, including queued user
messages, shell prompt submission, pending steer restoration, and thread
input snapshot/restore behavior. Completed in #22407.
3. Phase 3: extract protocol, replay, streaming, and tool lifecycle
handling, while preserving active-cell grouping, transcript
invalidation, interrupt deferral, and final-message separator behavior.
Completed in #22433.
4. Phase 4: extract settings, popups, and status surfaces, including
model/reasoning/collaboration/personality popups, permission prompts,
rate-limit UI, and connectors helpers. This PR.
5. Phase 5: clean up the remaining constructor and orchestration code
once the larger behavior domains have moved out, leaving `chatwidget.rs`
as the composition layer.

## Verification

- `cargo check -p codex-tui`
- `cargo test -p codex-tui chatwidget::tests::permissions`
- `cargo test -p codex-tui chatwidget::tests::status_surface_previews`
- `cargo test -p codex-tui chatwidget::tests::popups_and_settings`
- `cargo test -p codex-tui chatwidget::tests::status_and_layout`

`cargo test -p codex-tui` also compiles and begins running, but aborts
in the unchanged app-side test
`app::tests::discard_side_thread_keeps_local_state_when_server_close_fails`
with a reproducible stack overflow.
2026-05-13 11:26:37 -07:00
pakrym-oai
4454e1411b Deprecate TurnContext cwd and resolve_path (#22519)
## Why

`TurnContext::cwd` and `TurnContext::resolve_path` are being phased out
in favor of using the selected turn environment cwd directly.
Deprecating both APIs makes any new direct dependency visible while
preserving the existing migration path for current callers.

## What Changed

- Marked `TurnContext::cwd` and `TurnContext::resolve_path` as
deprecated with guidance to use the selected turn environment cwd
instead.
- Added exact `#[allow(deprecated)]` suppressions at each existing
direct usage site, including tests, rather than adding crate-wide
suppression.
- Kept the change behavior-preserving: current cwd reads, writes, and
path resolution continue to use the same values.

## Verification

- `just fmt`
- `cargo check -p codex-core`
- `cargo check -p codex-core --tests`
- `git diff --check`
2026-05-13 11:15:25 -07:00
Eric Ning
610b86fefb Pass Codex product SKU to ChatGPT backend (#22366)
# Description

We need to set the appropriate Product SKU for full functionality for
the apps endpoints for each type of client

# Testing

`./target/debug/codex --enable app`

<img width="1786" height="398" alt="CleanShot 2026-05-12 at 11 51 25@2x"
src="https://github.com/user-attachments/assets/2142f768-fc72-4fcb-8f39-9bd0d8569170"
/>

Regular slack flows seem to work, also curling these endpoints with the
correct SKU returns the right apps
2026-05-13 11:00:54 -07:00
jif-oai
fc26af377f feat: expose multi-agent v2 as model-only tools (#22514)
## Why

`code_mode_only` filters code-mode nested tools out of the top-level
tool list. For multi-agent v2, we need a rollout shape where the
collaboration tools remain callable as normal model tools without also
being embedded into the code-mode `exec` tool declaration.

Related to this:
https://openai-corpws.slack.com/archives/C0AQLHB4U75/p1778660267922549

## What Changed

- Adds `features.multi_agent_v2.non_code_mode_only`, including config
resolution, profile override handling, and generated schema coverage.
- Introduces `ToolExposure::DirectModelOnly` so a tool can be included
in the initial model-visible list while staying out of the nested
code-mode tool surface.
- Applies that exposure to the multi-agent v2 tools when the new flag is
set: `spawn_agent`, `send_message`, `followup_task`, `wait_agent`,
`close_agent`, and `list_agents`.
- Updates code-mode-only filtering so direct-model-only tools remain
visible while ordinary nested code-mode tools are still hidden.

## Verification

- Added config parsing/profile tests for `non_code_mode_only`.
- Added tool spec coverage for the code-mode-only multi-agent v2
exposure behavior.
2026-05-13 19:49:47 +02:00
Shijie Rao
157fffc6a4 Revert "Scope macOS signing secrets to release environment" (#22513)
Reverts openai/codex#22443
2026-05-13 10:41:45 -07:00
Owen Lin
2b3b220605 revert: mark Feature::RemoteControl as removed (#22520)
reverts: https://github.com/openai/codex/pull/22386
2026-05-13 17:32:15 +00:00
pakrym-oai
83decfa300 [codex] Remove unused legacy shell tools (#22246)
## Why

Recent session history showed no active use of the raw `shell`,
`local_shell`, or `container.exec` execution surfaces. Keeping those
handlers/specs wired into core leaves duplicate shell execution paths
alongside the supported `shell_command` and unified exec tools.

## What changed

- Removed the raw `shell` handler/spec and its `ShellToolCallParams`
protocol helper.
- Removed the legacy `local_shell` and `container.exec` handler/spec
plumbing while preserving persisted-history compatibility for old
response items.
- Normalized model/config `default` and `local` shell selections to
`shell_command`.
- Pruned tests that exercised removed raw-shell/local-shell/apply-patch
variants and kept coverage on `shell_command`, unified exec, and
freeform `apply_patch`.

## Verification

- `git diff --check`
- `cargo test -p codex-protocol`
- `cargo test -p codex-tools`
- `cargo test -p codex-core tools::handlers::shell`
- `cargo test -p codex-core tools::spec`
- `cargo test -p codex-core tools::router`
- `cargo test -p codex-core
active_call_preserves_triggering_command_context`
- `cargo test -p codex-core guardian_tests`
- `cargo test -p codex-core --test all shell_serialization`
- `cargo test -p codex-core --test all apply_patch_cli`
- `cargo test -p codex-core --test all shell_command_`
- `cargo test -p codex-core --test all local_shell`
- `cargo test -p codex-core --test all otel::`
- `cargo test -p codex-core --test all hooks::`
- `just fix -p codex-core`
- `just fix -p codex-tools`
2026-05-13 16:43:25 +00:00
jif-oai
7c7b4861d8 fix: drop underscored id headers (#22193)
## Why
Stop sending duplicate `session_id`/`thread_id` headers. We only want
the hyphenated forms as `_` is rejected by some proxies

Related discussion here:
https://openai.slack.com/archives/C095U48JNL9/p1778508316923179

## What
- Keep `session-id` and `thread-id`
- Remove the underscore aliases
2026-05-13 18:21:02 +02:00
jif-oai
fdda59c00b Introduce tool exposure for deferred registration (#22489)
## Why

Deferred tools were tracked with separate side-channel filtering after
tool specs had already been assembled. That made the registry
responsible for executing tools while the router/spec planner separately
decided whether those same tools should be exposed to the model up
front.

This PR makes exposure part of the tool handler contract so direct
versus deferred availability travels with the executable tool
registration.

Next step will be to simplify registration

## What Changed

- Adds `ToolExposure` to `codex-tools` and exposes it through
`ToolExecutor`, defaulting tools to `Direct`.
- Teaches dynamic tools and MCP handlers to mark deferred tools as
`Deferred` at construction time.
- Renames the registry object-safe wrapper from `AnyToolHandler` to
`RegisteredTool` and uses `ToolExposure` when deciding whether to
include a handler's spec in the initial model-visible tool list.
- Refactors tool spec planning to derive direct specs and deferred
search entries from registered handlers, removing the router's
special-case deferred dynamic tool filtering.

## Verification

- Not run.
2026-05-13 18:16:51 +02:00
Michael Bolin
889ee018e7 config: add strict config parsing (#20559)
## Why

Codex intentionally ignores unknown `config.toml` fields by default so
older and newer config files keep working across versions. That leniency
also makes typo detection hard because misspelled or misplaced keys
disappear silently.

This change adds an opt-in strict config mode so users and tooling can
fail fast on unrecognized config fields without changing the default
permissive behavior.

This feature is possible because `serde_ignored` exposes the exact
signal Codex needs: it lets Codex run ordinary Serde deserialization
while recording fields Serde would otherwise ignore. That avoids
requiring `#[serde(deny_unknown_fields)]` across every config type and
keeps strict validation opt-in around the existing config model.

## What Changed

### Added strict config validation

- Added `serde_ignored`-based validation for `ConfigToml` in
`codex-rs/config/src/strict_config.rs`.
- Combined `serde_ignored` with `serde_path_to_error` so strict mode
preserves typed config error paths while also collecting fields Serde
would otherwise ignore.
- Added strict-mode validation for unknown `[features]` keys, including
keys that would otherwise be accepted by `FeaturesToml`'s flattened
boolean map.
- Kept typed config errors ahead of ignored-field reporting, so
malformed known fields are reported before unknown-field diagnostics.
- Added source-range diagnostics for top-level and nested unknown config
fields, including non-file managed preference source names.

### Kept parsing single-pass per source

- Reworked file and managed-config loading so strict validation reuses
the already parsed `TomlValue` for that source.
- For actual config files and managed config strings, the loader now
reads once, parses once, and validates that same parsed value instead of
deserializing multiple times.
- Validated `-c` / `--config` override layers with the same
base-directory context used for normal relative-path resolution, so
unknown override keys are still reported when another override contains
a relative path.

### Scoped `--strict-config` to config-heavy entry points

- Added support for `--strict-config` on the main config-loading entry
points where it is most useful:
  - `codex`
  - `codex resume`
  - `codex fork`
  - `codex exec`
  - `codex review`
  - `codex mcp-server`
  - `codex app-server` when running the server itself
  - the standalone `codex-app-server` binary
  - the standalone `codex-exec` binary
- Commands outside that set now reject `--strict-config` early with
targeted errors instead of accepting it everywhere through shared CLI
plumbing.
- `codex app-server` subcommands such as `proxy`, `daemon`, and
`generate-*` are intentionally excluded from the first rollout.
- When app-server strict mode sees invalid config, app-server exits with
the config error instead of logging a warning and continuing with
defaults.
- Introduced a dedicated `ReviewCommand` wrapper in `codex-rs/cli`
instead of extending shared `ReviewArgs`, so `--strict-config` stays on
the outer config-loading command surface and does not become part of the
reusable review payload used by `codex exec review`.

### Coverage

- Added tests for top-level and nested unknown config fields, unknown
`[features]` keys, typed-error precedence, source-location reporting,
and non-file managed preference source names.
- Added CLI coverage showing invalid `--enable`, invalid `--disable`,
and unknown `-c` overrides still error when `--strict-config` is
present, including compound-looking feature names such as
`multi_agent_v2.subagent_usage_hint_text`.
- Added integration coverage showing both `codex app-server
--strict-config` and standalone `codex-app-server --strict-config` exit
with an error for unknown config fields instead of starting with
fallback defaults.
- Added coverage showing unsupported command surfaces reject
`--strict-config` with explicit errors.

## Example Usage

Run Codex with strict config validation enabled:

```shell
codex --strict-config
```

Strict config mode is also available on the supported config-heavy
subcommands:

```shell
codex --strict-config exec "explain this repository"
codex review --strict-config --uncommitted
codex mcp-server --strict-config
codex app-server --strict-config --listen off
codex-app-server --strict-config --listen off
```

For example, if `~/.codex/config.toml` contains a typo in a key name:

```toml
model = "gpt-5"
approval_polic = "on-request"
```

then `codex --strict-config` reports the misspelled key instead of
silently ignoring it. The path is shortened to `~` here for readability:

```text
$ codex --strict-config
Error loading config.toml:
~/.codex/config.toml:2:1: unknown configuration field `approval_polic`
  |
2 | approval_polic = "on-request"
  | ^^^^^^^^^^^^^^
```

Without `--strict-config`, Codex keeps the existing permissive behavior
and ignores the unknown key.

Strict config mode also validates ad-hoc `-c` / `--config` overrides:

```text
$ codex --strict-config -c foo=bar
Error: unknown configuration field `foo` in -c/--config override

$ codex --strict-config -c features.foo=true
Error: unknown configuration field `features.foo` in -c/--config override
```

Invalid feature toggles are rejected too, including values that look
like nested config paths:

```text
$ codex --strict-config --enable does_not_exist
Error: Unknown feature flag: does_not_exist

$ codex --strict-config --disable does_not_exist
Error: Unknown feature flag: does_not_exist

$ codex --strict-config --enable multi_agent_v2.subagent_usage_hint_text
Error: Unknown feature flag: multi_agent_v2.subagent_usage_hint_text
```

Unsupported commands reject the flag explicitly:

```text
$ codex --strict-config cloud list
Error: `--strict-config` is not supported for `codex cloud`
```

## Verification

The `codex-cli` `strict_config` tests cover invalid `--enable`, invalid
`--disable`, the compound `multi_agent_v2.subagent_usage_hint_text`
case, unknown `-c` overrides, app-server strict startup failure through
`codex app-server`, and rejection for unsupported commands such as
`codex cloud`, `codex mcp`, `codex remote-control`, and `codex
app-server proxy`.

The config and config-loader tests cover unknown top-level fields,
unknown nested fields, unknown `[features]` keys, source-location
reporting, non-file managed config sources, and `-c` validation for keys
such as `features.foo`.

The app-server test suite covers standalone `codex-app-server
--strict-config` startup failure for an unknown config field.

## Documentation

The Codex CLI docs on developers.openai.com/codex should mention
`--strict-config` as an opt-in validation mode for supported
config-heavy entry points once this ships.
2026-05-13 16:08:05 +00:00
cassirer-openai
702e6a3c64 [rollout-trace] Add a trace ID to MCP calls. (#22326)
This allows us to connect individual tool calls to the logs of the
invocations.
2026-05-13 16:03:33 +00:00