7919 Commits

  • feat: Track local paths for shared plugins (#20560)
    When a local plugin is shared, Codex now records the local plugin path
    by remote plugin id under CODEX_HOME/.tmp.
    
    plugin/share/list includes the remote share URL and the matching local
    plugin path when available, and plugin/share/delete
    clears the local mapping after deleting the remote share.
    
    Also add sharedURL to plugin/share/list.
  • Add remote plugin skill read API (#20150)
    ## Summary
    
    Adds an app-server `plugin/skill/read` method for remote plugin skill
    markdown. The new method calls the plugin-service skill detail endpoint
    and returns `skill_md_contents`, so clients can preview skills for
    remote plugins before the bundle is installed locally.
    
    ## Why
    
    Uninstalled remote plugin skills do not have local `SKILL.md` files.
    Without an on-demand remote read, the desktop plugin details UI cannot
    render the skill details modal for those skills.
    
    ## Validation
    
    - `just write-app-server-schema`
    - `just fmt`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server --test all --
    suite::v2::plugin_read::plugin_skill_read_reads_remote_skill_contents_when_remote_plugin_enabled
    --exact`
    - `just fix -p codex-app-server-protocol -p codex-core-plugins -p
    codex-app-server`
  • Refresh remote plugin cache on auth changes (#20265)
    ## Summary
    - Refresh the remote installed-plugin cache after login/logout instead
    of keying it by account or eagerly clearing it.
    - Reuse the existing single-flight remote installed refresh loop so
    newer queued auth refreshes replace older pending requests and the API
    result eventually overwrites or clears the cache.
    - Keep derived plugin/skills cache and MCP refresh side effects behind
    the existing effective-plugin-changed task when the refreshed installed
    state changes.
    - Leave `clear_plugin_related_caches` scoped to derived plugin/skills
    caches so share mutations do not drop remote installed plugins.
    
    ## Tests
    - `cargo fmt --all --manifest-path codex-rs/Cargo.toml` (passes; stable
    rustfmt warns that `imports_granularity = Item` is nightly-only)
    - `cargo test -p codex-core-plugins remote_installed_cache`
    - `cargo test -p codex-app-server
    skills_list_loads_remote_installed_plugin_skills_from_cache`
  • Color TUI statusline from active theme (#19631)
    ## Why
    
    Users have shared that the TUI can feel too visually flat because themes
    mostly show up in code syntax highlighting. The configurable statusline
    is a natural place to make the active theme more visible, while still
    letting users keep the existing monotone statusline if they prefer it.
    
    ## What Changed
    
    - Added a statusline styling helper that builds the rendered statusline
    from `(StatusLineItem, text)` segments, preserving item identity while
    keeping the plain text output unchanged.
    - Derived foreground accent colors from the active syntax theme by
    looking up TextMate scopes through the existing syntax highlighter, with
    conservative ANSI fallbacks when a scope does not provide a foreground.
    - Tuned theme-derived colors to keep the accents visible without making
    the statusline feel overly bright.
    - Added `[tui].status_line_use_colors`, defaulting to `true`, plus a
    separated `/statusline` toggle so users can enable or disable
    theme-derived statusline colors from the setup UI.
    - Updated the live statusline and `/statusline` preview to use the same
    styled builder, while keeping terminal-title preview text plain.
    - Kept statusline separators and active-agent add-ons subdued while
    removing blanket dimming from the whole passive statusline.
    
    ## Verification
    
    - `cargo test -p codex-tui status_line`
    - `cargo test -p codex-tui theme_picker`
    - `cargo test -p codex-tui foreground_style_for_scopes`
    - `cargo test -p codex-tui`
    - `cargo test -p codex-config`
    - `cargo test -p codex-core status_line_use_colors`
    - `cargo insta pending-snapshots --manifest-path tui/Cargo.toml`
    
    ## Visual
    
    <img width="369" height="23" alt="Screenshot 2026-04-30 at 6 16 08 PM"
    src="https://github.com/user-attachments/assets/11d03efb-8e4f-4450-8f4d-00a9659ef4cd"
    />
    
    <img width="385" height="23" alt="Screenshot 2026-04-30 at 6 16 02 PM"
    src="https://github.com/user-attachments/assets/a3d89f36-bdc1-42e8-8e84-61350e3999e2"
    />
  • Format multi-day goal durations in the TUI (#20558)
    ## Why
    
    Goal mode shows elapsed time in compact hour/minute form. That is easy
    to scan for shorter runs, but once a goal runs past 24 hours, large hour
    counts become harder to read at a glance.
    
    ## What changed
    
    Updated `codex-rs/tui/src/goal_display.rs` so unbudgeted goal elapsed
    time keeps the existing compact format below one day, then switches to a
    day-aware format once the elapsed time reaches 24 hours:
    
    - `23h 59m`
    - `1d 0h 0m`
    - `2d 23h 42m`
    
    The formatter now covers the 24-hour boundary in unit tests, and the TUI
    status-line snapshot for a completed elapsed goal now exercises the
    multi-day display.
    
    ## Verification
    
    - `cargo test -p codex-tui`
    
    Here's my longest-running test task:
    
    <img width="186" height="23" alt="image"
    src="https://github.com/user-attachments/assets/cedfcdab-7f6e-44e6-8495-8a39f63973fb"
    />
  • Make thread store process-scoped (#19474)
    - Build one app-server process ThreadStore from startup config and share
    it with ThreadManager and CodexMessageProcessor.
    - Remove per-thread/fork store reconstruction so effective thread config
    cannot switch the persistence backend.
    - Add params to ThreadStore create/resume for specifying thread
    metadata, since otherwise the metadata from store creation would be used
    (incorrectly).
  • [codex] Remove unused event messages (#20511)
    ## Why
    
    Several legacy `EventMsg` variants were still emitted or mapped even
    though clients either ignored them or had moved to item/lifecycle
    events. `Op::Undo` had also degraded to an unavailable shim, so this
    removes that dead task path instead of preserving a command that cannot
    do useful work.
    
    `McpStartupComplete`, `WebSearchBegin`, and `ImageGenerationBegin` are
    intentionally kept because useful consumers still depend on them: MCP
    startup completion drives readiness behavior, and the begin events let
    app-server/core consumers surface in-progress web-search and
    image-generation items before the final payload arrives.
    
    ## What Changed
    
    - Removed weak legacy event variants and payloads from `codex-protocol`,
    including legacy agent deltas, background events, and undo lifecycle
    events.
    - Kept/restored `EventMsg::McpStartupComplete`,
    `EventMsg::WebSearchBegin`, and `EventMsg::ImageGenerationBegin` with
    serializer and emission coverage.
    - Updated core, rollout, MCP server, app-server thread history,
    review/delegate filtering, and tests to rely on the useful replacement
    events that remain.
    - Removed `Op::Undo`, `UndoTask`, the undo test module, and stale TUI
    slash-command comments.
    - Stopped agent job/background progress and compaction retry notices
    from emitting `BackgroundEvent` payloads.
    
    ## Verification
    
    - `cargo check -p codex-protocol -p codex-app-server-protocol -p
    codex-core -p codex-rollout -p codex-rollout-trace -p codex-mcp-server`
    - `cargo test -p codex-protocol -p codex-app-server-protocol -p
    codex-rollout -p codex-rollout-trace -p codex-mcp-server`
    - `cargo test -p codex-core --test all suite::items`
    - `just fix -p codex-protocol -p codex-app-server-protocol -p codex-core
    -p codex-rollout -p codex-rollout-trace -p codex-mcp-server`
    - Earlier coverage on this PR also included `codex-mcp`, `codex-tui`,
    core library tests, MCP/plugin/delegate/review/agent job tests, and MCP
    startup TUI tests.
  • Surface admin-disabled remote plugin status (#20298)
    ## Summary
    
    Remote plugin-service returns plugin availability separately from a
    user's installed/enabled state. This adds `PluginAvailabilityStatus` to
    the app-server protocol, propagates remote catalog `status` into
    `PluginSummary`, and rejects install attempts for remote plugins marked
    `DISABLED_BY_ADMIN` before downloading or caching the bundle.
    
    This is the `openai/codex` half of the change. The companion
    `openai/openai` webview PR is
    https://github.com/openai/openai/pull/873269.
    
    ## Validation
    
    - `cargo run -p codex-app-server-protocol --bin write_schema_fixtures`
    - `cargo test -p codex-app-server --test all
    plugin_list_marks_remote_plugin_disabled_by_admin`
    - `cargo test -p codex-app-server --test all
    plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled`
    - `cargo test -p codex-app-server --test all
    plugin_install_rejects_remote_plugin_disabled_by_admin_before_download`
    - `cargo test -p codex-app-server-protocol schema_fixtures`
  • [codex] Improve PR babysitter CI diagnostics and guardrails (#20484)
    ## Summary
    
    - Surface failed GitHub Actions jobs in the PR babysitter watcher so
    Codex can fetch job logs as soon as a job fails, instead of waiting for
    the overall workflow run to complete.
    - Update babysit-pr skill instructions, GitHub API notes, and heuristics
    to prefer direct job log archives before falling back to `gh run view
    --log-failed`.
    - Add guardrails requiring explicit user confirmation before posting
    replies to human-authored review comments.
    - Add guardrails preventing Codex from patching unrelated flaky tests,
    CI infrastructure, runner issues, dependency outages, or other failures
    not caused by the PR branch.
    
    ## Validation
    
    - `python3 -m pytest
    .codex/skills/babysit-pr/scripts/test_gh_pr_watch.py`
  • [codex-analytics] centralize thread analytics state (#20300)
    ## Why
    
    Several analytics event families need the same per-thread attribution
    state: the app-server client/runtime associated with a thread and, for
    lifecycle-oriented events, the thread metadata captured during
    initialization. Keeping connection ids and lifecycle metadata in
    separate maps made each consumer rebuild the same thread context and
    made subagent attribution harder to resolve consistently.
    
    ## What changed
    
    - Replaces the separate thread connection and metadata maps with one
    reducer-owned `threads` map.
    - Routes guardian, compaction, turn-steer, and turn analytics through
    shared thread-state lookups while preserving turn-origin attribution for
    turn events and request-origin attribution for steer events.
    - Lets newly observed spawned subagent threads inherit their parent
    thread connection so later thread-scoped analytics can resolve through
    the same state model.
    - Adds regression coverage for standalone `SubAgentThreadStarted`
    publication plus the `SubAgentSource::ThreadSpawn` parent fallback
    through a thread-scoped consumer that depends on inherited connection
    state.
    
    ## Verification
    
    - `cargo test -p codex-analytics`
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/20300).
    * #18748
    * #18747
    * #17090
    * #17089
    * #20239
    * #20515
    * #20514
    * __->__ #20300
  • app-server: switch remote control to protocol v3 segmentation (#20341)
    ## Why
    
    Remote-control protocol v3 makes segmentation an explicit wire-level
    feature. The app-server transport needs to support that protocol
    directly so large messages can be chunked, acknowledged, replayed, and
    reassembled consistently.
    
    ## What changed
    
    - Bump the remote-control websocket protocol version from `2` to `3`.
    - Add explicit client/server chunk envelope variants plus chunk-aware
    acknowledgements.
    - Split oversized outbound server messages into bounded transport
    chunks.
    - Reassemble ordered inbound client chunks with bounded memory usage and
    stream/client invalidation handling.
    - Track inbound chunk cursors and outbound ack cursors as `(seq_id,
    segment_id)` so duplicate chunks and partial replays behave correctly.
    - Add focused coverage for chunk splitting, reassembly, duplicate
    suppression, and stream replacement behavior.
    
    ## Validation
    
    - Added targeted unit coverage for segmented message handling in
    `remote_control`.
    - Local validation is currently blocked before compilation because
    `packageproxy` does not serve the locked `rustls-webpki 0.103.13`
    dependency required by the workspace.
  • fix(exec_policy) heredoc parsing file_redirect (#20113)
    ## Summary
    Fixes a regression introduced in #10941 so that heredocs do not permit
    file redirects to be approved by rules, and adds scenario tests to cover
    this behavior.
    
    
    Previously, heredoc command parsing would allow redirects and
    environment variables:
    ```bash
    # commands_for_exec_policy() would parse this via parse_shell_lc_single_command_prefix
    PATH=/tmp/bad:$PATH cat <<'EOF' > /tmp/bad/hello.txt
    hello
    EOF
    ```
    This conflicts with the Codex Rules documentation; heredoc parsing logic
    should abide by the same strictness of parsing.
    
    
    ## Tests
    - [x] Updated unit tests accordingly
    - [x] Added scenario tests for these cases
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • execpolicy: unwrap PowerShell -Command wrappers on Windows (#20336)
    ## Why
    On Windows, Codex runs shell commands through a top-level
    `powershell.exe -NoProfile -Command ...` wrapper. `execpolicy` was
    matching that wrapper instead of the inner command, so prefix rules like
    `["git", "push"]` did not fire for PowerShell-wrapped commands even
    though the same normalization already happens for `bash -lc` on Unix.
    
    This change makes the Windows shell wrapper transparent to rule matching
    while preserving the existing Windows unmatched-command safelist and
    dangerous-command heuristics.
    
    ## What changed
    - add `parse_powershell_command_plain_commands()` in
    `shell-command/src/powershell.rs` to unwrap the top-level PowerShell
    `-Command` body with `extract_powershell_command()` and parse it with
    the existing PowerShell AST parser
    - update `core/src/exec_policy.rs` so `commands_for_exec_policy()`
    treats top-level PowerShell wrappers like `bash -lc` and evaluates rules
    against the parsed inner commands
    - carry a small `ExecPolicyCommandOrigin` through unmatched-command
    evaluation and expose `is_safe_powershell_words()` /
    `is_dangerous_powershell_words()` so Windows safelist and
    dangerous-command checks still work after unwrap
    - add Windows-focused tests for wrapped PowerShell prompt/allow matches,
    wrapper parsing, and unmatched safe/dangerous inner commands, and
    re-enable the end-to-end `execpolicy_blocks_shell_invocation` test on
    Windows
    
    ## Testing
    - `cargo test -p codex-shell-command`
  • Alias codex_hooks feature as hooks (#20522)
    # Why
    
    The hooks feature flag should use the concise canonical name `hooks`,
    while existing configs that still use `codex_hooks` continue to work
    during the rename.
    
    # What
    
    - change the canonical `Feature::CodexHooks` key from `codex_hooks` to
    `hooks`
    - register `codex_hooks` through the existing legacy-alias path
    - update the config schema and canonical config fixtures to prefer
    `hooks`
    - add regression coverage that both `hooks` and `codex_hooks` resolve to
    `Feature::CodexHooks`
    
    # Verification
    
    - `cargo test -p codex-features`
    - `cargo test -p codex-core config::schema_tests`
    - `cargo test -p codex-core
    pre_tool_use_blocks_shell_when_defined_in_config_toml`
    - `cargo test -p codex-app-server
    hooks_list_uses_each_cwds_effective_feature_enablement`
  • fix(app-server): mark thread/turns/list and exclude_turns as experime… (#20499)
    …ntal
    
    We have some bugs to work out and it is not quite ready to consume as a
    public API.
  • Emit analytics for remote plugin installs (#20267)
    ## Summary
    
    - emit `codex_plugin_installed` after a remote plugin install succeeds
    - keep local installs unchanged, but let remote installs override the
    analytics `plugin_id` with the backend remote plugin id
    (`plugins~Plugin_...`)
    - preserve the local/display identity in `plugin_name` and
    `marketplace_name`, plus capability metadata from the installed bundle
    - add regression coverage for local install analytics, remote install
    analytics, and analytics id override serialization
    
    ## Testing
    
    - `just fmt`
    - `cargo test -p codex-analytics`
    - `cargo test -p codex-app-server`
  • feat(tui): add vim composer mode (#18595)
    ## Why
    
    Codex now has configurable TUI keymaps, but the composer still behaves
    like a plain text field. Users who prefer modal editing need a way to
    keep Vim muscle memory while drafting prompts, and the keymap picker
    needs to expose Vim-specific actions if those bindings are configurable
    instead of hardcoded.
    
    ## What Changed
    
    - Adds composer Vim mode with insert/normal state, common normal-mode
    movement and editing commands, `d`/`y` operator-pending flows, and
    mode-aware footer and cursor indicators.
    - Adds `/vim`, an optional global `toggle_vim_mode` binding, and
    `tui.vim_mode_default` so Vim mode can be toggled per session or enabled
    as the default composer state.
    - Extends runtime and config keymaps with `vim_normal` and
    `vim_operator` contexts, exposes those contexts in `/keymap`, refreshes
    the config schema, and validates Vim bindings separately.
    - Integrates Vim normal mode with existing composer behavior: `/` opens
    slash command entry, `!` enters shell mode, `j`/`k` navigate history at
    history boundaries, successful submissions reset back to normal mode,
    and paste burst handling remains insert-mode only.
    - Teaches the TUI render path to apply and restore cursor style so Vim
    insert mode can use a bar cursor without leaving the terminal in that
    state after exit.
    
    ## Validation
    
    - `cargo test -p codex-tui keymap -- --nocapture` on the keymap/Vim
    coverage
    - `cargo insta pending-snapshots`
    
    ## Docs
    
    This introduces user-facing `/vim`, `tui.vim_mode_default`, and Vim
    keymap contexts under `tui.keymap`, so the public CLI configuration and
    slash-command docs should be updated before the feature ships.
  • Bypass review for always-allow MCP tools in auto-review (#20069)
    ## Why
    
    When an MCP or app tool is configured with approval mode `approve`
    (always allow), users expect that decision to be authoritative. In
    guardian auto-review mode, ARC could still return `ask-user`, which then
    routed the approval question into guardian with the ARC reason as
    context. That meant a tool explicitly configured as always allowed still
    went through both safety monitors before running.
    
    This change keeps the existing ARC behavior for non-auto-review
    sessions, but avoids the ARC-to-guardian sequence when
    `approvals_reviewer = auto_review` and the tool approval mode is
    `approve`.
    
    ## What changed
    
    - Short-circuit MCP tool approval handling when `approval_mode ==
    approve` and `approvals_reviewer == auto_review`.
    - Updated the MCP approval regression test so the auto-review case
    asserts neither ARC nor guardian is called.
    - Preserved existing tests that verify ARC can still block always-allow
    MCP tools outside guardian auto-review mode.
    
    ## Verification
    
    - `cargo test -p codex-core --lib mcp_tool_call`
  • fix(tui): set persist_extended_history: false (#20502)
    Large rollouts are no good. This updates the TUI to behave the same as
    the Codex App, which is also turning it off.
  • Sync remote installed plugin bundles (#20268)
    ## Summary
    - Download missing remote installed plugin bundles during app-server
    startup and plugin/list refresh.
    - Upgrade cached remote installed bundles when the backend installed
    version changes.
    - Remove stale remote installed bundle caches without writing remote
    plugin state into config.toml.
    
    ## Review note
    This is a clean PR branch cut from the current diff on top of latest
    `origin/main`. The diff intentionally has no `codex-rs/core/**` files,
    so CODEOWNERS should not request the core-directory owner review from
    stale PR history.
    
    ## Validation
    Already run on the source branch before creating this clean PR:
    - `just fmt`
    - `cargo test -p codex-core-plugins`
    - `cargo test -p codex-app-server --test all
    app_server_startup_sync_downloads_remote_installed_plugin_bundles --
    --nocapture`
    - `cargo test -p codex-app-server --test all
    plugin_list_sync_upgrades_and_removes_remote_installed_plugin_bundles --
    --nocapture`
    - `cargo test -p codex-app-server --test all
    app_server_startup_remote_plugin_sync_runs_once -- --nocapture`
    - `just fix -p codex-core-plugins`
    - `just fix -p codex-app-server`
    - `git diff --check`
  • fix: ignore dangerous project-level config keys (#20098)
    ## Description
    Ignore these top-level config keys when loading project-scoped
    config.toml files:
    ```
        "openai_base_url",
        "chatgpt_base_url",
        "model_provider",
        "model_providers",
        "profile",
        "profiles",
        "experimental_realtime_ws_base_url",
    ```
    
    ## What changed
    
    - Add a project-local config denylist for credential-routing fields such
    as `openai_base_url`, `chatgpt_base_url`, `model_provider`,
    `model_providers`, `profile`, `profiles`, and
    `experimental_realtime_ws_base_url`.
    - Strip those fields from project config layers before they participate
    in effective config merging, while leaving safe project-local settings
    intact.
    - Track ignored project-local keys on config layers and surface a
    startup warning telling users to move those settings to user-level
    `config.toml` if they intentionally need them.
    - Update profile behavior coverage so project-local `profile` /
    `profiles` entries are ignored instead of overriding user-level profile
    selection.
    
    ## Verification
    
    - `cargo test -p codex-config`
    - `cargo test -p codex-core
    project_layer_ignores_unsupported_config_keys`
    - `cargo test -p codex-core project_profiles_are_ignored`
    - `cargo test -p codex-core config::config_loader_tests`
  • [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_