Commit Graph

6455 Commits

  • feat: add config-change extension contributor (#22488)
    ## Why
    
    Extensions can observe thread and turn lifecycle events today, but there
    was no single host-owned hook for changes to the effective thread
    configuration. That makes features that need to react to model,
    permission, or tool-suggest updates either depend on individual mutation
    paths or risk going stale after runtime config refreshes.
    
    This adds a typed config-change contributor so extension-owned state can
    stay synchronized with the effective thread config while the host
    remains responsible for deciding when config changed.
    
    ## What Changed
    
    - Added `ConfigContributor<C>` to `codex_extension_api`, with
    before/after immutable snapshots of the effective config plus
    session/thread extension stores.
    - Added registry builder/accessor support through `config_contributor`
    and `config_contributors`.
    - Emits config-change callbacks after committed updates from session
    settings, per-turn setting updates, and `refresh_runtime_config`.
    - Builds effective config snapshots only when config contributors are
    registered, and suppresses no-op callbacks when the before/after
    snapshots are equal.
    - Added a core session regression test that verifies contributors
    observe both model changes and user-layer runtime config changes,
    including access to session and thread extension stores.
    
    ## Validation
    
    Added `config_change_contributor_observes_effective_config_changes` in
    `codex-rs/core/src/session/tests.rs` to cover the new contributor path.
  • Add service tier overrides to spawned agents (#22139)
    ## Why
    
    Spawned agents can already override `model` and `reasoning_effort`, but
    they have no equivalent way to opt into a model-supported service tier.
    That makes it impossible to preserve or intentionally select tiered
    execution behavior when delegating work to a sub-agent, even though the
    model catalog already advertises supported `service_tiers`.
    
    ## What changed
    
    - Add optional `service_tier` to both legacy and `MultiAgentV2`
    `spawn_agent` tool inputs.
    - Show each picker-visible model's supported service tier ids and
    descriptions in the `spawn_agent` tool guidance.
    - Resolve service tier selection after the child agent's effective model
    is known.
    - Inherit the parent tier when omitted and still supported by the final
    child model; otherwise clear it.
    - Reject explicit unsupported tier requests with a model-facing error.
    - Keep explicit `service_tier` usable on full-history forks, while still
    honoring the existing model/reasoning fork restrictions.
    - Hide `service_tier` alongside other spawn metadata when
    `hide_spawn_agent_metadata` is enabled.
    
    ## Verification
    
    Added focused coverage for:
    
    - v1/v2 `spawn_agent` schema exposure for `service_tier`
    - tier descriptions in spawn guidance
    - hidden-metadata suppression
    - explicit supported tier selection
    - explicit unknown and unsupported tier rejection
    - inherited tier preservation or clearing based on child-model support
    - full-history fork acceptance for explicit service tiers in both v1 and
    v2
    
    Local Rust tests were not run in this workspace per repo guidance; the
    new coverage is included for CI.
  • feat(tui): remove Zellij TUI workarounds (#22214)
    ## Why
    
    We added Zellij-specific TUI workarounds because older Zellij behavior
    did not work with Codex's normal terminal model:
    
    - #8555 made `tui.alternate_screen = "auto"` disable alternate screen in
    Zellij so transcript history stayed available.
    - #16578 avoided scroll-region operations in Zellij by emitting raw
    newlines and using a separate composer styling path.
    
    This PR removes both workarounds because the latest Zellij release
    tested locally (`zellij 0.44.1`) works correctly with Codex's standard
    TUI behavior: normal alternate-screen handling, redraw, and history
    insertion.
    
    ## What Changed
    
    - Removed the `InsertHistoryMode::Zellij` path and the Zellij-only
    newline scrollback insertion behavior.
    - Removed cached `is_zellij` state from the TUI and composer.
    - Removed Zellij-specific composer styling, the helper snapshot, and the
    `TerminalInfo::is_zellij()` convenience method that only served this
    workaround.
    - Changed `tui.alternate_screen = "auto"` to use alternate screen for
    Zellij too; `--no-alt-screen` and `tui.alternate_screen = "never"` still
    preserve the inline mode escape hatch.
    - Updated the generated config schema description for
    `tui.alternate_screen`.
    
    ## How to Test
    
    Manual smoke path used with `zellij 0.44.1`:
    
    1. Build and run this branch inside a Zellij `0.44.1` session with
    default config.
    2. Start Codex normally and produce enough assistant/tool output to
    create scrollback.
    3. Confirm the transcript remains readable, the composer renders
    normally, and scrolling through terminal history works.
    4. Resize the Zellij pane while output exists and confirm the TUI
    redraws without duplicated, missing, or stale rows.
    5. Compare with `--no-alt-screen` or `-c tui.alternate_screen=never` if
    you want to verify the inline fallback still works.
    
    Targeted tests:
    - `just write-config-schema`
    - `just fmt`
    - `just fix -p codex-tui`
    - `cargo test -p codex-terminal-detection`
    - `cargo test -p codex-tui alternate_screen_auto_uses_alt_screen`
    
    Attempted but did not complete locally:
    - `cargo test -p codex-tui` built and ran the new test successfully,
    then failed later on unrelated local failures in
    `status_permissions_full_disk_managed_*` and a stack overflow in
    `tests::fork_last_filters_latest_session_by_cwd_unless_show_all`.
    
    ## Documentation
    
    No developers.openai.com Codex documentation update is needed for this
    revert.
  • Make context contributors async (#22491)
    ## Summary
    - make ContextContributor return a boxed Send future
    - await context contributors during initial context assembly
    - update existing contributors and extension-api examples for the async
    contract
    
    ## Testing
    - cargo test -p codex-extension-api --examples
    - cargo test -p codex-git-attribution
    - cargo test -p codex-core
    build_initial_context_includes_git_attribution_from_extensions --
    --nocapture
    - cargo test -p codex-core
    build_initial_context_omits_git_attribution_when_feature_is_disabled --
    --nocapture
    - cargo test -p codex-core (fails in unrelated
    agent::control::tests::spawn_agent_fork_last_n_turns_keeps_only_recent_turns
    stack overflow)
    - just fix -p codex-extension-api
    - just fix -p codex-git-attribution
    - just fix -p codex-core
    - cargo clippy -p codex-extension-api --examples
  • feat: move extension scope ids into ExtensionData (#22490)
    ## Summary
    - add a scoped level_id to ExtensionData and expose it through
    level_id()
    - remove thread_id/turn_id parameters from extension contributor inputs
    where the scoped ExtensionData already carries that identity
    - move turn-scoped extension data onto TurnContext so token usage and
    lifecycle contributors can share the same turn store
    
    ## Testing
    - cargo check -p codex-extension-api -p codex-core --tests
    - cargo test -p codex-extension-api
    - cargo test -p codex-guardian
    - cargo test -p codex-core --lib
    record_token_usage_info_notifies_extension_contributors
    - cargo test -p codex-core --lib
    submission_loop_channel_close_emits_thread_stop_lifecycle
    - cargo test -p codex-core --lib
    submission_loop_channel_close_aborts_active_turn_before_thread_stop_lifecycle
    - just fix -p codex-extension-api
    - just fix -p codex-guardian
    - just fix -p codex-core
    - just fmt
    
    ## Note
    - Attempted cargo test -p codex-core; it aborted in
    agent::control::tests::spawn_agent_fork_last_n_turns_keeps_only_recent_turns
    with the existing stack overflow before the full suite completed.
  • Scope macOS signing secrets to release environment (#22443)
    ## Summary
    - Split macOS Rust release builds into a dedicated `build-macos` job
    - Attach the `macos-signing` environment only to the macOS signing/build
    job
    - Keep Linux release builds outside the Apple signing environment while
    preserving the existing shared release build steps
  • feat: add token usage contributor hook (#22485)
    ## Why
    
    Extensions need a stable place to observe token accounting after Codex
    folds model-provider usage into the session's cached `TokenUsageInfo`.
    Without a contributor hook, extension-owned features that need last-turn
    or cumulative token usage have to duplicate session plumbing or infer
    state from client-facing `TokenCount` notifications.
    
    ## What changed
    
    - Added `TokenUsageContributor` to `codex-extension-api`, passing
    session/thread `ExtensionData`, `ThreadId`, turn id, and the current
    `TokenUsageInfo`.
    - Added registry builder/storage support for token-usage contributors.
    - Invoked registered contributors from
    `Session::record_token_usage_info` after the session token cache is
    updated and before the client `TokenCount` notification is emitted.
    
    ## Testing
    
    - Added `record_token_usage_info_notifies_extension_contributors`,
    covering cumulative token usage updates and access to both extension
    stores.
  • fix: emit thread stop lifecycle on implicit shutdown (#22482)
    ## Why
    
    The thread lifecycle contributor hooks from #22476 should observe every
    session teardown. The explicit `Op::Shutdown` path already emitted
    `on_thread_stop`, but when `submission_loop` exited because its
    submission channel closed, it only tore down runtime services. That
    meant extensions could miss the thread-stop lifecycle signal on implicit
    runtime shutdown.
    
    ## What Changed
    
    - Split shared runtime teardown into `shutdown_runtime_services(...)`.
    - Split thread-stop lifecycle emission into
    `emit_thread_stop_lifecycle(...)`.
    - Reused those helpers from both explicit shutdown and the channel-close
    shutdown path.
    - Tracked whether `Op::Shutdown` was received so the explicit path does
    not double-emit lifecycle events after it exits the loop.
    - Added a regression test that closes the submission channel and asserts
    `ThreadLifecycleContributor::on_thread_stop` runs once with the expected
    thread/session stores.
    
    ## Testing
    
    - `cargo test -p codex-core
    submission_loop_channel_close_emits_thread_stop_lifecycle`
  • feat: add turn lifecycle contributors (#22480)
    ## Why
    
    Extensions can already contribute prompt, tool, turn-item, and
    thread-lifecycle behavior, but there was no explicit host-owned hook for
    per-turn setup and cleanup. That makes extension-private turn state
    awkward: an extension either has to stash it outside the turn lifecycle
    or depend on core runtime objects.
    
    This adds a small turn lifecycle boundary. Extensions receive stable
    identifiers plus the existing session, thread, and turn `ExtensionData`
    stores, while core keeps owning task scheduling, cancellation, and turn
    teardown.
    
    ## What Changed
    
    - Added `TurnLifecycleContributor` with `on_turn_start`, `on_turn_stop`,
    and `on_turn_abort` callbacks in `codex-rs/ext/extension-api`.
    - Added typed `TurnStartInput`, `TurnStopInput`, and `TurnAbortInput`
    payloads that expose `thread_id`, `turn_id`, `session_store`,
    `thread_store`, and `turn_store`.
    - Registered and re-exported turn lifecycle contributors through
    `ExtensionRegistry` and `ExtensionRegistryBuilder`.
    - Wired `Session` to emit turn start, stop, and abort callbacks from the
    existing turn/task lifecycle paths.
    - Carried the turn-scoped `ExtensionData` through `RunningTask` and
    `RemovedTask` so stop/abort callbacks receive the same turn store
    created at turn start.
    
    ## Verification
    
    - Not run locally.
  • feat: add thread lifecycle contributor hooks (#22476)
    ## Why
    
    Extensions that need thread-scoped state currently only get a start-time
    callback. That is enough for seeding stores, but it leaves the host
    without a shared extension seam for later thread rehydrate and flush
    work as thread ownership evolves. This PR turns that start-only seam
    into a host-owned thread lifecycle contributor contract so
    extension-private state can stay behind the extension API instead of
    leaking extra orchestration through core.
    
    ## What changed
    
    - Replaced `ThreadStartContributor` with `ThreadLifecycleContributor`
    and added typed lifecycle inputs for thread start, resume, and stop. The
    contract lives in
    [`contributors/thread_lifecycle.rs`](https://github.com/openai/codex/blob/d0e9211f70e58d6b07ef07e84f359d1b9aa25955/codex-rs/ext/extension-api/src/contributors/thread_lifecycle.rs#L1-L64).
    - Kept the existing start-time behavior intact by routing session
    construction through `on_thread_start`.
    - Invoked `on_thread_stop` during session shutdown before thread-scoped
    extension state is dropped, while isolating contributor failures behind
    warning logs.
    - Migrated `git-attribution` and `guardian` onto the lifecycle
    registration path.
    - Renamed the extension registry plumbing from start-specific
    contributors to lifecycle-specific contributors.
    
    ## Notes
    
    `on_thread_resume` is introduced at the API boundary here so extensions
    can target the final lifecycle shape; host resume dispatch can be wired
    where that runtime path is finalized.
  • [codex] isolate plugin/list from config serialization queue (#22437)
    ## Summary
    - move `plugin/list` from the shared `config` read queue onto a
    dedicated `plugin-list` shared-read queue
    - move `plugin/read` onto that same dedicated shared-read queue as well
    - keep the existing scheduler behavior unchanged
    - allow plugin list/read operations to proceed independently of
    config-family writes, accepting temporary stale or transient read errors
    during concurrent mutations
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-app-server-protocol`
  • [app-server] Gate login issuer override constant (#22338)
    Gate the debug-only login issuer override constant so release builds no
    longer warn that it is unused.
  • Refactor extension tools onto shared ToolExecutor (#22369)
    ## Why
    
    Extension tools were split across two public runtime contracts:
    `codex-tool-api` exposed `ToolBundle` plus its own call/spec/error
    types, while core native tools used `codex_tools::ToolExecutor`. That
    made contributed tool specs and execution behavior easy to drift apart
    and added another crate boundary for what should be one executable-tool
    seam.
    
    This PR makes `ToolExecutor` the single runtime contract and keeps
    extension-specific pinning in `codex-extension-api`.
    
    ## Remaining todo
    
    https://github.com/openai/codex/pull/22369/changes#diff-b935ea8245c3ce568a30cff660175fa6390b66b872ae409e1e2e965738250741R5
    Either generic `Invocation` or sub-extract the `ToolCall` and clean
    `ToolInvocation`
    
    ## What changed
    
    - Removed the `codex-tool-api` workspace crate and its dependencies from
    core and `codex-extension-api`.
    - Made `codex_tools::ToolExecutor` object-safe with `async_trait` so
    extension contributors can return a dyn executor.
    - Added the extension-facing aliases under
    `ext/extension-api/src/contributors/tools.rs`, including
    `ExtensionToolExecutor = dyn ToolExecutor<ToolCall, Output =
    ExtensionToolOutput>`.
    - Changed `ToolContributor::tools` to return extension executors
    directly instead of `ToolBundle`s.
    - Updated core’s extension tool handler/registry/router path to adapt
    those extension executors into the existing native `ToolInvocation`
    runtime path.
    - Added focused coverage for extension tools being registered,
    model-visible, dispatchable, and not replacing built-in tools.
    
    ## Verification
    
    - `cargo test -p codex-tools`
    - `cargo test -p codex-extension-api`
  • feat: extract shared tool executor interface (#22359)
    ## Why
    
    Codex still models model-visible tools and executable behavior largely
    inside `codex-core`, which makes it harder to evolve the tool system
    toward a single reusable abstraction for built-ins, MCP-backed tools,
    dynamic tools, and later tools injected from outside core.
    
    This PR takes the next incremental step in that direction by moving the
    common execution-facing pieces out of core and separating them from
    core-only orchestration. The intent is to let shared tool abstractions
    improve in one place, while `codex-core` keeps the parts that are still
    inherently host-specific today, such as `ToolInvocation`, dispatch
    wiring, and hook integration.
    
    This PR is mostly moving things around. The only interesting piece is
    this abstraction:
    https://github.com/openai/codex/pull/22359/changes#diff-81af519002548ba51ed102bdaaf77e081d40a1e73a6e5f9b104bbbc96a6f1b3dR13
    
    ## What changed
    
    - Added `codex_tools::ToolExecutor<Invocation>` as the shared execution
    trait for model-visible tools.
    - Moved the reusable execution support types from `codex-core` into
    `codex-tools`:
      - `FunctionCallError`
      - `ToolPayload`
      - `ToolOutput`
    - Refactored core tool implementations so that execution behavior lives
    on `ToolExecutor<ToolInvocation>`, while `ToolHandler` remains the
    core-local extension point for hook payloads, telemetry tags, diff
    consumers, and other orchestration concerns.
    - Kept the registry and dispatch flow behaviorally unchanged while
    making the shared/extracted boundary explicit across built-in, MCP,
    dynamic, extension-backed, shell, and multi-agent tool handlers.
    
    ## Verification
    
    - `cargo test -p codex-tools`
    - `just fix -p codex-tools`
    - `just fix -p codex-core`
    - `cargo test -p codex-core` progressed through the updated tool
    surfaces and then hit the existing unrelated multi-agent stack overflow
    in
    `tools::handlers::multi_agents::tests::tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtrees_closed`.
  • extension-api: add approval review contributor flow (#22344)
    ## Why
    
    `codex-extension-api` needs an approval hook that lets an installed
    extension own a rendered approval-review prompt and produce the final
    `ReviewDecision`. The prior interceptor stub only exposed a yes/no claim
    and did not model the review result itself, which left the host with the
    missing half of the control flow.
    
    ## What changed
    
    - Replaces `ApprovalInterceptorContributor` with
    [`ApprovalReviewContributor`](https://github.com/openai/codex/blob/c49d17531e15057a373a9b17f410cafb6299d0c1/codex-rs/ext/extension-api/src/contributors.rs#L43-L55),
    which may claim a rendered prompt and return an async `ReviewDecision`.
    - Re-exports the new contributor and future types from `extension-api`.
    - Adds registry support through `approval_review_contributor(...)` plus
    [`ExtensionRegistry::approval_review(...)`](https://github.com/openai/codex/blob/c49d17531e15057a373a9b17f410cafb6299d0c1/codex-rs/ext/extension-api/src/registry.rs#L90-L101),
    which returns the first installed contributor that claims the prompt.
  • chore: Keep view_image sandbox test in temp dir (#22355)
    ## Summary
    - move the `view_image` sandbox filesystem-read unit test onto a
    temporary cwd
    - keep the turn cwd and selected turn environment cwd aligned inside the
    test
    - avoid leaving `core/image.png` behind in the repo checkout after the
    test runs
    
    ## Root cause
    The test wrote `image.png` beneath `turn.cwd`, and the shared session
    test helper defaults that cwd to the current repo directory when no
    override is provided.
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-core
    tools::handlers::view_image::tests::handle_passes_sandbox_context_for_local_filesystem_reads`
  • feat: Add plugin share checkout (#22435)
    Adds plugin/share/checkout to turn a shared remote plugin into a local
    working copy under ~/plugins/<name>.
    
    Registers the copy in the managed personal marketplace and records the
    remote-to-local mapping for later share/save flows.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • add --dangerously-bypass-hook-trust CLI flag (#21768)
    # Why
    
    Hook trust happens through the TUI in `/hooks` so it can block
    non-interactive use cases. This flag will allow users that are using
    codex headlessly to bypass hooks when they want to.
    
    # What
    
    This adds one invocation-scoped escape hatch.
    
    - the CLI flag sets a runtime-only `bypass_hook_trust` override; there
    is no durable `config.toml` setting
    - hook discovery still respects normal enablement, so explicitly
    disabled hooks remain disabled
    - we show a `--dangerously-bypass-hook-trust is enabled. Enabled hooks
    may run without review for this invocation.` message on startup so
    accidental use is visible in both interactive and exec flows
    
    This keeps “enabled” and “trusted” as separate concepts in the normal
    path, while giving CI/E2E callers a stable way to opt into the
    exceptional path when they already control the hook set.
  • Use root repo hooks in linked worktrees (#21969)
    # Why
    
    Linked worktrees currently load their own project hook declarations, so
    the same repo can present different hook definitions depending on which
    checkout is active. https://github.com/openai/codex/pull/21762 tried to
    share trust by giving matching worktree hooks a shared synthetic key,
    but review pointed out that divergent worktree hook definitions would
    then fight over one `trusted_hash`.
    
    Instead of introducing a second trust model, this makes linked worktrees
    use the root checkout as the single source of truth for project hook
    declarations. Worktree-local project config can still diverge for
    unrelated settings, but project hooks now keep one real source path and
    one trust state per repo.
    
    # What
    
    - Teach project config loading to remember the matching root-checkout
    `.codex/` folder for actual linked-worktree project layers.
    - Keep ordinary project config sourced from the worktree, but replace
    project hook declarations with the root checkout's matching layer before
    hook discovery runs, including linked-worktree layers with `.codex/` but
    no local `config.toml`.
    - Make hook discovery use that authoritative hook folder for both
    `hooks.json` and TOML hook source paths, so linked worktrees produce the
    same hook key and trust state as the root checkout.
    - Cover the linked-worktree path plus regressions for missing worktree
    `config.toml` and nested non-worktree project roots.
  • Remove unavailable MCP placeholder tool backfill (#22439)
    ## Why
    
    `UnavailableDummyTools` kept synthetic placeholder tools alive for
    historical tool calls whose backing MCP tool was no longer available.
    That path adds stale model-visible tool specs and special routing at the
    point where unavailable MCP calls should use ordinary current-tool
    handling. This removes the runtime backfill instead of preserving a
    second compatibility lane.
    
    ## Is it safe to remove?
    
    The unavailable tools were added in #17853 after a CS issue when a
    previously-called MCP tool failed to load and was omitted from the CS
    spec. Now that we have tool search, I think this is resolved:
    - API merges tools from previous TST output into effective tool set so
    theyre always in CS spec
    - if an MCP tool surfaced by TST later becomes unavailable, the model
    can still call it and it will just return model-visible error
    - both TST output and function call output are dropped on compaction so
    model will not remember old calls to MCP post compaction
    
    ## What changed
    
    - Delete unavailable-tool collection, placeholder handler, router/spec
    plumbing, and obsolete placeholder coverage.
    - Keep `features.unavailable_dummy_tools` as a removed no-op feature
    tombstone so existing configs still parse cleanly.
    - Add an integration-style `tool_search` regression test showing that a
    deferred MCP tool surfaced through `tool_search` still routes through
    MCP and returns a model-visible tool-call error rather than `unsupported
    call`.
    
    ## Verification
    
    - `cargo test -p codex-core tool_search`
  • Refactor chatwidget input flow into modules (#22407)
    ## Why
    
    `chatwidget.rs` is still carrying too many unrelated responsibilities in
    one file. #22269 started a five-phase effort to move coherent behavior
    domains into focused modules while keeping `chatwidget.rs` as the
    composition layer.
    
    This PR is phase 2 of that plan. It extracts the input and submission
    flow as a mechanical move before the later protocol, popup/status, and
    constructor/orchestration phases.
    
    ## What Changed
    
    - Added `codex-rs/tui/src/chatwidget/input_flow.rs` for composer input
    results, queued user-message draining, pending-input previews, and
    mode-specific submission entry points.
    - Added `codex-rs/tui/src/chatwidget/input_submission.rs` for
    user-message construction/submission, shell prompt submission,
    structured mention resolution, and blocked image draft restoration.
    - Added `codex-rs/tui/src/chatwidget/input_restore.rs` for
    initial-message submission, pending steer restoration after interrupts,
    and thread input snapshot/restore behavior.
    - Registered the new modules and removed the moved `ChatWidget` impl
    methods from `codex-rs/tui/src/chatwidget.rs`.
    
    ## Follow-On Refactor Phases
    
    The five-phase plan from #22269 is:
    
    - Phase 1: mechanical helper and state moves. Completed in #22269.
    - Phase 2: extract input and submission flow, including queued user
    messages, shell prompt submission, pending steer restoration, and thread
    input snapshot/restore behavior. This PR.
    - Phase 3: extract protocol, replay, streaming, and tool lifecycle
    handling, while preserving active-cell grouping, transcript
    invalidation, interrupt deferral, and final-message separator behavior.
    - Phase 4: extract settings, popups, and status surfaces, including
    model/reasoning/collaboration/personality popups, permission prompts,
    rate-limit UI, and connectors helpers.
    - Phase 5: clean up the remaining constructor and orchestration code
    once the larger behavior domains have moved out, leaving `chatwidget.rs`
    as the composition layer.
  • Add support for UDS in codex --remote (#22414)
    ## Why
    
    Added support for UDS connections in `codex --remote`.
    
    TUI also now connects to local app-server using UDS by default if it is
    running and set to listen to UDS connection.
    
    ## What Changed
    
    - Introduced `RemoteAppServerEndpoint` with `WebSocket` and `UnixSocket`
    variants.
    - Reused the existing JSON-RPC-over-WebSocket protocol over either a TCP
    WebSocket stream or a UDS stream.
    - Updated `codex --remote` to accept `ws://host:port`,
    `wss://host:port`, `unix://`, and `unix://PATH`.
    - Kept `--remote-auth-token-env` restricted to `wss://` and loopback
    `ws://` remotes.
    - Added a fast TUI startup probe for the default daemon socket, falling
    back to the embedded app server when the daemon is absent or
    unresponsive.
    
    ## Verification
    
    - Manually verified that the updated remote flow works.
    - Added coverage for UDS remote round trips, WebSocket auth headers,
    auth-token transport policy, remote address parsing, and missing-daemon
    fallback.
    - Ran focused remote test coverage locally.
  • feat: Split shared workspace plugins by discoverability (#22425)
    - Keep shared-with-me as the plugin/list request kind, but return
    private plugins under workspace-shared-with-me-private.
    - Add workspace-shared-with-me-unlisted for installed workspace plugins
    with UNLISTED discoverability,
  • Encapsulate tool search entries in handlers (#22261)
    ## Why
    
    This builds on the handler-owned spec refactor by moving deferred
    tool-search metadata to the same handlers that already own tool specs.
    The registry builder no longer needs a separate prebuilt
    `tool_search_entries` path; it can collect searchable entries from
    deferred handlers directly.
    
    ## What changed
    
    - Added `search_info()` to tool handlers and implemented it for MCP and
    dynamic handlers.
    - Reused handler `spec()` output when constructing tool-search entries,
    adapting it into the deferred `LoadableToolSpec` shape expected by
    `tool_search`.
    - Simplified `build_tool_registry_builder(...)` so `tool_search`
    registration is based on deferred handlers with search info.
    - Removed the old standalone search-entry builders and now-unused
    `codex-tools` discovery helper exports.
    
    ## Verification
    
    - `cargo test -p codex-core tools::handlers::tool_search::tests:: --
    --nocapture`
    - `cargo test -p codex-core tools::spec_plan::tests::search_tool --
    --nocapture`
    - `cargo test -p codex-core tools::spec::tests:: -- --nocapture`
    - `cargo test -p codex-core tools::spec_plan::tests:: -- --nocapture`
    - `cargo test -p codex-tools`
    - `just fix -p codex-core`
    - `just fix -p codex-tools`
  • tools: infer code-mode namespace descriptions from specs (#22406)
    ## Why
    
    Code mode already builds the merged nested `ToolSpec`s that feed the
    `exec` prompt. Keeping a separate `tool_namespaces` map in the planning
    path duplicated that metadata and left extra wrapper plumbing in
    `spec.rs`.
    
    ## What changed
    
    - derive code-mode namespace descriptions from the merged
    `ToolSpec::Namespace` entries before building the code-mode handlers
    - extract `build_code_mode_handlers(...)` so the code-mode-specific
    planning stays in one place
    - remove `tool_namespaces` from `ToolRegistryBuildParams`
    - delete the now-unused `McpToolPlanInputs` wrapper and related test
    helper plumbing
    
    ## Testing
    
    - `cargo test -p codex-core spec_plan`
  • Remove CODEX_RS_SSE_FIXTURE test hook (#22413)
    ## Why
    
    `CODEX_RS_SSE_FIXTURE` let integration-style CLI, exec, and TUI tests
    bypass the normal Responses transport by reading SSE from local files.
    That kept test-only behavior wired through production client code. The
    affected tests can stay hermetic by using the existing
    `core_test_support::responses` mock server and passing `openai_base_url`
    instead.
    
    ## What Changed
    
    - Removed the `CODEX_RS_SSE_FIXTURE` flag,
    `codex_api::stream_from_fixture`, the `env-flags` dependency, and the
    checked-in SSE fixture files.
    - Repointed the affected core, exec, and TUI tests at `MockServer` with
    the existing SSE event constructors.
    - Removed the Bazel test data plumbing for the deleted fixtures and
    refreshed cargo/Bazel lock state.
    
    ## Verification
    
    - `cargo build -p codex-cli`
    - `cargo test -p codex-api`
    - `cargo test -p codex-core --test all responses_api_stream_cli`
    - `cargo test -p codex-core --test all
    integration_creates_and_checks_session_file`
    - `cargo test -p codex-exec --test all ephemeral`
    - `cargo test -p codex-exec --test all resume`
    - `cargo test -p codex-tui --test all
    resume_startup_does_not_consume_model_availability_nux_count`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `just fix -p codex-api -p codex-core -p codex-exec -p codex-tui`
    - `git diff --check`
  • Add allow_managed_hooks_only hook requirement (#20319)
    ## Why
    
    Enterprise-managed hook policy needs a narrow way to require Codex to
    ignore user-controlled lifecycle hooks without adopting the broader
    trust-precedence model from earlier hook work. This keeps the policy
    anchored in `requirements.toml`, so admins can opt into managed hooks
    only while normal `config.toml` files cannot enable the restriction
    themselves.
    
    ## What changed
    
    - Added `allow_managed_hooks_only` to the requirements data flow and
    preserved explicit `false` values.
    - Also adds it to /debug-config
    - Marked MDM, system, and legacy managed config layers as managed for
    hook discovery.
    - Updated hook discovery so `allow_managed_hooks_only = true`:
      - keeps managed requirements hooks and managed config-layer hooks,
    - skips user/project/session `hooks.json` and `[hooks]` entries with
    concise startup warnings,
      - skips current unmanaged plugin hooks,
    - ignores any `allow_managed_hooks_only` key placed in ordinary
    `config.toml` layers.
  • hooks: use new session IDs instead of thread IDs for hooks, apply parent's session ID to subagents' hooks (#22268)
    ## Why
    
    hook semantics treat `session_id` as shared across a root session and
    its subagents. Codex hooks were still emitting the current thread ID,
    which made spawned agents look like independent sessions and made it
    harder for hook integrations to correlate work across a root thread and
    its spawned helpers
    
    This change makes hooks use Codex's existing shared session identity so
    hook `session_id` matches the root-thread session across spawned
    subagents.
    
    ## What Changed
    
    - switch hook payloads to use the existing shared session identity from
    core instead of the current thread ID
    - cover all hook surfaces that expose `session_id`, including
    `SessionStart`, tool hooks, compact hooks, prompt-submit hooks, stop
    hooks, and legacy after-agent dispatch
  • feat: route guardian review model selection through providers (#22258)
    ## Why
    
    Guardian review selection was hard-coded in `core`, which worked for the
    default OpenAI path but did not give provider implementations a way to
    choose backend-specific reviewer model IDs. That matters for Amazon
    Bedrock: guardian review should run through the Bedrock/Mantle provider
    using Bedrock's `openai.gpt-5.4` model ID, instead of accidentally
    selecting a reviewer model that implies the OpenAI backend.
    
    ## What Changed
    
    - Added provider-owned approval review model selection via
    `ModelProvider::approval_review_model_selection`.
    - Moved the existing default selection policy into the provider
    abstraction: prefer the requested reviewer model when it is available,
    otherwise fall back to the active turn model, preferring `Low` reasoning
    when supported.
    - Added an Amazon Bedrock override that pins guardian review to
    `openai.gpt-5.4` with `Low` reasoning.
  • Restore app-server websocket listener with auth guard (#22404)
    ## Why
    PR #21843 removed the TCP websocket app-server listener, but that also
    removed functionality that still needs to exist. Restoring it as-is
    would reopen the old remote exposure problem, so this keeps the restored
    listener while making remote and non-loopback usage require explicit
    auth.
    
    ## What Changed
    - Mostly reverts #21843 and reapplies the small merge-conflict
    resolutions needed on top of current main.
    - Restores ws://IP:PORT parsing, the app-server TCP websocket acceptor,
    websocket auth CLI flags, and the associated tests.
    - The only intentional behavior change from the restored code is that
    non-loopback websocket listeners now fail startup unless --ws-auth
    capability-token or --ws-auth signed-bearer-token is configured.
    Loopback listeners remain available for local and SSH-forwarding
    workflows.
    
    ## Reviewer Focus
    Please focus review on the small auth-enforcement delta layered on top
    of the revert:
    
    - codex-rs/app-server-transport/src/transport/websocket.rs:
    start_websocket_acceptor now rejects unauthenticated non-loopback
    websocket binds before accepting connections.
    - codex-rs/app-server-transport/src/transport/auth.rs: helper logic
    classifies unauthenticated non-loopback listeners.
    - codex-rs/app-server/tests/suite/v2/connection_handling_websocket.rs:
    tests cover unauthenticated ws://0.0.0.0 startup rejection and
    authenticated non-loopback capability-token startup.
    
    Everything else is intended to be revert/merge-conflict restoration
    rather than new product behavior.
    
    ## Verification
    
    - Manually verified that TUI remoting is restored and that auth is
    enforced for non-localhost urls.
  • feat: Expose plugin versions and gate plugin sharing (#22397)
    - Adds localVersion to plugin summaries and remoteVersion to share
    context, including generated API schemas.
    - Hydrates local and remote plugin versions from manifests and remote
    release metadata.
    - Adds default-on plugin_sharing gate for shared-with-me listing and
    plugin/share/save, with disabled-path errors
        and focused coverage.
  • docs(skills): simplify plugin creator deeplink shape (#22240)
    ## Summary
    
    Plugin Creator now documents the shorter local-plugin handoff URL that
    the app can interpret directly.
    [#22221](https://github.com/openai/codex/pull/22221) teaches the skill
    to end marketplace-backed creation flows with named View and Share
    links; this follow-up updates those examples so the skill only emits the
    normalized plugin name, the absolute marketplace path, and optional
    share mode.
    
    The documented shape is:
    
    ```txt
    codex://plugins/<normalized-plugin-name>?marketplacePath=<absolute-marketplace-json-path>
    codex://plugins/<normalized-plugin-name>?marketplacePath=<absolute-marketplace-json-path>&mode=share
    ```
    
    The skill text now states exactly where the normalized plugin name
    belongs, exactly where the absolute marketplace path belongs, and that
    it should not add `pluginName` or `hostId` query parameters.
    
    ## Testing
    
    Tests: plugin-creator skill validation.
  • mark Feature::RemoteControl as removed (#22386)
    ## Why
    
    `remote_control` can appear in `config.toml`, CLI feature overrides, and
    the app-server config APIs. Before this PR, app-server startup treated
    `config.features.enabled(Feature::RemoteControl)` as the signal to start
    remote control ([base
    code](https://github.com/openai/codex/blob/5e3ee5eddfa5333f2e0b011880abf0cbf92bd295/codex-rs/app-server/src/lib.rs#L678-L680)).
    That meant a user with:
    
    ```toml
    [features]
    remote_control = true
    ```
    
    would accidentally opt every app-server process into remote control.
    Remote-control startup should instead be a per-process launch decision
    made by CLI flags.
    
    ## What Changed
    
    - Marks `Feature::RemoteControl` as `Stage::Removed`, keeping
    `remote_control` as a known compatibility key while making it
    config-inert.
    - Adds a hidden `--remote-control` process flag to `codex app-server`
    and standalone `codex-app-server`.
    - Plumbs that flag through
    `AppServerRuntimeOptions.remote_control_enabled` and makes app-server
    startup use only that runtime option to decide whether to start remote
    control.
    - Removes the app-server config mutation hook that reloaded config and
    toggled remote control at runtime.
    - Updates managed daemon spawning to use `codex app-server
    --remote-control --listen unix://` instead of `--enable remote_control`.
    
    Config APIs can still list, read, write, and set `remote_control`; those
    operations just no longer affect remote-control process enrollment.
  • [codex] Remove tool search bucket limit override (#22381)
    ## Why
    
    `tool_search` still carries the server-specific result-cap path added in
    #17684 for `computer-use`: when the model omitted `limit`, a matching
    result expanded the search to 20 and then `limit_results_by_bucket`
    applied per-bucket caps. That makes default result handling depend on a
    one-off server exception instead of the single
    `TOOL_SEARCH_DEFAULT_LIMIT` path.
    
    This PR removes that custom branch so omitted `limit` values use the
    ordinary global default consistently. The implementation being retired
    is the pre-change bucketed search path in
    [`tool_search.rs`](https://github.com/openai/codex/blob/5e3ee5eddfa5333f2e0b011880abf0cbf92bd295/codex-rs/core/src/tools/handlers/tool_search.rs#L121-L190).
    
    ## What changed
    
    - Collapse `ToolSearchHandler::search` back to one BM25 search with the
    resolved limit.
    - Remove `limit_results_by_bucket`, the `computer-use` constants, and
    the omitted-limit plumbing that only existed for the override.
    - Drop dead `ToolSearchEntry::limit_bucket` metadata from deferred MCP
    and dynamic search entries.
    - Remove tests and helpers that only asserted the deleted override
    behavior.
    - Add direct handler-level unit coverage for omitted/default and
    explicit `tool_search` result limits.
    
    ## Validation
    
    - `cargo test -p codex-core tool_search`
    - The matching unit tests passed, including the new omitted/default and
    explicit result-limit coverage.
    - The broader `--test all` search-tool fixture phase then failed before
    sending mocked response requests in
    `tool_search_indexes_only_enabled_non_app_mcp_tools` and
    `tool_search_uses_non_app_mcp_server_instructions_as_namespace_description`.
    - `cargo test -p codex-core`
    - The touched tool-search coverage passed before the run later aborted
    in
    `tools::handlers::multi_agents::tests::tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtrees_closed`
    with a stack overflow.
  • Refactor chatwidget state into modules (#22269)
    ## Why
    
    `chatwidget.rs` is still carrying too many unrelated responsibilities in
    one file. After #21866 consolidated some of the state it tracks, this
    starts the next phase by moving coherent state/helper clusters out of
    the main module without changing behavior.
    
    This PR is intentionally mechanical: it only moves existing functions,
    structs, and helpers into focused modules so the boundaries are easier
    to review before the less mechanical refactors that should follow.
    
    ## What Changed
    
    - Moved user-message, composer, queue, pending steer, and merge/remap
    helpers into `codex-rs/tui/src/chatwidget/user_messages.rs`.
    - Added `codex-rs/tui/src/chatwidget/exec_state.rs` for unified exec
    bookkeeping helpers.
    - Added `codex-rs/tui/src/chatwidget/rate_limits.rs` for rate-limit
    warning, prompt, and error classification state.
    - Moved plugin list fetch and install auth-flow state into
    `codex-rs/tui/src/chatwidget/plugins.rs`.
    - Made a couple of test-only `VecDeque` imports explicit now that those
    tests no longer inherit the parent module import.
    
    ## Verification
    
    - `cargo test -p codex-tui` was run
    
    ## Follow-On Refactor Phases
    
    This PR is phase 1: mechanical helper and state moves. Planned follow-up
    PRs:
    
    - Phase 2: extract input and submission flow, including queued user
    messages, shell prompt submission, pending steer restoration, and thread
    input snapshot/restore behavior.
    - Phase 3: extract protocol, replay, streaming, and tool lifecycle
    handling, while preserving active-cell grouping, transcript
    invalidation, interrupt deferral, and final-message separator behavior.
    - Phase 4: extract settings, popups, and status surfaces, including
    model/reasoning/collaboration/personality popups, permission prompts,
    rate-limit UI, and connectors helpers.
    - Phase 5: clean up the remaining constructor and orchestration code
    once the larger behavior domains have moved out, leaving `chatwidget.rs`
    as the composition layer.
  • Unify thread metadata updates above store (#22236)
    - make ThreadStore::update_thread_metadata accept a broad range of
    metadata patches
    - keep ThreadStore::append_items as raw canonical history append (no
    metadata side effects)
    - in the local store, write these metadata updates to a combination of
    sqlite and rollout jsonl files for backwards-compat. It special cases
    which fields need to go into jsonl vs sqlite vs whatever, confining the
    awkwardness to just this implementation
    - in remote stores we can simply persist the metadata directly to a
    database, no special casing required.
    - move the "implicit metadata updates triggered by appending rollout
    items" from the RolloutRecorder (which is local-threadstore-specific) to
    the LiveThread layer above the ThreadStore, inside of a private helper
    utility called ThreadMetadataSync. LiveThread calls ThreadStore
    append_items and update_metadata separately.
    - Add a generic update metadata method to ThreadManager that works on
    both live threads and "cold" threads
    - Call that ThreadManager method from app server code, so app server
    doesn't need to worry about whether the thread is live or not
  • [codex] Add search term coverage for tool_search (#22398)
    ## Why
    
    `tool_search` already had solid end-to-end coverage for discovery and
    follow-up execution, but it did not prove that distinct pieces of
    indexed search text actually work in integration. In particular, we were
    not exercising whether unique tool names, descriptions, namespaces,
    underscore-expanded dynamic names, and schema-property terms were
    sufficient to surface the expected deferred tools.
    
    This change adds focused integration coverage for those term sources so
    regressions in search text construction are caught by a real `TestCodex`
    flow instead of only by lower-level unit tests.
    
    ## What changed
    
    - added a small helper in `core/tests/suite/search_tool.rs` to assert
    that a `tool_search_output` contains an expected namespace child tool
    - added an MCP integration test that issues several `tool_search_call`s
    and verifies distinct query terms match the expected app tools:
      - exact tool name: `calendar_timezone_option_99`
      - tool description phrase: `uploaded document`
      - top-level schema property: `starts_at`
    - added a dynamic-tool integration test that verifies distinct query
    terms match the expected deferred dynamic tool:
      - exact name: `quasar_ping_beacon`
      - underscore-expanded name: `quasar ping beacon`
      - description phrase: `saffron metronome`
      - namespace: `orbit_ops`
      - schema property: `chrono_spec`
    
    ## Validation
    
    - `cargo test -p codex-core tool_search_matches_`
    
    ## Docs
    
    No documentation update needed.
  • core: box multi-agent handler futures (#22266)
    ## Why
    
    This is the base PR in the split stack for the permissions migration. It
    isolates stack-safety work that had been mixed into the larger
    permissions PR, so reviewers can evaluate the async-future changes
    separately from the permissions model changes in #22267.
    
    The main risk this addresses is large or recursive multi-agent futures
    overflowing smaller runner stacks. A follow-up review also called out
    that `shutdown_live_agent` must remain quiescent: callers should not
    remove a live agent from tracking or release its spawn slot until the
    worker loop has actually terminated.
    
    ## What Changed
    
    - Boxes the large async futures in the multi-agent spawn, resume, and
    close tool handlers.
    - Boxes the `AgentControl` spawn and recursive close/shutdown paths that
    can otherwise build very deep futures.
    - Keeps `shutdown_live_agent` waiting for thread termination before
    removing/releasing the live agent, preserving the previous shutdown
    ordering while still boxing the recursive close path.
    
    ## Verification Strategy
    
    The focused local coverage was `cargo test -p codex-core multi_agents`,
    which exercises the multi-agent spawn/resume/close handlers, cascade
    close/resume behavior, and the shutdown path touched by this PR.
    
    
    
    
    
    
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22266).
    * #22330
    * #22329
    * #22328
    * #22327
    * __->__ #22266
  • code-mode: Add pending-aware code mode execution (#22280)
    Introduce execute_to_pending and wait_to_pending APIs that freeze
    pending-mode runtimes until an explicit resume, while preserving the
    existing continuously-running execute path. Add runtime and service
    coverage for pending, resume, completion, and freeze behavior.
  • Refactor namespaced tool spec registration (#22256)
    ## Summary
    
    This refactor makes tool handlers the owner of the specs they can
    publish, so registry construction can register handlers once and
    separately publish only the specs that should be model-visible.
    
    The main motivation is deferred tools: MCP and dynamic tools still need
    handlers registered up front, but deferred tools should be discoverable
    through `tool_search` rather than emitted in the initial tool spec list.
    
    ## What changed
    
    - `McpHandler` and `DynamicToolHandler` can return their own `ToolSpec`.
    - `build_tool_registry_builder` now collects handlers, registers them
    through the no-spec path, and publishes only non-deferred handler specs.
    - Deferred MCP and dynamic tool names are combined into one
    `all_deferred_tools` set that drives spec filtering, code-mode
    deferred-tool signaling, and `tool_search` registration.
    - `tool_search` registration now requires both deferred tools and
    `namespace_tools`.
    - Namespace specs are merged in `spec_plan`, preserving top-level spec
    order, sorting tools within each namespace, and backfilling empty
    namespace descriptions.
    - Hosted web search and image-generation specs are included in the
    collected spec vector before namespace merge/publication, and tool-name
    tests that should not care about hosted relative order now compare sets.
    
    ## Testing
    
    - `cargo test -p codex-core tools::spec::tests:: -- --nocapture`
    - `cargo test -p codex-core tools::spec_plan::tests:: -- --nocapture`
    - `cargo test -p codex-core
    tools::router::tests::specs_filter_deferred_dynamic_tools --
    --nocapture`
    - `cargo test -p codex-core
    suite::prompt_caching::prompt_tools_are_consistent_across_requests --
    --nocapture`
    - `just fmt`
    - `just fix -p codex-core`
    - `cargo test -p codex-core -- --skip
    tools::handlers::multi_agents::tests::tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtrees_closed`
    passed the library suite after skipping the known stack-overflowing unit
    test.
    
    Full `cargo test -p codex-core` currently hits a stack overflow in
    `tools::handlers::multi_agents::tests::tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtrees_closed`;
    the same focused test reproduces on `origin/main`.
  • [codex] Remove workspace owner usage nudge gate (#20509)
    ## Summary
    - make workspace owner nudge handling unconditional in the TUI now that
    it is fully rolled out
    - keep `workspace_owner_usage_nudge` as a removed no-op compatibility
    flag so old configs/app overrides remain accepted during rollout
    - remove flag-disabled test setup
    
    ## Companion PR
    - https://github.com/openai/openai/pull/876351 removes the Codex Apps
    Statsig rollout gate override after this change is available to the
    app/runtime path
    
    ## Validation
    - `just write-config-schema`
    - `just fmt`
    - `cargo test -p codex-features`
    - `cargo test -p codex-tui status_and_layout`
  • feat(exec-server): use protobuf relay frames (#22343)
    ## Why
    
    Remote exec-server now needs one executor websocket to serve multiple
    harness JSON-RPC sessions. Rendezvous routes by `stream_id`, and the
    exec-server side needs to use the same stable relay frame contract
    instead of a hand-rolled JSON shape.
    
    The relay protocol also needs to make ownership boundaries clear:
    harness and executor endpoints own sequencing, acks, retries, duplicate
    suppression, segmentation, and reassembly; rendezvous only routes
    frames.
    
    ## What Changed
    
    - Add the checked-in `codex.exec_server.relay.v1.RelayMessageFrame`
    proto plus generated prost bindings for `codex-exec-server`.
    - Encode remote harness/executor relay traffic as binary protobuf
    websocket frames while keeping local websocket JSON-RPC unchanged.
    - Demux executor-side relay streams into independent
    `ConnectionProcessor` sessions keyed by `stream_id`.
    - Add a programmatic `RemoteExecutorConfig::with_bearer_token(...)`
    constructor for non-CLI callers and integration tests.
    - Add an integration test that starts the remote executor against a fake
    registry/rendezvous websocket and verifies two virtual streams share one
    executor websocket without cross-talk, including per-stream reset
    behavior.
    - Document the remote relay envelope, sequence ranges, `ack`/`ack_bits`,
    and endpoint responsibilities in `exec-server/README.md`.
    
    ## Verification
    
    - `cargo test -p codex-exec-server --test relay
    multiplexed_remote_executor_routes_independent_virtual_streams --
    --exact`
    - `cargo test -p codex-exec-server --test relay`
    - `cargo test -p codex-exec-server` passed outside the sandbox. The
    sandboxed run hit macOS `sandbox-exec: sandbox_apply: Operation not
    permitted` in filesystem sandbox tests.
  • test(tui): relax configured pet load timeout (#22392)
    ## Why
    
    Windows CI has been timing out in
    `configured_pet_load_is_deferred_until_after_construction` while waiting
    for the deferred configured-pet load event.
    
    The test still needs to prove construction returns before the pet image
    is available, but the background load slices the built-in pet
    spritesheet into frame cache files. That work can exceed the old 2
    second deadline on slower or more contended CI machines.
    
    ## What Changed
    
    - Increased the test wait for `ConfiguredPetLoaded` from 2 seconds to 30
    seconds.
    - Kept the post-construction assertion intact so the test still verifies
    that the pet is not loaded synchronously during `ChatWidget`
    construction.
    
    ## How to Test
    
    Targeted tests:
    - `cargo test -p codex-tui
    configured_pet_load_is_deferred_until_after_construction`
    - `just argument-comment-lint`
    
    Additional check:
    - `cargo test -p codex-tui` was run, but the broader crate suite did not
    complete successfully due to unrelated existing failures:
    -
    `status::tests::status_permissions_full_disk_managed_without_network_is_external_sandbox`
    -
    `status::tests::status_permissions_full_disk_managed_with_network_is_danger_full_access`
    - later abort in
    `tests::fork_last_filters_latest_session_by_cwd_unless_show_all` from
    stack overflow
  • code-mode: carry nested tool kind through runtime (#22377)
    ## Why
    
    Code mode only used nested spec lookup at execution time to rediscover
    whether a nested tool should be invoked as a function tool or a freeform
    tool.
    
    That information is already present in the enabled tool metadata that
    code mode builds to expose `tools.*` and `ALL_TOOLS`, so re-looking it
    up from the router was redundant and kept execution coupled to a
    separate spec lookup path.
    
    ## What Changed
    
    - thread `CodeModeToolKind` through the code-mode runtime `ToolCall`
    event and `CodeModeNestedToolCall`
    - emit the nested tool kind directly from the V8 callback using the
    already-enabled tool metadata
    - build nested tool payloads from the propagated kind instead of calling
    `find_spec`
    - remove the now-unused `find_spec` plumbing from the router and
    parallel runtime helpers
    - add unit coverage for function vs freeform payload shaping and update
    affected router tests
    
    ## Testing
    
    - `cargo test -p codex-code-mode`
    - `cargo test -p codex-core code_mode::tests`
    - `cargo test -p codex-core
    extension_tool_bundles_are_model_visible_and_dispatchable`
    - `cargo test -p codex-core
    model_visible_specs_filter_deferred_dynamic_tools`
  • chore(config) include_collaboration_mode_instructions (#22383)
    ## Summary
    Adds include_collaboration_mode_instructions, which is a config
    equivalent to include_permissions_instructions for collaboration modes.
    Desired for situations where we want to disable this instruction from
    entering the context
    
    ## Testing
    - [x] Added unit test
  • tools: remove is_mutating dispatch gating (#22382)
    ## Why
    
    Tool dispatch had two serialization mechanisms:
    
    - `supports_parallel_tool_calls` decides whether a tool participates in
    the shared parallel-execution lock.
    - `is_mutating` separately gated some calls inside dispatch.
    
    That second hook no longer carried its weight. The remaining
    parallel-support flag is already the per-tool concurrency policy, so
    keeping a second mutating gate made dispatch harder to follow and left
    behind extra session plumbing that only existed for that path.
    
    ## What changed
    
    - Removed `is_mutating` from tool handlers and deleted the
    `tool_call_gate` path that existed only to support it.
    - Simplified dispatch and routing to rely on the existing per-tool
    `supports_parallel_tool_calls` boolean.
    - Dropped the now-unused handler overrides and related session/test
    scaffolding.
    - Kept the router/parallel tests focused on the surviving per-tool
    behavior.
    - Removed the unused `codex-utils-readiness` dependency from
    `codex-core` as a follow-up fix for `cargo shear`.
    
    ## Testing
    
    - `cargo test -p codex-core
    parallel_support_does_not_match_namespaced_local_tool_names`
    - `cargo test -p codex-core mcp_parallel_support_uses_handler_data`
    - `cargo test -p codex-core
    tools_without_handlers_do_not_support_parallel`
  • [codex] Tighten unified exec sandbox setup (#22207)
    ## Summary
    - tighten unified exec sandbox initialization
    - preserve the requested process workdir independently from sandbox
    setup
    - add regression coverage for the updated invariant
    
    ## Validation
    - Ran `/tmp/cargo-tools/bin/just fmt`.
    - Ran the targeted `codex-core` regression test successfully.
    - Ran `cargo test -p codex-core`; it did not complete cleanly because
    unrelated existing agent/config-loader tests failed and the run later
    aborted on a stack overflow in
    `tools::handlers::multi_agents::tests::tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtrees_closed`.
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix: uv lock (#22323)
    Update the lock of UV
  • feat(tui): add ambient terminal pets (#21206)
    ## Why
    
    The Codex App has animated pets, but the TUI had no equivalent ambient
    companion surface. This brings that experience into terminal Codex while
    keeping the main chat flow usable: the pet should feel present, but it
    cannot cover transcript text, composer input, approvals, or picker
    content.
    
    The feature also needs to be terminal-aware. Different terminals support
    different image protocols, tmux can interfere with image rendering, and
    some users will want pets disabled entirely or anchored differently
    depending on their layout.
    
    <table>
    <tr><td>
    <img width="4110" height="2584" alt="CleanShot 2026-05-05 at 12 41
    45@2x"
    src="https://github.com/user-attachments/assets/68a1fcbc-2104-48d6-b834-69c6aaa95cdf"
    />
    <p align="center">macOS - Ghostty, iTerm2 and WezTerm with Custom
    Pet</p>
    </td></tr>
    <tr><td>
    ![Uploading CleanShot 2026-05-10 at 20.28.30.png…]()
    <p align="center">Windows Terminal</p>
    </td></tr>
    <tr><td>
    <img width="3902" height="2752" alt="CleanShot 2026-05-05 at 12 39
    02@2x"
    src="https://github.com/user-attachments/assets/300e2931-6b00-467e-91cb-ab8e28470500"
    />
    <p align="center">Linux - WezTerm and Ghostty</p>
    </td></tr>
    </table>
    
    ## What Changed
    
    - Add a TUI ambient pet renderer in `codex-rs/tui/src/pets/`.
    - Port the app-style pet animation states so the sprite changes with
    task status, waiting-for-input states, review/ready states, and
    failures.
    - Add `/pets` selection UI with a preview pane, loading state, built-in
    pet choices, and a first-row `Disable terminal pets` option.
    - Download built-in pet spritesheets on demand from the same public CDN
    path already used by Android, under
    `https://persistent.oaistatic.com/codex/pets/v1/...`, and cache them
    locally under `~/.codex/cache/tui-pets/`.
    - Keep custom pets local.
    - Add config support for pet selection, disabling pets, and choosing
    whether the pet follows the composer bottom or anchors to the terminal
    bottom.
    - Reserve layout space around the pet so transcript wrapping, live
    responses, and composer input do not render underneath the sprite.
    - Gate image rendering by terminal capability, disable image pets under
    tmux, and support both Kitty Graphics and SIXEL terminals.
    - Add redraw cleanup for terminal image artifacts, including sixel cell
    clearing.
    
    ## Current Scope
    
    - This is an initial TUI version of ambient pets, not full App parity.
    - It focuses on ambient sprite rendering, `/pets` selection, custom
    pets, terminal capability gating, and on-demand CDN-backed built-in
    assets.
    - The ambient text overlay is currently disabled, so the TUI renders the
    pet sprite without extra status text beside it.
    
    ## How to Test
    
    1. Start Codex TUI in a terminal with image support.
    2. Run `/pets`.
    3. Confirm the picker shows built-in pets plus custom pets, and the
    first item is `Disable terminal pets`.
    4. On a fresh `~/.codex/cache/tui-pets/`, move onto a built-in pet and
    confirm the first preview downloads the spritesheet from the shared
    Codex pets CDN and renders successfully.
    5. Move through the pet list and confirm subsequent built-in previews
    use the local cache.
    6. Select a pet, then send and receive messages. Confirm transcript and
    composer text wrap before the pet instead of rendering underneath the
    sprite.
    7. Change the pet anchor setting and confirm the pet can either follow
    the composer bottom or sit at the terminal bottom.
    8. Return to `/pets`, choose `Disable terminal pets`, and confirm the
    sprite disappears cleanly.
    
    Targeted tests:
    - `cargo test -p codex-tui ambient_pet_`
    - `cargo test -p codex-tui
    resize_reflow_wraps_transcript_early_when_pet_is_enabled`
    - `cargo insta pending-snapshots`