Commit Graph

1066 Commits

  • feat: do not close unified exec processes across turns (#10799)
    With this PR we do not close the unified exec processes (i.e. background
    terminals) at the end of a turn unless:
    * The user interrupt the turn
    * The user decide to clean the processes through `app-server` or
    `/clean`
    
    I made sure that `codex exec` correctly kill all the processes
  • tui: avoid no-op status-line redraws (#11155)
    Rate-limit snapshots are polled every 60s, which causes unconditional
    redraws.
    This causes spurious "tab changed" indicators in terminal apps.
  • [apps] Improve app loading. (#10994)
    There are two concepts of apps that we load in the harness:
    
    - Directory apps, which is all the apps that the user can install.
    - Accessible apps, which is what the user actually installed and can be
    $ inserted and be used by the model. These are extracted from the tools
    that are loaded through the gateway MCP.
    
    Previously we wait for both sets of apps before returning the full apps
    list. Which causes many issues because accessible apps won't be
    available to the UI or the model if directory apps aren't loaded or
    failed to load.
    
    In this PR we are separating them so that accessible apps can be loaded
    separately and are instantly available to be shown in the UI and to be
    provided in model context. We also added an app-server event so that
    clients can subscribe to also get accessible apps without being blocked
    on the full app list.
    
    - [x] Separate accessible apps and directory apps loading.
    - [x] `app/list` request will also emit `app/list/updated` notifications
    that app-server clients can subscribe. Which allows clients to get
    accessible apps list to render in the $ menu without being blocked by
    directory apps.
    - [x] Cache both accessible and directory apps with 1 hour TTL to avoid
    reloading them when creating new threads.
    - [x] TUI improvements to redraw $ menu and /apps menu when app list is
    updated.
  • Defer persistence of rollout file (#11028)
    - Defer rollout persistence for fresh threads (`InitialHistory::New`):
    keep rollout events in memory and only materialize rollout file + state
    DB row on first `EventMsg::UserMessage`.
    - Keep precomputed rollout path available before materialization.
    - Change `thread/start` to build thread response from live config
    snapshot and optional precomputed path.
    - Improve pre-materialization behavior in app-server/TUI: clearer
    invalid-request errors for file-backed ops and a friendlier `/fork` “not
    ready yet” UX.
    - Update tests to match deferred semantics across
    start/read/archive/unarchive/fork/resume/review flows.
    - Improved resilience of user_shell test, which should be unrelated to
    this change but must be affected by timing changes
    
    For Reviewers:
    * The primary change is in recorder.rs
    * Most of the other changes were to fix up broken assumptions in
    existing tests
    
    Testing:
    * Manually tested CLI
    * Exercised app server paths by manually running IDE Extension with
    rebuilt CLI binary
    * Only user-visible change is that `/fork` in TUI generates visible
    error if used prior to first turn
  • fix(tui): rehydrate drafts and restore image placeholders (#9040)
    Fixes #9050
    
    When a draft is stashed with Ctrl+C, we now persist the full draft state
    (text elements, local image paths, and pending paste payloads) in local
    history. Up/Down recall rehydrates placeholder elements and attachments
    so styling remains correct and large pastes still expand on submit.
    Persistent (cross‑session) history remains text‑only.
    
    Backtrack prefills now reuse the selected user message’s text elements
    and local image paths, so image placeholders/attachments rehydrate when
    rolling back.
    
    External editor replacements keep only attachments whose placeholders
    remain and then normalize image placeholders to `[Image #1]..[Image #N]`
    to keep the attachment mapping consistent.
    
    Docs:
    - docs/tui-chat-composer.md
    
    Testing:
    - just fix -p codex-tui
    - cargo test -p codex-tui
    
    Co-authored-by: Eric Traut <etraut@openai.com>
  • feat: include state of [experimental_network] in /debug-config output (#11039)
    #10958 introduced experimental support for a network config in
    `/etc/codex/requirements.toml`, so this extends `/debug-config` to
    surface this information, if set, which should make it easier to debug.
  • feat(core): add network constraints schema to requirements.toml (#10958)
    ## Summary
    
    Add `requirements.toml` schema support for admin-defined network
    constraints in the requirements layer
    
    example config:
    
    ```
    [experimental_network]
    enabled = true
    allowed_domains = ["api.openai.com"]
    denied_domains = ["example.com"]
    ```
  • Add resume_agent collab tool (#10903)
    Summary
    - add the new resume_agent collab tool path through core, protocol, and
    the app server API, including the resume events
    - update the schema/TypeScript definitions plus docs so resume_agent
    appears in generated artifacts and README
    - note that resumed agents rehydrate rollout history without overwriting
    their base instructions
    
    Testing
    - Not run (not requested)
  • Show left/right arrows to navigate in tui request_user_input (#10921)
    <img width="785" height="185" alt="Screenshot 2026-02-06 at 10 25 13 AM"
    src="https://github.com/user-attachments/assets/402a6e79-4626-4df9-b3da-bc2f28e64611"
    />
    
    <img width="784" height="213" alt="Screenshot 2026-02-06 at 10 26 37 AM"
    src="https://github.com/user-attachments/assets/cf9614b2-aa1e-4c61-8579-1d2c7e1c7dc1"
    />
    
    "left/right to navigate questions" in request_user_input footer
  • Do not poll for usage when using API Key auth (#10973)
    Fixes #10869
    
    - Gate TUI rate-limit polling on ChatGPT-auth providers only.
    - `prefetch_rate_limits()` now checks `should_prefetch_rate_limits()`.
    - New gate requires:
      - `config.model_provider.requires_openai_auth`
      - cached auth is ChatGPT (`CodexAuth::is_chatgpt_auth`)
    - Prevents `/wham/usage` polling in API/custom-endpoint profiles.
  • feat: add support for allowed_web_search_modes in requirements.toml (#10964)
    This PR makes it possible to disable live web search via an enterprise
    config even if the user is running in `--yolo` mode (though cached web
    search will still be available). To do this, create
    `/etc/codex/requirements.toml` as follows:
    
    ```toml
    # "live" is not allowed; "disabled" is allowed even though not listed explicitly.
    allowed_web_search_modes = ["cached"]
    ```
    
    Or set `requirements_toml_base64` MDM as explained on
    https://developers.openai.com/codex/security/#locations.
    
    ### Why
    - Enforce admin/MDM/`requirements.toml` constraints on web-search
    behavior, independent of user config and per-turn sandbox defaults.
    - Ensure per-turn config resolution and review-mode overrides never
    crash when constraints are present.
    
    ### What
    - Add `allowed_web_search_modes` to requirements parsing and surface it
    in app-server v2 `ConfigRequirements` (`allowedWebSearchModes`), with
    fixtures updated.
    - Define a requirements allowlist type (`WebSearchModeRequirement`) and
    normalize semantics:
      - `disabled` is always implicitly allowed (even if not listed).
      - An empty list is treated as `["disabled"]`.
    - Make `Config.web_search_mode` a `Constrained<WebSearchMode>` and apply
    requirements via `ConstrainedWithSource<WebSearchMode>`.
    - Update per-turn resolution (`resolve_web_search_mode_for_turn`) to:
    - Prefer `Live → Cached → Disabled` when
    `SandboxPolicy::DangerFullAccess` is active (subject to requirements),
    unless the user preference is explicitly `Disabled`.
    - Otherwise, honor the user’s preferred mode, falling back to an allowed
    mode when necessary.
    - Update TUI `/debug-config` and app-server mapping to display
    normalized `allowed_web_search_modes` (including implicit `disabled`).
    - Fix web-search integration tests to assert cached behavior under
    `SandboxPolicy::ReadOnly` (since `DangerFullAccess` legitimately prefers
    `live` when allowed).
  • fix(tui): conditionally restore status indicator using message phase (#10947)
    TLDR: use new message phase field emitted by preamble-supported models
    to determine whether an AgentMessage is mid-turn commentary. if so,
    restore the status indicator afterwards to indicate the turn has not
    completed.
    
    ### Problem
    `commit_tick` hides the status indicator while streaming assistant text.
    For preamble-capable models, that text can be commentary mid-turn, so
    hiding was correct during streaming but restore timing mattered:
    - restoring too aggressively caused jitter/flashing
    - not restoring caused indicator to stay hidden before subsequent work
    (tool calls, web search, etc.)
    
    ### Fix
    - Add optional `phase` to `AgentMessageItem` and propagate it from
    `ResponseItem::Message`
    - Keep indicator hidden during streamed commit ticks, restore only when:
      - assistant item completes as `phase=commentary`, and
      - stream queues are idle + task is still running.
    - Treat `phase=None` as final-answer behavior (no restore) to keep
    existing behavior for non-preamble models
    
    ### Tests
    Add/update tests for:
    - no idle-tick restore without commentary completion
    - commentary completion restoring status before tool begin
    - snapshot coverage for preamble/status behavior
    
    ---------
    
    Co-authored-by: Josh McKinney <joshka@openai.com>
  • TUI/Core: preserve duplicate skill/app mention selection across submit + resume (#10855)
    ## What changed
    
    - In `codex-rs/core/src/skills/injection.rs`, we now honor explicit
    `UserInput::Skill { name, path }` first, then fall back to text mentions
    only when safe.
    - In `codex-rs/tui/src/bottom_pane/chat_composer.rs`, mention selection
    is now token-bound (selected mention is tied to the specific inserted
    `$token`), and we snapshot bindings at submit time so selection is not
    lost.
    - In `codex-rs/tui/src/chatwidget.rs` and
    `codex-rs/tui/src/bottom_pane/mod.rs`, submit/queue paths now consume
    the submit-time mention snapshot (instead of rereading cleared composer
    state).
    - In `codex-rs/tui/src/mention_codec.rs` and
    `codex-rs/tui/src/bottom_pane/chat_composer_history.rs`, history now
    round-trips mention targets so resume restores the same selected
    duplicate.
    - In `codex-rs/tui/src/bottom_pane/skill_popup.rs` and
    `codex-rs/tui/src/bottom_pane/chat_composer.rs`, duplicate labels are
    normalized to `[Repo]` / `[App]`, app rows no longer show `Connected -`,
    and description space is a bit wider.
    
    <img width="550" height="163" alt="Screenshot 2026-02-05 at 9 56 56 PM"
    src="https://github.com/user-attachments/assets/346a7eb2-a342-4a49-aec8-68dfec0c7d89"
    />
    <img width="550" height="163" alt="Screenshot 2026-02-05 at 9 57 09 PM"
    src="https://github.com/user-attachments/assets/5e04d9af-cccf-4932-98b3-c37183e445ed"
    />
    
    
    ## Before vs now
    
    - Before: selecting a duplicate could still submit the default/repo
    match, and resume could lose which duplicate was originally selected.
    - Now: the exact selected target (skill path or app id) is preserved
    through submit, queue/restore, and resume.
    
    ## Manual test
    
    1. Build and run this branch locally:
       - `cd /Users/daniels/code/codex/codex-rs`
       - `cargo build -p codex-cli --bin codex`
       - `./target/debug/codex`
    2. Open mention picker with `$` and pick a duplicate entry (not the
    first one).
    3. Confirm duplicate UI:
       - repo duplicate rows show `[Repo]`
       - app duplicate rows show `[App]`
       - app description does **not** start with `Connected -`
    4. Submit the prompt, then press Up to restore draft and submit again.  
       Expected: it keeps the same selected duplicate target.
    5. Use `/resume` to reopen the session and send again.  
    Expected: restored mention still resolves to the same duplicate target.
  • Queue nudges while plan generating (#10457)
    ## Summary
    
    This PR fixes a UI/streaming race when nudged or steer-enabled messages
    are queued during an active Plan stream.
    
    Previously, `submit_user_message_with_mode` switched collaboration mode
    immediately (via `set_collaboration_mask`) even when the message was
    queued. If that happened mid-Plan stream, `active_mode_kind` could flip
    away from Plan before the turn finished, causing subsequent
    `on_plan_delta` updates to be ignored in the UI.
    
    Now, mode switching is deferred until the queued message is actually
    submitted.
    
    ## What changed
    
    - Added a per-message deferred mode override on `UserMessage`:
      - `collaboration_mode_override: Option<CollaborationModeMask>`
    - Updated `submit_user_message_with_mode` to:
      - create a `UserMessage` carrying the mode override
    - queue or submit that message without mutating global mode immediately
    - Updated `submit_user_message` to:
    - apply `collaboration_mode_override` just before constructing/sending
    `Op::UserTurn`
    - Kept queueing condition scoped to active Plan stream rendering:
    - queue only while plan output is actively streaming in TUI
    (`plan_stream_controller.is_some()`)
    
    ## Why
    
    This preserves Plan mode for the remainder of the in-flight Plan turn,
    so streamed plan deltas continue rendering correctly, while still
    ensuring the follow-up queued message is sent with the intended
    collaboration mode.
    
    ## Behavior after this change
    
    - If a nudged/steer submission happens while Plan output is actively
    streaming:
      - message is queued
      - UI stays in Plan mode for the running turn
    - once dequeued/submitted, mode override is applied and the message is
    sent in the intended mode
    - If no Plan stream is active:
    - submission proceeds immediately and mode override is applied as before
    
    ## Tests
    
    Added/updated coverage in `tui/src/chatwidget/tests.rs`:
    
    - `submit_user_message_with_mode_queues_while_plan_stream_is_active`
      - asserts mode remains Plan while queued
    - asserts mode switches to Code when queued message is actually
    submitted
    - `submit_user_message_with_mode_submits_when_plan_stream_is_not_active`
    - `steer_enter_queues_while_plan_stream_is_active`
    - `steer_enter_submits_when_plan_stream_is_not_active`
    
    Also updated existing `UserMessage { ... }` test fixtures to include the
    new field.
    
    ## Codex author
    `codex fork 019c1047-d5d5-7c92-a357-6009604dc7e8`
  • Removed "exec_policy" feature flag (#10851)
    This is no longer needed because it's on by default
  • Handle required MCP startup failures across components (#10902)
    Summary
    - add a `required` flag for MCP servers everywhere config/CLI data is
    touched so mandatory helpers can be round-tripped
    - have `codex exec` and `codex app-server` thread start/resume fail fast
    when required MCPs fail to initialize
  • Personality setting is no longer available in experimental menu (#10852)
    This PR removes the inaccurate "Disable in /experimental." statement now
    that the "personality" feature flag is no longer experimental.
    
    This addresses #10850
  • Gate app tooltips to macOS (#10784)
    - Gate app promo tips to macOS and use non-app copy elsewhere.
  • updates: use brew api for version check (#10809)
    ## Problem
    
    `codex` currently prompts you to update via `brew upgrade --cask codex`
    but the brew api does not return the new version
    
    > <img width="1500" height="822" alt="Screenshot 2026-02-05 at 12 36
    09 PM"
    src="https://github.com/user-attachments/assets/9e12929d-95e8-43f4-8fba-ab93f5f76e73"
    />
    
    ## Solution
    
    `codex-rs/tui/src/updates.rs` was using the [latest cask in
    github](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/c/codex.rb)
    but this does not agree with the brew api, which leads to the issue
    above. Instead we use the [brew api json
    endpoint](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/c/codex.rb)
    to ensure our version check agrees with the upgrade command.
  • fix(auth): isolate chatgptAuthTokens concept to auth manager and app-server (#10423)
    So that the rest of the codebase (like TUI) don't need to be concerned
    whether ChatGPT auth was handled by Codex itself or passed in via
    app-server's external auth mode.
  • feat(tui): add sortable resume picker with created/updated timestamp toggle (#10752)
    ## Summary
    
    - Add sorting support to the resume session picker with Tab key toggle
    - Sessions can now be sorted by either creation time or last updated
    time
    - Display the current sort mode in the picker header
    - Default to sorting by creation time (most recent first)
    
    ## Changes
    
    - Add `sort_key` field to `PickerState` to track current sort order
    - Pass sort key to `RolloutRecorder::list_threads()` for proper backend
    sorting
    - Add Tab key handler to toggle between `CreatedAt` and `UpdatedAt`
    sorting
    - Show current sort mode ("Created at" / "Updated at") in header
    - Add "Tab to toggle sort" keyboard hint
    - Intelligently hide secondary date column when terminal is narrow
    - Reload session list when sort order changes
    
    ## Test plan
    
    - [x] Unit tests for sort key toggle functionality
    - [x] Snapshot tests updated for new header format
    - [x] Test that Tab key triggers reload with new sort key
    - [x] Test column visibility adapts to narrow terminals
  • feat(tui): add /statusline command for interactive status line configuration (#10546)
    ## Summary
    - Adds a new `/statusline` command to configure TUI footer status line
    - Introduces reusable `MultiSelectPicker` component with keyboard
    navigation, optional ordering and toggle support
    - Implement status line setup modal that persist configuration to
    config.toml
    
      ## Status Line Items
      The following items can be displayed in the status line:
      - **Model**: Current model name (with optional reasoning level)
      - **Context**: Remaining/used context window percentage
      - **Rate Limits**: 5-day and weekly usage limits
      - **Git**: Current branch (with optimized lookups)
      - **Tokens**: Used tokens, input/output token counts
      - **Session**: Session ID (full or shortened prefix)
      - **Paths**: Current directory, project root
      - **Version**: Codex version
    
      ## Features
      - Live preview while configuring status line items
      - Fuzzy search filtering in the picker
      - Intelligent truncation when items don't fit
      - Items gracefully omit when data is unavailable
      - Configuration persists to `config.toml`
      - Validates and warns about invalid status line items
    
      ## Test plan
      - [x] Run `/statusline` and verify picker UI appears
      - [x] Toggle items on/off and verify live preview updates
      - [x] Confirm selection persists after restart
      - [x] Verify truncation behavior with many items selected
      - [x] Test git branch detection in and out of git repos
    
    ---------
    
    Co-authored-by: Josh McKinney <joshka@openai.com>
  • Leverage state DB metadata for thread summaries (#10621)
    Summary:
    - read conversation summaries and cwd info from the state DB when
    possible so we no longer rely on rollout files for metadata and avoid
    extra I/O
    - persist CLI version in thread metadata, surface it through summary
    builders, and add the necessary DB migration hooks
    - simplify thread listing by using enriched state DB data directly
    rather than reading rollout heads
    
    Testing:
    - Not run (not requested)
  • adding fork information (UI) when forking (#10246)
    - shows `/fork` command that ran in prev session
    - shows `session forked from name (uuid) || uuid (if name is not set)` as an event in new session
  • fix(tui): flush input buffer on init to prevent early exit on Windows (#10729)
    Fixes #10661.
    
    ### Problem
    On Windows, the sign-in menu can exit immediately if the OS-level input
    buffer contains trailing characters (like the Enter key from running the
    command).
    
    ### Solution
    **Flush Input Buffer on Init**: Use FlushConsoleInputBuffer on Windows
    (and cflush on Unix) in ui::init() to discard any input captured before
    the TUI was ready.
    
    Verified by @CodebyAmbrose in #10661.
  • Reload cloud requirements after user login (#10725)
    Reload cloud requirements after user login so it could take effect
    immediately.
  • Sync collaboration mode naming across Default prompt, tools, and TUI (#10666)
    ## Summary
    - add shared `ModeKind` helpers for display names, TUI visibility, and
    `request_user_input` availability
    - derive TUI mode filtering/labels from shared `ModeKind` metadata
    instead of local hardcoded matches
    - derive `request_user_input` availability text and unavailable error
    mode names from shared mode metadata
    - replace hardcoded known mode names in the Default collaboration-mode
    template with `{{KNOWN_MODE_NAMES}}` and fill it from
    `TUI_VISIBLE_COLLABORATION_MODES`
    - add regression tests for mode metadata sync and placeholder
    replacement
    
    ## Notes
    - `cargo test -p codex-core` integration target (`tests/all`) still
    shows pre-existing env-specific failures in this environment due missing
    `test_stdio_server` binary resolution; core unit tests are green.
    
    ## Codex author
    `codex resume 019c26ff-dfe7-7173-bc04-c9e1fff1e447`
  • chore(config) Default Personality Pragmatic (#10705)
    ## Summary
    Switch back to Pragmatic personality
    
    ## Testing
    - [x] Updated unit tests
  • fix: ensure status indicator present earlier in exec path (#10700)
    ensure status indicator present in all classifications of exec tool.
    fixes indicator disappearing after preambles, will look into using
    `phase` to avoid this class of error in a few hours.
    
    commands parsed as unknown faced this issue
    
    tested locally, added test for specific failure flow
  • fix(tui): restore working shimmer after preamble output (#10701)
    ## Problem
    When a turn streamed a preamble line before any tool activity,
    `ChatWidget` hid the status row while committing streamed lines and did
    not restore it until a later event (commonly `ExecCommandBegin`). During
    that idle gap, the UI looked finished even though the turn was still
    active.
    
    ## Mental model
    The bottom status row and transcript stream are separate progress
    affordances:
    - transcript stream shows committed output
    - status row (spinner/shimmer + header) shows liveness of an active turn
    
    While stream output is actively committing, hiding the status row is
    acceptable to avoid redundant visual noise. Once stream controllers go
    idle, an active turn must restore the status row immediately so liveness
    remains visible across preamble-to-tool gaps.
    
    ## Non-goals
    - No changes to streaming chunking policy or pacing.
    - No changes to final completion behavior (status still hides when task
    actually ends).
    - No refactor of status lifecycle ownership between `ChatWidget` and
    `BottomPane`.
    
    ## Tradeoffs
    - We keep the existing behavior of hiding the status row during active
    stream commits.
    - We add explicit restoration on the idle boundary when the task is
    still running.
    - This introduces one extra status update on idle transitions, which is
    small overhead but makes liveness semantics consistent.
    
    ## Architecture
    `run_commit_tick_with_scope` in `chatwidget.rs` now documents and
    enforces a two-phase contract:
    1. For each committed streamed cell, hide status and append transcript
    output.
    2. If controllers are present and all idle, restore status iff task is
    still running, preserving the current header.
    
    This keeps status ownership in `ChatWidget` while relying on
    `BottomPane` helpers:
    - `hide_status_indicator()` during active stream commits
    - `ensure_status_indicator()` +
    `set_status_header(current_status_header)` at stream-idle boundary
    
    Documentation pass additions:
    - Clarified the function-level contract and lifecycle intent in
    `run_commit_tick_with_scope`.
    - Added an explicit regression snapshot test comment describing the
    failing sequence.
    
    ## Observability
    Signal that the fix is present:
    - In the preamble-idle state, rendered output still includes `• Working
    (… esc to interrupt)`.
    - New snapshot:
    `codex_tui__chatwidget__tests__preamble_keeps_working_status.snap`.
    
    Debug path for future regressions:
    - Start at `run_commit_tick_with_scope` for hide/restore transitions.
    - Verify `bottom_pane.is_task_running()` at idle transition.
    - Confirm `current_status_header` continuity when status is recreated.
    - Use the new snapshot and targeted test sequence to reproduce
    deterministic preamble-idle behavior.
    
    ## Tests
    - Updated regression assertion:
    - `streaming_final_answer_keeps_task_running_state` now expects status
    widget to remain present while turn is running.
    - Renamed/updated behavioral regression:
      - `preamble_keeps_status_indicator_visible_until_exec_begin`.
    - Added snapshot regression coverage:
      - `preamble_keeps_working_status_snapshot`.
    - Snapshot file:
    `tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__preamble_keeps_working_status.snap`.
    
    Commands run:
    - `just fmt`
    - `cargo test -p codex-tui
    preamble_keeps_status_indicator_visible_until_exec_begin`
    - `cargo test -p codex-tui preamble_keeps_working_status_snapshot`
    
    ## Risks / Inconsistencies
    - Status visibility policy is still split across multiple event paths
    (`commit tick`, `turn complete`, `exec begin`), so future regressions
    can reintroduce ordering gaps.
    - Restoration depends on `is_task_running()` correctness; if task
    lifecycle flags drift, status behavior will drift too.
    - Snapshot proves rendered state, not animation cadence; cadence still
    relies on frame scheduling behavior elsewhere.
  • add none personality option (#10688)
    - add none personality enum value and empty placeholder behavior\n- add
    docs/schema updates and e2e coverage
  • Fix jitter in TUI apps/connectors picker (#10593)
    This PR fixes jitter in the TUI apps menu by making the description
    column stable during rendering and height measurement.
    Added a `stable_desc_col` option to
    `SelectionViewParams`/`ListSelectionView`, introduced stable variants of
    the shared row render/measure helpers in `selection_popup_common`, and
    enabled the stable mode for the apps/connectors picker in `chatwidget`.
    With these changes, only the apps/connectors picker uses this new
    option, though it could be used elsewhere in the future.
    
    Why: previously, the description column was computed from only currently
    visible rows, so as you scrolled or filtered, the column could shift and
    cause wrapping/height changes that looked jumpy. Computing it from all
    rows in this popup keeps alignment and layout consistent as users scroll
    through avaialble apps.
    
    
    
    **Before:**
    
    https://github.com/user-attachments/assets/3856cb72-5465-4b90-a993-65a2ffb09113
    
    
    
    
    
    **After:**
    
    https://github.com/user-attachments/assets/37b9d626-0b21-4c0f-8bb8-244c9ef971ff
  • tui: make Esc clear request_user_input notes while notes are shown (#10569)
    ## Summary
    
    This PR updates the `request_user_input` TUI overlay so `Esc` is
    context-aware:
    
    - When notes are visible for an option question, `Esc` now clears notes
    and exits notes mode.
    - When notes are not visible (normal option selection UI), `Esc` still
    interrupts as before.
    
    It also updates footer guidance text to match behavior.
    
    ## Changes
    
    - Added a shared notes-clear path for option questions:
    - `Tab` and `Esc` now both clear notes and return focus to options when
    notes are visible.
    - Updated footer hint text in notes-visible state:
      - from: `tab to clear notes | ... | esc to interrupt`
      - to: `tab or esc to clear notes | ...`
    - Hid `esc to interrupt` hint while notes are visible for option
    questions.
    - Kept `esc to interrupt` visible and functional in normal
    option-selection mode.
    - Updated tests to assert the new `Esc` behavior in notes mode.
    - Updated snapshot output for the notes-visible footer row.
    - Updated docs in `docs/tui-request-user-input.md` to reflect
    mode-specific `Esc` behavior.
  • feat(tui): pace catch-up stream chunking with hysteresis (#10461)
    ## Summary
    - preserve baseline streaming behavior (smooth mode still commits one
    line per 50ms tick)
    - extract adaptive chunking policy and commit-tick orchestration from
    ChatWidget into `streaming/chunking.rs` and `streaming/commit_tick.rs`
    - add hysteresis-based catch-up behavior with bounded batch draining to
    reduce queue lag without bursty single-frame jumps
    - document policy behavior, tuning guidance, and debug flow in rustdoc +
    docs
    
    ## Testing
    - just fmt
    - cargo test -p codex-tui
  • feat: add APIs to list and download public remote skills (#10448)
    Add API to list / download from remote public skills
  • Cleanup collaboration mode variants (#10404)
    ## Summary
    
    This PR simplifies collaboration modes to the visible set `default |
    plan`, while preserving backward compatibility for older partners that
    may still send legacy mode
    names.
    
    Specifically:
    - Renames the old Code behavior to **Default**.
    - Keeps **Plan** as-is.
    - Removes **Custom** mode behavior (fallbacks now resolve to Default).
    - Keeps `PairProgramming` and `Execute` internally for compatibility
    plumbing, while removing them from schema/API and UI visibility.
    - Adds legacy input aliasing so older clients can still send old mode
    names.
    
    ## What Changed
    
    1. Mode enum and compatibility
    - `ModeKind` now uses `Plan` + `Default` as active/public modes.
    - `ModeKind::Default` deserialization accepts legacy values:
      - `code`
      - `pair_programming`
      - `execute`
      - `custom`
    - `PairProgramming` and `Execute` variants remain in code but are hidden
    from protocol/schema generation.
    - `Custom` variant is removed; previous custom fallbacks now map to
    `Default`.
    
    2. Collaboration presets and templates
    - Built-in presets now return only:
      - `Plan`
      - `Default`
    - Template rename:
      - `core/templates/collaboration_mode/code.md` -> `default.md`
    - `execute.md` and `pair_programming.md` remain on disk but are not
    surfaced in visible preset lists.
    
    3. TUI updates
    - Updated user-facing naming and prompts from “Code” to “Default”.
    - Updated mode-cycle and indicator behavior to reflect only visible
    `Plan` and `Default`.
    - Updated corresponding tests and snapshots.
    
    4. request_user_input behavior
    - `request_user_input` remains allowed only in `Plan` mode.
    - Rejection messaging now consistently treats non-plan modes as
    `Default`.
    
    5. Schemas
    - Regenerated config and app-server schemas.
    - Public schema types now advertise mode values as:
      - `plan`
      - `default`
    
    ## Backward Compatibility Notes
    
    - Incoming legacy mode names (`code`, `pair_programming`, `execute`,
    `custom`) are accepted and coerced to `default`.
    - Outgoing/public schema surfaces intentionally expose only `plan |
    default`.
    - This allows tolerant ingestion of older partner payloads while
    standardizing new integrations on the reduced mode set.
    
    ## Codex author
    `codex fork 019c1fae-693b-7840-b16e-9ad38ea0bd00`
  • [Codex][CLI] Gate image inputs by model modalities (#10271)
    ###### Summary
    
    - Add input_modalities to model metadata so clients can determine
    supported input types.
    - Gate image paste/attach in TUI when the selected model does not
    support images.
    - Block submits that include images for unsupported models and show a
    clear warning.
    - Propagate modality metadata through app-server protocol/model-list
    responses.
      - Update related tests/fixtures.
    
      ###### Rationale
    
      - Models support different input modalities.
    - Clients need an explicit capability signal to prevent unsupported
    requests.
    - Backward-compatible defaults preserve existing behavior when modality
    metadata is absent.
    
      ###### Scope
    
      - codex-rs/protocol, codex-rs/core, codex-rs/tui
      - codex-rs/app-server-protocol, codex-rs/app-server
      - Generated app-server types / schema fixtures
    
      ###### Trade-offs
    
    - Default behavior assumes text + image when field is absent for
    compatibility.
      - Server-side validation remains the source of truth.
    
      ###### Follow-up
    
    - Non-TUI clients should consume input_modalities to disable unsupported
    attachments.
    - Model catalogs should explicitly set input_modalities for text-only
    models.
    
      ###### Testing
    
      - cargo fmt --all
      - cargo test -p codex-tui
      - env -u GITHUB_APP_KEY cargo test -p codex-core --lib
      - just write-app-server-schema
    - cargo run -p codex-cli --bin codex -- app-server generate-ts --out
    app-server-types
      - test against local backend
      
    <img width="695" height="199" alt="image"
    src="https://github.com/user-attachments/assets/d22dd04f-5eba-4db9-a7c5-a2506f60ec44"
    />
    
    ---------
    
    Co-authored-by: Josh McKinney <joshka@openai.com>
  • app tool tip (#10454)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the dedicated
    "Contributing" markdown file or your PR may be closed:
    https://github.com/openai/codex/blob/main/docs/contributing.md
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
    
    Include a link to a bug report or enhancement request.
  • Hide short worked-for label in final separator (#10452)
    - Hide the "Worked for" label in the final message separator unless
    elapsed time is over one minute.\n- Update/add tests to cover both
    hidden (<60s) and shown (>=61s) behavior.