- make multi_agent stable and enabled by default
- update feature and tool-spec coverage to match the new default
---------
Co-authored-by: Codex <noreply@openai.com>
- rename the multi-agent tool name the model sees to wait_agent
- update the model-facing prompts and tool descriptions to match
---------
Co-authored-by: Codex <noreply@openai.com>
Summary
- add the code_mode_only feature flag/config schema and wire its
dependency on code_mode
- update code mode tool descriptions to list nested tools with detailed
headers
- restrict available tools for prompt and exec descriptions when
code_mode_only is enabled and test the behavior
Testing
- Not run (not requested)
## Why
`zsh-fork` sessions launched through unified-exec need the escalation
socket to survive the wrapper -> server -> child handoff so later
intercepted `exec()` calls can still reach the escalation server.
The inherited-fd spawn path also needs to avoid closing Rust's internal
exec-error pipe, and the shell-escalation handoff needs to tolerate the
receive-side case where a transferred fd is installed into the same
stdio slot it will be mapped onto.
## What Changed
- Added `SpawnLifecycle::inherited_fds()` in
`codex-rs/core/src/unified_exec/process.rs` and threaded inherited fds
through `codex-rs/core/src/unified_exec/process_manager.rs` so
unified-exec can preserve required descriptors across both PTY and
no-stdin pipe spawn paths.
- Updated `codex-rs/core/src/tools/runtimes/shell/zsh_fork_backend.rs`
to expose the escalation socket fd through the spawn lifecycle.
- Added inherited-fd-aware spawn helpers in
`codex-rs/utils/pty/src/pty.rs` and `codex-rs/utils/pty/src/pipe.rs`,
including Unix pre-exec fd pruning that preserves requested inherited
fds while leaving `FD_CLOEXEC` descriptors alone. The pruning helper is
now named `close_inherited_fds_except()` to better describe that
behavior.
- Updated `codex-rs/shell-escalation/src/unix/escalate_client.rs` to
duplicate local stdio before transfer and send destination stdio numbers
in `SuperExecMessage`, so the wrapper keeps using its own
`stdin`/`stdout`/`stderr` until the escalated child takes over.
- Updated `codex-rs/shell-escalation/src/unix/escalate_server.rs` so the
server accepts the overlap case where a received fd reuses the same
stdio descriptor number that the child setup will target with `dup2`.
- Added comments around the PTY stdio wiring and the overlap regression
helper to make the fd handoff and controlling-terminal setup easier to
follow.
## Verification
- `cargo test -p codex-utils-pty`
- covers preserved-fd PTY spawn behavior, PTY resize, Python REPL
continuity, exec-failure reporting, and the no-stdin pipe path
- `cargo test -p codex-shell-escalation`
- covers duplicated-fd transfer on the client side and verifies the
overlap case by passing a pipe-backed stdin payload through the
server-side `dup2` path
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/13644).
* #14624
* __->__ #13644
## Summary
- launch Windows sandboxed children on a private desktop instead of
`Winsta0\Default`
- make private desktop the default while keeping
`windows.sandbox_private_desktop=false` as the escape hatch
- centralize process launch through the shared
`create_process_as_user(...)` path
- scope the private desktop ACL to the launching logon SID
## Why
Today sandboxed Windows commands run on the visible shared desktop. That
leaves an avoidable same-desktop attack surface for window interaction,
spoofing, and related UI/input issues. This change moves sandboxed
commands onto a dedicated per-launch desktop by default so the sandbox
no longer shares `Winsta0\Default` with the user session.
The implementation stays conservative on security with no silent
fallback back to `Winsta0\Default`
If private-desktop setup fails on a machine, users can still opt out
explicitly with `windows.sandbox_private_desktop=false`.
## Validation
- `cargo build -p codex-cli`
- elevated-path `codex exec` desktop-name probe returned
`CodexSandboxDesktop-*`
- elevated-path `codex exec` smoke sweep for shell commands, nested
`pwsh`, jobs, and hidden `notepad` launch
- unelevated-path full private-desktop compatibility sweep via `codex
exec` with `-c windows.sandbox=unelevated`
## Summary
- render code mode tool declarations as single-line TypeScript snippets
- make the JSON schema renderer emit inline object shapes for these
declarations
- update code mode/spec expectations to match the new inline rendering
## Testing
- `just fmt`
- `cargo test -p codex-core render_json_schema_to_typescript`
- `cargo test -p codex-core code_mode_augments_`
- `cargo test -p codex-core --test all exports_all_tools_metadata --
--nocapture`
## Summary
- move the multi-agent handlers suite into its own files for spawn,
wait, resume, send input, and close logic
- keep the aggregated module in place while delegating each handler to
its new file to keep things organized per handler
## Testing
- Not run (not requested)
## Summary
This lets skill loading split `permissions.network` into two distinct
pieces:
- `permissions.network.enabled` still feeds the skill
`PermissionProfile` and remains the coarse gate for whether the skill
can use network access at all.
- `permissions.network.allowed_domains` and
`permissions.network.denied_domains` are lifted into a new
`SkillManagedNetworkOverride` so managed-network sessions can start
per-skill scoped proxies with the right domain overrides.
The change also updates `SkillMetadata` construction sites and adds
loader tests covering YAML parsing plus normalization of the network
gate vs. domain override fields.
## Follow-up
A PR that uses the network_override to spin up a skill-specific proxy if
network_override is not none.
This change moves code_mode exec session settings out of the runtime API
and into an optional first-line pragma, so instead of calling runtime
helpers like set_yield_time() or set_max_output_tokens_per_exec_call(),
the model can write // @exec: {"yield_time_ms": ...,
"max_output_tokens": ...} at the top of the freeform exec source. Rust
now parses that pragma before building the source, validates it, and
passes the values directly in the exec start message to the code-mode
broker, which applies them at session start without any worker-runtime
mutation path. The @openai/code_mode module no longer exposes those
setter functions, the docs and grammar were updated to describe the
pragma form, and the existing code_mode tests were converted to use
pragma-based configuration instead.
## Summary
- return typed `ToolOutput` values from the multi-agent handlers instead
of plain `FunctionToolOutput`
- keep the regular function-call response shape as JSON text while
exposing structured values to code mode
- add output schemas for `spawn_agent`, `send_input`, `resume_agent`,
`wait`, and `close_agent`
## Verification
- `just fmt`
- focused multi-agent and integration tests passed earlier in this
branch during iteration
- after the final edit, I only reran formatting before opening this PR
## Summary
- hard-stop `js_repl` only for `TurnAbortReason::Interrupted`,
preserving the persistent REPL across replaced turns
- track the current top-level exec by turn and only reset when the
interrupted turn owns submitted work or a freshly started kernel for the
current exec attempt
- close both interrupt races: the write-window race by marking the exec
as submitted before async pipe writes begin, and the startup-window race
by tracking fresh-kernel ownership until submission
- add regression coverage for interrupted in-flight execs and the
pending-kernel-start window
## Why
Stopping a turn previously surfaced `aborted by user after Xs` even
though the underlying `js_repl` kernel could continue executing. Earlier
fixes also risked resetting the session-scoped REPL too broadly or
missing already-dispatched work. This change keeps cleanup scoped to
explicit stop semantics and makes the interrupt path line up with both
submitted execs and newly started kernels.
## Testing
- `just fmt`
- `cargo test -p codex-core`
- `just fix -p codex-core`
`cargo test -p codex-core` passes the updated `js_repl` coverage,
including the new startup-window regression test, but still has
unrelated integration failures in this environment outside `js_repl`.
---------
Co-authored-by: Codex <noreply@openai.com>
Summary
- move the existing multi-agent handler logic into each tool-specific
handler and inline helper implementations
- remove the old central dispatcher now that each handler encapsulates
its own behavior
- adjust handler specs and tests to match the new structure without
macros
Testing
- Not run (not requested)
Summary
- make all code-mode tools accessible as globals so callers only need
`tools.<name>`
- rename text/image helpers and key globals (store, load, ALL_TOOLS,
etc.) to reflect the new shared namespace
- update the JS bridge, runners, descriptions, router, and tests to
follow the new API
Testing
- Not run (not requested)
## Summary
This changes `js_repl` so saved references to `codex.tool(...)` and
`codex.emitImage(...)` keep working across cells.
Previously, those helpers were recreated per exec and captured that
exec's `message.id`. If a persisted object or saved closure reused an
old helper in a later cell, the nested tool/image call could fail with
`js_repl exec context not found`.
This patch:
- keeps stable `codex.tool` and `codex.emitImage` helper identities in
the kernel
- resolves the current exec dynamically at call time using
`AsyncLocalStorage`
- adds regression coverage for persisted helper references across cells
- updates the js_repl docs and project-doc instructions to describe the
new behavior and its limits
## Why
We already support persistent top-level bindings across `js_repl` cells,
so persisted objects should be able to reuse `codex` helpers in later
active cells. The bug was that helper identity was exec-scoped, not
kernel-scoped.
Using `AsyncLocalStorage` fixes the cross-cell reuse case without
falling back to a single global active exec that could accidentally
attribute stale background callbacks to the wrong cell.
- Update the code-mode executor, wait handler, and protocol plumbing to
use cell IDs instead of session IDs for node communication
- Switch tool metadata, wait description, and suite tests to refer to
cell IDs so user-visible messages match the new terminology
**Testing**
- Not run (not requested)
## Summary
Dynamic tool responses containing literal U+2028 / U+2029 would cause
await codex.tool(...) to hang even though the response had already
arrived. This PR replaces the kernel’s readline-based stdin handling
with byte-oriented JSONL framing that handles these characters properly.
## Testing
- `cargo test -p codex-core`
- tested the binary on a repro case and confirmed it's fixed
---------
Co-authored-by: Codex <noreply@openai.com>
## Summary
- create the turn-scoped `ToolCallRuntime` before starting the code mode
worker so the worker reuses the same runtime and router
- thread the shared runtime through the code mode service/worker path
and use it for nested tool calls
- model aborted tool calls as a concrete `ToolOutput` so aborted
responses still produce valid tool output shapes
## Testing
- `just fmt`
- `cargo test -p codex-core` (still running locally)
Summary
- expose the default yield timeout through code mode runtime so the
handler, wait tool, and protocol share the same 10s value that matches
unified exec
- document the timeout change in the tool descriptions and propagate the
value all the way into the runner metadata
- adjust Cargo.lock to keep the dependency tree in sync with the added
code mode tool dependency
Testing
- Not run (not requested)
as reported in https://github.com/openai/codex/issues/14367 users can
explicitly enable unified_exec which will bypass the sandbox even when
it should be enabled.
Until we support unified_exec with the Windows Sandbox, we will disallow
it unless the sandbox is disabled
## Summary
- rename the public feature flag for `spawn_agents_on_csv()` from
`spawn_csv` to `enable_fanout`
- regenerate the config schema so only `enable_fanout` is advertised
- keep the behavior the same: enabling `enable_fanout` still pulls in
`multi_agent`
## Notes
- this is a hard rename with no `spawn_csv` compatibility alias
- the internal enum remains `Feature::SpawnCsv` to keep the patch small
## Testing
- `cd codex-rs && just fmt`
- `cd codex-rs && cargo test -p codex-core` (running locally;
`suite::agent_jobs::*` and rename-specific coverage passed so far)
- **Summary**
- migrate the code mode handler, service, worker, process, runner, and
bridge assets into the `tools/code_mode` module tree
- split Execution, protocol, and handler logic into dedicated files and
relocate the tool definition into `code_mode/spec.rs`
- update core references and tests to stitch the new organization
together
- **Testing**
- Not run (not requested)
## Summary
- start a code mode worker once per turn and let it pump nested tool
calls through a dedicated queue
- simplify code mode request/response dispatch around request ids and
generic runner-unavailable errors
- clean up the code mode process API and runner protocol plumbing
## Testing
- not run yet
## Why
PR #13783 moved the `codex.rs` unit tests into `codex_tests.rs`. This
applies the same extraction pattern across the rest of `codex-rs/core`
so the production modules stay focused on runtime code instead of large
inline test blocks.
Keeping the tests in sibling files also makes follow-up edits easier to
review because product changes no longer have to share a file with
hundreds or thousands of lines of test scaffolding.
## What changed
- replaced each inline `mod tests { ... }` in `codex-rs/core/src/**`
with a path-based module declaration
- moved each extracted unit test module into a sibling `*_tests.rs`
file, using `mod_tests.rs` for `mod.rs` modules
- preserved the existing `cfg(...)` guards and module-local structure so
the refactor remains structural rather than behavioral
## Testing
- `cargo test -p codex-core --lib` (`1653 passed; 0 failed; 5 ignored`)
- `just fix -p codex-core`
- `cargo fmt --check`
- `cargo shear`
## Summary
- address the follow-up review nits from #13996 in a separate PR
- make the approvals test command a raw string and keep the
managed-network path using env proxy routing
- inline `--apply-seccomp-then-exec` in the Linux sandbox inner command
builder
- remove the bubblewrap-specific sandbox metric tag path and drop the
`use_legacy_landlock` shim from `sandbox_tag`/`TurnMetadataState::new`
- restore the `Feature` import that `origin/main` currently still needs
in `connectors.rs`
## Testing
- `cargo test -p codex-linux-sandbox`
- focused `codex-core` tests were rerun/started, but the final
verification pass was interrupted when I pushed at request
## Summary
- make bubblewrap the default Linux sandbox and keep
`use_legacy_landlock` as the only override
- remove `use_linux_sandbox_bwrap` from feature, config, schema, and
docs surfaces
- update Linux sandbox selection, CLI/config plumbing, and related
tests/docs to match the new default
- fold in the follow-up CI fixes for request-permissions responses and
Linux read-only sandbox error text
## Summary
- persist the code mode runner process in the session-scoped code mode
store
- switch the runner protocol from `init` to `start` with explicit
session ids
- handle runner-side session processing without the init waiter queue
## Validation
- just fmt
- cargo check -p codex-core
- node --check codex-rs/core/src/tools/code_mode_runner.cjs
- Clarify that spawn_agent requires explicit user permission for
delegation or parallel agent work.
- Add a regression test covering the new description text.
## Summary
This PR keeps app-server RPC request trace context alive for the full
lifetime of the work that request kicks off (e.g. for `thread/start`,
this is `app-server rpc handler -> tokio background task -> core op
submissions`). Previously we lose trace lineage once the request handler
returns or hands work off to background tasks.
This approach is especially relevant for `thread/start` and other RPC
handlers that run in a non-blocking way. In the near future we'll most
likely want to make all app-server handlers run in a non-blocking way by
default, and only queue operations that must operate in order (e.g.
thread RPCs per thread?), so we want to make sure tracing in app-server
just generally works.
Depends on https://github.com/openai/codex/pull/14300
**Before**
<img width="155" height="207" alt="image"
src="https://github.com/user-attachments/assets/c9487459-36f1-436c-beb7-fafeb40737af"
/>
**After**
<img width="299" height="337" alt="image"
src="https://github.com/user-attachments/assets/727392b2-d072-4427-9dc4-0502d8652dea"
/>
## What changed
- Keep request-scoped trace context around until we send the final
response or error, or the connection closes.
- Thread that trace context through detached `thread/start` work so
background startup stays attached to the originating request.
- Pass request trace context through to downstream core operations,
including:
- thread creation
- resume/fork flows
- turn submission
- review
- interrupt
- realtime conversation operations
- Add tracing tests that verify:
- remote W3C trace context is preserved for `thread/start`
- remote W3C trace context is preserved for `turn/start`
- downstream core spans stay under the originating request span
- request-scoped tracing state is cleaned up correctly
- Clean up shutdown behavior so detached background tasks and spawned
threads are drained before process exit.
- add model and reasoning effort to app-server collab spawn items and
notifications
- regenerate app-server protocol schemas for the new fields
---------
Co-authored-by: Codex <noreply@openai.com>
## Stack
fix: fail closed for unsupported split windows sandboxing #14172
fix: preserve split filesystem semantics in linux sandbox #14173
-> fix: align core approvals with split sandbox policies #14171
refactor: centralize filesystem permissions precedence #14174
## Why This PR Exists
This PR is intentionally narrower than the title may suggest.
Most of the original split-permissions migration already landed in the
earlier `#13434 -> #13453` stack. In particular:
- `#13439` already did the broad runtime plumbing for split filesystem
and network policies.
- `#13445` already moved `apply_patch` safety onto filesystem-policy
semantics.
- `#13448` already switched macOS Seatbelt generation to split policies.
- `#13449` and `#13453` already handled Linux helper and bubblewrap
enforcement.
- `#13440` already introduced the first protocol-side helpers for
deriving effective filesystem access.
The reason this PR still exists is that after the follow-on
`[permissions]` work and the new shared precedence helper in `#14174`, a
few core approval paths were still deciding behavior from the legacy
`SandboxPolicy` projection instead of the split filesystem policy that
actually carries the carveouts.
That means this PR is mostly a cleanup and alignment pass over the
remaining core consumers, not a fresh sandbox backend migration.
## What Is Actually New Here
- make unmatched-command fallback decisions consult
`FileSystemSandboxPolicy` instead of only legacy `DangerFullAccess` /
`ReadOnly` / `WorkspaceWrite` categories
- thread `file_system_sandbox_policy` into the shell, unified-exec, and
intercepted-exec approval paths so they all use the same split-policy
semantics
- keep `apply_patch` safety on the same effective-access rules as the
shared protocol helper, rather than letting it drift through
compatibility projections
- add loader-level regression coverage proving legacy `sandbox_mode`
config still builds split policies and round-trips back without semantic
drift
## What This PR Does Not Do
This PR does not introduce new platform backend enforcement on its own.
- Linux backend parity remains in `#14173`.
- Windows fail-closed handling remains in `#14172`.
- The shared precedence/model changes live in `#14174`.
## Files To Focus On
- `core/src/exec_policy.rs`: unmatched-command fallback and approval
rendering now read the split filesystem policy directly
- `core/src/tools/sandboxing.rs`: default exec-approval requirement keys
off `FileSystemSandboxPolicy.kind`
- `core/src/tools/handlers/shell.rs`: shell approval requests now carry
the split filesystem policy
- `core/src/unified_exec/process_manager.rs`: unified-exec approval
requests now carry the split filesystem policy
- `core/src/tools/runtimes/shell/unix_escalation.rs`: intercepted exec
fallback now uses the same split-policy approval semantics
- `core/src/safety.rs`: `apply_patch` safety keeps using effective
filesystem access rather than legacy sandbox categories
- `core/src/config/config_tests.rs`: new regression coverage for legacy
`sandbox_mode` no-drift behavior through the split-policy loader
## Notes
- `core/src/codex.rs` and `core/src/codex_tests.rs` are just small
fallout updates for `RequestPermissionsResponse.scope`; they are not the
point of the PR.
- If you reviewed the earlier `#13439` / `#13445` stack, the main review
question here is simply: “are there any remaining approval or
patch-safety paths that still reconstruct semantics from legacy
`SandboxPolicy` instead of consuming the split filesystem policy
directly?”
## Testing
- cargo test -p codex-core
legacy_sandbox_mode_config_builds_split_policies_without_drift
- cargo test -p codex-core request_permissions
- cargo test -p codex-core intercepted_exec_policy
- cargo test -p codex-core
restricted_sandbox_requires_exec_approval_on_request
- cargo test -p codex-core
unmatched_on_request_uses_split_filesystem_policy_for_escalation_prompts
- cargo test -p codex-core explicit_
- cargo clippy -p codex-core --tests -- -D warnings
## Why
to support a new bring your own search tool in Responses
API(https://developers.openai.com/api/docs/guides/tools-tool-search#client-executed-tool-search)
we migrating our bm25 search tool to use official way to execute search
on client and communicate additional tools to the model.
## What
- replace the legacy `search_tool_bm25` flow with client-executed
`tool_search`
- add protocol, SSE, history, and normalization support for
`tool_search_call` and `tool_search_output`
- return namespaced Codex Apps search results and wire namespaced
follow-up tool calls back into MCP dispatch
## Summary
This PR narrows original image detail handling to a single opt-in
feature:
- `image_detail_original` lets the model request `detail: "original"` on
supported models
- Omitting `detail` preserves the default resized behavior
The model only sees `detail: "original"` guidance when the active model
supports it:
- JS REPL instructions include the guidance and examples only on
supported models
- `view_image` only exposes a `detail` parameter when the feature and
model can use it
The image detail API is intentionally narrow and consistent across both
paths:
- `view_image.detail` supports only `"original"`; otherwise omit the
field
- `codex.emitImage(..., detail)` supports only `"original"`; otherwise
omit the field
- Unsupported explicit values fail clearly at the API boundary instead
of being silently reinterpreted
- Unsupported explicit `detail: "original"` requests fall back to normal
behavior when the feature is disabled or the model does not support
original detail
## Summary
This PR adds two read-only path helpers to `js_repl`:
- `codex.cwd`
- `codex.homeDir`
They are exposed alongside the existing `codex.tmpDir` helper so the
REPL can reference basic host path context without reopening direct
`process` access.
## Implementation
- expose `codex.cwd` and `codex.homeDir` from the js_repl kernel
- make `codex.homeDir` come from the kernel process environment
- pass session dependency env through js_repl kernel startup so
`codex.homeDir` matches the env a shell-launched process would see
- keep existing shell `HOME` population behavior unchanged
- update js_repl prompt/docs and add runtime/integration coverage for
the new helpers
# 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.
- include the requested sub-agent model and reasoning effort in the
spawn begin event\n- render that metadata next to the spawned agent name
and role in the TUI transcript
---------
Co-authored-by: Codex <noreply@openai.com>