Commit Graph

160 Commits

  • feat(tui): reland token activity command (#27925)
    ## Why
    
    [#25345](https://github.com/openai/codex/pull/25345) was approved,
    green, and squash-merged into its stacked base branch,
    `fcoury/tokenmaxxing-api`. Four minutes later, that base branch was
    force-pushed back to an API-only rebased head while preparing
    [#25344](https://github.com/openai/codex/pull/25344) for `main`. As a
    result, the squash commit from #25345 was orphaned and the TUI command
    never reached `main` or a release.
    
    This PR relands the orphaned TUI change from
    [`411410b8`](https://github.com/openai/codex/commit/411410b85c2d8eb050d441f17396c5c4048d866f)
    on current `main`.
    
    ## What changed
    
    - Add `/usage`, `/usage daily`, `/usage weekly`, and `/usage cumulative`
    for account token activity.
    - Fetch account usage asynchronously through the existing
    `account/usage/read` app-server RPC.
    - Render daily, weekly, and cumulative activity with theme-aware
    terminal palettes and bounded transient cards.
    - Preserve transcript ordering while assistant streams, history
    consolidations, active cells, and hooks complete.
    - Hide `/usage` from completion when backend auth is unavailable while
    keeping typed-command guidance.
    - Carry current-main behavior forward for cwd-aware Markdown parsing,
    Windows Terminal color detection, and personal access token auth.
    - Clear pending usage cards on thread rollback and delay completed cards
    until live hook output is committed.
    - Add focused regression and snapshot coverage for loading, auth errors,
    invalid views, rollback, hook ordering, layout, and charts.
    
    ## Prior review
    
    The original implementation was approved by Eric Traut in #25345 after
    testing multiple themes and light/dark terminals. This PR preserves that
    reviewed implementation while adapting it to current `main` and adding
    regression coverage for newer rollback and hook lifecycle behavior.
    
    ## Validation
    
    - `just test -p codex-tui token_activity palette renderable
    usage_command` — 37 passed.
    - Focused rollback, hook-ordering, and error snapshot tests — 4 passed.
    - `just fix -p codex-tui` — passed.
    - `UV_CACHE_DIR=/private/tmp/codex-uv-cache just fmt` — passed.
    - `cargo insta pending-snapshots` — no pending snapshots.
    - `just test -p codex-tui` — 2,870 passed; two unrelated guardian
    feature-flag tests failed because their expected `OverrideTurnContext`
    event was absent:
    -
    `update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`
    -
    `update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
    - `just argument-comment-lint` could not complete because the local
    Bazel LLVM `compiler-rt` repository is missing `include/sanitizer/*.h`.
    The touched Rust diff was manually inspected and no missing
    opaque-literal argument comments were found.
  • [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.
  • [codex] restore source-specific import copy (#27703)
    ## Summary
    
    - restore source-specific wording across the `/import` picker, lifecycle
    messages, diagnostics, and help text
    - update the matching Unix and Windows snapshots
    - leave import behavior unchanged
    
    ## Why
    
    The import path currently supports one source, so the UI should identify
    that source directly instead of presenting the flow as
    provider-agnostic.
    
    ## Validation
    
    - `just test -p codex-tui external_agent_config_migration` (12 passed)
    - `just fix -p codex-tui`
    - `just fmt`
  • Add session delete commands in CLI and TUI (#27476)
    ## Summary
    
    The app server exposes `thread/delete`, but users cannot invoke it from
    the CLI or TUI. Because deletion is irreversible, the user-facing
    commands need deliberate confirmation and safer handling of name-based
    targets.
    
    - Add `codex delete <SESSION>` with interactive confirmation,
    restricting `--force` to UUID targets.
    - Resolve exact names across active and archived sessions, including
    renamed sessions, and validate prompted UUID targets before
    confirmation.
    - Add a `/delete` command with a confirmation popup that warns the
    current session and its subagent threads will be permanently deleted.
    
    ## Manual testing
    
    - Deleted by UUID with `--force` and verified the rollout, session-index
    entry, and database row were removed.
    - Exercised name-based confirmation for both cancellation and
    affirmative deletion; cancellation preserved the session and
    confirmation removed it.
    - Verified deletion refuses to proceed without `--force`, while
    `--force` rejects names, including duplicate names.
    - Verified duplicate-name confirmation displays the concrete UUID
    selected.
    - Deleted an archived session by name.
    - Verified an already-missing UUID fails before displaying a
    confirmation prompt.
    - Exercised `/delete` in the TUI: the popup defaults to No, cancellation
    preserves the session, and confirmation deletes the session and exits.
    - Verified that `codex delete` works for both archived and non-archived
    sessions.
  • [codex] add /import for external agents (#27071)
    ## Why
    
    External-agent import should be discoverable and deliberate without
    blocking startup or claiming the public `codex [PROMPT]` CLI namespace.
    The slash command keeps the flow local to the interactive TUI and reuses
    the existing app-server import API.
    
    ## What changed
    
    - add the user-facing `/import` slash command
    - detect external-agent importable items only when the command is
    invoked
    - run imports through the embedded local app-server
    - show start and completion messages, refresh configuration, and block
    duplicate imports while one is pending
    - reject the flow for unsupported remote and local-daemon sessions
    
    ## Validation
    
    - `just test -p codex-tui external_agent_config_migration` (10 passed)
    - manually exercised an isolated TUI fixture with existing
    external-agent setup and session data using a fresh `CODEX_HOME`
    - verified picker customization, plugin and session detection, import
    completion, repeated invocation, and imported-session resume context
    - the broader `just test -p codex-tui` run passed 2,805 tests, with 2
    unrelated guardian feature-flag failures and 4 skipped tests
    
    ## Draft follow-ups
    
    - review whether completion messaging should remain attached to the
    initiating chat if the user switches chats during an import
    - review shutdown semantics for an in-progress background import
    
    ## Stack
    
    1. [#27064](https://github.com/openai/codex/pull/27064): remove the
    startup migration flow
    2. [#27065](https://github.com/openai/codex/pull/27065): extract the
    picker renderer
    3. [#27070](https://github.com/openai/codex/pull/27070): add the
    external-agent import picker UX
    4. [#27071](https://github.com/openai/codex/pull/27071): expose the flow
    through `/import`
    
    **This PR is stack item 4.** Draft while the lower stack dependencies
    are reviewed.
  • feat(tui): restore output-free cancelled prompts (#25316)
    ## TL;DR
    
    When you press Esc or Ctrl+C after sending a prompt but before any
    output was rendering, it restores the last composer and the message.
    
    ## Summary
    
    Cancelling a prompt immediately after submission should behave like
    returning to edit that prompt, not like discarding the user's draft.
    Today, pressing `Esc` or `Ctrl+C` before Codex responds leaves the
    submitted prompt in the transcript and returns an empty composer,
    forcing the user to recall or retype it.
    
    When an interrupted turn has not produced substantive visible output,
    restore its submitted prompt directly into the composer and roll back
    that latest turn. This also covers the first prompt in a fresh thread,
    before the TUI has retained a local user-history cell. The restored
    draft keeps its text, image attachments, and active collaboration mode
    so it can be edited and resubmitted in place.
    
    Restoration is intentionally suppressed once the turn has produced
    user-visible activity such as assistant output, tool work, hooks, or
    patches. A transient thinking status does not make the prompt
    ineligible. Rollback also rebuilds terminal scrollback from the retained
    transcript cells so repeated cancellations and terminal resizes do not
    duplicate history.
    
    ## How to Test
    
    1. Start the TUI with `cargo run -p codex-cli --bin codex`.
    2. In a fresh thread, submit the first prompt and press `Esc` before
    Codex emits substantive output. Confirm that the prompt returns to the
    composer for editing and its submitted transcript row is removed.
    3. Repeat with `Ctrl+C`, then repeat after at least one completed turn.
    Confirm the same behavior.
    4. Submit a prompt, wait for assistant output or tool activity, then
    cancel. Confirm that the transcript remains intact and the prompt is not
    restored into the composer.
    5. Cancel several output-free prompts and resize the terminal between
    attempts. Confirm that the startup banner, tip, and transcript history
    do not duplicate in scrollback.
    
    Targeted tests:
    - `just test -p codex-tui cancelled_turn_edit_restores_prompt`
    - `just test -p codex-tui
    output_free_interrupted_turn_requests_prompt_restore`
    - `just test -p codex-tui
    visible_output_prevents_cancelled_turn_prompt_restore`
    - `just test -p codex-tui
    thinking_status_keeps_cancelled_turn_prompt_restore_eligible`
    - `just test -p codex-tui
    patch_activity_prevents_cancelled_turn_prompt_restore`
    
    The full `just test -p codex-tui` run completed with `2746` passing
    tests and two unrelated existing guardian feature-flag failures. `just
    argument-comment-lint` remains blocked locally by the existing Bazel
    LLVM `compiler-rt` sanitizer-header glob failure; the touched Rust diff
    was manually audited for positional literal comments.
  • Add /archive slash command (#25027)
    ## Why
    
    TUI users can archive saved sessions from other surfaces, but there is
    no in-session command for archiving the active session. Since archiving
    the active session also exits the TUI, the command should ask for
    explicit confirmation instead of firing immediately.
    
    I'm also working on [a companion
    PR](https://github.com/openai/codex/pull/25021) that adds `codex
    archive` and `codex unarchive` top-level CLI commands.
    
    ## What changed
    
    - Adds a new `/archive` slash command described as `archive this session
    and exit`.
    - Shows a confirmation dialog with `No, don't archive` selected first
    and `Yes, archive and exit` as the explicit action.
    - On confirmation, calls the existing `thread/archive` app-server RPC
    for the active main session and exits after success.
    - Keeps `/archive` disabled while a task is running and unavailable in
    side conversations.
    
    ## Verification
    
    Added focused TUI coverage for the `/archive` confirmation flow,
    disabled-while-task-running behavior, and the `/ar` slash-command popup
    snapshot.
  • Use thread config for TUI MCP inventory (#24532)
    ## Summary
    `/mcp` in the TUI should reflect the current loaded thread, including
    project-local MCP servers from that thread config. Before this change,
    `mcpServerStatus/list` only read the latest global MCP config, so the
    active chat could miss project-local servers.
    
    This adds optional `threadId` to `mcpServerStatus/list`. When present,
    app-server resolves the loaded thread and lists MCP status from the
    refreshed effective config for that thread; when omitted, existing
    global config behavior stays unchanged.
    
    The TUI now sends the active chat thread id for `/mcp` and `/mcp
    verbose`, carries that origin through the async inventory result, and
    ignores stale completions if the user has switched threads before the
    fetch returns. The app-server schemas were regenerated.
    
    ## Follow-up
    Once this app-server API change lands, the desktop app should make the
    same `threadId` plumbing so its MCP inventory also uses the current
    thread config.
    
    Fixes #23874
  • tui: plumb permission profile selection (#23708)
    ## Why
    
    The named-profile `/permissions` picker needs a small TUI action path
    that can select permission profiles without folding the menu UI and
    profile metadata into the same review.
    
    ## What changed
    
    - Carry permission-profile selections through the TUI app event flow.
    - Persist selected profiles while preserving the existing approval
    settings and guardrail prompts.
    - Keep the legacy `/permissions` picker behavior in this layer; the
    profile-mode menu stays in the follow-up PR.
    
    ## Stack
    
    1. [#22931](https://github.com/openai/codex/pull/22931):
    runtime/session/network propagation for active permission profiles.
    2. **This PR**: TUI selection plumbing and guardrail flow.
    3. [#21559](https://github.com/openai/codex/pull/21559): profile-aware
    `/permissions` menu and custom profile display.
    
    <img width="1632" height="1186" alt="image"
    src="https://github.com/user-attachments/assets/69ddcd5e-b57c-468d-8c1d-246916323c15"
    />
    
    ## Validation
    
    - `git diff --cached --check` before commit.
    - Full test run skipped at the user request while pushing the split
    stack.
  • [2 of 2] Start fresh TUI thread in background (#23176)
    ## Why
    
    After the terminal-probe work in #23175, fresh-session startup still
    waits for `thread/start` before the chat input can become usable. The
    chat widget already has the machinery to hold early submissions until a
    session is configured, so fresh `thread/start` does not need to stay on
    the input-ready hot path.
    
    Refs #16335.
    
    ## What
    
    This PR starts fresh app-server threads in a background task, reports
    completion through a startup app event, and attaches the primary session
    once `thread/start` returns. Resume and fork startup paths remain
    synchronous.
    
    ## Benchmark
    
    In the local pty startup benchmark, this PR's pre-optimization base
    branch, #23175, measured about 152ms median from launch to accepted chat
    input. The stacked result measured about 66ms median, for an approximate
    additional savings of 85-95ms. For broader context, the original `main`
    baseline before either startup optimization was about 250.5ms median. We
    also measured Codex 0.117.0 on the same machine at about 64.6ms median,
    so the stacked branch is back in the old-startup-time range.
    
    ## Stack
    
    1. [#23175: [1 of 2] Optimize TUI startup terminal
    probes](https://github.com/openai/codex/pull/23175) — base PR
    2. [#23176: [2 of 2] Start fresh TUI thread in
    background](https://github.com/openai/codex/pull/23176) — this PR
    
    ## Verification
    
    - `cargo test -p codex-tui`
  • [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`
  • Remove resurrected /collab slash command (#22535)
    ## Summary
    `/collab` was intentionally removed in
    [#12012](https://github.com/openai/codex/pull/12012), but the
    TUI/app-server migration accidentally brought that slash-command path
    back. This restores the earlier product decision so the TUI no longer
    advertises or dispatches `/collab`. This command was redundant because
    it did the same thing as `/plan` but in a less-intuitive way.
    
    ## What Changed
    - Remove `SlashCommand::Collab` from the TUI slash-command surface.
    - Delete the picker and app-event plumbing that only existed to service
    `/collab`.
    - Remove obsolete TUI test coverage for the deleted picker flow.
  • feat(tui): add ambient terminal pets (#21206)
    ## Why
    
    The Codex App has animated pets, but the TUI had no equivalent ambient
    companion surface. This brings that experience into terminal Codex while
    keeping the main chat flow usable: the pet should feel present, but it
    cannot cover transcript text, composer input, approvals, or picker
    content.
    
    The feature also needs to be terminal-aware. Different terminals support
    different image protocols, tmux can interfere with image rendering, and
    some users will want pets disabled entirely or anchored differently
    depending on their layout.
    
    <table>
    <tr><td>
    <img width="4110" height="2584" alt="CleanShot 2026-05-05 at 12 41
    45@2x"
    src="https://github.com/user-attachments/assets/68a1fcbc-2104-48d6-b834-69c6aaa95cdf"
    />
    <p align="center">macOS - Ghostty, iTerm2 and WezTerm with Custom
    Pet</p>
    </td></tr>
    <tr><td>
    ![Uploading CleanShot 2026-05-10 at 20.28.30.png…]()
    <p align="center">Windows Terminal</p>
    </td></tr>
    <tr><td>
    <img width="3902" height="2752" alt="CleanShot 2026-05-05 at 12 39
    02@2x"
    src="https://github.com/user-attachments/assets/300e2931-6b00-467e-91cb-ab8e28470500"
    />
    <p align="center">Linux - WezTerm and Ghostty</p>
    </td></tr>
    </table>
    
    ## What Changed
    
    - Add a TUI ambient pet renderer in `codex-rs/tui/src/pets/`.
    - Port the app-style pet animation states so the sprite changes with
    task status, waiting-for-input states, review/ready states, and
    failures.
    - Add `/pets` selection UI with a preview pane, loading state, built-in
    pet choices, and a first-row `Disable terminal pets` option.
    - Download built-in pet spritesheets on demand from the same public CDN
    path already used by Android, under
    `https://persistent.oaistatic.com/codex/pets/v1/...`, and cache them
    locally under `~/.codex/cache/tui-pets/`.
    - Keep custom pets local.
    - Add config support for pet selection, disabling pets, and choosing
    whether the pet follows the composer bottom or anchors to the terminal
    bottom.
    - Reserve layout space around the pet so transcript wrapping, live
    responses, and composer input do not render underneath the sprite.
    - Gate image rendering by terminal capability, disable image pets under
    tmux, and support both Kitty Graphics and SIXEL terminals.
    - Add redraw cleanup for terminal image artifacts, including sixel cell
    clearing.
    
    ## Current Scope
    
    - This is an initial TUI version of ambient pets, not full App parity.
    - It focuses on ambient sprite rendering, `/pets` selection, custom
    pets, terminal capability gating, and on-demand CDN-backed built-in
    assets.
    - The ambient text overlay is currently disabled, so the TUI renders the
    pet sprite without extra status text beside it.
    
    ## How to Test
    
    1. Start Codex TUI in a terminal with image support.
    2. Run `/pets`.
    3. Confirm the picker shows built-in pets plus custom pets, and the
    first item is `Disable terminal pets`.
    4. On a fresh `~/.codex/cache/tui-pets/`, move onto a built-in pet and
    confirm the first preview downloads the spritesheet from the shared
    Codex pets CDN and renders successfully.
    5. Move through the pet list and confirm subsequent built-in previews
    use the local cache.
    6. Select a pet, then send and receive messages. Confirm transcript and
    composer text wrap before the pet instead of rendering underneath the
    sprite.
    7. Change the pet anchor setting and confirm the pet can either follow
    the composer bottom or sit at the terminal bottom.
    8. Return to `/pets`, choose `Disable terminal pets`, and confirm the
    sprite disappears cleanly.
    
    Targeted tests:
    - `cargo test -p codex-tui ambient_pet_`
    - `cargo test -p codex-tui
    resize_reflow_wraps_transcript_early_when_pet_is_enabled`
    - `cargo insta pending-snapshots`
  • Fix goal update and add /goal edit command in TUI (#21954)
    ## Why
    
    Users have requested the ability to edit a goal's objective after a goal
    has been created. This PR exposes a new `/goal edit` command in the TUI
    to address this request.
    
    In the process of implementing this, I also noticed an existing bug in
    the goal runtime. When a goal's objective is updated through the
    `thread/goal/set` app server API, the goal runtime didn't emit a new
    steering prompt to tell the agent about the new objective. This PR also
    fixes this hole.
    
    ## What Changed
    
    - Adds `/goal edit` in the TUI, opening an edit box prefilled with the
    current goal objective.
    - Keeps active and paused goals in their current state, resets completed
    goals to active, keeps budget-limited goals budget-limited, and
    preserves the existing token budget.
    - Changes the existing `thread/goal/set` behavior so editing an
    objective preserves goal accounting instead of resetting it. The older
    reset-on-new-objective behavior was left over from before
    `thread/goal/clear`; clients that need to reset accounting can now clear
    the existing goal and create a new one.
    - Reuses the existing goal set API path; this does not add or change
    app-server protocol surface area.
    - Adds a dedicated goal runtime steering prompt when an externally
    persisted goal mutation changes the objective, so active turns receive
    the updated objective.
    
    ## Validation
    
    - Make sure `/goal edit` returns an error if no goal currently exists
    - Make sure `/goal edit` displays an edit box that can be optionally
    canceled with no side effects
    - Make sure that an edited goal results in a steer so the agent starts
    pursuing the new objective
    - Make sure the new objective is reflected in the goal if you use
    `/goal` to display the goal summary
    - Make sure that `/goal edit` doesn't reset the token budget, time/token
    accounting on the updated goal
  • feat(tui): render responsive Markdown tables in TUI (#22052)
    ## Why
    
    The TUI currently treats Markdown tables as ordinary wrapped text, which
    makes table-heavy responses hard to read and brittle across narrow panes
    and terminal resizes.
    
    This change teaches the TUI to render Markdown tables responsively while
    preserving the raw Markdown source needed to re-render streamed and
    finalized transcript content after width changes. The goal is to keep
    tables legible during streaming, after resize, and once a turn has
    finished, without corrupting scrollback ordering.
    
    ## What Changed
    
    - add table detection and responsive table rendering in the Markdown
    renderer
    - render standard tables with Unicode box-drawing borders when the pane
    is wide enough
    - add a vertical readability fallback for constrained or dense tables so
    narrow panes still show each row clearly
    - keep links and `<br>` content inside table cells instead of leaking
    text outside the table
    - avoid table normalization inside fenced or indented code blocks
    - preserve raw streamed Markdown source and keep the active table as a
    mutable tail until finalization
    - consolidate finalized streamed content into source-backed transcript
    cells so post-resize re-rendering stays correct
    - add snapshot and targeted streaming/resize regression coverage for the
    new table behavior
    
    ## How to Test
    
    1. Start Codex TUI from this branch.
    2. Paste this exact prompt:
    `This is a session to test codex, no need to do any thinking, just end
    different markdown tables, with columns exploring different markdown
    contents, like links, bold italic, code, etc. Make them different sizes,
    some 30+ rows, some not and intertwine them with some paragraphs with
    complex formatting as well.`
    3. Confirm the response includes several Markdown tables mixed with
    richly formatted paragraphs.
    4. Confirm wide-enough tables render with box-drawing borders instead of
    plain wrapped pipe text.
    5. Resize the terminal narrower while the answer is still streaming and
    confirm the in-progress table stays coherent instead of duplicating
    headers or leaving broken scrollback behind.
    6. Resize again after the turn finishes and confirm the finalized
    transcript re-renders cleanly at the new width.
    7. In a narrow pane, verify dense tables fall back to the vertical
    per-row layout instead of producing unreadable wrapped columns.
    8. Also verify pipe-heavy fenced code blocks still render as code, not
    as tables.
    
    Targeted tests:
    - `cargo test -p codex-tui table_readability_fallback --no-fail-fast`
    - `cargo test -p codex-tui markdown_render --no-fail-fast`
    - `cargo test -p codex-tui streaming::controller --no-fail-fast`
    - `cargo test -p codex-tui table_resize_lifecycle --no-fail-fast`
    
    ## Docs
    
    No developer docs update appears necessary.
  • Improve hooks trust flow in TUI (#21755)
    # Why
    Hooks that need trust review were easy to miss, and the existing TUI
    flow made users discover `/hooks` manually before they could decide
    whether to inspect or trust them.
    
    # What
    - add a startup review prompt for new or changed hooks before normal
    composer use
    - add a top-level `t` shortcut in `/hooks` to trust every review-needed
    hook at once
    - make pending-review rows and helper copy use warning styling
    
    ## TUI
    
    ### Startup review interstitial
    
    ```text
    Hooks need review
    2 hooks are new or changed.
    Hooks can run outside the sandbox after you trust them.
    
    › 1. Review hooks
      2. Trust all and continue
      3. Continue without trusting (hooks won't run)
    ```
    
    ### Top-level `/hooks` page when review is needed
    
    ```text
    Hooks
    Lifecycle hooks from config and enabled plugins.
    
    ⚠ 1 hook needs review before it can run.
    
    Event                 Installed   Active   Review   Description
    PreToolUse            1           0        1        Before a tool executes
    ...
    
    Press t to trust all; enter to review hooks; esc to close
    ```
  • [codex] Generalize service tier slash commands (#21745)
    ## Why
    
    `/fast` was wired as a one-off slash command even though model metadata
    now exposes service tiers as catalog data. That meant adding another
    tier, such as a slower/cheaper tier, would require more hardcoded TUI
    plumbing instead of letting the model catalog drive the available
    commands.
    
    This change makes service-tier commands data-driven: each advertised
    `service_tiers` entry becomes a `/name` command using the catalog
    description, while the request path sends the tier `id` only when the
    selected model supports it.
    
    ## What Changed
    
    - Removed the hardcoded `/fast` slash-command variant and introduced
    dynamic service-tier command items in the composer and command popup.
    - Added toggle behavior for service-tier commands: invoking `/name`
    selects that tier, and invoking it again clears the selection.
    - Preserved the existing Fast-mode keybinding/status affordances by
    resolving the current model tier whose name is `fast`, while still
    sending the tier request value such as `priority`.
    - Persisted service-tier selections as raw request strings so non-fast
    tiers can round-trip through config.
    - Updated the Bedrock catalog entry to advertise fast support through
    `service_tiers` with `id: "priority"` and `name: "fast"`.
    - Added defensive filtering in core so unsupported selected service
    tiers are omitted from `/responses` requests.
    
    ## Validation
    
    - Added/updated coverage for dynamic service-tier slash command lookup,
    popup descriptions, composer dispatch, TUI fast toggling, and
    unsupported-tier omission in core request construction.
    - Local tests were not run per request.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Move message history out of core (#21278)
    ## Why
    
    Message history was implemented inside `codex-core` and surfaced through
    core protocol ops and `SessionConfiguredEvent` fields even though the
    current consumer is TUI-local prompt recall. That made core own UI
    history persistence and exposed `history_log_id` / `history_entry_count`
    through surfaces that app-server and other clients do not need.
    
    This change moves message history persistence out of core and keeps the
    recall plumbing local to the TUI.
    
    ## What changed
    
    - Added a new `codex-message-history` crate for appending, looking up,
    trimming, and reading metadata from `history.jsonl`.
    - Removed core protocol history ops/events: `AddToHistory`,
    `GetHistoryEntryRequest`, and `GetHistoryEntryResponse`.
    - Removed `history_log_id` and `history_entry_count` from
    `SessionConfiguredEvent` and updated exec/MCP/test fixtures accordingly.
    - Updated the TUI to dispatch local app events for message-history
    append/lookup and keep its persistent-history metadata in TUI session
    state.
    
    ## Validation
    
    - `cargo test -p codex-message-history -p codex-protocol`
    - `cargo test -p codex-exec event_processor_with_json_output`
    - `cargo test -p codex-mcp-server outgoing_message`
    - `cargo test -p codex-tui`
    - `just fix -p codex-message-history -p codex-protocol -p codex-core -p
    codex-tui -p codex-exec -p codex-mcp-server`
  • hook trust metadata and enforcement (#20321)
    # Why
    
    We want shared hook trust that both the app and the TUI can build on,
    but the metadata is only useful if runtime behavior agrees with it. This
    PR adds a single backend trust model for hooks so unmanaged hooks cannot
    run until the current definition has been reviewed, while managed hooks
    remain runnable and non-configurable.
    
    # What
    
    - persist `trusted_hash` alongside hook state in `config.toml`
    - expose `currentHash` and derived `trustStatus` through `hooks/list`
    - derive trust from normalized hook definitions so equivalent hooks from
    `config.toml` and `hooks.json` share the same trust identity
    - gate unmanaged hooks on trust before they enter the runnable handler
    set
    
    # Reviewer Notes
    
    - key file to review is `codex-rs/hooks/src/engine/discovery.rs`
    - the only **core** change is schema related
  • feat(tui): add raw scrollback mode (#20819)
    ## Why
    
    Granular copy is particularly difficult with the current output. Part of
    it was solved with the introduction of the `/copy` command but when you
    only need to copy parts of a response, you still encounter some issues:
    
    - When you copy a paragraph, the result is a sequence of separate lines
    instead of one correctly joined paragraph.
    - When a word wraps, part of it stays on the original line and the rest
    appears at the start of the next line.
    - When you copy a long command, extra line breaks are often inserted,
    and command arguments can be split across multiple lines.
    
    
    https://github.com/user-attachments/assets/0ef85c84-9363-4aad-b43a-15fce062a443
    
    ## Solution
    
    Now that we own the scrollback and we re-create it when we resize, we
    have the opportunity of toggling between the raw text and the rich text
    we see today.
    
    - Add TUI raw scrollback mode with `tui.raw_output_mode`, `/raw
    [on|off]`, and the configurable `tui.keymap.global.toggle_raw_output`
    action.
    - Render transcript cells through rich/raw-aware paths so raw mode
    preserves source text and lets the terminal soft-wrap selection-friendly
    output.
    - Bind raw-mode toggle to `alt-r` by default, with the keybinding path
    toggling silently while `/raw` continues to emit confirmation messages.
    
    ## Related Issues
    
    Likely addressed by raw mode:
    
    - #12200: clean copy for multiline and soft-wrapped output. Raw mode
    removes Codex-inserted wrapping/indentation and lets the terminal
    soft-wrap logical lines.
    - #9252: command suggestions gain unwanted leading spaces when copied.
    Raw mode renders transcript text without the rich-mode left
    padding/gutter.
    - #8258: prompt output is hard to copy because of leading indentation.
    Raw mode renders user/source-backed transcript text without that
    decorative indentation.
    
    Partially or conditionally addressed:
    
    - #2880: copy/export message as Markdown. Raw mode exposes raw Markdown
    for terminal selection, but this PR does not add a dedicated
    export/copy-message command.
    - #19820: mouse drag selection + copy in the TUI. Raw mode improves
    terminal-native selection of output/history text, but this PR does not
    implement in-TUI mouse selection, highlighting, auto-copy, or composer
    selection.
    - #18979: copied content is divided into two parts. This should improve
    cases caused by Codex-inserted wraps/padding in rendered output; if the
    report is about pasting into the composer/input path, that remains
    outside this PR.
    
    ## Validation
    
    - `just write-config-schema`
    - `just fmt`
    - `cargo test -p codex-config`
    - `cargo test -p codex-tui`
    - `just fix -p codex-tui`
    - `just argument-comment-lint`
    - `cargo test -p codex-tui
    raw_output_mode_can_change_without_inserting_notice -- --nocapture`
    - `cargo test -p codex-tui
    raw_slash_command_toggles_and_accepts_on_off_args -- --nocapture`
    - `cargo test -p codex-tui raw_output_toggle -- --nocapture`
    - `git diff --check`
    - `cargo insta pending-snapshots`
  • feat(tui): add PR summary statusline items (#20892)
    ## Why?
    
    The Codex App already exposes branch and PR context in its
    branch-details UI. This brings the same context into the CLI footer as
    opt-in statusline items, so users can choose the extra signal without
    making the default footer busier.
    
    ## What?
    
    Add optional `pull-request-number` and `branch-changes` items to the
    configurable TUI status line.
    
    - `pull-request-number` shows the open PR for the current checkout and
    renders as a clickable terminal hyperlink when OSC 8 links are
    supported.
    - `branch-changes` shows committed additions/deletions against the
    repository default branch, or `No changes` when the branch has no
    committed diff.
    
    <img width="1257" height="261" alt="CleanShot 2026-05-03 at 20 44 15"
    src="https://github.com/user-attachments/assets/10b4380b-c3e9-4729-9ee1-3f742068fa47"
    />
    
    ## Architecture
    
    This follows the same client/app-server split as the Codex App: the TUI
    owns presentation, caching, and optional rendering, while
    workspace-sensitive `git` and `gh` discovery runs through app-server.
    
    The new TUI-local `workspace_command` layer sends bounded,
    non-interactive `command/exec` requests to the active app-server. That
    makes the implementation remote-friendly: the TUI does not decide
    whether commands run in an embedded local workspace or a remote
    workspace, and it does not bypass app-server sandbox or permission
    policy.
    
    The branch summary logic stays internal to `codex-tui` because this PR
    only needs TUI statusline behavior. The command boundary is still
    isolated behind `WorkspaceCommandExecutor`, so the lookup code can be
    lifted or reused later without changing statusline rendering.
    
    ## How?
    
    - Add a TUI `WorkspaceCommandExecutor` abstraction backed by app-server
    `command/exec`.
    - Add branch summary probes for:
      - current branch name,
      - open PR metadata,
      - committed branch diff stats against the default branch.
    - Prefer remote-tracking default branch refs for diff stats, avoiding
    stale or absent local `main` branches.
    - Resolve PRs with `gh pr view` first, then fall back to
    commit-associated PR lookup across parent/fork repos.
    - Add `/statusline` picker entries, preview values, rendering, and OSC 8
    clickable PR links.
    - Keep all probes best-effort so missing `git`, missing `gh`, auth
    failures, or non-git directories hide optional items instead of
    surfacing footer errors.
    
    ## Validation
    
    - `cargo test -p codex-tui branch_summary -- --nocapture`
    - Snapshot coverage for the `/statusline` preview/setup rendering paths
    - Hyperlink rendering coverage for clickable PR statusline cells
  • feat(tui): add keymap debug inspector (#20794)
    ## Why
    
    We constantly get bug reports about keys not being recognized by Codex
    when the terminal is not handling the key press. Running `/keymap debug`
    or `/keymap` and going to the Debug tab, we can allow the user to either
    understand that the key being pressed is not being recognized or to
    check what it's being recognized as and report or reassign that key.
    
    | Menu | Inspector | Hint |
    |---|---|---|
    | <img width="1369" height="796" alt="CleanShot 2026-05-02 at 12 57 12"
    src="https://github.com/user-attachments/assets/512b6faa-344e-4aee-9c00-b4bdc633a662"
    /> | <img width="1261" height="754" alt="CleanShot 2026-05-02 at 12 56
    36"
    src="https://github.com/user-attachments/assets/a6ddae7d-e174-4ee4-893f-e6bec4fff4ab"
    /> | <img width="1369" height="796" alt="CleanShot 2026-05-02 at 12 57
    30"
    src="https://github.com/user-attachments/assets/db507784-f40a-4cff-ac23-a61d9703769b"
    /> |
    ## Summary
    - add a Debug tab to `/keymap` and support `/keymap debug` for direct
    access
    - show what key Codex receives, the config key representation, raw event
    details, and matching actions
    - add a progressive missing-key hint that escalates after a few seconds
    with no detected keypress
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-tui keymap_setup::tests::debug_view`
    - `cargo test -p codex-tui keymap_setup::tests`
    - `cargo test -p codex-tui slash_keymap`
    - `cargo test -p codex-tui` (unit tests passed; integration test
    `suite::model_availability_nux::resume_startup_does_not_consume_model_availability_nux_count`
    failed locally by itself with `codex resume` exiting 1 and terminal
    probe escape output)
    - `just fix -p codex-tui`
    - `just argument-comment-lint`
    - `cargo insta pending-snapshots`
    - `git diff --check`
  • Speed up /side parent restore replay (#20815)
    ## Why
    
    Returning from a `/side` conversation restores the parent thread by
    replaying its snapshot into the TUI. For very long parent threads,
    replaying every transcript row can take noticeable time even though most
    rows immediately scroll out of terminal history.
    
    ## What Changed
    
    - Buffer thread-switch replay for parent restores when terminal resize
    reflow is enabled.
    - Reuse the existing resize-reflow tail renderer so only the retained
    transcript tail is written back to scrollback when a row cap is
    configured.
  • /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.
  • 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"
    />
  • 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>
  • 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`
  • /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.
  • 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`
  • Use /goal resume for paused goals (#20082)
    ## Why
    
    The paused goal statusline currently points users at `/goal` to unpause
    a goal, but bare `/goal` is the summary command and does not change the
    goal state. Instead of making `/goal` mutate state only when a goal is
    paused, this gives the action an explicit command that reads naturally
    in the UI.
    
    ## What Changed
    
    - Replace `/goal unpause` with `/goal resume` for reactivating a paused
    goal.
    - Update the paused goal statusline and `/goal` summary copy to point at
    `/goal resume`.
  • /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
  • tui: use permission profiles for sandbox state (#20008)
    ## Summary
    - Move TUI permission state from legacy `SandboxPolicy` values to
    canonical `PermissionProfile` values across presets, app events, chat
    widget state, app commands, thread routing, and cached thread session
    state.
    - Keep app-server compatibility boundaries explicit: embedded sessions
    send `permissionProfile`, while remote sessions send only a legacy
    `sandbox` projection and fall back to read-only when a custom profile
    cannot be projected.
    - Update status/add-dir UI summaries and snapshots to render the active
    permission profile, including workspace profiles selected by the new
    built-in defaults.
    
    ## Verification
    - `rg '\bSandboxPolicy\b' codex-rs/tui -n` returns no matches.
    - `cargo test -p codex-tui`
    - `cargo check -p codex-tui --tests`
    - `cargo test -p codex-tui additional_dirs`
    - `just fmt`
    - `just fix -p codex-tui`
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/20008).
    * #20041
    * #20040
    * #20037
    * #20035
    * #20034
    * #20033
    * #20032
    * #20030
    * #20028
    * #20027
    * #20026
    * #20024
    * #20021
    * #20018
    * #20016
    * #20015
    * #20013
    * #20011
    * #20010
    * __->__ #20008
  • feat(tui): add configurable keymap support (#18593)
    ## Why
    
    The TUI currently handles keyboard shortcuts as hard-coded event matches
    spread across app, composer, pager, list, approval, and navigation code.
    That makes shortcuts hard to customize, makes displayed hints easy to
    drift from actual behavior, and makes future keymap work riskier because
    there is no central action inventory.
    
    This PR adds the foundation for configurable, action-based keymaps
    without adding the interactive remapping UI yet. Onboarding
    intentionally stays on fixed startup shortcuts because users cannot
    reasonably configure keymaps before completing onboarding.
    
    This is PR1 in the keymap stack:
    
    - PR1: #18593: configurable keymap foundation
    - PR2: #18594: `/keymap` picker and guided remapping UI
    - PR3: #18595: Vim composer mode and the remap option
    
    ## Design Notes
    
    The new model resolves named actions into concrete runtime bindings once
    from config, then passes those bindings to the UI surfaces that handle
    input or render shortcut hints.
    
    The main concepts are:
    
    - **Context**: a scope where an action is active, such as `global`,
    `chat`, `composer`, `editor`, `pager`, `list`, or `approval`.
    - **Action**: a named operation inside a context, such as
    `global.open_transcript`, `composer.submit`, or `pager.close`.
    - **Binding**: one or more single-key shortcuts assigned to an action,
    written as config strings such as `ctrl-t`, `alt-backspace`, or
    `page-down`. Multi-step sequences such as `ctrl-x ctrl-s`, `g g`, or
    leader-key flows are not part of this PR.
    - **Resolution order**: context-specific config wins first, supported
    global fallbacks come next, and built-in defaults fill in anything
    unset.
    - **Explicit unbinding**: an empty array removes an action binding in
    that scope and does not fall through to a fallback binding.
    - **Conflict validation**: a resolved keymap rejects duplicate active
    bindings inside the same scope so one keypress cannot dispatch two
    actions.
    
    ## What Changed
    
    - Added `TuiKeymap` config support under `[tui.keymap]`, including typed
    contexts/actions, key alias normalization, generated schema coverage,
    and user-facing config errors.
    - Added `RuntimeKeymap` resolution in `codex-rs/tui/src/keymap.rs`,
    including fallback precedence, built-in defaults, explicit unbinding,
    and per-context conflict validation.
    - Rewired existing TUI handlers to consume resolved keymap actions
    instead of directly matching hard-coded keys in each component.
    - Updated key hint rendering and footer/pager/list surfaces so displayed
    shortcuts follow the resolved keymap.
    - Kept onboarding shortcuts fixed in
    `codex-rs/tui/src/onboarding/keys.rs` instead of exposing them through
    `[tui.keymap]`.
    
    ## Validation
    
    The branch includes focused coverage for config parsing, key
    normalization, runtime fallback resolution, explicit unbinding,
    duplicate-key conflict validation, default keymap consistency,
    onboarding startup key behavior, and UI hint snapshots affected by
    resolved key bindings.
  • Add /auto-review-denials retry approval flow (#19058)
    ## Why
    
    Auto-review can deny an action that the user later decides they want to
    retry. Today there is no TUI surface for selecting a recent denial and
    sending explicit approval context back into the session, so users have
    to restate intent manually and the retry can be reviewed without the
    original denied action context.
    
    This adds a narrow TUI-driven path for approving a recent denied action
    while still keeping the retry inside the normal auto-review flow.
    
    ## What Changed
    
    - Added `/auto-review-denials` to open a picker of recent denied
    auto-review actions.
    - Added a small in-memory TUI store for the 10 most recent denied
    auto-review events.
    - Selecting a denial sends the structured denied event back through the
    existing core/app-server op path.
    - Core now injects a developer message containing the approved action
    JSON rather than the full assessment event.
    - Auto-review transcript collection now preserves this specific approval
    developer message so follow-up review sessions can see the user approval
    context.
    - Added TUI snapshot/unit coverage for the picker and approval dispatch
    path.
    - Added core coverage for retaining the approval developer message in
    the auto-review transcript.
    
    ## Verification
    
    - `cargo test -p codex-core
    collect_guardian_transcript_entries_keeps_manual_approval_developer_message`
    - `cargo test -p codex-tui auto_review_denials`
    - `cargo test -p codex-tui
    approving_recent_denial_emits_structured_core_op_once`
    
    ## Notes
    
    This intentionally keeps retries going through auto-review. The approval
    signal is context for the exact previously denied action, not a blanket
    bypass for similar future actions.
  • fix(tui): reflow scrollback on terminal resize (#18575)
    Fixes multiple scrollback and terminal resize issues: #5538, #5576,
    #8352, #12223, #16165, and #15380.
    
    ## Why
    
    Codex writes finalized transcript output into terminal scrollback after
    wrapping it for the current viewport width. A later terminal resize
    could leave that scrollback shaped for the old width, so wider windows
    kept narrow output and narrower windows could show stale wrapping
    artifacts until enough new output replaced the visible area.
    
    This is also the foundation PR for responsive markdown tables. Table
    rendering needs finalized transcript content to be width-sensitive after
    insertion, not only while content is first streaming. Markdown table
    rendering itself stays in #18576.
    
    ## Stack
    
    - PR1: resize backlog reflow and interrupt cleanup
    - #18576: markdown table support
    
    ## What Changed
    
    - Rebuild source-backed transcript history when the terminal width
    changes. `terminal_resize_reflow` is introduced through the experimental
    feature system, but is enabled by default for this rollout so we can
    validate behavior across real terminals.
    - Preserve assistant and plan stream source so finalized streaming
    output can participate in resize reflow after consolidation.
    - Debounce resize work, but force a final source-backed reflow when a
    resize happened during active or unconsolidated streaming output.
    - Clear stale pending history lines on resize so old-width wrapped
    output is not emitted just before rebuilt scrollback.
    - Bound replay work with `[tui.terminal_resize_reflow].max_rows`:
    omitted uses terminal-specific defaults, `0` keeps all rendered rows,
    and a positive value sets an explicit cap. The cap applies both while
    initially replaying a resumed transcript into scrollback and when
    rebuilding scrollback after terminal resize.
    - Consolidate interrupted assistant streams before cleanup, then clear
    pending stream output and active-tail state consistently.
    - Move resize reflow and thread event buffering helpers out of `app.rs`
    into dedicated TUI modules.
    - Add focused coverage for resize reflow, feature-gated behavior,
    streaming source preservation, interrupted output cleanup,
    unicode-neutral text, terminal-specific row caps, and composer/layout
    stability.
    
    ## Runtime Bounds
    
    Resize reflow keeps only the most recent rendered rows when a row cap is
    active. The default is `auto`, which maps to the detected terminal's
    default scrollback size where Codex can identify it: VS Code `1000`,
    Windows Terminal `9001`, WezTerm `3500`, and Alacritty `10000`.
    Terminals without a dedicated mapping use the conservative fallback of
    `1000` rows. Users can override this with `[tui.terminal_resize_reflow]
    max_rows = N`, or set `max_rows = 0` to disable row limiting.
    
    ## Validation
    
    - `just fmt`
    - `git diff --check`
    - `cargo test --manifest-path codex-rs/Cargo.toml -p codex-tui reflow`
    - `cargo test --manifest-path codex-rs/Cargo.toml -p codex-tui
    transcript_reflow`
    - `just fix -p codex-tui`
    - PR CI in progress on the squashed branch
  • 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.
  • 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.
  • /plugins: Add inline enablement toggles (#18395)
    This PR adds inline enable/disable controls to the new /plugins browse
    menu. Installed plugins can now be toggled directly from the list with
    keyboard interaction, and the associated config-write plumbing is
    included so the UI and persisted plugin state stay in sync. This also
    includes the queued-write handling needed to avoid stale toggle
    completions overwriting newer intent.
    
    - Add toggleable plugin rows for installed plugins in /plugins
    - Support Space to enable or disable without leaving the list
    - Persist plugin enablement through the existing app/config write path
    - Preserve the current selection while the list refreshes after a toggle
    - Add tests and snapshot updates for toggling behavior
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • perf(tui): defer startup skills refresh (#18370)
    # Summary
    
    This removes startup `skills/list` from the critical path to first
    input. In release measurements, median startup-to-input time improved
    from `307.5 ms` to `191.0 ms` across 30 measured runs with 5 warmups.
    
    # Background
    
    Startup currently waits for a forced `skills/list` app-server request
    before scheduling the first usable TUI frame. That makes skill metadata
    freshness part of the process-launch-to-input path, even though the
    prompt can safely accept normal input before skill metadata has finished
    loading.
    
    I measured startup from process launch until the TUI reports that the
    user can type. The measurement harness watched the startup measurement
    record, killed Codex after a successful sample, and enforced a timeout
    so repeated runs would not leave TUI processes behind. The debug runs
    had enough outliers that I used median as the primary signal and ran a
    baseline self-compare to understand the noise floor.
    
    # Why skills/list
    
    The `skills/list` cut was the best practical optimization because it
    improved startup without changing the important readiness contract: when
    the prompt is shown, it is still backed by an active session. Only
    enrichment data arrives later.
    
    | Candidate | Result | Decision |
    | --- | --- | --- |
    | Defer startup `skills/list` | Debug median improved from `524.0 ms` to
    `348.0 ms`; release median improved from `307.5 ms` to `191.0 ms`. |
    Keep |
    | Defer fresh `thread/start` | Debug median improved from `494.0 ms` to
    `256.0 ms`, but the prompt could appear before an active thread was
    attached. | Reject as too risky for this PR |
    | Avoid forced skills config reload | Debug median moved from `509.0 ms`
    to `512.0 ms`. | Reject as neutral |
    | Skip fresh history metadata | Debug median moved from `496.5 ms` to
    `531.5 ms`. | Reject as regression/noise |
    | Defer app-server startup | Not implemented because it would only
    permit a loading frame unless the TUI gained a deliberate pre-server
    state. | Out of scope |
    
    # Implementation
    
    `App::refresh_startup_skills` now clones the app-server request handle,
    spawns a background task, and issues the same forced `skills/list`
    request after the first frame is scheduled. When the request completes,
    the task sends `AppEvent::SkillsListLoaded` back through the normal app
    event queue.
    
    The existing skills response handling still converts the app-server
    response, updates the chat widget, and emits invalid `SKILL.md`
    warnings. Explicit user-initiated skills refreshes still use the
    existing synchronous app command path, so callers that intentionally
    requested fresh skill state do not race ahead of their own refresh.
    
    # Tradeoffs
    
    The main tradeoff is a narrow theoretical race at startup: skill mention
    completion depends on a background `skills/list` response, so it could
    briefly show stale or empty metadata if opened before that response
    arrives. In manual testing, pressing `$` as soon as possible after
    launch still showed populated skill metadata, so this risk appears
    minimal in normal use. Plain input remains available immediately, and
    the UI updates through the existing skills response path once the
    refresh completes.
    
    This PR does not change how skills are discovered, cached,
    force-reloaded, displayed, enabled, or warned about. It only changes
    when the startup refresh is allowed to complete relative to the first
    usable TUI frame.
    
    # Verification
    
    - `cargo test -p codex-tui`
  • feat(tui): add clear-context plan implementation (#17499)
    ## TL;DR
    
    - Adds a second Plan Mode handoff: implement the approved plan after
    clearing context.
    - Keeps the existing same-thread `Yes, implement this plan` action
    unchanged.
    - Reuses the `/clear` thread-start path and submits the approved plan as
    the fresh thread's first prompt.
    - Covers the new popup option, event plumbing, initial-message behavior,
    and disabled states in TUI tests.
    
    ## Problem
    
    Plan Mode already asks whether to implement an approved plan, but the
    only affirmative path continues in the same thread. That is useful when
    the planning conversation itself is still valuable, but it does not
    support the workflow where exploratory planning context is discarded and
    implementation starts from the final approved plan as the only
    model-visible handoff.
    
    <img width="1253" height="869" alt="image"
    src="https://github.com/user-attachments/assets/90023d75-c330-4919-bed8-518671c3474b"
    />
    
    ## Mental model
    
    There are now two implementation choices after a proposed plan. The
    existing choice, `Yes, implement this plan`, is unchanged: it switches
    to Default mode and submits `Implement the plan.` in the current thread.
    The new choice, `Yes, clear context and implement`, treats the proposed
    plan as a handoff artifact. It clears the UI/session context through the
    same thread-start source used by `/clear`, then submits an initial
    prompt containing the approved plan after the fresh thread is
    configured.
    
    The important distinction is that the new path is not compaction. The
    model receives a deliberate implementation prompt built from the
    approved plan markdown, not a summary of the previous planning
    transcript. Both implementation choices require the Default
    collaboration preset to be available, so the popup does not offer a
    coding handoff when the fresh thread would fall back to another mode.
    
    ## Non-goals
    
    This change does not alter `/clear`, `/compact`, or the existing
    same-context Plan Mode implementation option. It does not add protocol
    surface area or app-server schema changes. It also does not carry the
    previous transcript path or a generated planning summary into the new
    model context.
    
    ## Tradeoffs
    
    The fresh-context option relies on the approved plan being sufficiently
    complete. That matches the Plan Mode contract, but it means vague plans
    will produce weaker implementation starts than a compacted transcript
    would. The upside is that rejected ideas, exploratory dead ends, and
    planning corrections do not leak into the implementation turn.
    
    The current implementation stores the latest proposed plan in
    `ChatWidget` rather than deriving it from history cells at selection
    time. This keeps the popup action simple and deterministic, but it makes
    the cache lifecycle important: it must be reset when a new task starts
    so an old plan cannot be submitted later.
    
    ## Architecture
    
    The TUI stores the most recent completed proposed-plan markdown when a
    plan item completes. The Plan Mode approval popup uses that cache to
    enable the fresh-context option and to build a first-turn prompt that
    instructs the model to implement the approved plan in a fresh context.
    
    Selecting the new option emits a TUI-internal
    `ClearUiAndSubmitUserMessage` event. `App` handles that event by reusing
    the existing clear flow: clear terminal state, reset app UI state, start
    a new app-server thread with `ThreadStartSource::Clear`, and attach a
    replacement `ChatWidget` with an initial user message. The existing
    initial-message suppression in `enqueue_primary_thread_session` ensures
    the prompt is submitted only after the new session is configured and any
    startup replay is rendered.
    
    ## Observability
    
    The previous thread remains resumable through the existing clear-session
    summary hint. There is no new telemetry or protocol event for this path,
    so debugging should start at the TUI event boundary: confirm the popup
    emitted `ClearUiAndSubmitUserMessage`, confirm the app-server thread
    start used `ThreadStartSource::Clear`, then confirm the fresh widget
    submitted the initial user message after `SessionConfigured`.
    
    ## Tests
    
    The Plan Mode popup snapshots cover the new option and preserve the
    original option as the first/default action. Unit coverage verifies the
    original same-context option still emits `SubmitUserMessageWithMode`,
    the new option emits `ClearUiAndSubmitUserMessage` with the approved
    plan embedded verbatim, and the clear-context option is disabled when
    Default mode is unavailable or no approved plan exists. The broader
    `codex-tui` test package passes with the updated fresh-thread
    initial-message plumbing.
  • [codex] Revoke ChatGPT tokens on logout (#17825)
    ## Summary
    
    This changes Codex logout so managed ChatGPT auth is revoked against
    AuthAPI before local auth state is removed. CLI logout, TUI `/logout`,
    and the app-server account logout path now use the token-revoking logout
    flow instead of only deleting `auth.json` / credential store state.
    
    ## Root Cause
    
    Logout previously cleared only local auth storage. That removed Codex's
    local credentials but did not ask the backend to invalidate the
    refresh/access token state associated with a managed ChatGPT login.
    
    ## Behavior
    
    For managed ChatGPT auth, logout sends the stored refresh token to
    `https://auth.openai.com/oauth/revoke` with `token_type_hint:
    refresh_token` and the Codex OAuth client id, then deletes all local
    auth stores after revocation succeeds. If only an access token is
    available, it falls back to revoking that access token. API key auth and
    externally supplied `chatgptAuthTokens` are still only cleared locally
    because Codex does not own a refresh token for those modes.
    
    Revocation failures are fail-closed: if Codex cannot load stored auth or
    the backend revoke call fails, logout returns an error and leaves local
    auth in place so the user can retry instead of silently clearing local
    state while backend tokens remain valid.
    
    ## Validation
    ran local version of `codex-cli` with staging overrides/harness for auth
    
    ran `codex login` then `codex logout`:
    
    saw auth.json clear and  backend revocation endpoints were called
    
    ```
    POST /oauth/revoke
    status: 200
    
    revoking access token
    should clear auth session
    clearing auth session due to token revocation
    successfully revoked session and access token
    CANONICAL-API-LINE Response: status='200' method='POST' path='/oauth/revoke
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>