Commit Graph

2481 Commits

Author SHA1 Message Date
Michael Bolin
4e27a87ec6 codex-tools: extract configured tool specs (#16129)
## Why

This continues the `codex-tools` migration by moving another passive
tool-spec layer out of `codex-core`.

After `ToolSpec` moved into `codex-tools`, `codex-core` still owned
`ConfiguredToolSpec` and `create_tools_json_for_responses_api()`. Both
are data-model and serialization helpers rather than runtime
orchestration, so keeping them in `core/src/tools/registry.rs` and
`core/src/tools/spec.rs` left passive tool-definition code coupled to
`codex-core` longer than necessary.

## What changed

- moved `ConfiguredToolSpec` into `codex-rs/tools/src/tool_spec.rs`
- moved `create_tools_json_for_responses_api()` into
`codex-rs/tools/src/tool_spec.rs`
- re-exported the new surface from `codex-rs/tools/src/lib.rs`, which
remains exports-only
- updated `core/src/client.rs`, `core/src/tools/registry.rs`, and
`core/src/tools/router.rs` to consume the extracted types and serializer
from `codex-tools`
- moved the tool-list serialization test into
`codex-rs/tools/src/tool_spec_tests.rs`
- added focused unit coverage for `ConfiguredToolSpec::name()`
- simplified `core/src/tools/spec_tests.rs` to use the extracted
`ConfiguredToolSpec::name()` directly and removed the now-redundant
local `tool_name()` helper
- updated `codex-rs/tools/README.md` so the crate boundary reflects the
newly extracted tool-spec wrapper and serialization helper

## Test plan

- `cargo test -p codex-tools`
- `CARGO_TARGET_DIR=/tmp/codex-core-configured-spec cargo test -p
codex-core --lib tools::spec::`
- `CARGO_TARGET_DIR=/tmp/codex-core-configured-spec cargo test -p
codex-core --lib client::`
- `just fix -p codex-tools -p codex-core`
- `just argument-comment-lint`

## References

- #15923
- #15928
- #15944
- #15953
- #16031
- #16047
2026-03-28 14:24:14 -07:00
Michael Bolin
bc53d42fd9 codex-tools: extract tool spec models (#16047)
## Why

This continues the `codex-tools` migration by moving another passive
tool-definition layer out of `codex-core`.

After `ResponsesApiTool` and the lower-level schema adapters moved into
`codex-tools`, `core/src/client_common.rs` was still owning `ToolSpec`
and the web-search request wire types even though they are serialized
data models rather than runtime orchestration. Keeping those types in
`codex-core` makes the crate boundary look smaller than it really is and
leaves non-runtime tool-shape code coupled to core.

## What changed

- moved `ToolSpec`, `ResponsesApiWebSearchFilters`, and
`ResponsesApiWebSearchUserLocation` into
`codex-rs/tools/src/tool_spec.rs`
- added focused unit tests in `codex-rs/tools/src/tool_spec_tests.rs`
for:
  - `ToolSpec::name()`
  - web-search config conversions
  - `ToolSpec` serialization for `web_search` and `tool_search`
- kept `codex-rs/tools/src/lib.rs` exports-only by re-exporting the new
module from `lib.rs`
- reduced `core/src/client_common.rs` to a compatibility shim that
re-exports the extracted tool-spec types for current core call sites
- updated `core/src/tools/spec_tests.rs` to consume the extracted
web-search types directly from `codex-tools`
- updated `codex-rs/tools/README.md` so the crate contract reflects that
`codex-tools` now owns the passive tool-spec request models in addition
to the lower-level Responses API structs

## Test plan

- `cargo test -p codex-tools`
- `cargo test -p codex-core --lib tools::spec::`
- `cargo test -p codex-core --lib client_common::`
- `just fix -p codex-tools -p codex-core`
- `just argument-comment-lint`

## References

- #15923
- #15928
- #15944
- #15953
- #16031
2026-03-28 13:37:00 -07:00
Eric Traut
48144a7fa4 Remove remaining custom prompt support (#16115)
## Summary
- remove protocol and core support for discovering and listing custom
prompts
- simplify the TUI slash-command flow and command popup to built-in
commands only
- delete obsolete custom prompt tests, helpers, and docs references
- clean up downstream event handling for the removed protocol events
2026-03-28 13:49:37 -06:00
Michael Bolin
61dfe0b86c chore: clean up argument-comment lint and roll out all-target CI on macOS (#16054)
## Why

`argument-comment-lint` was green in CI even though the repo still had
many uncommented literal arguments. The main gap was target coverage:
the repo wrapper did not force Cargo to inspect test-only call sites, so
examples like the `latest_session_lookup_params(true, ...)` tests in
`codex-rs/tui_app_server/src/lib.rs` never entered the blocking CI path.

This change cleans up the existing backlog, makes the default repo lint
path cover all Cargo targets, and starts rolling that stricter CI
enforcement out on the platform where it is currently validated.

## What changed

- mechanically fixed existing `argument-comment-lint` violations across
the `codex-rs` workspace, including tests, examples, and benches
- updated `tools/argument-comment-lint/run-prebuilt-linter.sh` and
`tools/argument-comment-lint/run.sh` so non-`--fix` runs default to
`--all-targets` unless the caller explicitly narrows the target set
- fixed both wrappers so forwarded cargo arguments after `--` are
preserved with a single separator
- documented the new default behavior in
`tools/argument-comment-lint/README.md`
- updated `rust-ci` so the macOS lint lane keeps the plain wrapper
invocation and therefore enforces `--all-targets`, while Linux and
Windows temporarily pass `-- --lib --bins`

That temporary CI split keeps the stricter all-targets check where it is
already cleaned up, while leaving room to finish the remaining Linux-
and Windows-specific target-gated cleanup before enabling
`--all-targets` on those runners. The Linux and Windows failures on the
intermediate revision were caused by the wrapper forwarding bug, not by
additional lint findings in those lanes.

## Validation

- `bash -n tools/argument-comment-lint/run.sh`
- `bash -n tools/argument-comment-lint/run-prebuilt-linter.sh`
- shell-level wrapper forwarding check for `-- --lib --bins`
- shell-level wrapper forwarding check for `-- --tests`
- `just argument-comment-lint`
- `cargo test` in `tools/argument-comment-lint`
- `cargo test -p codex-terminal-detection`

## Follow-up

- Clean up remaining Linux-only target-gated callsites, then switch the
Linux lint lane back to the plain wrapper invocation.
- Clean up remaining Windows-only target-gated callsites, then switch
the Windows lint lane back to the plain wrapper invocation.
2026-03-27 19:00:44 -07:00
Eric Traut
d65deec617 Remove the legacy TUI split (#15922)
This is the part 1 of 2 PRs that will delete the `tui` /
`tui_app_server` split. This part simply deletes the existing `tui`
directory and marks the `tui_app_server` feature flag as removed. I left
the `tui_app_server` feature flag in place for now so its presence
doesn't result in an error. It is simply ignored.

Part 2 will rename the `tui_app_server` directory `tui`. I did this as
two parts to reduce visible code churn.
2026-03-27 22:56:44 +00:00
iceweasel-oai
307e427a9b don't include redundant write roots in apply_patch (#16030)
apply_patch sometimes provides additional parent dir as a writable root
when it is already writable. This is mostly a no-op on Mac/Linux but
causes actual ACL churn on Windows that is best avoided. We are also
seeing some actual failures with these ACLs in the wild, which I haven't
fully tracked down, but it's safe/best to avoid doing it altogether.
2026-03-27 15:41:51 -07:00
Matthew Zeng
5b71e5104f [mcp] Bypass read-only tool checks. (#16044)
- [x] Auto / unspecified approval mode: read-only tools now skip before
guardian routing.
- [x] Approve / always-allow mode: read-only tools still skip, now via
the shared early return.
- [x] Prompt mode: read-only tools no longer skip; they continue to
approval.
2026-03-27 15:22:04 -07:00
Michael Bolin
16d4ea9ca8 codex-tools: extract responses API tool models (#16031)
## Why

The previous extraction steps moved shared tool-schema parsing into
`codex-tools`, but `codex-core` still owned the generic Responses API
tool models and the last adapter layer that turned parsed tool
definitions into `ResponsesApiTool` values.

That left `core/src/tools/spec.rs` and `core/src/client_common.rs`
holding a chunk of tool-shaping code that does not need session state,
runtime plumbing, or any other `codex-core`-specific dependency. As a
result, `codex-tools` owned the parsed tool definition, but `codex-core`
still owned the generic wire model that those definitions are converted
into.

This change moves that boundary one step further. `codex-tools` now owns
the reusable Responses/tool wire structs and the shared conversion
helpers for dynamic tools, MCP tools, and deferred MCP aliases.
`codex-core` continues to own `ToolSpec` orchestration and the remaining
web-search-specific request shapes.

## What changed

- added `tools/src/responses_api.rs` to own `ResponsesApiTool`,
`FreeformTool`, `ToolSearchOutputTool`, namespace output types, and the
shared `ToolDefinition -> ResponsesApiTool` adapter helpers
- added `tools/src/responses_api_tests.rs` for deferred-loading
behavior, adapter coverage, and namespace serialization coverage
- rewired `core/src/tools/spec.rs` to use the extracted dynamic/MCP
adapter helpers instead of defining those conversions locally
- rewired `core/src/tools/handlers/tool_search.rs` to use the extracted
deferred MCP adapter and namespace output types directly
- slimmed `core/src/client_common.rs` so it now keeps `ToolSpec` and the
web-search-specific wire types, while reusing the extracted tool models
from `codex-tools`
- moved the extracted seam tests out of `core` and updated
`codex-rs/tools/README.md` plus `tools/src/lib.rs` to reflect the
expanded `codex-tools` boundary

## Test plan

- `cargo test -p codex-tools`
- `cargo test -p codex-core --lib tools::spec::`
- `cargo test -p codex-core --lib tools::handlers::tool_search::`
- `just fix -p codex-tools -p codex-core`
- `just argument-comment-lint`

## References

- [#15923](https://github.com/openai/codex/pull/15923) `codex-tools:
extract shared tool schema parsing`
- [#15928](https://github.com/openai/codex/pull/15928) `codex-tools:
extract MCP schema adapters`
- [#15944](https://github.com/openai/codex/pull/15944) `codex-tools:
extract dynamic tool adapters`
- [#15953](https://github.com/openai/codex/pull/15953) `codex-tools:
introduce named tool definitions`
2026-03-27 14:26:54 -07:00
bwanner-oai
82e8031338 Add usage-based business plan types (#15934)
## Summary
- add `self_serve_business_usage_based` and `enterprise_cbp_usage_based`
to the public/internal plan enums and regenerate the app-server + Python
SDK artifacts
- map both plans through JWT login and backend rate-limit payloads, then
bucket them with the existing Team/Business entitlement behavior in
cloud requirements, usage-limit copy, tooltips, and status display
- keep the earlier display-label remap commit on this branch so the new
Team-like and Business-like plans render consistently in the UI

## Testing
- `just write-app-server-schema`
- `uv run --project sdk/python python
sdk/python/scripts/update_sdk_artifacts.py generate-types`
- `just fix -p codex-protocol -p codex-login -p codex-core -p
codex-backend-client -p codex-cloud-requirements -p codex-tui -p
codex-tui-app-server -p codex-backend-openapi-models`
- `just fmt`
- `just argument-comment-lint`
- `cargo test -p codex-protocol
usage_based_plan_types_use_expected_wire_names`
- `cargo test -p codex-login usage_based`
- `cargo test -p codex-backend-client usage_based`
- `cargo test -p codex-cloud-requirements usage_based`
- `cargo test -p codex-core usage_limit_reached_error_formats_`
- `cargo test -p codex-tui plan_type_display_name_remaps_display_labels`
- `cargo test -p codex-tui remapped`
- `cargo test -p codex-tui-app-server
plan_type_display_name_remaps_display_labels`
- `cargo test -p codex-tui-app-server remapped`
- `cargo test -p codex-tui-app-server
preserves_usage_based_plan_type_wire_name`

## Notes
- a broader multi-crate `cargo test` run still hits unrelated existing
guardian-approval config failures in
`codex-rs/core/src/config/config_tests.rs`
2026-03-27 14:25:13 -07:00
xl-openai
81abb44f68 plugins: Clean up stale curated plugin sync temp dirs and add sync metrics (#16035)
1. Keep curated plugin staging directories under TempDir ownership until
activation succeeds, so failed git/HTTP sync attempts do not leak
plugins-clone-*.
2. Best-effort clean up stale plugins-clone-* directories before
creating a new staged repo, using a conservative age threshold.
3. Emit OTEL counters for curated plugin startup sync transport attempts
and final outcome across git and HTTP paths.
2026-03-27 14:21:18 -07:00
pakrym-oai
8002594ee3 Normalize /mcp tool grouping for hyphenated server names (#15946)
Fix display for servers with special characters.
2026-03-27 14:58:29 -06:00
Michael Bolin
15fbf9d4f5 fix: fix Windows CI regression introduced in #15999 (#16027)
#15999 introduced a Windows-only `\r\n` mismatch in review-exit template
handling. This PR normalizes those template newlines and separates that
fix from [#16014](https://github.com/openai/codex/pull/16014) so it can
be reviewed independently.
2026-03-27 12:06:07 -07:00
Michael Bolin
caee620a53 codex-tools: introduce named tool definitions (#15953)
## Why

This continues the `codex-tools` migration by moving one more piece of
generic tool-definition bookkeeping out of `codex-core`.

The earlier extraction steps moved shared schema parsing into
`codex-tools`, but `core/src/tools/spec.rs` still had to supply tool
names separately and perform ad hoc rewrites for deferred MCP aliases.
That meant the crate boundary was still awkward: the parsed shape coming
back from `codex-tools` was missing part of the definition that
`codex-core` ultimately needs to assemble a `ResponsesApiTool`.

This change introduces a named `ToolDefinition` in `codex-tools` so both
MCP tools and dynamic tools cross the crate boundary in the same
reusable model. `codex-core` still owns the final `ResponsesApiTool`
assembly, but less of the generic tool-definition shaping logic stays
behind in `core`.

## What changed

- replaced `ParsedToolDefinition` with a named `ToolDefinition` in
`codex-rs/tools/src/tool_definition.rs`
- added `codex-rs/tools/src/tool_definition_tests.rs` for `renamed()`
and `into_deferred()`
- updated `parse_dynamic_tool()` and `parse_mcp_tool()` to return
`ToolDefinition`
- simplified `codex-rs/core/src/tools/spec.rs` so it adapts
`ToolDefinition` into `ResponsesApiTool` instead of rewriting names and
deferred fields inline
- updated parser tests and `codex-rs/tools/README.md` to reflect the
named tool-definition model

## Test plan

- `cargo test -p codex-tools`
- `cargo test -p codex-core --lib tools::spec::`
2026-03-27 12:02:55 -07:00
Michael Bolin
617475e54b codex-tools: extract dynamic tool adapters (#15944)
## Why

`codex-tools` already owned the shared JSON schema parser and the MCP
tool schema adapter, but `core/src/tools/spec.rs` still parsed dynamic
tools directly.

That left the tool-schema boundary split in two different ways:

- MCP tools flowed through `codex-tools`, while dynamic tools were still
parsed in `codex-core`
- the extracted dynamic-tool path initially introduced a
dynamic-specific parsed shape even though `codex-tools` already had very
similar MCP adapter output

This change finishes that extraction boundary in one step. `codex-core`
still owns `ResponsesApiTool` assembly, but both MCP tools and dynamic
tools now enter that layer through `codex-tools` using the same parsed
tool-definition shape.

## What changed

- added `tools/src/dynamic_tool.rs` and sibling
`tools/src/dynamic_tool_tests.rs`
- introduced `parse_dynamic_tool()` in `codex-tools` and switched
`core/src/tools/spec.rs` to use it for dynamic tools
- added `tools/src/parsed_tool_definition.rs` so both MCP and dynamic
adapters return the same `ParsedToolDefinition`
- updated `core/src/tools/spec.rs` to build `ResponsesApiTool` through a
shared local adapter helper instead of separate MCP and dynamic assembly
paths
- expanded `core/src/tools/spec_tests.rs` so the dynamic-tool adapter
test asserts the full converted `ResponsesApiTool`, including
`defer_loading`
- updated `codex-rs/tools/README.md` to reflect the shared parsed
tool-definition boundary

## Test plan

- `cargo test -p codex-tools`
- `cargo test -p codex-core --lib tools::spec::`

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/15944).
* #15953
* __->__ #15944
2026-03-27 09:12:36 -07:00
viyatb-oai
ec089fd22a fix(sandbox): fix bwrap lookup for multi-entry PATH (#15973)
## Summary
- split the joined `PATH` before running system `bwrap` lookup
- keep the existing workspace-local `bwrap` skip behavior intact
- add regression tests that exercise real multi-entry search paths

## Why
The PATH-based lookup added in #15791 still wrapped the raw `PATH`
environment value as a single `PathBuf` before passing it through
`join_paths()`. On Unix, a normal multi-entry `PATH` contains `:`, so
that wrapper path is invalid as one path element and the lookup returns
`None`.

That made Codex behave as if no system `bwrap` was installed even when
`bwrap` was available on `PATH`, which is what users in #15340 were
still hitting on `0.117.0-alpha.25`.

## Impact
System `bwrap` discovery now works with normal multi-entry `PATH` values
instead of silently falling back to the vendored binary.

Fixes #15340.

## Validation
- `just fmt`
- `cargo test -p codex-sandboxing`
- `cargo test -p codex-linux-sandbox`
- `just fix -p codex-sandboxing`
- `just argument-comment-lint`
2026-03-27 08:41:06 -07:00
jif-oai
426f28ca99 feat: spawn v2 as inter agent communication (#15985)
Co-authored-by: Codex <noreply@openai.com>
2026-03-27 15:45:19 +01:00
jif-oai
2b71717ccf Use codex-utils-template for review exit XML (#15999) 2026-03-27 15:30:28 +01:00
jif-oai
f044ca64df Use codex-utils-template for search tool descriptions (#15996) 2026-03-27 15:08:24 +01:00
jif-oai
37b057f003 Use codex-utils-template for collaboration mode presets (#15995) 2026-03-27 14:51:07 +01:00
jif-oai
7d5d9f041b Use codex-utils-template for review prompts (#16001) 2026-03-27 14:50:01 +01:00
jif-oai
6a0c4709ca feat: spawn v2 make task name as mandatory (#15986) 2026-03-27 11:30:22 +01:00
Celia Chen
dd30c8eedd chore: refactor network permissions to use explicit domain and unix socket rule maps (#15120)
## Summary

This PR replaces the legacy network allow/deny list model with explicit
rule maps for domains and unix sockets across managed requirements,
permissions profiles, the network proxy config, and the app server
protocol.

Concretely, it:

- introduces typed domain (`allow` / `deny`) and unix socket permission
(`allow` / `none`) entries instead of separate `allowed_domains`,
`denied_domains`, and `allow_unix_sockets` lists
- updates config loading, managed requirements merging, and exec-policy
overlays to read and upsert rule entries consistently
- exposes the new shape through protocol/schema outputs, debug surfaces,
and app-server config APIs
- rejects the legacy list-based keys and updates docs/tests to reflect
the new config format

## Why

The previous representation split related network policy across multiple
parallel lists, which made merging and overriding rules harder to reason
about. Moving to explicit keyed permission maps gives us a single source
of truth per host/socket entry, makes allow/deny precedence clearer, and
gives protocol consumers access to the full rule state instead of
derived projections only.

## Backward Compatibility

### Backward compatible

- Managed requirements still accept the legacy
`experimental_network.allowed_domains`,
`experimental_network.denied_domains`, and
`experimental_network.allow_unix_sockets` fields. They are normalized
into the new canonical `domains` and `unix_sockets` maps internally.
- App-server v2 still deserializes legacy `allowedDomains`,
`deniedDomains`, and `allowUnixSockets` payloads, so older clients can
continue reading managed network requirements.
- App-server v2 responses still populate `allowedDomains`,
`deniedDomains`, and `allowUnixSockets` as legacy compatibility views
derived from the canonical maps.
- `managed_allowed_domains_only` keeps the same behavior after
normalization. Legacy managed allowlists still participate in the same
enforcement path as canonical `domains` entries.

### Not backward compatible

- Permissions profiles under `[permissions.<profile>.network]` no longer
accept the legacy list-based keys. Those configs must use the canonical
`[domains]` and `[unix_sockets]` tables instead of `allowed_domains`,
`denied_domains`, or `allow_unix_sockets`.
- Managed `experimental_network` config cannot mix canonical and legacy
forms in the same block. For example, `domains` cannot be combined with
`allowed_domains` or `denied_domains`, and `unix_sockets` cannot be
combined with `allow_unix_sockets`.
- The canonical format can express explicit `"none"` entries for unix
sockets, but those entries do not round-trip through the legacy
compatibility fields because the legacy fields only represent allow/deny
lists.
## Testing
`/target/debug/codex sandbox macos --log-denials /bin/zsh -c 'curl
https://www.example.com' ` gives 200 with config
```
[permissions.workspace.network.domains]
"www.example.com" = "allow"
```
and fails when set to deny: `curl: (56) CONNECT tunnel failed, response
403`.

Also tested backward compatibility path by verifying that adding the
following to `/etc/codex/requirements.toml` works:
```
[experimental_network]
allowed_domains = ["www.example.com"]
```
2026-03-27 06:17:59 +00:00
Michael Bolin
be5afc65d3 codex-tools: extract MCP schema adapters (#15928)
## Why

`codex-tools` already owns the shared tool input schema model and parser
from the first extraction step, but `core/src/tools/spec.rs` still owned
the MCP-specific adapter that normalizes `rmcp::model::Tool` schemas and
wraps `structuredContent` into the call result output schema.

Keeping that adapter in `codex-core` means the reusable MCP schema path
is still split across crates, and the unit tests for that logic stay
anchored in `codex-core` even though the runtime orchestration does not
need to move yet.

This change takes the next small step by moving the reusable MCP schema
adapter into `codex-tools` while leaving `ResponsesApiTool` assembly in
`codex-core`.

## What changed

- added `tools/src/mcp_tool.rs` and sibling
`tools/src/mcp_tool_tests.rs`
- introduced `ParsedMcpTool`, `parse_mcp_tool()`, and
`mcp_call_tool_result_output_schema()` in `codex-tools`
- updated `core/src/tools/spec.rs` to consume parsed MCP tool parts from
`codex-tools`
- removed the now-redundant MCP schema unit tests from
`core/src/tools/spec_tests.rs`
- expanded `codex-rs/tools/README.md` to describe this second migration
step

## Test plan

- `cargo test -p codex-tools`
- `cargo test -p codex-core --lib tools::spec::`
2026-03-26 19:57:26 -07:00
viyatb-oai
81fa04783a feat(windows-sandbox): add network proxy support (#12220)
## Summary

This PR makes Windows sandbox proxying enforceable by routing proxy-only
runs through the existing `offline` sandbox user and reserving direct
network access for the existing `online` sandbox user.

In brief:

- if a Windows sandbox run should be proxy-enforced, we run it as the
`offline` user
- the `offline` user gets firewall rules that block direct outbound
traffic and only permit the configured localhost proxy path
- if a Windows sandbox run should have true direct network access, we
run it as the `online` user
- no new sandbox identity is introduced

This brings Windows in line with the intended model: proxy use is not
just env-based, it is backed by OS-level egress controls. Windows
already has two sandbox identities:

- `offline`: intended to have no direct network egress
- `online`: intended to have full network access

This PR makes proxy-enforced runs use that model directly.

### Proxy-enforced runs

When proxy enforcement is active:

- the run is assigned to the `offline` identity
- setup extracts the loopback proxy ports from the sandbox env
- Windows setup programs firewall rules for the `offline` user that:
  - block all non-loopback outbound traffic
  - block loopback UDP
  - block loopback TCP except for the configured proxy ports
- optionally allow broader localhost access when `allow_local_binding=1`

So the sandboxed process can only talk to the local proxy. It cannot
open direct outbound sockets or do local UDP-based DNS on its own.The
proxy then performs the real outbound network access outside that
restricted sandbox identity.

### Direct-network runs

When proxy enforcement is not active and full network access is allowed:

- the run is assigned to the `online` identity
- no proxy-only firewall restrictions are applied
- the process gets normal direct network access

### Unelevated vs elevated

The restricted-token / unelevated path cannot enforce per-identity
firewall policy by itself.

So for Windows proxy-enforced runs, we transparently use the logon-user
sandbox path under the hood, even if the caller started from the
unelevated mode. That keeps enforcement real instead of best-effort.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-26 17:27:38 -07:00
Michael Bolin
e6e2999209 permissions: remove macOS seatbelt extension profiles (#15918)
## Why

`PermissionProfile` should only describe the per-command permissions we
still want to grant dynamically. Keeping
`MacOsSeatbeltProfileExtensions` in that surface forced extra macOS-only
approval, protocol, schema, and TUI branches for a capability we no
longer want to expose.

## What changed

- Removed the macOS-specific permission-profile types from
`codex-protocol`, the app-server v2 API, and the generated
schema/TypeScript artifacts.
- Deleted the core and sandboxing plumbing that threaded
`MacOsSeatbeltProfileExtensions` through execution requests and seatbelt
construction.
- Simplified macOS seatbelt generation so it always includes the fixed
read-only preferences allowlist instead of carrying a configurable
profile extension.
- Removed the macOS additional-permissions UI/docs/test coverage and
deleted the obsolete macOS permission modules.
- Tightened `request_permissions` intersection handling so explicitly
empty requested read lists are preserved only when that field was
actually granted, avoiding zero-grant responses being stored as active
permissions.
2026-03-26 17:12:45 -07:00
Michael Bolin
44d28f500f codex-tools: extract shared tool schema parsing (#15923)
## Why

`parse_tool_input_schema` and the supporting `JsonSchema` model were
living in `core/src/tools/spec.rs`, but they already serve callers
outside `codex-core`.

Keeping that shared schema parsing logic inside `codex-core` makes the
crate boundary harder to reason about and works against the guidance in
`AGENTS.md` to avoid growing `codex-core` when reusable code can live
elsewhere.

This change takes the first extraction step by moving the schema parsing
primitive into its own crate while keeping the rest of the tool-spec
assembly in `codex-core`.

## What changed

- added a new `codex-tools` crate under `codex-rs/tools`
- moved the shared tool input schema model and sanitizer/parser into
`tools/src/json_schema.rs`
- kept `tools/src/lib.rs` exports-only, with the module-level unit tests
split into `json_schema_tests.rs`
- updated `codex-core` to use `codex-tools::JsonSchema` and re-export
`parse_tool_input_schema`
- updated `codex-app-server` dynamic tool validation to depend on
`codex-tools` directly instead of reaching through `codex-core`
- wired the new crate into the Cargo workspace and Bazel build graph
2026-03-27 00:03:35 +00:00
Michael Bolin
5906c6a658 chore: remove skill metadata from command approval payloads (#15906)
## Why

This is effectively a follow-up to
[#15812](https://github.com/openai/codex/pull/15812). That change
removed the special skill-script exec path, but `skill_metadata` was
still being threaded through command-approval payloads even though the
approval flow no longer uses it to render prompts or resolve decisions.

Keeping it around added extra protocol, schema, and client surface area
without changing behavior.

Removing it keeps the command-approval contract smaller and avoids
carrying a dead field through app-server, TUI, and MCP boundaries.

## What changed

- removed `ExecApprovalRequestSkillMetadata` and the corresponding
`skillMetadata` field from core approval events and the v2 app-server
protocol
- removed the generated JSON and TypeScript schema output for that field
- updated app-server, MCP server, TUI, and TUI app-server approval
plumbing to stop forwarding the field
- cleaned up tests that previously constructed or asserted
`skillMetadata`

## Testing

- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-protocol`
- `cargo test -p codex-app-server-test-client`
- `cargo test -p codex-mcp-server`
- `just argument-comment-lint`
2026-03-26 15:32:03 -07:00
viyatb-oai
b52abff279 chore: move bwrap config helpers into dedicated module (#15898)
## Summary
- move the bwrap PATH lookup and warning helpers out of config/mod.rs
- move the related tests into a dedicated bwrap_tests.rs file

## Validation
- git diff --check
- skipped heavier local tests per request

Follow-up to #15791.
2026-03-26 15:15:59 -07:00
Michael Bolin
dfb36573cd sandboxing: use OsString for SandboxCommand.program (#15897)
## Why

`SandboxCommand.program` represents an executable path, but keeping it
as `String` forced path-backed callers to run `to_string_lossy()` before
the sandbox layer ever touched the command. That loses fidelity earlier
than necessary and adds avoidable conversions in runtimes that already
have a `PathBuf`.

## What changed

- Changed `SandboxCommand.program` to `OsString`.
- Updated `SandboxManager::transform` to keep the program and argv in
`OsString` form until the `SandboxExecRequest` conversion boundary.
- Switched the path-backed `apply_patch` and `js_repl` runtimes to pass
`into_os_string()` instead of `to_string_lossy()`.
- Updated the remaining string-backed builders and tests to match the
new type while preserving the existing Linux helper `arg0` behavior.

## Verification

- `cargo test -p codex-sandboxing`
- `just argument-comment-lint -p codex-core -p codex-sandboxing`
- `cargo test -p codex-core` currently fails in unrelated existing
config tests: `config::tests::approvals_reviewer_*` and
`config::tests::smart_approvals_alias_*`
2026-03-26 20:38:33 +00:00
Michael Bolin
b23789b770 [codex] import token_data from codex-login directly (#15903)
## Why
`token_data` is owned by `codex-login`, but `codex-core` was still
re-exporting it. That let callers pull auth token types through
`codex-core`, which keeps otherwise unrelated crates coupled to
`codex-core` and makes `codex-core` more of a build-graph bottleneck.

## What changed
- remove the `codex-core` re-export of `codex_login::token_data`
- update the remaining `codex-core` internals that used
`crate::token_data` to import `codex_login::token_data` directly
- update downstream callers in `codex-rs/chatgpt`,
`codex-rs/tui_app_server`, `codex-rs/app-server/tests/common`, and
`codex-rs/core/tests` to import `codex_login::token_data` directly
- add explicit `codex-login` workspace dependencies and refresh lock
metadata for crates that now depend on it directly

## Validation
- `cargo test -p codex-chatgpt --locked`
- `just argument-comment-lint`
- `just bazel-lock-update`
- `just bazel-lock-check`

## Notes
- attempted `cargo test -p codex-core --locked` and `cargo test -p
codex-core auth_refresh --locked`, but both ran out of disk while
linking `codex-core` test binaries in the local environment
2026-03-26 13:34:02 -07:00
rreichel3-oai
86764af684 Protect first-time project .codex creation across Linux and macOS sandboxes (#15067)
## Problem

Codex already treated an existing top-level project `./.codex` directory
as protected, but there was a gap on first creation.

If `./.codex` did not exist yet, a turn could create files under it,
such as `./.codex/config.toml`, without going through the same approval
path as later modifications. That meant the initial write could bypass
the intended protection for project-local Codex state.

## What this changes

This PR closes that first-creation gap in the Unix enforcement layers:

- `codex-protocol`
- treat the top-level project `./.codex` path as a protected carveout
even when it does not exist yet
- avoid injecting the default carveout when the user already has an
explicit rule for that exact path
- macOS Seatbelt
- deny writes to both the exact protected path and anything beneath it,
so creating `./.codex` itself is blocked in addition to writes inside it
- Linux bubblewrap
- preserve the same protected-path behavior for first-time creation
under `./.codex`
- tests
- add protocol regressions for missing `./.codex` and explicit-rule
collisions
- add Unix sandbox coverage for blocking first-time `./.codex` creation
  - tighten Seatbelt policy assertions around excluded subpaths

## Scope

This change is intentionally scoped to protecting the top-level project
`.codex` subtree from agent writes.

It does not make `.codex` unreadable, and it does not change the product
behavior around loading project skills from `.codex` when project config
is untrusted.

## Why this shape

The fix is pointed rather than broad:
- it preserves the current model of “project `.codex` is protected from
writes”
- it closes the security-relevant first-write hole
- it avoids folding a larger permissions-model redesign into this PR

## Validation

- `cargo test -p codex-protocol`
- `cargo test -p codex-sandboxing seatbelt`
- `cargo test -p codex-exec --test all
sandbox_blocks_first_time_dot_codex_creation -- --nocapture`

---------

Co-authored-by: Michael Bolin <mbolin@openai.com>
2026-03-26 16:06:53 -04:00
Michael Bolin
b3e069e8cb skills: remove unused skill permission metadata (#15900)
## Why

Skill metadata accepted a `permissions` block and stored the result on
`SkillMetadata`, but that data was never consumed by runtime behavior.
Leaving the dead parsing path in place makes it look like skills can
widen or otherwise influence execution permissions when, in practice,
declared skill permissions are ignored.

This change removes that misleading surface area so the skill metadata
model matches what the system actually uses.

## What changed

- removed `permission_profile` and `managed_network_override` from
`core-skills::SkillMetadata`
- stopped parsing `permissions` from skill metadata in
`core-skills/src/loader.rs`
- deleted the loader tests that only exercised the removed permissions
parsing path
- cleaned up dependent `SkillMetadata` constructors in tests and TUI
code that were only carrying `None` for those fields

## Testing

- `cargo test -p codex-core-skills`
- `cargo test -p codex-tui
submission_prefers_selected_duplicate_skill_path`
- `just argument-comment-lint`
2026-03-26 19:33:23 +00:00
viyatb-oai
b6050b42ae fix: resolve bwrap from trusted PATH entry (#15791)
## Summary
- resolve system bwrap from PATH instead of hardcoding /usr/bin/bwrap
- skip PATH entries that resolve inside the current workspace before
launching the sandbox helper
- keep the vendored bubblewrap fallback when no trusted system bwrap is
found

## Validation
- cargo test -p codex-core bwrap --lib
- cargo test -p codex-linux-sandbox
- just fix -p codex-core
- just fix -p codex-linux-sandbox
- just fmt
- just argument-comment-lint
- cargo clean
2026-03-26 12:13:51 -07:00
Matthew Zeng
3360f128f4 [plugins] Polish tool suggest prompts. (#15891)
- [x] Polish tool suggest prompts to distinguish between missing
connectors and discoverable plugins, and be very precise about the
triggering conditions.
2026-03-26 18:52:59 +00:00
Matthew Zeng
25134b592c [mcp] Fix legacy_tools (#15885)
- [x] Fix legacy_tools
2026-03-26 11:08:49 -07:00
jif-oai
970386e8b2 fix: root as std agent (#15881) 2026-03-26 18:57:34 +01:00
Michael Bolin
e36ebaa3da fix: box apply_patch test harness futures (#15835)
## Why

`#[large_stack_test]` made the `apply_patch_cli` tests pass by giving
them more stack, but it did not address why those tests needed the extra
stack in the first place.

The real problem is the async state built by the `apply_patch_cli`
harness path. Those tests await three helper boundaries directly:
harness construction, turn submission, and apply-patch output
collection. If those helpers inline their full child futures, the test
future grows to include the whole harness startup and request/response
path.

This change replaces the workaround from #12768 with the same basic
approach used in #13429, but keeps the fix narrower: only the helper
boundaries awaited directly by `apply_patch_cli` stay boxed.

## What Changed

- removed `#[large_stack_test]` from
`core/tests/suite/apply_patch_cli.rs`
- restored ordinary `#[tokio::test(flavor = "multi_thread",
worker_threads = 2)]` annotations in that suite
- deleted the now-unused `codex-test-macros` crate and removed its
workspace wiring
- boxed only the three helper boundaries that the suite awaits directly:
  - `apply_patch_harness_with(...)`
  - `TestCodexHarness::submit(...)`
  - `TestCodexHarness::apply_patch_output(...)`
- added comments at those boxed boundaries explaining why they remain
boxed

## Testing

- `cargo test -p codex-core --test all suite::apply_patch_cli --
--nocapture`

## References

- #12768
- #13429
2026-03-26 17:32:04 +00:00
nicholasclark-openai
8d479f741c Add MCP connector metrics (#15805)
## Summary
- enrich `codex.mcp.call` with `tool`, `connector_id`, and sanitized
`connector_name` for actual MCP executions
- record `codex.mcp.call.duration_ms` for actual MCP executions so
connector-level latency is visible in metrics
- keep skipped, blocked, declined, and cancelled paths on the plain
status-only `codex.mcp.call` counter

## Included Changes
- `codex-rs/core/src/mcp_tool_call.rs`: add connector-sliced MCP count
and duration metrics only for executed tool calls, while leaving
non-executed outcomes as status-only counts
- `codex-rs/core/src/mcp_tool_call_tests.rs`: cover metric tag shaping,
connector-name sanitization, and the new duration metric tags

## Testing
- `cargo test -p codex-core`
- `just fix -p codex-core`
- `just fmt`

## Notes
- `cargo test -p codex-core` still hits existing unrelated failures in
approvals-reviewer config tests and the sandboxed JS REPL `mktemp` test
- full workspace `cargo test` was not run

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-26 17:08:02 +00:00
jif-oai
352f37db03 fix: max depth agent still has v2 tools (#15880) 2026-03-26 17:36:12 +01:00
Matthew Zeng
c9214192c5 [plugins] Update the suggestable plugins list. (#15829)
- [x] Update the suggestable plugins list to be featured plugins.
2026-03-26 15:53:22 +00:00
jif-oai
6d2f4aaafc feat: use ProcessId in exec-server (#15866)
Use a full struct for the ProcessId to increase readability and make it
easier in the future to make it evolve if needed
2026-03-26 16:45:36 +01:00
jif-oai
26c66f3ee1 fix: flaky (#15869) 2026-03-26 16:07:32 +01:00
Michael Bolin
01fa4f0212 core: remove special execve handling for skill scripts (#15812) 2026-03-26 07:46:04 -07:00
jif-oai
7dac332c93 feat: exec-server prep for unified exec (#15691)
This PR partially rebase `unified_exec` on the `exec-server` and adapt
the `exec-server` accordingly.

## What changed in `exec-server`

1. Replaced the old "broadcast-driven; process-global" event model with
process-scoped session events. The goal is to be able to have dedicated
handler for each process.
2. Add to protocol contract to support explicit lifecycle status and
stream ordering:
- `WriteResponse` now returns `WriteStatus` (Accepted, UnknownProcess,
StdinClosed, Starting) instead of a bool.
  - Added seq fields to output/exited notifications.
  - Added terminal process/closed notification.
3. Demultiplexed remote notifications into per-process channels. Same as
for the event sys
4. Local and remote backends now both implement ExecBackend.
5. Local backend wraps internal process ID/operations into per-process
ExecProcess objects.
6. Remote backend registers a session channel before launch and
unregisters on failed launch.

## What changed in `unified_exec`

1. Added unified process-state model and backend-neutral process
wrapper. This will probably disappear in the future, but it makes it
easier to keep the work flowing on both side.
- `UnifiedExecProcess` now handles both local PTY sessions and remote
exec-server processes through a shared `ProcessHandle`.
- Added `ProcessState` to track has_exited, exit_code, and terminal
failure message consistently across backends.
2. Routed write and lifecycle handling through process-level methods.

## Some rationals

1. The change centralizes execution transport in exec-server while
preserving policy and orchestration ownership in core, avoiding
duplicated launch approval logic. This comes from internal discussion.
2. Session-scoped events remove coupling/cross-talk between processes
and make stream ordering and terminal state explicit (seq, closed,
failed).
3. The failure-path surfacing (remote launch failures, write failures,
transport disconnects) makes command tool output and cleanup behavior
deterministic

## Follow-ups:
* Unify the concept of thread ID behind an obfuscated struct
* FD handling
* Full zsh-fork compatibility
* Full network sandboxing compatibility
* Handle ws disconnection
2026-03-26 15:22:34 +01:00
jif-oai
4a5635b5a0 feat: clean spawn v1 (#15861)
Avoid the usage of path in the v1 spawn
2026-03-26 15:01:00 +01:00
jif-oai
b00a05c785 feat: drop artifact tool and feature (#15851) 2026-03-26 13:21:24 +01:00
jif-oai
7ef3cfe63e feat: replace askama by custom lib (#15784)
Finalise the drop of `askama` to use our internal lib instead
2026-03-26 10:33:25 +01:00
viyatb-oai
937cb5081d fix: fix old system bubblewrap compatibility without falling back to vendored bwrap (#15693)
Fixes #15283.

## Summary
Older system bubblewrap builds reject `--argv0`, which makes our Linux
sandbox fail before the helper can re-exec. This PR keeps using system
`/usr/bin/bwrap` whenever it exists and only falls back to vendored
bwrap when the system binary is missing. That matters on stricter
AppArmor hosts, where the distro bwrap package also provides the policy
setup needed for user namespaces.

For old system bwrap, we avoid `--argv0` instead of switching binaries:
- pass the sandbox helper a full-path `argv0`,
- keep the existing `current_exe() + --argv0` path when the selected
launcher supports it,
- otherwise omit `--argv0` and re-exec through the helper's own
`argv[0]` path, whose basename still dispatches as
`codex-linux-sandbox`.

Also updates the launcher/warning tests and docs so they match the new
behavior: present-but-old system bwrap uses the compatibility path, and
only absent system bwrap falls back to vendored.

### Validation

1. Install Ubuntu 20.04 in a VM
2. Compile codex and run without bubblewrap installed - see a warning
about falling back to the vendored bwrap
3. Install bwrap and verify version is 0.4.0 without `argv0` support
4. run codex and use apply_patch tool without errors

<img width="802" height="631" alt="Screenshot 2026-03-25 at 11 48 36 PM"
src="https://github.com/user-attachments/assets/77248a29-aa38-4d7c-9833-496ec6a458b8"
/>
<img width="807" height="634" alt="Screenshot 2026-03-25 at 11 47 32 PM"
src="https://github.com/user-attachments/assets/5af8b850-a466-489b-95a6-455b76b5050f"
/>
<img width="812" height="635" alt="Screenshot 2026-03-25 at 11 45 45 PM"
src="https://github.com/user-attachments/assets/438074f0-8435-4274-a667-332efdd5cb57"
/>
<img width="801" height="623" alt="Screenshot 2026-03-25 at 11 43 56 PM"
src="https://github.com/user-attachments/assets/0dc8d3f5-e8cf-4218-b4b4-a4f7d9bf02e3"
/>

---------

Co-authored-by: Michael Bolin <mbolin@openai.com>
2026-03-25 23:51:39 -07:00
Andrei Eternal
c4d9887f9a [hooks] add non-streaming (non-stdin style) shell-only PostToolUse support (#15531)
CHAINED PR - note that base is eternal/hooks-pretooluse-bash, not main
-- so the following PR should be first

Matching post-tool hook to the pre-tool functionality here:
https://github.com/openai/codex/pull/15211

So, PreToolUse calls for plain shell calls, allows blocking. This
PostToolUse call runs after the command executed

example run:

```
› as a test, run in parallel the following commands:
  - echo 'one'
  - echo '[block-pre-tool-use]'
  - echo '[block-post-tool-use]'


⚠ MCP startup incomplete (failed: notion, linear)

• Cruising through those three commands in parallel now, and I’ll share the exact outputs right after
  they land.

• Running PreToolUse hook: checking the observatory runes

• Running PreToolUse hook: checking the observatory runes

• Running PreToolUse hook: checking the observatory runes

PreToolUse hook (blocked)
  warning: wizard-tower PreToolUse demo blocked a Bash command on purpose.
  feedback: PreToolUse demo blocked the command. Remove [block-pre-tool-use] to continue.

PreToolUse hook (completed)
  warning: wizard-tower PreToolUse demo inspected Bash: echo 'one'

PreToolUse hook (completed)
  warning: wizard-tower PreToolUse demo inspected Bash: echo '[block-post-tool-use]'

• Ran echo '[block-post-tool-use]'
  └ [block-post-tool-use]

• Ran echo 'one'
  └ one

• Running PostToolUse hook: reading the observatory aftermath

• Running PostToolUse hook: reading the observatory aftermath

PostToolUse hook (blocked)
  warning: wizard-tower PostToolUse demo blocked the Bash follow-through on purpose.
  feedback: PostToolUse demo blocked the result after execution. Remove [block-post-tool-use] to
continue.

PostToolUse hook (completed)
  warning: wizard-tower PostToolUse demo inspected Bash: echo 'one'
  hook context: PostToolUse demo saw the Bash result. Response preview: one

──────────────────────────────────────────────────────────────────────────────────────────────────────

• Ran them in parallel. Little wave report:

  - echo 'one' completed and returned one.
  - echo '[block-pre-tool-use]' was blocked before execution by the PreToolUse hook.
  - echo '[block-post-tool-use]' ran, but its result was blocked afterward by the PostToolUse hook.

  The hook messages were:

  - PreToolUse demo blocked the command. Remove [block-pre-tool-use] to continue.
  - PostToolUse demo blocked the result after execution. Remove [block-post-tool-use] to continue.


› for the blocked messages (correctly blocked in this test) did the tool output land back into
  context?


• Yeah, from this run:

  - PreToolUse block: the command itself did not execute, so no command output landed in context. Only
    the block message did.
  - PostToolUse block: the command did execute, but the actual stdout did not come back into context.
    Only the post-tool block message did.

  So the blocked notifications landed in context, but the blocked command payloads themselves did not.
  Nice clean guardrail, aloha.
```
2026-03-25 19:18:03 -07:00
Matthew Zeng
78799c1bcf [mcp] Improve custom MCP elicitation (#15800)
- [x] Support don't ask again for custom MCP tool calls.
- [x] Don't run arc in yolo mode.
- [x] Run arc for custom MCP tools in always allow mode.
2026-03-26 01:02:37 +00:00