Commit Graph

489 Commits

  • 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::`
  • 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`
  • 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_*`
  • [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.
  • 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
  • feat: clean spawn v1 (#15861)
    Avoid the usage of path in the v1 spawn
  • 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>
  • [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.
    ```
  • Extract codex-core-skills crate (#15749)
    ## Summary
    - move skill loading and management into codex-core-skills
    - leave codex-core with the thin integration layer and shared wiring
    
    ## Testing
    - CI
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [apps][tool_suggest] Remove tool_suggest's dependency on tool search. (#14856)
    - [x] Remove tool_suggest's dependency on tool search.
  • Use AbsolutePathBuf for cwd state (#15710)
    Migrate `cwd` and related session/config state to `AbsolutePathBuf` so
    downstream consumers consistently see absolute working directories.
    
    Add test-only `.abs()` helpers for `Path`, `PathBuf`, and `TempDir`, and
    update branch-local tests to use them instead of
    `AbsolutePathBuf::try_from(...)`.
    
    For the remaining TUI/app-server snapshot coverage that renders absolute
    cwd values, keep the snapshots unchanged and skip the Windows-only cases
    where the platform-specific absolute path layout differs.
  • chore: remove grep_files handler (#15775)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the dedicated
    "Contributing" markdown file or your PR may be closed:
    https://github.com/openai/codex/blob/main/docs/contributing.md
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
    
    Include a link to a bug report or enhancement request.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [plugins] Add a flag for tool search. (#15722)
    - [x] Add a flag for tool search.
  • fix: support split carveouts in windows restricted-token sandbox (#14172)
    ## Summary
    - keep legacy Windows restricted-token sandboxing as the supported
    baseline
    - support the split-policy subset that restricted-token can enforce
    directly today
    - support full-disk read, the same writable root set as legacy
    `WorkspaceWrite`, and extra read-only carveouts under those writable
    roots via additional deny-write ACLs
    - continue to fail closed for unsupported split-only shapes, including
    explicit unreadable (`none`) carveouts, reopened writable descendants
    under read-only carveouts, and writable root sets that do not match the
    legacy workspace roots
    
    ## Example
    Given a filesystem policy like:
    
    ```toml
    ":root" = "read"
    ":cwd" = "write"
    "./docs" = "read"
    ```
    
    the restricted-token backend can keep the workspace writable while
    denying writes under `docs` by layering an extra deny-write carveout on
    top of the legacy workspace-write roots.
    
    A policy like:
    
    ```toml
    "/workspace" = "write"
    "/workspace/docs" = "read"
    "/workspace/docs/tmp" = "write"
    ```
    
    still fails closed, because the unelevated backend cannot reopen the
    nested writable descendant safely.
    
    ## Stack
    -> fix: support split carveouts in windows restricted-token sandbox
    #14172
    fix: support split carveouts in windows elevated sandbox #14568
  • [plugins] Flip on additional flags. (#15719)
    - [x] Flip on additional flags.
  • [plugins] Flip the flags. (#15713)
    - [x] Flip the `plugins` and `apps` flags.
  • Move string truncation helpers into codex-utils-string (#15572)
    - move the shared byte-based middle truncation logic from `core` into
    `codex-utils-string`
    - keep token-specific truncation in `codex-core` so rollout can reuse
    the shared helper in the next stacked PR
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Drop sandbox_permissions from sandbox exec requests (#15665)
    ## Summary
    - drop `sandbox_permissions` from the sandboxing `ExecOptions` and
    `ExecRequest` adapter types
    - remove the now-unused plumbing from shell, unified exec, JS REPL, and
    apply-patch runtime call sites
    - default reconstructed `ExecParams` to `SandboxPermissions::UseDefault`
    where the lower-level API still requires the field
    
    ## Testing
    - `just fmt`
    - `just argument-comment-lint`
    - `cargo test -p codex-core` (still running locally; first failures
    observed in `suite::cli_stream::responses_mode_stream_cli`,
    `suite::cli_stream::responses_mode_stream_cli_supports_openai_base_url_config_override`,
    and
    `suite::cli_stream::responses_mode_stream_cli_supports_openai_base_url_env_fallback`)
  • [plugins] Additional gating for tool suggest and apps. (#15573)
    - [x] Additional gating for tool suggest and apps.
  • fix: keep zsh-fork release assets after removing shell-tool-mcp (#15644)
    ## Why
    
    `shell-tool-mcp` and the Bash fork are no longer needed, but the patched
    zsh fork is still relevant for shell escalation and for the
    DotSlash-backed zsh-fork integration tests.
    
    Deleting the old `shell-tool-mcp` workflow also deleted the only
    pipeline that rebuilt those patched zsh binaries. This keeps the package
    removal, while preserving a small release path that can be reused
    whenever `codex-rs/shell-escalation/patches/zsh-exec-wrapper.patch`
    changes.
    
    ## What changed
    
    - removed the `shell-tool-mcp` workspace package, its npm
    packaging/release jobs, the Bash test fixture, and the remaining
    Bash-specific compatibility wiring
    - deleted the old `.github/workflows/shell-tool-mcp.yml` and
    `.github/workflows/shell-tool-mcp-ci.yml` workflows now that their
    responsibilities have been replaced or removed
    - kept the zsh patch under
    `codex-rs/shell-escalation/patches/zsh-exec-wrapper.patch` and updated
    the `codex-rs/shell-escalation` docs/code to describe the zsh-based flow
    directly
    - added `.github/workflows/rust-release-zsh.yml` to build only the three
    zsh binaries that `codex-rs/app-server/tests/suite/zsh` needs today:
      - `aarch64-apple-darwin` on `macos-15`
      - `x86_64-unknown-linux-musl` on `ubuntu-24.04`
      - `aarch64-unknown-linux-musl` on `ubuntu-24.04`
    - extracted the shared zsh build/smoke-test/stage logic into
    `.github/scripts/build-zsh-release-artifact.sh`, made that helper
    directly executable, and now invoke it directly from the workflow so the
    Linux and macOS jobs only keep the OS-specific setup in YAML
    - wired those standalone `codex-zsh-*.tar.gz` assets into
    `rust-release.yml` and added `.github/dotslash-zsh-config.json` so
    releases also publish a `codex-zsh` DotSlash file
    - updated the checked-in `codex-rs/app-server/tests/suite/zsh` fixture
    comments to explain that new releases come from the standalone zsh
    assets, while the checked-in fixture remains pinned to the latest
    historical release until a newer zsh artifact is published
    - tightened a couple of follow-on cleanups in
    `codex-rs/shell-escalation`: the `ExecParams::command` comment now
    describes the shell `-c`/`-lc` string more clearly, and the README now
    points at the same `git.code.sf.net` zsh source URL that the workflow
    uses
    
    ## Testing
    
    - `cargo test -p codex-shell-escalation`
    - `just argument-comment-lint`
    - `bash -n .github/scripts/build-zsh-release-artifact.sh`
    - attempted `cargo test -p codex-core`; unrelated existing failures
    remain, but the touched `tools::runtimes::shell::unix_escalation::*`
    coverage passed during that run
  • feat: communication pattern v2 (#15647)
    See internal communication
  • feat: list agents for sub-agent v2 (#15621)
    Add a `list_agents` for multi-agent v2, optionally path based
    
    This return the task and status of each agent in the matched path
  • Move sandbox policy transforms into codex-sandboxing (#15599)
    ## Summary
    - move the pure sandbox policy transform helpers from `codex-core` into
    `codex-sandboxing`
    - move the corresponding unit tests with the extracted implementation
    - update `core` and `app-server` callers to import the moved APIs
    directly, without re-exports or proxy methods
    
    ## Testing
    - cargo test -p codex-sandboxing
    - cargo test -p codex-core sandboxing
    - cargo test -p codex-app-server --lib
    - just fix -p codex-sandboxing
    - just fix -p codex-core
    - just fix -p codex-app-server
    - just fmt
    - just argument-comment-lint
  • [codex-cli][app-server] Update self-serve business usage limit copy in error returned (#15478)
    ## Summary
    - update the self-serve business usage-based limit message to direct
    users to their admin for additional credits
    - add a focused unit test for the self_serve_business_usage_based plan
    branch
    
    Added also: 
    
    If you are at a rate limit but you still have credits, codex cli would
    tell you to switch the model. We shouldnt do this if you have credits so
    fixed this.
    
    ## Test
    - launched the source-built CLI and verified the updated message is
    shown for the self-serve business usage-based plan
    
    ![Test
    screenshot](https://raw.githubusercontent.com/openai/codex/5cc3c013ef17ac5c66dfd9395c0d3c4837602231/docs/images/self-serve-business-usage-limit.png)
  • Move macOS sandbox builders into codex-sandboxing (#15593)
    ## Summary
    - move macOS permission merging/intersection logic and tests from
    `codex-core` into `codex-sandboxing`
    - move seatbelt policy builders, permissions logic, SBPL assets, and
    their tests into `codex-sandboxing`
    - keep `codex-core` owning only the seatbelt spawn wrapper and switch
    call sites to import the moved APIs directly
    
    ## Notes
    - no re-exports added
    - moved the seatbelt tests with the implementation so internal helpers
    could stay private
    - local verification is still finishing while this PR is open
  • feat: custom watcher for multi-agent v2 (#15576)
    The new wait tool just returns `Wait timed out.` or `Wait completed.`.
    The actual content is done through the notification watcher
  • feat: custom watcher for multi-agent v2 (#15570)
    Custom watcher that sends an InterAgentCommunication on end of turn
  • feat: use serde to differenciate inter agent communication (#15560)
    Use `serde` to encode the inter agent communication to an assistant
    message and use the decode to see if this is such a message
    
    Note: this assume serde on small pattern is fast enough
  • [hooks] add non-streaming (non-stdin style) shell-only PreToolUse support (#15211)
    - add `PreToolUse` hook for bash-like tool execution only at first
    - block shell execution before dispatch with deny-only hook behavior
    - introduces common.rs matcher framework for matching when hooks are run
    
    example run:
    
    ```
    › run three parallel echo commands, and the second one should echo "[block-pre-tool-use]" as a test
    
    
    • Running the three echo commands in parallel now and I’ll report the output directly.
    
    • Running PreToolUse hook: name for demo pre tool use hook
    
    • Running PreToolUse hook: name for demo pre tool use hook
    
    • Running PreToolUse hook: name for demo pre tool use hook
    
    PreToolUse hook (completed)
      warning: wizard-tower PreToolUse demo inspected Bash: echo "first parallel echo"
      
    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 "third parallel echo"
    
    • Ran echo "first parallel echo"
      └ first parallel echo
    
    • Ran echo "third parallel echo"
      └ third parallel echo
    
    • Three little waves went out in parallel.
    
      1. printed first parallel echo
      2. was blocked before execution because it contained the exact test string [block-pre-tool-use]
      3. printed third parallel echo
    
      There was also an unrelated macOS defaults warning around the successful commands, but the echoes
      themselves worked fine. If you want, I can rerun the second one with a slightly modified string so
      it passes cleanly.
    ```
  • feat: new op type for sub-agents communication (#15556)
    Add `InterAgentCommunication` for v2 agent communication
  • feat: structured multi-agent output (#15515)
    Send input now sends messages as assistant message and with this format:
    
    ```
    author: /root/worker_a
    recipient: /root/worker_a/tester
    other_recipients: []
    Content: bla bla bla. Actual content. Only text for now
    ```
  • Code mode on v8 (#15276)
    Moves Code Mode to a new crate with no dependencies on codex. This
    create encodes the code mode semantics that we want for lifetime,
    mounting, tool calling.
    
    The model-facing surface is mostly unchanged. `exec` still runs raw
    JavaScript, `wait` still resumes or terminates a `cell_id`, nested tools
    are still available through `tools.*`, and helpers like `text`, `image`,
    `store`, `load`, `notify`, `yield_control`, and `exit` still exist.
    
    The major change is underneath that surface:
    
    - Old code mode was an external Node runtime.
    - New code mode is an in-process V8 runtime embedded directly in Rust.
    - Old code mode managed cells inside a long-lived Node runner process.
    - New code mode manages cells in Rust, with one V8 runtime thread per
    active `exec`.
    - Old code mode used JSON protocol messages over child stdin/stdout plus
    Node worker-thread messages.
    - New code mode uses Rust channels and direct V8 callbacks/events.
    
    This PR also fixes the two migration regressions that fell out of that
    substrate change:
    
    - `wait { terminate: true }` now waits for the V8 runtime to actually
    stop before reporting termination.
    - synchronous top-level `exit()` now succeeds again instead of surfacing
    as a script error.
    
    ---
    
    - `core/src/tools/code_mode/*` is now mostly an adapter layer for the
    public `exec` / `wait` tools.
    - `code-mode/src/service.rs` owns cell sessions and async control flow
    in Rust.
    - `code-mode/src/runtime/*.rs` owns the embedded V8 isolate and
    JavaScript execution.
    - each `exec` spawns a dedicated runtime thread plus a Rust
    session-control task.
    - helper globals are installed directly into the V8 context instead of
    being injected through a source prelude.
    - helper modules like `tools.js` and `@openai/code_mode` are synthesized
    through V8 module resolution callbacks in Rust.
    
    ---
    
    Also added a benchmark for showing the speed of init and use of a code
    mode env:
    ```
    $ cargo bench -p codex-code-mode --bench exec_overhead -- --samples 30 --warm-iterations 25 --tool-counts 0,32,128
    Finished [`bench` profile [optimized]](https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles) target(s) in 0.18s
         Running benches/exec_overhead.rs (target/release/deps/exec_overhead-008c440d800545ae)
    exec_overhead: samples=30, warm_iterations=25, tool_counts=[0, 32, 128]
    scenario       tools samples    warmups      iters      mean/exec       p95/exec       rssΔ p50       rssΔ max
    cold_exec          0      30          0          1         1.13ms         1.20ms        8.05MiB        8.06MiB
    warm_exec          0      30          1         25       473.43us       512.49us      912.00KiB        1.33MiB
    cold_exec         32      30          0          1         1.03ms         1.15ms        8.08MiB        8.11MiB
    warm_exec         32      30          1         25       509.73us       545.76us      960.00KiB        1.30MiB
    cold_exec        128      30          0          1         1.14ms         1.19ms        8.30MiB        8.34MiB
    warm_exec        128      30          1         25       575.08us       591.03us      736.00KiB      864.00KiB
    memory uses a fresh-process max RSS delta for each scenario
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • chore(core) Remove Feature::PowershellUtf8 (#15128)
    ## Summary
    This feature has been enabled for powershell for a while now, let's get
    rid of the logic
    
    ## Testing
    - [x] Unit tests
  • feat: change multi-agent to use path-like system instead of uuids (#15313)
    This PR add an URI-based system to reference agents within a tree. This
    comes from a sync between research and engineering.
    
    The main agent (the one manually spawned by a user) is always called
    `/root`. Any sub-agent spawned by it will be `/root/agent_1` for example
    where `agent_1` is chosen by the model.
    
    Any agent can contact any agents using the path.
    
    Paths can be used either in absolute or relative to the calling agents
    
    Resume is not supported for now on this new path
  • Split features into codex-features crate (#15253)
    - Split the feature system into a new `codex-features` crate.
    - Cut `codex-core` and workspace consumers over to the new config and
    warning APIs.
    
    Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com>
    Co-authored-by: Codex <noreply@openai.com>