Commit Graph

6048 Commits

  • [codex] Migrate thread turns list to thread store (#19280)
    - migrate `thread/turns/list` to ThreadStore. Uses ThreadStore for most
    data now but merges in the in-memory state from thread manager
    - keep v2 `thread/list` pathless-store friendly by converting
    `StoredThread` directly to API `Thread`
    - add regression coverage for pathless store history/listing
  • [plugin] Add Canva to suggesteable list. (#20474)
    - [x] Add Canva to suggesteable list.
  • install WFP filters for Windows sandbox setup (#20101)
    ## Summary
    
    This PR installs a first wave of WFP (Windows Filtering Platform)
    filters that reduce the surface area of network egress vulnerabilities
    for the Windows Sandbox.
    
    - Add persistent Windows Filtering Platform provider, sublayer, and
    filters for the Windows sandbox offline account.
    - Install WFP filters during elevated full setup, log failures
    non-fatally, and emit setup metrics when analytics are enabled.
    - Bump the Windows sandbox setup version so existing users rerun full
    setup and receive the new filters.
    
    ## What WFP is
    Windows Filtering Platform (WFP) is the low-level Windows networking
    policy engine underneath things like Windows Firewall. It lets
    privileged code install persistent filtering rules at specific network
    stack layers, with conditions like "only traffic from this Windows
    account" or "only this remote port," and an action like block.
    
    In this change, we create a Codex-owned persistent WFP provider and
    sublayer, then install block filters scoped to the Windows sandbox's
    offline user account via `ALE_USER_ID`. That means the filters are
    targeted at sandboxed processes running as that account, rather than
    globally affecting the host.
    
    ## Initial filter set
    We are starting with 12 concrete WFP filters across a few high-value
    bypass surfaces. The table below describes the filter families rather
    than one filter per row:
    
    | Area | Concrete filters | Purpose |
    | --- | --- | --- |
    | ICMP | 4 filters: ICMP v4/v6 on `ALE_AUTH_CONNECT` and
    `ALE_RESOURCE_ASSIGNMENT` | Block direct ping-style network reachability
    checks from the offline account. |
    | DNS | 2 filters: remote port `53` on `ALE_AUTH_CONNECT_V4/V6` | Block
    direct DNS queries that bypass our intended proxy/offline path. |
    | DNS-over-TLS | 2 filters: remote port `853` on
    `ALE_AUTH_CONNECT_V4/V6` | Block encrypted DNS attempts that could
    bypass ordinary DNS interception. |
    | SMB / NetBIOS | 4 filters: remote ports `445` and `139` on
    `ALE_AUTH_CONNECT_V4/V6` | Block Windows file-sharing/network share
    traffic from sandboxed processes. |
    
    For IPv4/IPv6 coverage, the port-based filters are installed on both
    `ALE_AUTH_CONNECT_V4` and `ALE_AUTH_CONNECT_V6`. ICMP also gets both
    connect-layer and resource-assignment-layer coverage because ICMP
    traffic is shaped differently from ordinary TCP/UDP port traffic.
    
    ## Validation
    - `cargo fmt -p codex-windows-sandbox` (completed with existing
    stable-rustfmt warnings about `imports_granularity = Item`)
    - `cargo test -p codex-windows-sandbox wfp::tests`
    - `cargo test -p codex-windows-sandbox` (fails in existing legacy
    PowerShell sandbox tests because `Microsoft.PowerShell.Utility` could
    not be loaded; WFP tests passed before that failure)
  • feat(rollouts): store EventMsg::ApplyPatchEnd in limited history mode (#20463)
    The Codex App treats apply patch tool calls quite load-bearing in the UI
    (always shown on a completed turn), so we'd like to persist
    `EventMsg::ApplyPatchEnd` to guarantee that when a client reconnects to
    app-server mid-turn, we always have the full diff to display at the end
    of that turn.
  • [codex] Fix elevated Windows sandbox named-pipe access (#20270)
    ## Summary
    - add elevated-only token constructors that include the current token
    user SID in the restricted SID list
    - switch the elevated Windows command runner to use those constructors
    - leave the unelevated restricted-token path unchanged
    
    ## Why
    Windows named pipes created by tools like Ninja use the platform's
    default named-pipe ACL when no explicit security descriptor is provided.
    In the elevated sandbox, the pipe owner has access, but the
    write-restricted token can still fail its restricted-SID access check
    because the sandbox user SID was not in the restricting SID set. That
    causes child processes to exit successfully while Ninja never receives
    the expected pipe completion/close behavior and hangs.
    
    Including the elevated sandbox user's SID in the restricting SID list
    lets the restricted check succeed for these owner-scoped pipe objects
    without broadening the unelevated sandbox to the real signed-in user.
    
    ## Impact
    - fixes the minimal Ninja hang repro in the elevated Windows sandbox
    - preserves the existing unelevated sandbox behavior and write
    protections
    - keeps the change scoped to the elevated runner rather than changing
    shared token semantics
    - this does not affect file-writes for the sandbox because the sandbox
    users themselves do not receive any additional permissions over what the
    capability SIDs already have. In fact we don't even explicitly grant the
    sandbox user ACLs anywhere.
    
    ## Validation
    - `cargo build -p codex-windows-sandbox --quiet`
    - verified the stock `ninja.exe` minimal repro exits normally on host
    and in the elevated sandbox
    - verified the same repro still hangs in the unelevated sandbox, which
    is the intended scope of this change
  • fix: show correct Bedrock runtime endpoint in /status (#20275)
    ## Why
    
    `/status` was showing the configured `ModelProviderInfo.base_url` for
    Amazon Bedrock, which can be stale or misleading because the actual
    Bedrock Mantle endpoint is derived at runtime from the resolved AWS
    region. This made sessions report the wrong provider endpoint even
    though requests used the correct runtime URL.
    
    ## What changed
    
    - Added `ModelProvider::runtime_base_url()` so provider implementations
    can expose the request-time base URL through the shared runtime provider
    abstraction.
    - Moved Bedrock region-to-Mantle URL resolution into
    `amazon_bedrock::mantle::runtime_base_url()`, keeping region resolution
    private to the Mantle module.
    - Overrode `runtime_base_url()` for Amazon Bedrock so it returns the
    resolved Mantle endpoint instead of the configured default.
    - Resolved and cached the runtime provider base URL during TUI startup,
    then used that cached value when rendering `/status`.
    - Added status coverage that verifies Bedrock displays the runtime URL
    and ignores the configured Bedrock `base_url` when they differ.
    
    ## Verification
    model provider is resolved correctly in local build:
    <img width="696" height="245" alt="Screenshot 2026-04-29 at 5 01 36 PM"
    src="https://github.com/user-attachments/assets/a13c10a5-3720-41ab-8ace-3c4bc573f971"
    />
  • Add /hooks browser for lifecycle hooks (#19882)
    ## Why
    
    `hooks/list` and `hooks/config/write` give us read/write access to hooks
    and their state. This hooks up the TUI as a client so users can inspect
    and manage that state directly.
    
    ## What
    
    - add a two-page `/hooks` browser in the TUI: an event overview with
    installed/active counts, followed by a per-event handler page with
    toggle controls and detail rendering
    - thread managed-state metadata through hook discovery and `hooks/list`
    so the UI can label admin-managed hooks and suppress toggles for them
    - persist hook toggles through the existing config-write path and add
    snapshot coverage for the event list, handler list, managed-hook, and
    empty states
    
    ## Stack
    
    1. openai/codex#19705
    2. openai/codex#19778
    3. openai/codex#19840
    4. This PR - openai/codex#19882
    
    ## Reviewer Notes
    
    - Main UI logic is in
    `codex-rs/tui/src/bottom_pane/hooks_browser_view.rs`; most of the diff
    is the new view plus its snapshot coverage
    - Request / write plumbing for opening the browser and persisting
    toggles is in `codex-rs/tui/src/app/background_requests.rs` and
    `codex-rs/tui/src/chatwidget/hooks.rs`
    - Outside the TUI, the only behavioral change in this PR is threading
    `is_managed` through hook discovery and `hooks/list` so managed hooks
    render as non-toggleable
    - The `codex-rs/tui/src/status/snapshots/` churn is unrelated merge
    fallout from the stacked base branch's newer permission-label rendering
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [Codex] Add browser use external feature flag (#20245)
    ## Summary
    
    - Adds a separate feature control for external-browser Browser Use
    integrations.
    - Registers `browser_use_external` as a stable, default-enabled
    requirements-owned feature key.
    - Updates feature registry tests and regenerates the config schema.
    
    Codex validation:
    - `cargo fmt -- --config imports_granularity=Item`
    - `cargo run -p codex-core --bin codex-write-config-schema`
    - `cargo test -p codex-features`
    
    ## Addendum
    
    This gives enterprise policy a coarse control for Browser Use outside
    the Codex-managed in-app browser. The existing `browser_use` feature is
    the Browser Use control, while `browser_use_external` can gate
    extension/native integrations for external browsers as that surface
    grows
  • Stop emitting item/fileChange/outputDelta output delta notifications (#20471)
    ## Why
    
    `item/fileChange/outputDelta` text output was only the tool's summary or
    error text and not used by client surfaces.
    
    We keep `item/fileChange/outputDelta` in the app-server protocol as a
    deprecated compatibility entry, but the server no longer emits it.
    
    ## What changed
    
    - stop the `apply_patch` runtime from emitting `ExecCommandOutputDelta`
    events
    - simplify `item_event_to_server_notification` so command output deltas
    always map to `item/commandExecution/outputDelta`
    - remove the app-server bookkeeping that tried to detect whether an
    output delta belonged to a file change
    - mark `item/fileChange/outputDelta` as a deprecated legacy protocol
    entry in the v2 types, schema, and README
    - simplify the file-change approval tests so they only wait for
    completion instead of expecting output-delta notifications
    
    ## Testing
    
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-thread-manager-sample`
    - `cargo test -p codex-app-server-protocol
    protocol::event_mapping::tests::exec_command_output_delta_maps_to_command_execution_output_delta
    -- --exact`
    - `cargo test -p codex-app-server
    turn_start_file_change_approval_accept_for_session_persists_v2 --
    --exact` *(failed before the test assertions because the wiremock
    `/responses` mock received 0 requests in setup)*
  • Remove core protocol dependency [2/2] (#20325)
    ## Why
    
    With the local model layer and app-server routing in place from PR1,
    this PR moves the active TUI runtime onto app-server notifications. The
    affected pieces share the same event flow, so the command surface,
    session state, bottom-pane prompts, chat rendering, history/status
    views, and tests move together to keep the stacked branch buildable.
    
    This PR also removes the obsolete compatibility surface that is no
    longer used after the migration. The proposed protocol-boundary verifier
    layer was dropped from the stack; enforcing that final boundary will be
    simpler once `codex-tui` no longer needs any `codex_protocol`
    references.
    
    This PR is part 2 of a 2-PR stack:
    
    1. Add TUI-owned replacement models and extract app-server event
    routing.
    2. Move the active TUI flow to app-server notifications and delete
    obsolete adapter code.
    
    ## What changed
    
    - Rewired app command and session handling to use app-server request and
    notification shapes.
    - Moved approval overlays, request-user-input flows, MCP elicitation,
    realtime events, and review commands onto the app-server-facing model
    surface.
    - Updated chat rendering, history cells, status views, multi-agent UI,
    replay state, and TUI tests to use app-server notifications plus the
    local models introduced in PR1.
    - Deleted `codex-rs/tui/src/app/app_server_adapter.rs` and the
    superseded `chatwidget/tests/background_events.rs` fixture path.
    
    ## Verification
    
    - `cargo check -p codex-tui --tests`
    - Top of stack: `cargo test -p codex-tui`
  • Move item event mapping into app-server-protocol (#20299)
    ## Why
    
    Follow-up to #20291.
    
    The v2 item-event-to-notification translation had been embedded in
    `app-server/src/bespoke_event_handling.rs`, which made it hard to reuse
    anywhere else. This PR moves that stateless mapping into shared protocol
    code so other entry points can produce the same `ServerNotification`
    payloads without copying app-server logic.
    
    That also lets `thread-manager-sample` demonstrate the same notification
    surface that the app server exposes, instead of only printing the final
    assistant message.
    
    ## What changed
    
    - move `item_event_to_server_notification` into
    `codex-app-server-protocol::protocol::event_mapping`
    - keep the mapper tests next to the shared implementation in
    `codex-app-server-protocol`
    - re-export the mapper from `codex-core-api` so lightweight consumers
    can use it without reaching into `app-server-protocol` directly
    - simplify `app-server/src/bespoke_event_handling.rs` so it delegates
    the stateless event-to-notification projection to the shared helper
    - update `thread-manager-sample` to:
      - print mapped notifications as newline-delimited JSON
      - use the shared mapper through `codex-core-api`
    - enable the default feature set so the sample exposes the normal tool
    surface
    - use a `read_only` permission profile so shell commands can run in the
    sample without widening permissions
    
    ## Testing
    
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-core-api`
    - `cargo test -p codex-app-server bespoke_event_handling::tests`
    - `cargo test -p codex-thread-manager-sample`
    - `cargo run -p codex-thread-manager-sample -- "briefly explore the repo
    with pwd and ls, then summarize it"`
  • Remove core protocol dependency [1/2] (#20324)
    ## Why
    
    This stack moves `codex-tui` away from the core protocol event surface
    and toward app-server API shapes plus TUI-owned local models. This first
    PR sets up the lower-risk foundation: it introduces the local model
    surface and extracts app-server event routing into focused TUI modules
    while preserving the existing behavior for the larger migration in PR2.
    
    This PR is part 1 of a 2-PR stack:
    
    1. Add TUI-owned replacement models and extract app-server event
    routing.
    2. Move the active TUI flow to app-server notifications and delete
    obsolete adapter code.
    
    ## What changed
    
    - Added TUI-owned approval, diff, session state, session resume, token
    usage, and user-message models.
    - Added `app/app_server_event_targets.rs` and `app/app_server_events.rs`
    to hold app-server event targeting and dispatch logic outside `app.rs`.
    - Updated app/status tests to use the local model layer and added
    focused routing coverage.
    - Boxed a few large async TUI test futures so this base layer remains
    checkable without overflowing the default test stack.
    
    ## Verification
    
    - `cargo check -p codex-tui --tests`
  • [Extension] Allowlist Chrome Extension in the tool_suggest tool (#20458)
    ### Summary
    Allowlist chrome extension in tool_suggest tool
    
    ### Screenshot
    Allowlist chrome extension in tool_suggest tool
    <img width="808" height="309" alt="chrome_internal"
    src="https://github.com/user-attachments/assets/ed769d77-b635-4a40-a0c5-fbff05af3036"
    />
  • /plugins: remove marketplace (#19843)
    This PR adds marketplace removal to the /plugins menu, giving users a
    way to remove user-configured plugin marketplaces. It adds a `Ctrl+R`
    shortcut to remove selected marketplace tabs, a confirmation prompt,
    loading and error states, and the app-server request flow needed to
    perform marketplace/remove. After a successful removal, the TUI
    refreshes config, plugin mentions, user config, and plugin data so the
    removed marketplace disappears from the menu and other surfaces in the
    TUI.
    
    - Add `Ctrl+R` removal option for user-configured marketplace tabs
    - Show marketplace removal confirmation, loading, and error states
    - Route `marketplace/remove` through the TUI background request flow
    - Refresh config, plugin mentions, and plugin data after successful
    removal
    - Adds reusable per-tab footer hints so removal guidance only appears on
    applicable tabs
    - Add test coverage for `Ctrl+R` behavior while plugin search is active
    
    Steps to test:
    - Add a marketplace using the TUI /plugins menu
    - Use Ctrl+R to remove the marketplace
    - Accept the confirmation prompt
    - Confirm the marketplace is removed when the process completes.
  • Mark goals feature as experimental (#20083)
    ## Why
    
    The `goals` feature flag is ready to move out of the hidden
    under-development bucket and into the user-facing experimental surface.
    Marking it experimental lets users discover it through the experimental
    features UI while still making clear that it is opt-in.
    
    ## What changed
    
    - Changed `goals` from `Stage::UnderDevelopment` to
    `Stage::Experimental` in `codex-rs/features/src/lib.rs`.
    - Added experimental menu metadata for the feature with the description
    `Set a persistent goal Codex can continue over time`.
    
    ## Verification
    
    - `cargo test -p codex-features`
  • fix(core): truncate large mcp tool outputs in rollouts (#20260)
    ## Why
    Large MCP tool call outputs can make rollout JSONL files enormous. In
    the session that motivated this change, the biggest JSONL records were:
    - `event_msg/mcp_tool_call_end`
    - `response_item/function_call_output`
    
    both containing the same unbounded MCP payloads - just 3 MCP tool calls
    that each were multi-hundred MBs 😱
    
    This PR truncates both of those JSONL records.
    
    ## How
    
    #### For `response_item/function_call_output`
    Unified exec already bounds tool output before it is injected into
    model-facing history, which also keeps the corresponding rollout
    `response_item/function_call_output` records small.
    
    MCP should follow the same pattern: truncate the model-facing tool
    output at the tool-output boundary, while leaving code-mode/raw hook
    consumers alone.
    
    #### For `event_msg/mcp_tool_call_end`
    `McpToolCallEnd` also needs its own bounded event copy because it is the
    app-server/replay/UI event shape that backs `ThreadItem::McpToolCall`.
    Unfortunately this is _not_ downstream of the `ToolOutput` trait.
    
    ## Model behavior 
    Model behavior is actually unchanged as a result of this PR. 
    
    Before this PR, MCP output was:
    1. Converted to `FunctionCallOutput`.
    2. Recorded into in-memory history.
    3. Truncated by `ContextManager::record_items()` before later model
    turns saw it.
    
    After this branch, MCP output is truncated earlier, in
    `McpToolOutput::response_payload()`, using the same helper. Then
    `ContextManager::record_items()` sees an already-truncated output and
    effectively has little/no additional work to do.
    
    So the model should still see the same kind of truncated function-call
    output. The practical difference is where truncation happens: earlier,
    before rollout persistence/app-server emission can see the giant
    payload.
    
    ## Verification
    
    - `cargo test -p codex-core mcp_tool_output`
    - `cargo test -p codex-core
    mcp_tool_call::tests::truncate_mcp_tool_result_for_event`
    - `cargo test -p codex-core
    mcp_post_tool_use_payload_uses_model_tool_name_args_and_result`
    - `just fmt`
    - `just fix -p codex-core`
    - `git diff --check`
  • realtime: rename provider session ids (#20361)
    ## Summary
    
    Codex is repurposing `session` to mean a thread group, so the realtime
    provider session id should no longer use `session_id` / `sessionId` in
    Codex-facing protocol payloads. This PR renames that provider-specific
    field to `realtime_session_id` / `realtimeSessionId` and intentionally
    breaks clients that still send the old field names.
    
    ## What Changed
    
    - Renamed realtime provider session fields in `ConversationStartParams`,
    `RealtimeConversationStartedEvent`, and `RealtimeEvent::SessionUpdated`.
    - Renamed app-server v2 realtime request and notification fields to
    `realtimeSessionId`.
    - Removed legacy serde aliases for `session_id` / `sessionId`; clients
    must send the new names.
    - Propagated the rename through core realtime startup, app-server
    adapters, codex-api websocket handling, and TUI realtime state.
    - Regenerated app-server protocol schema/TypeScript outputs and updated
    app-server README examples.
    - Kept upstream Realtime API concepts unchanged: provider `session.id`
    parsing and `x-session-id` headers still use the upstream wire names.
    
    ## Testing
    
    - CI is running on the latest pushed commit.
    - Earlier local verification on this PR:
      - `cargo test -p codex-protocol`
    - `CODEX_SKIP_VENDORED_BWRAP=1 cargo test -p codex-core
    realtime_conversation`
      - `cargo test -p codex-app-server-protocol`
    - `CODEX_SKIP_VENDORED_BWRAP=1 cargo test -p codex-app-server
    realtime_conversation`
    - attempted `CODEX_SKIP_VENDORED_BWRAP=1 cargo test -p codex-tui` (local
    linker bus error while linking the test binary)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Gate multi-agent v2 tools independently of collab (#20246)
    ## Why
    
    `multi_agents_v2` is meant to be independently gated from the older
    `collab` feature. The tool registry still treated the
    collaboration-style agent tools as `collab`-only, so enabling
    `multi_agents_v2` without `collab` omitted the v2 agent tools. Review
    and guardian sub-sessions also need to keep agent spawning disabled even
    when the outer session has `multi_agents_v2` enabled.
    
    ## What changed
    
    - Include the collab-backed agent tools when either `multi_agents_v2` or
    `collab` is enabled.
    - Explicitly disable `multi_agents_v2` for review and guardian review
    sub-sessions, matching the existing `spawn_csv` and `collab`
    restrictions.
    - Add a registry test that enables `multi_agents_v2`, disables `collab`,
    and verifies the v2 agent tools are present while legacy `send_input`
    and `resume_agent` remain hidden.
    
    ## Testing
    
    - Added
    `test_build_specs_multi_agent_v2_does_not_require_collab_feature`.
  • Make missing config clears no-ops (#20334)
    ## Why
    
    Fixes #20145.
    
    `config/value/write` treats a JSON `null` value as a request to clear
    the config key. Clearing a key that is already absent should be
    idempotent, but clearing a nested key such as `features.personality`
    from an empty `config.toml` returned `configPathNotFound` because
    `clear_path` treated the missing `features` parent table as an error.
    
    That makes app-server reset flows brittle because clients have to read
    first and avoid sending a clear request unless the parent path already
    exists.
    
    ## What Changed
    
    - Updated app-server config clearing so missing intermediate tables, or
    non-table parents, are treated as an unchanged no-op.
    - Removed the now-unreachable `MergeError::PathNotFound` path from
    config write merging.
    - Added a regression test covering `features.personality = null` against
    an empty user config.
    
    ## Verification
    
    - `cargo test -p codex-app-server clear_missing_nested_config_is_noop`
    - `cargo test -p codex-app-server` was run; the config manager unit
    suite passed, but one unrelated integration test failed because
    `turn_start_emits_thread_scoped_warning_notification_for_trimmed_skills`
    expected `7` trimmed skills and observed `8`.
    - `just fix -p codex-app-server`
  • feat: Add workspace plugin sharing APIs (#20278)
    1. Adds v2 plugin/share/save, plugin/share/list, and plugin/share/delete
    RPCs.
    2. Implements save by archiving a local plugin root, enforcing a size
    limit, uploading through the workspace upload flow, and supporting
    updates via remotePluginId.
    3. Lists created workspace plugins
    4. Deletes a previously uploaded/shared plugin.
  • ci: increase Windows release workflow timeouts (#20343)
    ## Why
    
    #20271 increased the `90`-minute timeout in `rust-release.yml`, but it
    did not update the reusable Windows workflow in
    `rust-release-windows.yml`. As a result, the Windows release compile
    jobs were still capped at `60` minutes and the `windows-x64` primary
    build could continue timing out.
    
    We are keeping the existing `90`-minute timeout in `rust-release.yml`.
    That increase was still directionally correct because the top-level
    release build benefits from extra headroom; the mistake was assuming it
    also covered the reusable Windows jobs.
    
    ## What Changed
    - increase the reusable Windows release workflow timeouts in
    `rust-release-windows.yml` from `60` minutes to `90` minutes
    - update the comment in `rust-release.yml` so it no longer implies that
    the top-level timeout covers the Windows reusable jobs
  • Add persisted hook enablement state (#19840)
    ## Why
    
    After `hooks/list` exposes the hook inventory, clients need a way to
    persist user hook preferences, make those changes effective in
    already-open sessions, and distinguish user-controllable hooks from
    managed requirements without adding another bespoke app-server write
    API.
    
    ## What
    
    - Extends `hooks/list` entries with effective `enabled` state.
    - Persists user-level hook state under `hooks.state.<hook-id>` so the
    model can grow beyond a single boolean over time.
    - Uses the existing `config/batchWrite` path for hook state updates
    instead of introducing a dedicated hook write RPC.
    - Refreshes live session hook engines after config writes so
    already-open threads observe updated enablement without a restart.
    
    ## Stack
    
    1. openai/codex#19705
    2. openai/codex#19778
    3. This PR - openai/codex#19840
    4. openai/codex#19882
    
    ## Reviewer Notes
    
    The generated schema files account for much of the raw diff. The core
    behavior is in:
    
    - `hooks/src/config_rules.rs`, which resolves per-hook user state from
    the config layer stack.
    - `hooks/src/engine/discovery.rs`, which projects effective enablement
    into `hooks/list` from source-derived managedness.
    - `config/src/hook_config.rs`, which defines the new `hooks.state`
    representation.
    - `core/src/session/mod.rs`, which rebuilds live hook state after user
    config reloads.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [plugins] Allow MSFT curated plugins in tool_suggest (#20304)
    ## Summary
    - [x] Move the allowlist out of core crate
    - [x] Add Teams, SharePoint, Outlook Email, and Outlook Calendar to the
    tool_suggest discoverable plugin allowlist
    - [x] Add focused coverage for Microsoft curated plugin discovery
    
    ## Testing
    - just fmt
    - cargo test -p codex-core-plugins
    - cargo test -p codex-core
    list_tool_suggest_discoverable_plugins_returns_
  • [codex-analytics] prevent stale guardian events from satisfying reused reviews (#20080)
    ## Why
    
    Reused Guardian review trunks can still have older child-turn events
    queued when a later review starts. The review waiter currently accepts
    the first terminal event it sees from the shared child session, so a
    stale `TurnComplete` can be attributed to the new review. That produces
    impossible analytics combinations such as non-null TTFT with sub-10 ms
    completion latency and zero token deltas on `trunk_reused` reviews.
    
    ## What changed
    
    - Preserve the child turn id returned by the Guardian review
    `Op::UserTurn` submission.
    - Restrict Guardian review waiting to events correlated with that
    submitted child turn.
    - Restrict timeout/abort draining to terminal events for the same child
    turn.
    - Add regression coverage for stale prior-turn completions, stale
    prior-turn errors, and interrupt draining in
    `codex-rs/core/src/guardian/review_session.rs`.
    
    ## Verification
    
    - `cargo test -p codex-core guardian::review_session::tests::`
    - `cargo clippy -p codex-core --tests -- -D warnings`
  • tui: return from side chat on Ctrl-D (#20282)
    ## Why
    
    Fixes #20264.
    
    Side conversations are an ephemeral layer on top of the main chat.
    Pressing `Ctrl+D` from an empty side-chat composer should unwind back to
    the parent thread, matching the existing side-return behavior, instead
    of falling through to the global quit shortcut and exiting Codex.
    
    ## What changed
    
    The side-return shortcut matcher now treats `Ctrl+D` the same way it
    already treats `Esc` and `Ctrl+C`. Because app-level side-return
    handling runs before the chat widget's global quit handling, this
    returns from `/side` while preserving normal `Ctrl+D` quit behavior
    outside side conversations.
    
    The existing shortcut coverage was updated to include lowercase and
    uppercase `Ctrl+D` key events.
    
    ## Verification
    
    - `cargo test -p codex-tui
    side_return_shortcuts_match_esc_ctrl_c_and_ctrl_d`
    - `cargo test -p codex-tui` starts successfully and the new shortcut
    test passes, but the broader suite later aborts in the unrelated
    existing test
    `app::tests::attach_live_thread_for_selection_rejects_unmaterialized_fallback_threads`
    with a stack overflow.
  • Reduce the surface of collaboration modes (#20149)
    Collaboration modes were slightly invasive both into ThreadManager
    construction and ModelProvider
  • Import external agent sessions in background (#20284)
    Summary:
    - Return from external agent import before session history import
    finishes
    - Run session import work in the background and emit the existing
    completion notification when it is done
    - Serialize session imports so duplicate requests do not create
    duplicate imported threads
    
    Verification:
    - cargo test -p codex-app-server external_agent_config_
    - cargo test -p codex-external-agent-sessions
    - just fix -p codex-app-server
    - just fix -p codex-external-agent-sessions
    - git diff --check
  • Consume ai-title from external sessions and add end marker (#20261)
    ## Summary
    - Support Claude Code `ai-title` / `aiTitle` records when detecting and
    importing external agent sessions.
    - Preserve existing `custom-title` / `customTitle` precedence; only fall
    back to `aiTitle` when no custom title is present.
    - Add coverage for both detection and import title selection, including
    the custom-title-over-ai-title case.
    
    ## Testing
    - `cargo test -p codex-external-agent-sessions`
    - `just fix -p codex-external-agent-sessions`
  • Add hooks/list app-server RPC (#19778)
    ## Why
    
    We need a way to list the available hooks to expose via the TUI and App
    so users can view and manage their hooks
    
    ## What
    
    - Adds `hooks/list` for one or more `cwd` values that returns discovered
    hook metadata
    
    ## Stack
    
    1. openai/codex#19705
    2. This PR - openai/codex#19778
    3. openai/codex#19840
    4. openai/codex#19882
    
    ## Review Notes
    
    The generated schema files account for most of the raw diff, these files
    have the core change:
    
    - `hooks/src/engine/discovery.rs` builds the inventory entries during
    hook discovery while leaving runtime handlers focused on execution.
    - `app-server/src/codex_message_processor.rs` wires `hooks/list` into
    the app-server flow for each requested `cwd`.
    - `app-server-protocol/src/protocol/v2.rs` defines the new v2
    request/response payloads exposed on the wire.
    
    ### Core Changes
    
    `core/src/plugins/manager.rs` adds `plugins_for_layer_stack(...)` so
    `skills/list` and `hooks/list`can resolve plugin state for each
    requested `cwd`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • chore: increase release build timeout from 60 min to 90 (#20271)
    Build times are creeping up, so increase the timeout as a precaution.
  • Update Codex login success page UX (#20136)
    ## Summary
    
    update the local login success page to match the Codex desktop auth UX
    use theme-aware colors and an inline 20px Codex mark
    keep the actual localhost success page aligned with the browser auth UX
    PR
    
    ## Tests
    
    <img width="1728" height="1117" alt="Screenshot 2026-04-29 at 12 00
    34 PM"
    src="https://github.com/user-attachments/assets/76a40c3f-07c3-452c-97da-e7c43717cd2c"
    />
  • Enforce workspace metadata protections in Linux sandbox (#19852)
    ## Summary
    
    Enforce FileSystemSandboxPolicy protected metadata names in the Linux
    bubblewrap adapter so `.git`, `.agents`, and `.codex` remain read only
    inside writable workspace roots unless the policy grants an explicit
    write carveout.
    
    ## Scope
    
    1. Translate protected metadata names from FileSystemSandboxPolicy into
    bubblewrap masks for existing metadata paths.
    2. Represent missing protected metadata paths as guarded mount targets
    so agents cannot create `.git`, `.agents`, or `.codex` under writable
    roots.
    3. Preserve normal git discovery for existing repos, worktrees, and
    parent repos.
    4. Keep explicit user write grants working when policy allows a
    protected metadata path directly.
    
    ## Not in scope
    
    1. No shell preflight UX.
    2. No TUI runtime profile propagation.
    3. No macOS Seatbelt changes in this PR.
    
    ## Reviewer focus
    
    1. This should be reviewed as the Linux enforcement adapter for the
    policy primitive from PR 19846.
    2. macOS enforcement already landed in PR 19847.
    3. The important invariant is that `FileSystemSandboxPolicy` is the
    source of truth for `.git`, `.agents`, and `.codex`.
    
    ## Validation
    
    1. `git diff` whitespace check passed.
    2. `cargo fmt` check passed with the existing stable rustfmt warning
    about `imports_granularity`.
    3. Full Linux sandbox Cargo test suite passed on the devbox.
    4. Devbox forty six case suite passed at head
    `012accb703c13bd28df5b40079a9bf183036336a`.
    5. Devbox summary: pass 46, fail 0.
    6. The devbox suite was run through `just c sandbox linux`.
    7. Focused repo test for Viyat parent repo case passed on the devbox.
  • stop blocking unified_exec on Windows (#19435)
    ## Summary
    - remove the Windows-specific unified-exec environment block from tool
    selection
    - keep `unified_exec` default-off on Windows unless the feature is
    explicitly enabled
    - normalize model-provided `shell_type = unified_exec` to
    `shell_command` when the feature is disabled
    - drop obsolete tests tied to the removed environment gate and keep the
    feature-flag regression coverage
    
    ## Why
    Now that the session/long-lived process backend is implemented for the
    Windows sandbox, we don't need to hard disable it anymore. We will be
    rolling out slowly using a feature gate.
    
    ## Impact
    This allows manual Windows opt-in in CLI and app-backed flows while
    preserving the existing default-off behavior for Windows users.
    
    ---------
    
    Co-authored-by: canvrno-oai <kbond@openai.com>
    Co-authored-by: Codex <noreply@openai.com>
  • Add codex-core public API listing (#20243)
    Summary:
    - Add a checked-in codex-core public API listing generated by
    cargo-public-api.
    - Add scripts/regen-public-api.sh with an embedded crate list,
    auto-install for cargo-public-api 0.51.0, pinned nightly, and --check
    mode.
    - Add Rust CI jobs on the codex Linux x64 runner pool to verify the
    listing stays up to date.
    
    Testing:
    - bash -n scripts/regen-public-api.sh
    - just regen-public-api --check
    - yq '.' .github/workflows/rust-ci.yml
    .github/workflows/rust-ci-full.yml
    - git diff --check
  • Add agent graph store interface (#19229)
    ## Summary
    
    Persisted subagent parent/child topology currently leaks through
    `StateRuntime`'s SQLite-specific thread-spawn helpers. This PR
    introduces a narrow `AgentGraphStore` boundary so follow-up work can
    route graph operations through a local or remote store without coupling
    orchestration code directly to the state DB graph API.
    
    ## Changes
    
    - Adds the new `codex-agent-graph-store` crate.
    - Defines a flat `AgentGraphStore` trait for the v1 graph surface:
    upsert edge, set edge status, list direct children, and list
    descendants.
    - Adds public graph types for `ThreadSpawnEdgeStatus`,
    `AgentGraphStoreError`, and `AgentGraphStoreResult`.
    - Implements `LocalAgentGraphStore` on top of an existing
    `codex_state::StateRuntime`, preserving today's SQLite-backed
    `thread_spawn_edges` behavior.
    - Registers the crate in Cargo/Bazel metadata.
    
    This PR only adds the local contract and implementation; call-site
    migration and the remote gRPC store are left to the follow-up PRs in the
    stack.
    
    ## Testing
    
    - `cargo test -p codex-agent-graph-store`
    
    The new unit tests cover local parity with the existing `StateRuntime`
    graph methods, `Open`/`Closed` filtering, status updates, and stable
    breadth-first descendant ordering.
  • [mcp] Fix plugin MCP approval policy. (#19537)
    Plugin MCP servers are loaded from plugin manifests rather than
    top-level `[mcp_servers]`, so their tool approval preferences need to be
    stored and applied through the owning plugin config. Without this,
    choosing "Always allow" for a plugin MCP tool could write a preference
    that was not reliably used on later tool calls.
    
    ## Summary
    - Add plugin-scoped MCP policy config under
    `plugins.<plugin>.mcp_servers`, including server enablement, tool
    allow/deny lists, server defaults, and per-tool approval modes.
    - Overlay plugin MCP policy onto manifest-provided server configs when
    plugins are loaded.
    - Route persistent "Always allow" writes for plugin MCP tools back to
    the owning `plugins.<plugin>.mcp_servers.<server>.tools.<tool>` config
    entry.
    - Reload user config after persisting an approval and make the plugin
    load cache config-aware so stale plugin MCP policy is not reused after
    `config.toml` changes.
    - Regenerate the config schema and add coverage for plugin MCP policy
    loading, approval lookup, persistence, and stale-cache prevention.
    
    ## Testing
    - `cargo test -p codex-config`
    - `cargo test -p codex-core-plugins`
    - `cargo test -p codex-core --lib plugin_mcp`
  • Escape turn metadata headers as ASCII JSON (#19620)
    ## Why
    
    `x-codex-turn-metadata` is sent as an HTTP/WebSocket header, but Codex
    was serializing the metadata JSON with raw UTF-8 string contents. When a
    workspace path contains non-ASCII characters, common HTTP stacks can
    reject or corrupt that header before the request reaches the provider.
    
    Fixes #17468. Also addresses the duplicate WebSocket report in #19581.
    
    ## What changed
    
    - Added `codex_utils_string::to_ascii_json_string`, a shared helper that
    serializes JSON normally while escaping non-ASCII string content as
    `\uXXXX`.
    - Switched turn metadata header serialization, including merged
    Responses API client metadata, to use the ASCII-safe JSON helper.
    - Added coverage for non-ASCII workspace paths and non-ASCII client
    metadata while preserving the same parsed JSON values.
    
    ## Verification
    
    - `cargo test -p codex-utils-string`
    - `cargo test -p codex-core turn_metadata`
    - `just bazel-lock-check`
  • docs: discourage #[async_trait] and #[allow(async_fn_in_trait)] (#20242)
    ## Why
    
    We have run into two avoidable problems when introducing async trait
    APIs in Rust:
    
    - `#[async_trait]` has caused materially worse build times in this
    repository.
    - `#[allow(async_fn_in_trait)]` makes it too easy to ship a public trait
    without spelling out whether the returned future is `Send`, which hides
    an important part of the trait contract.
    
    We already have a good example of the preferred alternative in
    [#16630](https://github.com/openai/codex/pull/16630) /
    [`3c7f013f9735`](https://github.com/openai/codex/commit/3c7f013f9735),
    but that guidance currently lives only as prior art in the codebase.
    This PR documents the rule in `AGENTS.md` so contributors are more
    likely to follow the native RPITIT pattern before these two shortcuts
    spread further.
    
    ## What Changed
    
    - added Rust guidance in `AGENTS.md` discouraging both `#[async_trait]`
    and `#[allow(async_fn_in_trait)]`
    - pointed contributors to the native RPITIT pattern with explicit `Send`
    bounds on the returned future
    - clarified that implementations may still use `async fn` when they
    satisfy that trait contract
    
    ## Verification
    
    - docs-only change; no tests run
  • [apps] Add apps MCP path override (#20231)
    Summary
    
    - Add `[features.apps_mcp_path_override]` config with a `path` field for
    overriding only the built-in apps MCP path.
    - Keep existing host/base URL derivation unchanged and append the
    configured path after that base.
    - Regenerate the config schema with the custom feature-config case.
    
    Test Plan
    
    - Not run for latest revision; only `just fmt` and `just
    write-config-schema` were run.
    - Earlier revision: `cargo test -p codex-features`
    - Earlier revision: `cargo test -p codex-mcp`
  • Fallback login callback port when default is busy (#19334)
    ## Summary
    - Keep the preferred ChatGPT login callback port `1455` first.
    - Preserve the existing `/cancel` recovery for stale Codex login
    servers.
    - Fall back to the registered localhost callback port `1457` when `1455`
    remains unavailable.
    
    ## Why
    Cursor and Codex Desktop both use the ChatGPT account login callback
    server. On Windows, Cursor can already be listening on `127.0.0.1:1455`
    / `[::1]:1455`, causing Codex Desktop sign-in to fail with:
    
    `Local callback port 1455 is already in use on this machine.`
    
    Codex already attempted to cancel a stale Codex login server on that
    port, but if the listener does not release the port, the old behavior
    was to fail. The new behavior falls back to `1457`, which matches the
    fixed redirect URI being registered server-side in
    `openai/openai#863817`. This keeps the OAuth `redirect_uri` inside
    Hydra's exact allow-list instead of choosing an arbitrary ephemeral
    port.
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-login`
    - `git diff --check HEAD~1..HEAD`
  • [app-server] centralize client response analytics (#20059)
    ## Why
    
    The precursor PR keeps successful client responses typed until
    app-server's outgoing response seam. This follow-up uses that seam to
    move successful client-response analytics out of individual handlers and
    into the shared sender path, while keeping filtering decisions inside
    `codex-analytics`.
    
    ## What changed
    
    - Emit successful client-response analytics centrally from
    `OutgoingMessageSender::send_response`.
    - Remove duplicate handler-local response tracking for the current
    thread/turn lifecycle responses.
    - Keep analytics ingestion selective inside `AnalyticsEventsClient`, so
    unrelated client traffic is ignored before cloning or boxing.
    - Collapse client-response analytics facts onto one typed path and
    normalize payloads in the reducer.
    - Add direct client-filter coverage plus sender-level coverage for the
    centralized forwarding path.
    
    ## Verification
    
    - `cargo test -p codex-analytics`
    - `cargo test -p codex-app-server outgoing_message::tests --lib`
  • Require remote plugin detail before uninstall (#19966)
    ## Summary
    - Fetch remote plugin detail before sending the uninstall request.
    - Use the detail response to derive the marketplace namespace and plugin
    name for cache cleanup.
    - Stop the uninstall before the backend POST if detail lookup fails, so
    backend state and local cache state do not diverge.
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-app-server plugin_uninstall`
    - `cargo test -p codex-core-plugins`
    - `git diff --check`
  • [app-server] type client response payloads (#20050)
    ## Why
    
    `pr17088` adds typed server-originated request/response plumbing, but
    successful client responses are still erased into bare JSON-RPC `result`
    values before app-server can make any typed decision about them.
    
    This precursor PR keeps successful client responses typed until the
    outgoing response seam. It is intentionally limited to
    protocol/app-server plumbing so the analytics behavior change can review
    separately on top.
    
    ## What changed
    
    - Add `ClientResponsePayload` as the pre-serialization client response
    body type.
    - Route app-server successful response paths through the typed payload
    seam while preserving existing handler-local analytics behavior.
    - Keep `InterruptConversation` JSON-RPC-only because it has no
    `ClientResponse` variant.
    - Move the new payload conversion tests into a dedicated protocol test
    module.
    
    ## Verification
    
    - `cargo check -p codex-app-server`
    - `cargo test -p codex-app-server-protocol`