Commit Graph

2488 Commits

  • codex-tools: extract utility tool specs (#16154)
    ## Why
    
    The previous `codex-tools` migration steps moved the shared schema
    models, local-host specs, collaboration specs, and related adapters out
    of `codex-core`, but `core/src/tools/spec.rs` still contained a grab bag
    of pure utility tool builders. Those specs do not need session state or
    handler logic; they only describe wire shapes for tools that
    `codex-core` already knows how to execute.
    
    Moving that remaining low-coupling layer into `codex-tools` keeps the
    migration moving in meaningful chunks and trims another large block of
    passive tool-spec construction out of `codex-core` without touching the
    runtime-coupled handlers.
    
    ## What changed
    
    - extended `codex-tools` to own the pure spec builders for:
      - code-mode `exec` / `wait`
      - `js_repl` / `js_repl_reset`
    - MCP resource tools `list_mcp_resources`,
    `list_mcp_resource_templates`, and `read_mcp_resource`
      - utility tools `list_dir` and `test_sync_tool`
    - split those builders across small module files with sibling
    `*_tests.rs` coverage, keeping `src/lib.rs` exports-only
    - rewired `core/src/tools/spec.rs` to call the extracted builders and
    deleted the duplicated core-local implementations
    - moved the direct JS REPL grammar seam test out of
    `core/src/tools/spec_tests.rs` so it now lives with the extracted
    implementation in `codex-tools`
    - updated `codex-rs/tools/README.md` so the documented crate boundary
    matches the new utility-spec surface
    
    ## Test plan
    
    - `CARGO_TARGET_DIR=/tmp/codex-tools-utility-specs cargo test -p
    codex-tools`
    - `CARGO_TARGET_DIR=/tmp/codex-core-utility-specs cargo test -p
    codex-core --lib tools::spec::`
    - `just fix -p codex-tools -p codex-core`
    - `just argument-comment-lint`
    
    ## References
    
    - #15923
    - #15928
    - #15944
    - #15953
    - #16031
    - #16047
    - #16129
    - #16132
    - #16138
    - #16141
  • codex-tools: extract collaboration tool specs (#16141)
    ## Why
    
    The recent `codex-tools` migration steps have moved shared tool models
    and low-coupling spec helpers out of `codex-core`, but
    `core/src/tools/spec.rs` still owned a large block of pure
    collaboration-tool spec construction. Those builders do not need session
    state or runtime behavior; they only need a small amount of core-owned
    configuration injected at the seam.
    
    Moving that cohesive slice into `codex-tools` makes the crate boundary
    more honest and removes a substantial amount of passive tool-spec logic
    from `codex-core` without trying to move the runtime-coupled multi-agent
    handlers at the same time.
    
    ## What changed
    
    - added `agent_tool.rs`, `request_user_input_tool.rs`, and
    `agent_job_tool.rs` to `codex-tools`, with sibling `*_tests.rs` coverage
    and an exports-only `lib.rs`
    - moved the pure `ToolSpec` builders for:
    - collaboration tools such as `spawn_agent`, `send_input`,
    `send_message`, `assign_task`, `resume_agent`, `wait_agent`,
    `list_agents`, and `close_agent`
      - `request_user_input`
      - agent-job specs `spawn_agents_on_csv` and `report_agent_job_result`
    - rewired `core/src/tools/spec.rs` to call the extracted builders while
    still supplying the core-owned inputs, such as spawn-agent role
    descriptions and wait timeout bounds
    - updated the `core/src/tools/spec.rs` seam tests to build expected
    collaboration specs through `codex-tools`
    - updated `codex-rs/tools/README.md` so the crate documentation reflects
    the broader collaboration-tool boundary
    
    ## Test plan
    
    - `CARGO_TARGET_DIR=/tmp/codex-tools-collab-specs cargo test -p
    codex-tools`
    - `CARGO_TARGET_DIR=/tmp/codex-core-collab-specs cargo test -p
    codex-core --lib tools::spec::`
    - `just fix -p codex-tools -p codex-core`
    - `just argument-comment-lint`
    
    ## References
    
    - #15923
    - #15928
    - #15944
    - #15953
    - #16031
    - #16047
    - #16129
    - #16132
    - #16138
  • [mcp] Increase MCP startup timeout. (#16080)
    - [x] Increase MCP startup timeout to 30s, as the current 10s causes a
    lot of local MCPs to timeout.
  • Remove TUI voice transcription feature (#16114)
    Removes the partially-completed TUI composer voice transcription flow,
    including its feature flag, app events, and hold-to-talk state machine.
  • codex-tools: extract local host tool specs (#16138)
    ## Why
    
    `core/src/tools/spec.rs` still bundled a set of pure local-host tool
    builders with the orchestration that actually decides when those tools
    are exposed and which handlers back them. That made `codex-core`
    responsible for JSON/tool-shape construction that does not depend on
    session state, and it kept the `codex-tools` migration from taking a
    meaningfully larger bite out of `spec.rs`.
    
    This PR moves that reusable spec-building layer into `codex-tools` while
    leaving feature gating, handler registration, and runtime-coupled
    descriptions in `codex-core`.
    
    ## What changed
    
    - added `codex-rs/tools/src/local_tool.rs` for the pure builders for
    `exec_command`, `write_stdin`, `shell`, `shell_command`, and
    `request_permissions`
    - added `codex-rs/tools/src/view_image.rs` for the `view_image` tool
    spec and output schema so the extracted modules stay right-sized
    - rewired `codex-rs/core/src/tools/spec.rs` to call those extracted
    builders instead of constructing these specs inline
    - kept the `request_permissions` description source in `codex-core`,
    with `codex-tools` taking the description as input so the crate boundary
    does not grow a dependency on handler/runtime code
    - moved the direct constructor coverage for this slice from
    `codex-rs/core/src/tools/spec_tests.rs` into
    `codex-rs/tools/src/local_tool_tests.rs` and
    `codex-rs/tools/src/view_image_tests.rs`
    - updated `codex-rs/tools/README.md` to reflect that `codex-tools` now
    owns this local-host spec layer
    
    ## Test plan
    
    - `CARGO_TARGET_DIR=/tmp/codex-tools-local-host cargo test -p
    codex-tools`
    - `CARGO_TARGET_DIR=/tmp/codex-core-local-tools cargo test -p codex-core
    --lib tools::spec::`
    - `just argument-comment-lint`
    
    ## References
    
    - #15923
    - #15928
    - #15944
    - #15953
    - #16031
    - #16047
    - #16129
    - #16132
  • codex-tools: extract code mode tool spec adapters (#16132)
    ## Why
    
    The longer-term `codex-tools` migration is to move pure tool-definition
    and tool-spec plumbing out of `codex-core` while leaving session- and
    runtime-coupled orchestration behind.
    
    The remaining code-mode adapter layer in
    `core/src/tools/code_mode_description.rs` was a good next extraction
    seam because it only transformed `ToolSpec` values for code mode and
    already delegated the low-level description rendering to
    `codex-code-mode`.
    
    ## What Changed
    
    - added `codex-rs/tools/src/code_mode.rs` with
    `augment_tool_spec_for_code_mode()` and
    `tool_spec_to_code_mode_tool_definition()`
    - added focused unit coverage in `codex-rs/tools/src/code_mode_tests.rs`
    - rewired `core/src/tools/spec.rs` and `core/src/tools/code_mode/mod.rs`
    to use the extracted adapters from `codex-tools`
    - removed the old `core/src/tools/code_mode_description.rs` shim and its
    test file from `codex-core`
    - added the `codex-code-mode` dependency to `codex-tools`, updated
    `Cargo.lock`, and refreshed the `codex-tools` README to reflect the
    expanded boundary
    
    ## Test Plan
    
    - `cargo test -p codex-tools`
    - `CARGO_TARGET_DIR=/tmp/codex-core-code-mode-adapters cargo test -p
    codex-core --lib tools::spec::`
    - `CARGO_TARGET_DIR=/tmp/codex-core-code-mode-adapters cargo test -p
    codex-core --lib tools::code_mode::`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `just argument-comment-lint`
    
    ## References
    
    - #15923
    - #15928
    - #15944
    - #15953
    - #16031
    - #16047
    - #16129
  • core: fix stale curated plugin cache refresh races (#16126)
    ## Why
    
    The `plugin/list` force-sync path can race app-server startup's curated
    plugin cache refresh.
    
    Startup was capturing the configured curated plugin IDs from the initial
    config snapshot. If `plugin/list` with `forceRemoteSync` removed curated
    plugin entries from `config.toml` while that background refresh was
    still in flight, the startup task could recreate cache directories for
    plugins that had just been uninstalled.
    
    That leaves the `plugin/list` response logically correct but the on-disk
    cache stale, which matches the flaky Ubuntu arm failure seen in
    `codex-app-server::all
    suite::v2::plugin_list::plugin_list_force_remote_sync_reconciles_curated_plugin_state`
    while validating [#16047](https://github.com/openai/codex/pull/16047).
    
    ## What
    
    - change `codex-rs/core/src/plugins/manager.rs` so startup curated-repo
    refresh rereads the current user `config.toml` before deciding which
    curated plugin cache entries to refresh
    - factor the configured-plugin parsing so the same logic can be reused
    from either the config layer stack or the persisted user config value
    - add a regression test that verifies curated plugin IDs are read from
    the latest user config state before cache refresh runs
    
    ## Testing
    
    - `cargo test -p codex-core
    configured_curated_plugin_ids_from_codex_home_reads_latest_user_config
    -- --nocapture`
    - `cargo test -p codex-app-server
    suite::v2::plugin_list::plugin_list_force_remote_sync_reconciles_curated_plugin_state
    -- --nocapture`
    - `just argument-comment-lint`
  • 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
  • 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
  • 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
  • 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.
  • 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.
  • 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.
  • [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.
  • 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`
  • 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`
  • 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.
  • Normalize /mcp tool grouping for hyphenated server names (#15946)
    Fix display for servers with special characters.
  • 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.
  • 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::`
  • 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
  • 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`
  • 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"]
    ```
  • 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::`
  • 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>
  • 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.
  • 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
  • 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`
  • 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.
  • 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_*`
  • [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
  • 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>
  • 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`
  • 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
  • [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.
  • [mcp] Fix legacy_tools (#15885)
    - [x] Fix legacy_tools
  • 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
  • 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>
  • [plugins] Update the suggestable plugins list. (#15829)
    - [x] Update the suggestable plugins list to be featured plugins.
  • 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