Commit Graph

435 Commits

  • Use ApiPathString in app-server filesystem permission paths (#28367)
    ## Why
    
    Clients running an app-server on one OS and an exec-server on another OS
    need to be able to pass sandbox config to app-server that refers to
    resources on the executor's foreign OS.
    
    ## What
    
    `AbsolutePathBuf` can't represent these paths and we don't want users to
    be exposed to `PathUri` yet, so this moves the public app-server API to
    be expressed in terms of `ApiPathString`.
    
    Stacked on #28165.
    
    - change app-server v2 filesystem permission paths, including legacy
    read/write roots, to `ApiPathString`
    - localize API paths through `PathUri` when converting into the current
    native core permission types
    - make path-bearing permission conversions fallible and surface
    localization failures instead of silently treating malformed grants as
    ordinary denials
    - propagate conversion failures through app-server and TUI approval
    handling
    - regenerate the app-server JSON and TypeScript schemas
    - leave migration TODOs on native-path conversions so they can be
    removed once core permission paths use `PathUri`
  • [2 of 3] Support long pasted text in TUI goals (#27509)
    ## Stack
    
    1. [1 of 3] Support long raw TUI goal objectives - #27508
    2. **[2 of 3] Support long pasted text in TUI goals** - this PR
    3. [3 of 3] Support images in TUI goals - #27510
    
    ## Why
    
    Large text pasted into the TUI composer is represented as a paste
    placeholder plus pending paste metadata. For `/goal`, preserving only
    the visible placeholder is not enough: the agent would see a short
    placeholder string instead of the actual pasted text, and the long-text
    support from the first PR would never see the payload.
    
    The TUI also needs to avoid writing stale sidecar files when a user
    pastes a large block and then deletes its placeholder before submitting
    the goal.
    
    ## What Changed
    
    - Introduces a TUI `GoalDraft` for goal submissions so `/goal`, `/goal
    edit`, and queued goal commands can carry objective text plus text
    elements and pending paste payloads.
    - Materializes active pasted-text placeholders to `pasted-text-N.txt`
    files through the app-server filesystem path introduced in #27508.
    - Rewrites active paste placeholders in the persisted objective to file
    references, while leaving literal placeholder-looking text alone.
    - Filters out deleted paste placeholders so otherwise-small goals do not
    require `$CODEX_HOME` or remote filesystem writes.
    - Preserves pending paste metadata when a `/goal` command is queued
    before a thread exists.
    
    ## Verification
    
    - Added goal materialization tests for active paste placeholders,
    deleted paste placeholders, and whitespace-only paste payloads.
    - Added/updated TUI slash-command tests for large pasted text, queued
    `/goal` commands before thread start, and queued oversized goal
    behavior.
    
    ## Manual Testing
    
    - Used real terminal bracketed-paste sequences through a remote TUI
    session. A 1,228-byte multiline paste became `pasted-text-1.txt`; its
    first/last lines and byte count matched exactly, and the persisted
    objective referenced the server-host path.
    - Pasted a large block, deleted its placeholder, and submitted a small
    replacement objective. No new directory or sidecar file was created.
    - Added two same-length large pastes to one goal. The composer
    disambiguated their visible placeholders, and materialization preserved
    order and contents in `pasted-text-1.txt` and `pasted-text-2.txt`.
    - Submitted a whitespace-only large paste and verified the goal was
    rejected as empty without writing a file.
    - Submitted a pasted-text replacement while another goal was active,
    verified no file was written before confirmation, then canceled and
    confirmed the original goal remained unchanged.
    - Combined a large paste with enough raw text to exceed 4,000 characters
    after placeholder rewriting. The paste sidecar and `goal-objective.md`
    were created in the same remote attachment directory, and `/goal edit`
    restored the rewritten objective with its sidecar reference.
  • Remove TUI realtime voice support (#27801)
    ## Why
    
    Removes the realtime audio support from TUI.
    
    ## What Changed
    
    - Removed the TUI `/realtime` and realtime `/settings` command paths.
    - Deleted TUI voice capture/playback, WebRTC session handling,
    audio-device selection UI, and recording-meter code.
    - Removed TUI realtime tests and snapshots that covered the deleted
    surfaces.
    - Dropped the TUI-only `cpal` and `codex-realtime-webrtc` dependencies
    and refreshed the Rust/Bazel locks.
  • [1 of 3] Support long raw TUI goal objectives (#27508)
    ## Stack
    
    1. **[1 of 3] Support long raw TUI goal objectives** - this PR
    2. [2 of 3] Support long pasted text in TUI goals - #27509
    3. [3 of 3] Support images in TUI goals - #27510
    
    ## Why
    
    `thread/goal/set` limits persisted objective text to 4000 characters.
    The TUI used to reject raw `/goal` objectives above that limit, even
    though the client can make them usable by writing the long text to a
    file and storing a short objective that points at that file.
    
    This also needs to work for remote app-server sessions: filesystem API
    calls must create files on the app-server host, and the stored path must
    be meaningful to the agent on that host.
    
    ## What Changed
    
    - Adds an app-server-host path helper so TUI code can build paths that
    are resolved on the app-server host rather than the TUI host.
    - Adds TUI app-server session helpers for `fs/createDirectory`,
    `fs/writeFile`, `fs/readFile`, and `fs/remove` that work for embedded
    and remote app-server sessions without changing the app-server protocol.
    - Materializes oversized raw `/goal` objectives into
    `$CODEX_HOME/attachments/<uuid>/goal-objective.md` through the
    app-server filesystem APIs, then stores a short, readable objective that
    directs the agent to that file.
    - Reads managed objective files back for `/goal edit`. Other goal UI
    renders the readable stored objective normally, without
    managed-file-specific presentation logic.
    - Recognizes managed references only when they name the expected
    generated file under the app server's reported `$CODEX_HOME`, and cleans
    up newly materialized files when goal replacement or setting does not
    complete.
    
    ## Verification
    
    - Added/updated TUI tests for raw oversized `/goal` submission, large
    inline-paste expansion, queued oversized goals, app-facing
    materialization before `thread/goal/set`, managed-path validation,
    editing, and cleanup.
    - Added/updated app-server-client remote coverage for initialized remote
    Codex home handling.
    
    ## Manual Testing
    
    - Ran the real TUI against a Unix-socket app server with different local
    and server `$CODEX_HOME` directories. Oversized goals wrote only under
    the server home, and persisted references used the server-canonical path
    rather than the TUI path.
    - Exercised 3,999-, 4,000-, and 4,001-character raw objectives. The
    first two stayed inline without new files; the 4,001-character objective
    became a managed objective file.
    - Submitted a larger 8,275-character objective, verified its full
    contents on the app-server host, and observed the goal continuation open
    the referenced server-side file.
    - Opened `/goal edit` for a managed objective and verified the full text
    was restored through remote `fs/readFile`.
    - Submitted an oversized replacement while a goal was active, verified
    no file was written before confirmation, then canceled and confirmed
    that the existing goal and attachment count were unchanged.
  • Remove TUI legacy core test_support dependencies (#27484)
    ## Why
    
    The TUI now sits on the app-server layer, but
    `app-server-client::legacy_core` still exposed core test helpers solely
    for TUI tests. We've been whittling away the remaining dependencies.
    This is the next step on that journey.
    
    There is no functional change — just a refactor, and this affects only
    test code, so it should be low risk.
    
    ## What changed
    
    - remove the `legacy_core::test_support` re-export and call
    model-manager test helpers directly
    - keep the bundled model-preset cache local to TUI test support
    - import constraint types directly from `codex-config`
  • Surface TUI config write error causes (#26537)
    ## Summary
    
    TUI config writes currently wrap app-server failures with local context
    like `config/batchWrite failed in TUI`, but several user-visible paths
    only render the outer error. That hides the actionable app-server
    message, such as validation constraints or read-only `CODEX_HOME`
    failures, leaving users with a dead-end diagnostic.
    
    This change adds a small formatter next to the TUI config write helpers
    that renders the error source chain, then uses it for model persistence,
    feature persistence, project trust, status line writes, hook trust, and
    hook enablement.
    
    Fixes #26077
  • Constrain Windows sandbox requirements (#23766)
    # Why
    
    Managed requirements can already constrain sandbox policy choices, but
    Windows sandbox implementation selection was still resolved
    independently from those requirements. That left the TUI able to
    continue through the unelevated fallback even when an organization wants
    to require the elevated Windows sandbox implementation.
    
    # What
    
    - Add `[windows].allowed_sandbox_implementations` requirements support
    for the Windows `elevated` and `unelevated` implementations.
    - Apply that allowlist during core config resolution so disallowed
    configured or feature-selected Windows sandbox implementations fall back
    to an allowed implementation with the existing requirements warning
    path.
    - Reuse the existing TUI Windows setup prompts to block disallowed
    unelevated continuation, keep required elevated setup in front of the
    user, and refuse to persist a TUI-selected Windows sandbox mode that
    requirements disallow.
    
    # Semantics
    
    | Allowed | Selected | Effective |
    | --- | --- | --- |
    | `["elevated"]` | `unelevated` / unset | `elevated` |
    | `["unelevated"]` | `elevated` / unset | `unelevated` |
    | `["elevated", "unelevated"]` | `elevated` | `elevated` |
    | `["elevated", "unelevated"]` | `unelevated` | `unelevated` |
    | `["elevated", "unelevated"]` | unset | `elevated` |
    
    Availability is handled by interactive setup surfaces after allowlist
    resolution. If the effective elevated implementation is not ready,
    elevated-only requirements block on setup. When unelevated is also
    allowed, the UI may offer the existing unelevated fallback.
    
    ## TUI Screens
    
    If elevated setup is not already complete:
    ```
      Your organization requires the default Codex agent sandbox to continue. Set it up to protect your files and control
      network access.
      Learn more <https://developers.openai.com/codex/windows>
    
    › 1. Set up default sandbox (requires Administrator permissions)
      2. Quit
    ```
    
    If admin setup fails under `["elevated"]`:
    ```
      Couldn't set up your sandbox with Administrator permissions
    
      Your organization requires the default sandbox before Codex can continue.
      Learn more <https://developers.openai.com/codex/windows>
    
    › 1. Try setting up admin sandbox again
      2. Quit
    ```
    
    # Next Steps
    
    
    - extend the requirements/readout surface, such as
    `configRequirements/read`, so clients can inspect the loaded
    `[windows].allowed_sandbox_implementations` requirement instead of
    inferring it from Windows setup state
    - consider extending `windowsSandbox/readiness` as well
    - update the App startup guide, setup flow, and banner surfaces so an
    elevated-only requirement omits any continue-unelevated escape hatch and
    blocks startup until a permitted implementation is ready;
    - preserve the existing unelevated fallback path when requirements allow
    it, including the `["unelevated"]` case where elevated is disallowed
  • Honor client-resolved service tier defaults (#23537)
    ## Why
    
    Model catalog responses can now advertise a nullable
    `default_service_tier` for each model. Codex needs to preserve three
    distinct states all the way from config/app-server inputs to inference:
    
    - no explicit service tier, so the client may apply the current model
    catalog default when FastMode is enabled
    - explicit `default`, meaning the user intentionally wants standard
    routing
    - explicit catalog tier ids such as `priority`, `flex`, or future tiers
    
    Keeping those states distinct prevents the UI from showing one tier
    while core sends another, especially after model switches or app-server
    `thread/start` / `turn/start` updates.
    
    ## What Changed
    
    - Plumbed `default_service_tier` through model catalog protocol types,
    app-server model responses, generated schemas, model cache fixtures, and
    provider/model-manager conversions.
    - Added the request-only `default` service tier sentinel and normalized
    legacy config spelling so `fast` in `config.toml` still materializes as
    the runtime/request id `priority`.
    - Moved catalog default resolution to the TUI/client side, including
    recomputing the effective service tier when model/FastMode-dependent
    surfaces change.
    - Updated app-server thread lifecycle config construction so
    `serviceTier: null` preserves explicit standard-routing intent by
    mapping to `default` instead of internal `None`.
    - Kept core responsible for validating explicit tiers against the
    current model and stripping `default` before `/v1/responses`, without
    applying catalog defaults itself.
    
    ## Validation
    
    - `CARGO_INCREMENTAL=0 cargo build -p codex-cli`
    - `CARGO_INCREMENTAL=0 cargo test -p codex-app-server model_list`
    - `cargo test -p codex-tui service_tier`
    - `cargo test -p codex-protocol service_tier_for_request`
    - `cargo test -p codex-core get_service_tier`
    - `RUST_MIN_STACK=8388608 CARGO_INCREMENTAL=0 cargo test -p codex-core
    service_tier`
  • [2 of 4] tui: route app and skill enablement through app server (#22914)
    ## Why
    App and skill toggles are user config mutations too. When the TUI is
    attached to a remote app server, writing those toggles into the local
    `config.toml` makes the UI report success without updating the server
    that actually owns the session.
    
    This is **[2 of 4]** in a stacked series that moves TUI-owned config
    mutations onto app-server APIs.
    
    ## What changed
    - Routed app enable/disable persistence through app-server config batch
    writes.
    - Routed skill enable/disable persistence through `skills/config/write`.
    - Avoided refreshing local config from disk after these writes when the
    TUI is connected to a remote app server.
    
    ## Config keys affected
    - `apps.<app_id>.enabled`
    - `apps.<app_id>.disabled_reason`
    - `[[skills.config]]` entries keyed by `path`, with `enabled = false`
    used for persisted disables
    
    ## Suggested manual validation
    - Connect the TUI to a remote app server, disable an app, reconnect, and
    confirm the app remains disabled from remote config rather than local
    disk state.
    - Re-enable the same app and confirm both `apps.<app_id>.enabled` and
    `apps.<app_id>.disabled_reason` are cleared remotely.
    - Disable a skill in the manage-skills UI and confirm a remote
    `[[skills.config]]` disable entry appears.
    - Re-enable that skill and confirm the disable entry is removed and the
    effective enabled state updates without relying on local config reloads.
    
    ## Stack
    1. [#22913](https://github.com/openai/codex/pull/22913) `[1 of 4]`
    primary settings writes
    2. [#22914](https://github.com/openai/codex/pull/22914) `[2 of 4]` app
    and skill enablement
    3. [#22915](https://github.com/openai/codex/pull/22915) `[3 of 4]`
    feature and memory toggles
    4. [#22916](https://github.com/openai/codex/pull/22916) `[4 of 4]`
    startup and onboarding bookkeeping
  • tui: pass active permission profiles through app commands (#22891)
    ## Why
    
    This continues the permissions migration by keeping the TUI command
    boundary aligned with the app-server protocol direction from #22795:
    callers should select a permission profile by id instead of passing a
    concrete `PermissionProfile` value around as the turn configuration.
    
    `AppCommand` is internal to the TUI, but it is the path that eventually
    becomes `thread/turn/start`, so carrying concrete profile details there
    made it too easy for UI code to keep relying on the old whole-profile
    replacement model.
    
    ## What changed
    
    - `AppCommand::UserTurn` and `AppCommand::OverrideTurnContext` now carry
    `Option<ActivePermissionProfile>` instead of `PermissionProfile`.
    - Composer submissions copy the active permission profile id from the
    current session snapshot; legacy snapshots intentionally submit no
    active profile id.
    - Permission preset UI events now carry only the active built-in profile
    id. The app derives the concrete built-in `PermissionProfile` internally
    only when updating its local config/status snapshot.
    - Permission presets expose their built-in active profile id, and preset
    selection preserves that id in both the immediate turn override and the
    local TUI config snapshot.
    - Turn routing sends `TurnPermissionsOverride::ActiveProfile` when an
    active id is present, and only falls back to the legacy sandbox
    projection for the remaining runtime override path.
    
    ## How to review
    
    Start with `codex-rs/tui/src/app_command.rs` to verify the command shape
    no longer exposes `PermissionProfile`.
    
    Then read `codex-rs/tui/src/app/thread_routing.rs` to verify the
    app-server turn-start conversion: active ids go through as ids, while
    the legacy sandbox fallback is still constrained to the existing runtime
    override case.
    
    Finally, check `codex-rs/tui/src/chatwidget/permission_popups.rs`,
    `codex-rs/tui/src/app/event_dispatch.rs`,
    `codex-rs/tui/src/app/config_persistence.rs`, and
    `codex-rs/utils/approval-presets/src/lib.rs` to see how preset
    selections stay id-only across TUI events while the local display/config
    mirror still gets a concrete built-in profile.
    
    ## Verification
    
    Latest local verification after the id-only `AppEvent` cleanup:
    
    - `cargo check -p codex-tui --tests`
    - `cargo test -p codex-tui
    permissions_selection_sends_approvals_reviewer_in_override_turn_context`
    - `cargo test -p codex-tui update_feature_flags_enabling_guardian`
    - `cargo test -p codex-utils-approval-presets`
    - `just fmt`
    - `just fix -p codex-tui -p codex-utils-approval-presets`
    
    Earlier in the same PR, before the final event-shape cleanup:
    
    - `cargo test -p codex-tui turn_permissions_`
    - `cargo test -p codex-tui submission_`
    - `cargo test -p codex-tui
    session_configured_syncs_widget_config_permissions_and_cwd`
    - `RUST_MIN_STACK=16777216 cargo test -p codex-tui`
  • Split ChatWidget state into focused modules (#21866)
    ## Summary
    
    `ChatWidget` has been carrying several independent domains in one large
    state bag: transcript bookkeeping, turn lifecycle, queued input, status
    surfaces, connectors, review mode, and protocol dispatch. That makes
    otherwise-local changes hard to reason about because unrelated fields
    and side effects live beside each other in `chatwidget.rs`.
    
    This is the first cleanup PR in a larger decomposition effort. It does
    not try to make `chatwidget.rs` small in one sweep; instead, it
    establishes focused state boundaries that later handler, popup,
    rendering, and effect-synchronization extractions can build on.
    
    This PR keeps `ChatWidget` as the composition layer while moving focused
    state into smaller `codex-tui` modules. The widget still owns effects
    that touch the bottom pane, app events, command submission, redraw
    scheduling, and terminal-title updates.
    
    ## Changes
    
    - Add focused state modules under `codex-rs/tui/src/chatwidget/` for
    input queues, turn lifecycle, transcript bookkeeping, status state,
    connectors, review mode, and app-server protocol dispatch.
    - Update `ChatWidget` to hold grouped state structs and route
    input/lifecycle/status operations through those focused helpers.
    - Move app-server notification dispatch into `chatwidget/protocol.rs`
    while leaving feature handlers and side effects on `ChatWidget`.
    - Replace the large manual `ChatWidget` test literal with the normal
    constructor plus narrow test overrides, so future state moves do not
    require every field to be restated in test setup.
    - Update existing tests to access the new grouped state or narrower
    helpers without changing snapshot behavior.
    
    ## Longer-term direction
    
    Follow-up PRs can continue shrinking `chatwidget.rs` by moving behavior,
    not just state, into focused modules:
    
    - Extract input/submission flow, turn/stream handling, and tool-cell
    lifecycles into domain modules that call the new state reducers.
    - Move popup/settings builders and rendering helpers out of the main
    widget file so `ChatWidget` stays focused on composition.
    - Reduce direct `BottomPane` mutation by applying domain-specific sync
    outputs at clearer boundaries.
  • Validate /goal objective length in TUI (#20746)
    ## Why
    
    Long `/goal` definitions currently reach lower-level goal validation and
    can produce an opaque failure. This bug was reported by a user. Pasted
    instruction blocks are especially confusing because the composer can
    still contain a paste placeholder before expansion, which may otherwise
    fall into the generic prompt-size error path.
    
    There was also a related paste edge case where `/goal ` followed by a
    multiline block whose first pasted line was blank looked like a bare
    `/goal` command. That showed the goal usage/summary instead of setting
    the pasted objective.
    
    ## What Changed
    
    This adds TUI-side preflight validation for `/goal <objective>` using
    the shared `MAX_THREAD_GOAL_OBJECTIVE_CHARS` limit. Oversized typed,
    queued, and pasted goal objectives now fail locally with a goal-specific
    message that recommends putting longer instructions in a file and
    referencing that file from the goal.
    
    The TUI now also lets inline-argument slash commands consume later-line
    arguments before treating the first line as a bare command, so `/goal `
    followed by blank lines and then objective text sets the goal instead of
    opening the bare `/goal` flow.
    
    ## Manual Testing
    
    1. Start the TUI with goals enabled and an active session.
    2. Submit `/goal ` followed by exactly 4,000 objective characters. It
    should continue through the normal goal-setting path.
    3. Submit `/goal ` followed by 4,001 objective characters. It should not
    set a goal, and should show `Goal objective is too long: 4,001
    characters. Limit: 4,000 characters.` followed by the guidance to put
    longer instructions in a file and reference that file from the goal.
    4. Type `/goal `, paste a large block that becomes a `[Pasted Content
    ... chars]` placeholder, then submit. It should validate the expanded
    pasted text and show the goal-specific file guidance rather than the
    generic prompt-size error.
    5. Type `/goal `, paste a multiline block whose first line is blank,
    then submit. It should set the objective from the non-blank pasted
    content instead of showing `Usage: /goal <objective>` or the bare goal
    summary.
    6. While a turn is running, queue an oversized `/goal` command. When the
    queue drains, it should show the same goal-specific error and should not
    emit a goal-setting request.
  • /plugins: add marketplace upgrade flow (#20478)
    This PR adds marketplace upgrade to the `/plugins` menu so users can
    update configured marketplaces. It adds a `Ctrl+U` shortcut on eligible
    marketplace tabs, a loading state, and the app-server request flow
    needed to perform `marketplace/upgrade`. After a successful upgrade, the
    TUI refreshes plugin data, plugin mentions, and user config so updated
    marketplace contents show up across the menu and other plugin surfaces.
    It also preserves the current marketplace tab on no-op and failure paths
    and surfaces backend error details directly in the TUI.
    
    - Add a `Ctrl+U` upgrade option for user-configured marketplace tabs in
    `/plugins`
    - Show the upgrade footer hint only on upgradeable marketplace tabs
    - Show a loading state during `marketplace/upgrade`
    - Surface already-up-to-date and per-marketplace failure results from
    the backend
    - Refresh plugin data, plugin mentions, and user config after successful
    upgrades
    - Add tests and snapshot updates for the shortcut flow, loading state,
    and failure messaging
    
    Steps to test:
    1. Add a `/plugin` marketplace to Codex TUI.
    2. Open `/plugins`, move to that marketplace tab, and confirm the footer
    shows `Ctrl+U` to upgrade.
    3. Press `Ctrl+U` and confirm the popup switches into an upgrade loading
    state.
    4. When the request finishes, confirm you see the expected result:
    updated marketplace contents on success, an already-up-to-date message
    on no-op, or backend error details on failure. On no-op or failure,
    confirm the popup stays on the same marketplace tab.
  • 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`
  • Reduce the surface of collaboration modes (#20149)
    Collaboration modes were slightly invasive both into ThreadManager
    construction and ModelProvider
  • TUI: Remove core protocol dependency [3/7] (#20174)
    ## Why
    
    This is part 3 of a 7-PR stack to remove direct
    `codex_protocol::protocol` usage from `codex-tui` while keeping each
    layer reviewable and shippable.
    
    With `AppCommand` now explicit, the internal app event bus can carry TUI
    commands directly instead of bouncing through core `Op` values.
    
    ## What changed
    
    - Changed `AppEvent::CodexOp` and `AppEvent::SubmitThreadOp` to carry
    `AppCommand`.
    - Updated app-event senders and direct emitters to submit `AppCommand`
    values.
    - Adjusted tests to match `AppCommand` or convert back through
    `into_core()` where they intentionally assert legacy payload equality.
    
    ## Verification
    
    - `cargo test -p codex-tui --no-run`
  • TUI: Remove core protocol dependency [1/7] (#20172)
    ## Why
    
    This is part 1 of a 7-PR stack to remove direct
    `codex_protocol::protocol` usage from `codex-tui` while keeping each
    layer reviewable and shippable.
    
    This first layer reduces the size of the later `chatwidget` diff by
    mechanically moving MCP startup bookkeeping out of the central widget
    file without changing the event shapes or behavior.
    
    ## What changed
    
    - Extracted MCP startup status handling into
    `tui/src/chatwidget/mcp_startup.rs`.
    - Kept the existing core event types in place for this purely mechanical
    move.
    - Updated the MCP startup tests to import the moved test-only event
    types directly.
    
    ## Verification
    
    - `cargo test -p codex-tui chatwidget::tests::mcp_startup`
  • /plugins: add marketplace install flow (#18704)
    This PR adds a new feature to the `/plugins` menu that gives users the
    ability to add new plugin marketplaces. It introduces an Add Marketplace
    tab to the right of installed marketplaces, a source prompt, loading and
    error states, and the app-server request flow needed to perform the
    install. After a successful `marketplace/add`, the popup refreshes back
    into the newly added marketplace tab so the new plugins are immediately
    visible.
    
    - Add an Add Marketplace tab to the `/plugins` menu
    - Prompt for marketplace source input from git repo, URL, or local path
    - Show loading and error states during `marketplace/add`
    - Refresh plugin data after success and switch into the newly added
    marketplace tab
    - Add tests and snapshot updates
  • Show action required in terminal title (#18372)
    Implements #18162
    
    This updates the TUI terminal title to show an explicit action-required
    state when Codex is blocked on user approval or input. The terminal
    title now uses the activity title item to cover both active work and
    blocked-on-user states, while still accepting the legacy spinner config
    value.
    
    Changes
    - Rename the terminal title item from `spinner` to `activity` while
    preserving legacy config compatibility
    - Show `[ ! ] Action Required `while approval or input overlays are
    active, with a blinking `[ . ]` alternate state
    - Suppress the normal working spinner while Codex is blocked on user
    action
    - Add targeted coverage for action-required title behavior and legacy
    title-item parsing
    
    Testing
    - Trigger an approval or input modal and confirm the tab title
    alternates between `[ ! ] Action Required` and `[ . ] Action Required`
    - Disable the activity title item and confirm the action-required title
    does not appear
    - Resolve the prompt and confirm the title returns to the normal
    spinning/idel state
    
    
    https://github.com/user-attachments/assets/e9ecc530-a6be-4fd7-b9a6-d550a790eb2c
  • Add goal TUI UX (5 / 5) (#18077)
    Adds the TUI user experience for goals on top of the core runtime from
    PR 4.
    
    ## Why
    
    Users need a direct TUI control surface for long-running goals. The UI
    should make the current goal visible, support common goal actions
    without waiting for a model turn, and avoid confusing end-of-turn
    notifications while an active goal is immediately continuing.
    
    ## What changed
    
    - Added `/goal` summary rendering for the current goal, including
    active, paused, budget-limited, and complete states.
    - Added `/goal <objective>` creation/replacement through the app-server
    goal API rather than a model prompt.
    - Added `/goal clear`, `/goal pause`, and `/goal unpause` command
    variants.
    - Added a confirmation menu when the user enters a new goal while
    another goal already exists.
    - Updated `/goal` help and summary tip text so it reflects the supported
    command variants without advertising slash-command token budgets.
    - Added footer/statusline goal indicators, including elapsed time and
    token budget display when a budget exists from API/tool-created goals.
    - Consumes goal updated/cleared notifications so the TUI stays in sync
    with external app-server changes.
    - Suppresses end-of-turn desktop notifications only when a goal is still
    active and follow-up work is expected.
    - Preserves slash-command history behavior and avoids leaking queued
    `/goal` state into unrelated submissions.
    
    ## Verification
    
    - Added TUI unit and snapshot coverage for goal command availability,
    summary rendering, control commands, replacement menu behavior,
    status/footer display, notification handling, and command history.
  • permissions: remove legacy read-only access modes (#19449)
    ## Why
    
    `ReadOnlyAccess` was a transitional legacy shape on `SandboxPolicy`:
    `FullAccess` meant the historical read-only/workspace-write modes could
    read the full filesystem, while `Restricted` tried to carry partial
    readable roots. The partial-read model now belongs in
    `FileSystemSandboxPolicy` and `PermissionProfile`, so keeping it on
    `SandboxPolicy` makes every legacy projection reintroduce lossy
    read-root bookkeeping and creates unnecessary noise in the rest of the
    permissions migration.
    
    This PR makes the legacy policy model narrower and explicit:
    `SandboxPolicy::ReadOnly` and `SandboxPolicy::WorkspaceWrite` represent
    the old full-read sandbox modes only. Split readable roots, deny-read
    globs, and platform-default/minimal read behavior stay in the runtime
    permissions model.
    
    ## What changed
    
    - Removes `ReadOnlyAccess` from
    `codex_protocol::protocol::SandboxPolicy`, including the generated
    `access` and `readOnlyAccess` API fields.
    - Updates legacy policy/profile conversions so restricted filesystem
    reads are represented only by `FileSystemSandboxPolicy` /
    `PermissionProfile` entries.
    - Keeps app-server v2 compatible with legacy `fullAccess` read-access
    payloads by accepting and ignoring that no-op shape, while rejecting
    legacy `restricted` read-access payloads instead of silently widening
    them to full-read legacy policies.
    - Carries Windows sandbox platform-default read behavior with an
    explicit override flag instead of depending on
    `ReadOnlyAccess::Restricted`.
    - Refreshes generated app-server schema/types and updates tests/docs for
    the simplified legacy policy shape.
    
    ## Verification
    
    - `cargo check -p codex-app-server-protocol --tests`
    - `cargo check -p codex-windows-sandbox --tests`
    - `cargo test -p codex-app-server-protocol sandbox_policy_`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19449).
    * #19395
    * #19394
    * #19393
    * #19392
    * #19391
    * __->__ #19449
  • Move marketplace add/remove and startup sync out of core. (#19099)
    Move more things to core-plugins.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add safety check notification and error handling (#19055)
    Adds a new app-server notification that fires when a user account has
    been flagged for potential safety reasons.
  • feat(auto-review) short-circuit (#18890)
    ## Summary
    Short circuit the convo if auto-review hits too many denials
    
    ## Testing
    - [x] Added unit tests
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • /statusline & /title - Shared preview values (#18435)
    This PR makes the `/statusline` and `/title` setup UIs share one
    preview-value source instead of each surface using its own examples.
    Both pickers now render consistent live values when available, and
    stable placeholders when they are not. It also resolves live preview
    values at the shared preview-item layer, so `/title` preview can use
    real runtime values for title-specific cases like status text, task
    progress, and project-name fallback behavior.
    
    - Adds a shared preview data model for status surfaces
    - Maps status-line items and terminal-title items onto that shared
    preview list
    - Feeds both setup views from the same chatwidget-derived preview data,
    with terminal-title-specific formatting applied before `/title` preview
    renders
    - Keeps project-root preview aligned with status-line behavior while
    project in /title keeps its title fallback/truncation behavior
    - Adds snapshot coverage for live-only, hardcoded-only, and mixed cases
    
    Test Steps
    - Open Codex TUI and launch `/statusline`.
    - Toggle and reorder items, then verify the preview uses current session
    values when possible, and placeholder values for missing values (ex: no
    thread ID).
    - Open `/title` and verify it shows the same normalized values,
    including live status/task-progress values when available.
  • Remove simple TUI legacy_core reexports (#18631)
    ## Problem
    The TUI still imported path utilities and config-loader symbols through
    app-server-client's legacy_core facade even though those APIs already
    exist in utility/config crates. This is part of our ongoing effort to
    whittle away at these old dependencies.
    
    ## Solution
    Rewire imports to avoid the TUI directly importing from the core crate
    and instead import from common lower-level crates. This PR doesn't
    include any functional changes; it's just a simple rewiring.
  • Add verbose diagnostics for /mcp (#18610)
    Fixes #18539.
    
    ## Summary
    The recent `/mcp` performance work kept the default command fast by
    avoiding resource and resource-template inventory probes, but it also
    removed useful diagnostics for users trying to confirm MCP server state.
    
    This keeps bare `/mcp` on the fast tools/auth path and adds `/mcp
    verbose` for the slower diagnostic view. Verbose mode requests full MCP
    server status from the app-server and restores status, resources, and
    resource templates in the TUI output.
    
    ## Testing
    In addition to running automation, I manually tested the feature to
    confirm that it works.
  • [codex] Add workspace owner usage nudge UI (#18221)
    ## Summary
    
    Third PR in the split from #17956. Stacked on #18220.
    
    - shows workspace-owner/member-specific rate-limit messages behind
    `workspace_owner_usage_nudge`
    - prompts workspace members to notify the owner or request a usage-limit
    increase
    - sends the confirmed nudge through the app-server API and renders
    completion feedback
    - adds focused TUI snapshot coverage for prompts and completion states
    - feature gate
    
    ## Validation
    
    - `cargo test -p codex-backend-client`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server rate_limits`
    - `cargo test -p codex-tui workspace_`
    - `cargo test -p codex-tui status_`
    - `just fmt`
    - `just fix -p codex-backend-client`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-app-server`
    - `just fix -p codex-tui`
  • TUI: remove simple legacy_core re-exports (#18605)
    ## Summary
    
    The TUI still imported several symbols through the transitional
    app-server-client `legacy_core` facade even though those symbols are
    already owned by smaller crates. This PR narrows that facade by rewiring
    those imports directly to their owner crates.
    
    ## Changes
    
    No functional changes, just import rewiring. This is part of our ongoing
    effort to whittle away at the `legacy_core` namespace, which represents
    all of the remaining symbols that the TUI imports from the core.
  • Add /side conversations (#18190)
    The TUI supports long-running turns and agent threads, but quick side
    questions have required interrupting the main flow or manually
    forking/navigating threads. This PR adds a guarded `/side` flow so users
    can ask brief side-conversation questions in an ephemeral fork while
    keeping the primary thread focused. This also helps address the feature
    request in #18125.
    
    The implementation creates one side conversation at a time, lets `/side`
    open either an empty side thread or immediately submit `/side
    <question>`, and returns to the parent with Esc or Ctrl+C. Side
    conversations get hidden developer guardrails that treat inherited
    history as reference-only and steer the model away from workspace
    mutations unless explicitly requested in the side conversation.
    
    The TUI hides most slash commands while side mode is active, leaving
    only `/copy`, `/diff`, `/mention`, and `/status` available there.
  • Queue slash and shell prompts in the TUI (#18542)
    ## Why
    
    Users have asked to queue follow-up slash commands while a task is
    running, including in #14081, #14588, #14286, and #13779. The previous
    TUI behavior validated slash commands immediately, so commands that are
    only meaningful once the current turn is idle could not be queued
    consistently.
    
    The queue should preserve what the user typed and defer command parsing
    until the item is actually dispatched. This also gives `/fast`, `/review
    ...`, `/rename ...`, `/model`, `/permissions`, and similar slash
    workflows the same FIFO behavior as plain queued prompts.
    
    ## What Changed
    
    - Added a queued-input action enum so queued items can be dispatched as
    plain prompts, slash commands, or user shell commands.
    - Changed `Tab` queueing to accept slash-led prompts without validating
    them up front, then parse and dispatch them when dequeued.
    - Added `!` shell-command queueing for `Tab` while a task is running,
    while preserving existing `Enter` behavior for immediate shell
    execution.
    - Moved queued slash dispatch through shared slash-command parsing so
    inline commands, unavailable commands, unknown commands, and local
    config commands report at dequeue time.
    - Continued queue draining after local-only actions and after slash menu
    cancellation or selection when no task is running.
    - Preserved slash-popup completion behavior so `/mo<Tab>` completes to
    `/model ` instead of queueing the prefix.
    - Updated pending-input preview snapshots to show queued follow-up
    inputs.
    
    ## Verification
    
    I did a bunch of manual validation (and found and fixed a few bugs along
    the way).
  • feat: Budget skill metadata and surface trimming as a warning (#18298)
    Cap the model-visible skills section to a small share of the context
    window, with a fallback character budget, and keep only as many implicit
    skills as fit within that budget.
    
    Emit a non-fatal warning when enabled skills are omitted, and add a new
    app-server warning notification
    
    Record thread-start skill metrics for total enabled skills, kept skills,
    and whether truncation happened
    
    ---------
    
    Co-authored-by: Matthew Zeng <mzeng@openai.com>
    Co-authored-by: Codex <noreply@openai.com>
  • TUI: enforce core boundary (#17399)
    Problem: The TUI still depended on `codex-core` directly in a number of
    places, and we had no enforcement from keeping this problem from getting
    worse.
    
    Solution: Route TUI core access through
    `codex-app-server-client::legacy_core`, add CI enforcement for that
    boundary, and re-export this legacy bridge inside the TUI as
    `crate::legacy_core` so the remaining call sites stay readable. There is
    no functional change in this PR — just changes to import targets.
    
    Over time, we can whittle away at the remaining symbols in this legacy
    namespace with the eventual goal of removing them all. In the meantime,
    this linter rule will prevent us from inadvertently importing new
    symbols from core.
  • Revert "Option to Notify Workspace Owner When Usage Limit is Reached" (#17391)
    Reverts openai/codex#16969
    
    #sev3-2026-04-10-accountscheckversion-500s-for-openai-workspace-7300
  • fix(guardian, app-server): introduce guardian review ids (#17298)
    ## Description
    
    This PR introduces `review_id` as the stable identifier for guardian
    reviews and exposes it in app-server `item/autoApprovalReview/started`
    and `item/autoApprovalReview/completed` events.
    
    Internally, guardian rejection state is now keyed by `review_id` instead
    of the reviewed tool item ID. `target_item_id` is still included when a
    review maps to a concrete thread item, but it is no longer overloaded as
    the review lifecycle identifier.
    
    ## Motivation
    
    We'd like to give users the ability to preempt a guardian review while
    it's running (approve or decline).
    
    However, we can't implement the API that allows the user to override a
    running guardian review because we didn't have a unique `review_id` per
    guardian review. Using `target_item_id` is not correct since:
    - with execve reviews, there can be multiple execve calls (and therefore
    guardian reviews) per shell command
    - with network policy reviews, there is no target item ID
    
    The PR that actually implements user overrides will use `review_id` as
    the stable identifier.
  • Option to Notify Workspace Owner When Usage Limit is Reached (#16969)
    ## Summary
    - Replace the manual `/notify-owner` flow with an inline confirmation
    prompt when a usage-based workspace member hits a credits-depleted
    limit.
    - Fetch the current workspace role from the live ChatGPT
    `accounts/check/v4-2023-04-27` endpoint so owner/member behavior matches
    the desktop and web clients.
    - Keep owner, member, and spend-cap messaging distinct so we only offer
    the owner nudge when the workspace is actually out of credits.
    
    ## What Changed
    - `backend-client`
    - Added a typed fetch for the current account role from
    `accounts/check`.
      - Mapped backend role values into a Rust workspace-role enum.
    - `app-server` and protocol
      - Added `workspaceRole` to `account/read` and `account/updated`.
    - Derived `isWorkspaceOwner` from the live role, with a fallback to the
    cached token claim when the role fetch is unavailable.
    - `tui`
      - Removed the explicit `/notify-owner` slash command.
    - When a member is blocked because the workspace is out of credits, the
    error now prompts:
    - `Your workspace is out of credits. Request more from your workspace
    owner? [y/N]`
      - Choosing `y` sends the existing owner-notification request.
    - Choosing `n`, pressing `Esc`, or accepting the default selection
    dismisses the prompt without sending anything.
    - Selection popups now honor explicit item shortcuts, which is how the
    `y` / `n` interaction is wired.
    
    ## Reviewer Notes
    - The main behavior change is scoped to usage-based workspace members
    whose workspace credits are depleted.
    - Spend-cap reached should not show the owner-notification prompt.
    - Owners and admins should continue to see `/usage` guidance instead of
    the member prompt.
    - The live role fetch is best-effort; if it fails, we fall back to the
    existing token-derived ownership signal.
    
    ## Testing
    - Manual verification
      - Workspace owner does not see the member prompt.
    - Workspace member with depleted credits sees the confirmation prompt
    and can send the nudge with `y`.
    - Workspace member with spend cap reached does not see the
    owner-notification prompt.
    
    ### Workspace member out of usage
    
    https://github.com/user-attachments/assets/341ac396-eff4-4a7f-bf0c-60660becbea1
    
    ### Workspace owner
    <img width="1728" height="1086" alt="Screenshot 2026-04-09 at 11 48
    22 AM"
    src="https://github.com/user-attachments/assets/06262a45-e3fc-4cc4-8326-1cbedad46ed6"
    />
  • Update guardian output schema (#17061)
    ## Summary
    - Update guardian output schema to separate risk, authorization,
    outcome, and rationale.
    - Feed guardian rationale into rejection messages.
    - Split the guardian policy into template and tenant-config sections.
    
    ## Validation
    - `cargo test -p codex-core mcp_tool_call`
    - `env -u CODEX_SANDBOX_NETWORK_DISABLED INSTA_UPDATE=always cargo test
    -p codex-core guardian::`
    
    ---------
    
    Co-authored-by: Owen Lin <owen@openai.com>
  • Use model metadata for Fast Mode status (#16949)
    Fast Mode status was still tied to one model name in the TUI and
    model-list plumbing. This changes the model metadata shape so a model
    can advertise additional speed tiers, carries that field through the
    app-server model list, and uses it to decide when to show Fast Mode
    status.
    
    For people using Codex, the behavior is intended to stay the same for
    existing models. Fast Mode still requires the existing signed-in /
    feature-gated path; the difference is that the UI can now recognize any
    model the model list marks as Fast-capable, instead of requiring a new
    client-side slug check.
  • Codex/windows bazel rust test coverage no rs (#16528)
    # Why this PR exists
    
    This PR is trying to fix a coverage gap in the Windows Bazel Rust test
    lane.
    
    Before this change, the Windows `bazel test //...` job was nominally
    part of PR CI, but a non-trivial set of `//codex-rs/...` Rust test
    targets did not actually contribute test signal on Windows. In
    particular, targets such as `//codex-rs/core:core-unit-tests`,
    `//codex-rs/core:core-all-test`, and `//codex-rs/login:login-unit-tests`
    were incompatible during Bazel analysis on the Windows gnullvm platform,
    so they never reached test execution there. That is why the
    Cargo-powered Windows CI job could surface Windows-only failures that
    the Bazel-powered job did not report: Cargo was executing those tests,
    while Bazel was silently dropping them from the runnable target set.
    
    The main goal of this PR is to make the Windows Bazel test lane execute
    those Rust test targets instead of skipping them during analysis, while
    still preserving `windows-gnullvm` as the target configuration for the
    code under test. In other words: use an MSVC host/exec toolchain where
    Bazel helper binaries and build scripts need it, but continue compiling
    the actual crate targets with the Windows gnullvm cfgs that our current
    Bazel matrix is supposed to exercise.
    
    # Important scope note
    
    This branch intentionally removes the non-resource-loading `.rs` test
    and production-code changes from the earlier
    `codex/windows-bazel-rust-test-coverage` branch. The only Rust source
    changes kept here are runfiles/resource-loading fixes in TUI tests:
    
    - `codex-rs/tui/src/chatwidget/tests.rs`
    - `codex-rs/tui/tests/manager_dependency_regression.rs`
    
    That is deliberate. Since the corresponding tests already pass under
    Cargo, this PR is meant to test whether Bazel infrastructure/toolchain
    fixes alone are enough to get a healthy Windows Bazel test signal,
    without changing test behavior for Windows timing, shell output, or
    SQLite file-locking.
    
    # How this PR changes the Windows Bazel setup
    
    ## 1. Split Windows host/exec and target concerns in the Bazel test lane
    
    The core change is that the Windows Bazel test job now opts into an MSVC
    host platform for Bazel execution-time tools, but only for `bazel test`,
    not for the Bazel clippy build.
    
    Files:
    
    - `.github/workflows/bazel.yml`
    - `.github/scripts/run-bazel-ci.sh`
    - `MODULE.bazel`
    
    What changed:
    
    - `run-bazel-ci.sh` now accepts `--windows-msvc-host-platform`.
    - When that flag is present on Windows, the wrapper appends
    `--host_platform=//:local_windows_msvc` unless the caller already
    provided an explicit `--host_platform`.
    - `bazel.yml` passes that wrapper flag only for the Windows `bazel test
    //...` job.
    - The Bazel clippy job intentionally does **not** pass that flag, so
    clippy stays on the default Windows gnullvm host/exec path and continues
    linting against the target cfgs we care about.
    - `run-bazel-ci.sh` also now forwards `CODEX_JS_REPL_NODE_PATH` on
    Windows and normalizes the `node` executable path with `cygpath -w`, so
    tests that need Node resolve the runner's Node installation correctly
    under the Windows Bazel test environment.
    
    Why this helps:
    
    - The original incompatibility chain was mostly on the **exec/tool**
    side of the graph, not in the Rust test code itself. Moving host tools
    to MSVC lets Bazel resolve helper binaries and generators that were not
    viable on the gnullvm exec platform.
    - Keeping the target platform on gnullvm preserves cfg coverage for the
    crates under test, which is important because some Windows behavior
    differs between `msvc` and `gnullvm`.
    
    ## 2. Teach the repo's Bazel Rust macro about Windows link flags and
    integration-test knobs
    
    Files:
    
    - `defs.bzl`
    - `codex-rs/core/BUILD.bazel`
    - `codex-rs/otel/BUILD.bazel`
    - `codex-rs/tui/BUILD.bazel`
    
    What changed:
    
    - Replaced the old gnullvm-only linker flag block with
    `WINDOWS_RUSTC_LINK_FLAGS`, which now handles both Windows ABIs:
      - gnullvm gets `-C link-arg=-Wl,--stack,8388608`
    - MSVC gets `-C link-arg=/STACK:8388608`, `-C
    link-arg=/NODEFAULTLIB:libucrt.lib`, and `-C link-arg=ucrt.lib`
    - Threaded those Windows link flags into generated `rust_binary`,
    unit-test binaries, and integration-test binaries.
    - Extended `codex_rust_crate(...)` with:
      - `integration_test_args`
      - `integration_test_timeout`
    - Used those new knobs to:
    - mark `//codex-rs/core:core-all-test` as a long-running integration
    test
      - serialize `//codex-rs/otel:otel-all-test` with `--test-threads=1`
    - Added `src/**/*.rs` to `codex-rs/tui` test runfiles, because one
    regression test scans source files at runtime and Bazel does not expose
    source-tree directories unless they are declared as data.
    
    Why this helps:
    
    - Once host-side MSVC tools are available, we still need the generated
    Rust test binaries to link correctly on Windows. The MSVC-side
    stack/UCRT flags make those binaries behave more like their Cargo-built
    equivalents.
    - The integration-test macro knobs avoid hardcoding one-off test
    behavior in ad hoc BUILD rules and make the generated test targets more
    expressive where Bazel and Cargo have different runtime defaults.
    
    ## 3. Patch `rules_rs` / `rules_rust` so Windows MSVC exec-side Rust and
    build scripts are actually usable
    
    Files:
    
    - `MODULE.bazel`
    - `patches/rules_rs_windows_exec_linker.patch`
    - `patches/rules_rust_windows_bootstrap_process_wrapper_linker.patch`
    - `patches/rules_rust_windows_build_script_runner_paths.patch`
    - `patches/rules_rust_windows_exec_msvc_build_script_env.patch`
    - `patches/rules_rust_windows_msvc_direct_link_args.patch`
    - `patches/rules_rust_windows_process_wrapper_skip_temp_outputs.patch`
    - `patches/BUILD.bazel`
    
    What these patches do:
    
    - `rules_rs_windows_exec_linker.patch`
    - Adds a `rust-lld` filegroup for Windows Rust toolchain repos,
    symlinked to `lld-link.exe` from `PATH`.
      - Marks Windows toolchains as using a direct linker driver.
      - Supplies Windows stdlib link flags for both gnullvm and MSVC.
    - `rules_rust_windows_bootstrap_process_wrapper_linker.patch`
    - For Windows MSVC Rust targets, prefers the Rust toolchain linker over
    an inherited C++ linker path like `clang++`.
    - This specifically avoids the broken mixed-mode command line where
    rustc emits MSVC-style `/NOLOGO` / `/LIBPATH:` / `/OUT:` arguments but
    Bazel still invokes `clang++.exe`.
    - `rules_rust_windows_build_script_runner_paths.patch`
    - Normalizes forward-slash execroot-relative paths into Windows path
    separators before joining them on Windows.
    - Uses short Windows paths for `RUSTC`, `OUT_DIR`, and the build-script
    working directory to avoid path-length and quoting issues in third-party
    build scripts.
    - Exposes `RULES_RUST_BAZEL_BUILD_SCRIPT_RUNNER=1` to build scripts so
    crate-local patches can detect "this is running under Bazel's
    build-script runner".
    - Fixes the Windows runfiles cleanup filter so generated files with
    retained suffixes are actually retained.
    - `rules_rust_windows_exec_msvc_build_script_env.patch`
    - For exec-side Windows MSVC build scripts, stops force-injecting
    Bazel's `CC`, `CXX`, `LD`, `CFLAGS`, and `CXXFLAGS` when that would send
    GNU-flavored tool paths/flags into MSVC-oriented Cargo build scripts.
    - Rewrites or strips GNU-only `--sysroot`, MinGW include/library paths,
    stack-protector, and `_FORTIFY_SOURCE` flags on the MSVC exec path.
    - The practical effect is that build scripts can fall back to the Visual
    Studio toolchain environment already exported by CI instead of crashing
    inside Bazel's hermetic `clang.exe` setup.
    - `rules_rust_windows_msvc_direct_link_args.patch`
    - When using a direct linker on Windows, stops forwarding GNU driver
    flags such as `-L...` and `--sysroot=...` that `lld-link.exe` does not
    understand.
    - Passes non-`.lib` native artifacts as explicit `-Clink-arg=<path>`
    entries when needed.
    - Filters C++ runtime libraries to `.lib` artifacts on the Windows
    direct-driver path.
    - `rules_rust_windows_process_wrapper_skip_temp_outputs.patch`
    - Excludes transient `*.tmp*` and `*.rcgu.o` files from process-wrapper
    dependency search-path consolidation, so unstable compiler outputs do
    not get treated as real link search-path inputs.
    
    Why this helps:
    
    - The host-platform split alone was not enough. Once Bazel started
    analyzing/running previously incompatible Rust tests on Windows, the
    next failures were in toolchain plumbing:
    - MSVC-targeted Rust tests were being linked through `clang++` with
    MSVC-style arguments.
    - Cargo build scripts running under Bazel's Windows MSVC exec platform
    were handed Unix/GNU-flavored path and flag shapes.
    - Some generated paths were too long or had path-separator forms that
    third-party Windows build scripts did not tolerate.
    - These patches make that mixed Bazel/Cargo/Rust/MSVC path workable
    enough for the test lane to actually build and run the affected crates.
    
    ## 4. Patch third-party crate build scripts that were not robust under
    Bazel's Windows MSVC build-script path
    
    Files:
    
    - `MODULE.bazel`
    - `patches/aws-lc-sys_windows_msvc_prebuilt_nasm.patch`
    - `patches/ring_windows_msvc_include_dirs.patch`
    - `patches/zstd-sys_windows_msvc_include_dirs.patch`
    
    What changed:
    
    - `aws-lc-sys`
    - Detects Bazel's Windows MSVC build-script runner via
    `RULES_RUST_BAZEL_BUILD_SCRIPT_RUNNER` or a `bazel-out` manifest-dir
    path.
    - Uses `clang-cl` for Bazel Windows MSVC builds when no explicit
    `CC`/`CXX` is set.
    - Allows prebuilt NASM on the Bazel Windows MSVC path even when `nasm`
    is not available directly in the runner environment.
    - Avoids canonicalizing `CARGO_MANIFEST_DIR` in the Bazel Windows MSVC
    case, because that path may point into Bazel output/runfiles state where
    preserving the given path is more reliable than forcing a local
    filesystem canonicalization.
    - `ring`
    - Under the Bazel Windows MSVC build-script runner, copies the
    pregenerated source tree into `OUT_DIR` and uses that as the
    generated-source root.
    - Adds include paths needed by MSVC compilation for
    Fiat/curve25519/P-256 generated headers.
    - Rewrites a few relative includes in C sources so the added include
    directories are sufficient.
    - `zstd-sys`
    - Adds MSVC-only include directories for `compress`, `decompress`, and
    feature-gated dictionary/legacy/seekable sources.
    - Skips `-fvisibility=hidden` on MSVC targets, where that
    GCC/Clang-style flag is not the right mechanism.
    
    Why this helps:
    
    - After the `rules_rust` plumbing started running build scripts on the
    Windows MSVC exec path, some third-party crates still failed for
    crate-local reasons: wrong compiler choice, missing include directories,
    build-script assumptions about manifest paths, or Unix-only C compiler
    flags.
    - These crate patches address those crate-local assumptions so the
    larger toolchain change can actually reach first-party Rust test
    execution.
    
    ## 5. Keep the only `.rs` test changes to Bazel/Cargo runfiles parity
    
    Files:
    
    - `codex-rs/tui/src/chatwidget/tests.rs`
    - `codex-rs/tui/tests/manager_dependency_regression.rs`
    
    What changed:
    
    - Instead of asking `find_resource!` for a directory runfile like
    `src/chatwidget/snapshots` or `src`, these tests now resolve one known
    file runfile first and then walk to its parent directory.
    
    Why this helps:
    
    - Bazel runfiles are more reliable for explicitly declared files than
    for source-tree directories that happen to exist in a Cargo checkout.
    - This keeps the tests working under both Cargo and Bazel without
    changing their actual assertions.
    
    # What we tried before landing on this shape, and why those attempts did
    not work
    
    ## Attempt 1: Force `--host_platform=//:local_windows_msvc` for all
    Windows Bazel jobs
    
    This did make the previously incompatible test targets show up during
    analysis, but it also pushed the Bazel clippy job and some unrelated
    build actions onto the MSVC exec path.
    
    Why that was bad:
    
    - Windows clippy started running third-party Cargo build scripts with
    Bazel's MSVC exec settings and crashed in crates such as `tree-sitter`
    and `libsqlite3-sys`.
    - That was a regression in a job that was previously giving useful
    gnullvm-targeted lint signal.
    
    What this PR does instead:
    
    - The wrapper flag is opt-in, and `bazel.yml` uses it only for the
    Windows `bazel test` lane.
    - The clippy lane stays on the default Windows gnullvm host/exec
    configuration.
    
    ## Attempt 2: Broaden the `rules_rust` linker override to all Windows
    Rust actions
    
    This fixed the MSVC test-lane failure where normal `rust_test` targets
    were linked through `clang++` with MSVC-style arguments, but it broke
    the default gnullvm path.
    
    Why that was bad:
    
    -
    `@@rules_rs++rules_rust+rules_rust//util/process_wrapper:process_wrapper`
    on the gnullvm exec platform started linking with `lld-link.exe` and
    then failed to resolve MinGW-style libraries such as `-lkernel32`,
    `-luser32`, and `-lmingw32`.
    
    What this PR does instead:
    
    - The linker override is restricted to Windows MSVC targets only.
    - The gnullvm path keeps its original linker behavior, while MSVC uses
    the direct Windows linker.
    
    ## Attempt 3: Keep everything on pure Windows gnullvm and patch the V8 /
    Python incompatibility chain instead
    
    This would have preserved a single Windows ABI everywhere, but it is a
    much larger project than this PR.
    
    Why that was not the practical first step:
    
    - The original incompatibility chain ran through exec-side generators
    and helper tools, not only through crate code.
    - `third_party/v8` is already special-cased on Windows gnullvm because
    `rusty_v8` only publishes Windows prebuilts under MSVC names.
    - Fixing that path likely means deeper changes in
    V8/rules_python/rules_rust toolchain resolution and generator execution,
    not just one local CI flag.
    
    What this PR does instead:
    
    - Keep gnullvm for the target cfgs we want to exercise.
    - Move only the Windows test lane's host/exec platform to MSVC, then
    patch the build-script/linker boundary enough for that split
    configuration to work.
    
    ## Attempt 4: Validate compatibility with `bazel test --nobuild ...`
    
    This turned out to be a misleading local validation command.
    
    Why:
    
    - `bazel test --nobuild ...` can successfully analyze targets and then
    still exit 1 with "Couldn't start the build. Unable to run tests"
    because there are no runnable test actions after `--nobuild`.
    
    Better local check:
    
    ```powershell
    bazel build --nobuild --keep_going --host_platform=//:local_windows_msvc //codex-rs/login:login-unit-tests //codex-rs/core:core-unit-tests //codex-rs/core:core-all-test
    ```
    
    # Which patches probably deserve upstream follow-up
    
    My rough take is that the `rules_rs` / `rules_rust` patches are the
    highest-value upstream candidates, because they are fixing generic
    Windows host/exec + MSVC direct-linker behavior rather than
    Codex-specific test logic.
    
    Strong upstream candidates:
    
    - `patches/rules_rs_windows_exec_linker.patch`
    - `patches/rules_rust_windows_bootstrap_process_wrapper_linker.patch`
    - `patches/rules_rust_windows_build_script_runner_paths.patch`
    - `patches/rules_rust_windows_exec_msvc_build_script_env.patch`
    - `patches/rules_rust_windows_msvc_direct_link_args.patch`
    - `patches/rules_rust_windows_process_wrapper_skip_temp_outputs.patch`
    
    Why these seem upstreamable:
    
    - They address general-purpose problems in the Windows MSVC exec path:
      - missing direct-linker exposure for Rust toolchains
      - wrong linker selection when rustc emits MSVC-style args
    - Windows path normalization/short-path issues in the build-script
    runner
      - forwarding GNU-flavored CC/link flags into MSVC Cargo build scripts
      - unstable temp outputs polluting process-wrapper search-path state
    
    Potentially upstreamable crate patches, but likely with more care:
    
    - `patches/zstd-sys_windows_msvc_include_dirs.patch`
    - `patches/ring_windows_msvc_include_dirs.patch`
    - `patches/aws-lc-sys_windows_msvc_prebuilt_nasm.patch`
    
    Notes on those:
    
    - The `zstd-sys` and `ring` include-path fixes look fairly generic for
    MSVC/Bazel build-script environments and may be straightforward to
    propose upstream after we confirm CI stability.
    - The `aws-lc-sys` patch is useful, but it includes a Bazel-specific
    environment probe and CI-specific compiler fallback behavior. That
    probably needs a cleaner upstream-facing shape before sending it out, so
    upstream maintainers are not forced to adopt Codex's exact CI
    assumptions.
    
    Probably not worth upstreaming as-is:
    
    - The repo-local Starlark/test target changes in `defs.bzl`,
    `codex-rs/*/BUILD.bazel`, and `.github/scripts/run-bazel-ci.sh` are
    mostly Codex-specific policy and CI wiring, not generic rules changes.
    
    # Validation notes for reviewers
    
    On this branch, I ran the following local checks after dropping the
    non-resource-loading Rust edits:
    
    ```powershell
    cargo test -p codex-tui
    just --shell 'C:\Program Files\Git\bin\bash.exe' --shell-arg -lc -- fix -p codex-tui
    python .\tools\argument-comment-lint\run-prebuilt-linter.py -p codex-tui
    just --shell 'C:\Program Files\Git\bin\bash.exe' --shell-arg -lc fmt
    ```
    
    One local caveat:
    
    - `just argument-comment-lint` still fails on this Windows machine for
    an unrelated Bazel toolchain-resolution issue in
    `//codex-rs/exec:exec-all-test`, so I used the direct prebuilt linter
    for `codex-tui` as the local fallback.
    
    # Expected reviewer takeaway
    
    If this PR goes green, the important conclusion is that the Windows
    Bazel test coverage gap was primarily a Bazel host/exec toolchain
    problem, not a need to make the Rust tests themselves Windows-specific.
    That would be a strong signal that the deleted non-resource-loading Rust
    test edits from the earlier branch should stay out, and that future work
    should focus on upstreaming the generic `rules_rs` / `rules_rust`
    Windows fixes and reducing the crate-local patch surface.
  • remove temporary ownership re-exports (#16626)
    Stacked on #16508.
    
    This removes the temporary `codex-core` / `codex-login` re-export shims
    from the ownership split and rewrites callsites to import directly from
    `codex-model-provider-info`, `codex-models-manager`, `codex-api`,
    `codex-protocol`, `codex-feedback`, and `codex-response-debug-context`.
    
    No behavior change intended; this is the mechanical import cleanup layer
    split out from the ownership move.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Remove codex-core config type shim (#16529)
    ## Why
    
    This finishes the config-type move out of `codex-core` by removing the
    temporary compatibility shim in `codex_core::config::types`. Callers now
    depend on `codex-config` directly, which keeps these config model types
    owned by the config crate instead of re-expanding `codex-core` as a
    transitive API surface.
    
    ## What Changed
    
    - Removed the `codex-rs/core/src/config/types.rs` re-export shim and the
    `core::config::ApprovalsReviewer` re-export.
    - Updated `codex-core`, `codex-cli`, `codex-tui`, `codex-app-server`,
    `codex-mcp-server`, and `codex-linux-sandbox` call sites to import
    `codex_config::types` directly.
    - Added explicit `codex-config` dependencies to downstream crates that
    previously relied on the `codex-core` re-export.
    - Regenerated `codex-rs/core/config.schema.json` after updating the
    config docs path reference.
  • Fix TUI app-server permission profile conversions (#16284)
    Addresses #16283
    
    Problem: TUI app-server permission approvals could drop filesystem
    grants because request and response payloads were round-tripped through
    mismatched camelCase and snake_case JSON shapes.
    Solution: Replace the lossy JSON round-trips with typed app-server/core
    permission conversions so requested and granted permission profiles,
    including filesystem paths and scope, are preserved end to end.
  • fix(guardian): make GuardianAssessmentEvent.action strongly typed (#16448)
    ## Description
    
    Previously the `action` field on `EventMsg::GuardianAssessment`, which
    describes what Guardian is reviewing, was typed as an arbitrary JSON
    blob. This PR cleans it up and defines a sum type representing all the
    various actions that Guardian can review.
    
    This is a breaking change (on purpose), which is fine because:
    - the Codex app / VSCE does not actually use `action` at the moment
    - the TUI code that consumes `action` is updated in this PR as well
    - rollout files that serialized old `EventMsg::GuardianAssessment` will
    just silently drop these guardian events
    - the contract is defined as unstable, so other clients have a fair
    warning :)
    
    This will make things much easier for followup Guardian work.
    
    ## Why
    
    The old guardian review payloads worked, but they pushed too much shape
    knowledge into downstream consumers. The TUI had custom JSON parsing
    logic for commands, patches, network requests, and MCP calls, and the
    app-server protocol was effectively just passing through an opaque blob.
    
    Typing this at the protocol boundary makes the contract clearer.
  • Fix stale /status rate limits in active TUI sessions (#16201)
    Fix stale weekly limit in `/status` (#16194): /status reused the
    session’s cached rate-limit snapshot, so the weekly remaining limit
    could stay frozen within an active session.
    
    With this change, we now dynamically update the rate limits after status
    is displayed.
    
    I needed to delete a few low-value test cases from the chatWidget tests
    because the test.rs file is really large, and the new tests in this PR
    pushed us over the 512K mandated limit. I'm working on a separate PR to
    refactor that test file.
  • Refactor chatwidget tests into topical modules (#16361)
    Problem: `chatwidget/tests.rs` had grown into a single oversized test
    blob that was hard to maintain and exceeded the repo's blob size limit.
    
    Solution: split the chatwidget tests into topical modules with a thin
    root `tests.rs`, shared helper utilities, preserved snapshot naming, and
    hermetic test config so the refactor stays stable and passes the
    `codex-tui` test suite.
  • Route TUI /feedback submission through the app server (#16184)
    The TUI’s `/feedback` flow was still uploading directly through the
    local feedback crate, which bypassed app-server behavior such as
    auth-derived feedback tags like chatgpt_user_id and made TUI feedback
    handling diverge from other clients. It also meant that remove TUI
    sessions failed to upload the correct feedback logs and session details.
    
    Testing: Manually tested `/feedback` flow and confirmed that it didn't
    regress.
  • [codex] Normalize Windows path in MCP startup snapshot test (#16204)
    ## Summary
    A Windows-only snapshot assertion in the app-server MCP startup warning
    test compared the raw rendered path, so CI saw `C:\tmp\project` instead
    of the normalized `/tmp/project` snapshot fixture.
    
    ## Fix
    Route that snapshot assertion through the existing
    `normalize_snapshot_paths(...)` helper so the test remains
    platform-stable.
  • Fix app-server TUI MCP startup warnings regression (#16041)
    This addresses #16038
    
    The default `tui_app_server` path stopped surfacing MCP startup failures
    during cold start, even though the legacy TUI still showed warnings like
    `MCP startup incomplete (...)`. The app-server bridge emitted per-server
    startup status notifications, but `tui_app_server` ignored them, so
    failed MCP handshakes could look like a clean startup.
    
    This change teaches `tui_app_server` to consume MCP startup status
    notifications, preserve the immediate per-server failure warning, and
    synthesize the same aggregate startup warning the legacy TUI shows once
    startup settles.
  • Remove TUI voice transcription feature (#16114)
    Removes the partially-completed TUI composer voice transcription flow,
    including its feature flag, app events, and hold-to-talk state machine.