Commit Graph

1111 Commits

  • Nicer highlighting of slash commands, /plan accepts prompt args and pasted images (#10269)
    ## Summary
    - Make typed slash commands become text elements when the user hits
    space, including paste‑burst spaces.
    - Enable `/plan` to accept inline args and submit them in plan mode,
    mirroring `/review` behavior and blocking submission while a task is
    running.
    - Preserve text elements/attachments for slash commands that take args.
    
    <img width="1510" height="500" alt="image"
    src="https://github.com/user-attachments/assets/446024df-b69a-4249-85db-1a85110e07f1"
    />
    
    ## Changes
    - Add safe helper to insert element ranges in the textarea.
    - Extend command‑with‑args pipeline to carry text elements and reuse
    submission prep.
    - Update `/plan` dispatch to switch to plan mode then submit prompt +
    elements.
    - Document new composer behavior and add tests.
    
    ## Notes
    - `/plan` is blocked during active tasks (same as `/review`).
    - Slash‑command elementization recognizes built‑ins and `/prompts:`
    custom commands only.
    
    ## Codex author
    `codex fork 019c16d3-4520-7bb0-9b9d-48720d40a8ab`
  • Session picker shows thread_name if set (#10340)
    - shows names of threads in the ResumePicker used by `/resume` and
    `codex resume` if set, default to preview (previous behaviour) if none
    - adds a `find_thread_names_by_ids` that maps names to IDs in
    `codex-rs/core/src/rollout/session_index.rs`. It reads sequentially in
    normal (instead of reverse order in `codex resume <name>`) the index
    mapping file. This function is called from a list of session (default
    page is 25, pages loaded depends of height of terminal), for which most
    of them will always have at least one session unnamed and require the
    whole file to be read therefore. Could be better and sqlite integration
    will make this better
    - those reads won't be needed when leveraging sqlite
     
    
    Opened questions:
    - We could rename the TUI "Conversation" column to "Name" or "Thread"
    that would feel more accurate. Could be a fast-follow if we implement
    auto-naming as it'll always be a name instead?
  • chore(config) Rename config setting to personality (#10314)
    ## Summary
    Let's make the setting name consistent with the SlashCommand!
    
    ## Testing
    - [x] Updated tests
  • Add websocket telemetry metrics and labels (#10316)
    Summary
    - expose websocket telemetry hooks through the responses client so
    request durations and event processing can be reported
    - record websocket request/event metrics and emit runtime telemetry
    events that the history UI now surfaces
    - improve tests to cover websocket telemetry reporting and guard runtime
    summary updates
    
    
    <img width="824" height="79" alt="Screenshot 2026-01-31 at 5 28 12 PM"
    src="https://github.com/user-attachments/assets/ea9a7965-d8b4-4e3c-a984-ef4fdc44c81d"
    />
  • feat(core,tui,app-server) personality migration (#10307)
    ## Summary
    Keep existing users on Pragmatic, to preserve behavior while new users
    default to Friendly
    
    ## Testing
    - [x] Tested locally
    - [x] add integration tests
  • feat: show runtime metrics in console (#10278)
    Summary of changes:
    
    - Adds a new feature flag: runtime_metrics
      - Declared in core/src/features.rs
      - Added to core/config.schema.json
      - Wired into OTEL init in core/src/otel_init.rs
    
    - Enables on-demand runtime metric snapshots in OTEL
      - Adds runtime_metrics: bool to otel/src/config.rs
      - Enables experimental custom reader features in otel/Cargo.toml
      - Adds snapshot/reset/summary APIs in:
        - otel/src/lib.rs
        - otel/src/metrics/client.rs
        - otel/src/metrics/config.rs
        - otel/src/metrics/error.rs
    
    - Defines metric names and a runtime summary builder
      - New files:
        - otel/src/metrics/names.rs
        - otel/src/metrics/runtime_metrics.rs
      - Summarizes totals for:
        - Tool calls
        - API requests
        - SSE/streaming events
    
    - Instruments metrics collection in OTEL manager
      - otel/src/traces/otel_manager.rs now records:
        - API call counts + durations
        - SSE event counts + durations (success/failure)
        - Tool call metrics now use shared constants
    
    - Surfaces runtime metrics in the TUI
      - Resets runtime metrics at turn start in tui/src/chatwidget.rs
    - Displays metrics in the final separator line in
    tui/src/history_cell.rs
    
    - Adds tests
      - New OTEL tests:
        - otel/tests/suite/snapshot.rs
        - otel/tests/suite/runtime_summary.rs
      - New TUI test:
    - final_message_separator_includes_runtime_metrics in
    tui/src/history_cell.rs
    
    Scope:
    - 19 files changed
    - ~652 insertions, 38 deletions
    
    
    <img width="922" height="169" alt="Screenshot 2026-01-30 at 4 11 34 PM"
    src="https://github.com/user-attachments/assets/1efd754d-a16d-4564-83a5-f4442fd2f998"
    />
  • Fix minor typos in comments and documentation (#10287)
    ## Summary
    
    I have read the contribution guidelines.  
    All changes in this PR are limited to text corrections and do not modify
    any business logic, runtime behavior, or user-facing functionality.
    
    ## Details
    
    This PR fixes several minor typos, including:
    
    - `create` -> `crate`
    - `analagous` -> `analogous`
    - `apply-patch` -> `apply_patch`
    - `codecs` -> `codex`
    - ` '/" ` -> ` '/' `
    - `Respesent` -> `Represent`
  • add missing fields to WebSearchAction and update app-server types (#10276)
    - add `WebSearchAction` to app-server v2 types
    - add `queries` to `WebSearchAction::Search` type
    
    Updated tests.
  • Add enforce_residency to requirements (#10263)
    Add `enforce_residency` to requirements.toml and thread it through to a
    header on `default_client`.
  • fix: update file search directory when session CWD changes (#9279)
    ## Summary
    
    Fixes #9041
    
    - Adds update_search_dir() method to FileSearchManager to allow updating
    the search directory after initialization
    - Calls this method when the session CWD changes: new session, resume,
    or fork
    
    ## Problem
    
    The FileSearchManager was created once with the initial search_dir and
    never updated. When a user:
    
    1. Starts Codex in a non-git directory (e.g., /tmp/random)
    2. Resumes or forks a session from a different workspace
    3. The @filename lookup still searched the original directory
    
    This caused no matches to be returned even when files existed in the
    current workspace.
    
    ## Solution
    
    Update FileSearchManager.search_dir whenever the session working
    directory changes:
    - AppEvent::NewSession: Use current config CWD
    - SessionSelection::Resume: Use resumed session CWD
    - SessionSelection::Fork: Use forked session CWD
    
    ## Test plan
    
    - [ ] Start Codex in /tmp/test-dir (non-git)
    - [ ] Resume a session from a project with actual files
    - [ ] Verify @filename returns matches from the resumed session
    directory
    
    ---------
    
    Co-authored-by: Eric Traut <etraut@openai.com>
  • file-search: multi-root walk (#10240)
    Instead of a separate walker for each root in a multi-root walk, use a
    single walker.
  • Hide /approvals from the slash-command list (#10265)
    `/permissions` is the replacement. `/approvals` still available when
    typing.
  • Make plan highlight use popup grey background (#10253)
    ## Summary
    - align proposed plan background with popup surface color by reusing
    `user_message_bg`
    - remove the custom blue-tinted plan background
    
    <img width="1572" height="1568" alt="image"
    src="https://github.com/user-attachments/assets/63a5341e-4342-4c07-b6b0-c4350c3b2639"
    />
  • chore: rename ChatGpt -> Chatgpt in type names (#10244)
    When using ChatGPT in names of types, we should be consistent, so this
    renames some types with `ChatGpt` in the name to `Chatgpt`. From
    https://rust-lang.github.io/api-guidelines/naming.html:
    
    > In `UpperCamelCase`, acronyms and contractions of compound words count
    as one word: use `Uuid` rather than `UUID`, `Usize` rather than `USize`
    or `Stdin` rather than `StdIn`. In `snake_case`, acronyms and
    contractions are lower-cased: `is_xid_start`.
    
    This PR updates existing uses of `ChatGpt` and changes them to
    `Chatgpt`. Though in all cases where it could affect the wire format, I
    visually inspected that we don't change anything there. That said, this
    _will_ change the codegen because it will affect the spelling of type
    names.
    
    For example, this renames `AuthMode::ChatGPT` to `AuthMode::Chatgpt` in
    `app-server-protocol`, but the wire format is still `"chatgpt"`.
    
    This PR also updates a number of types in `codex-rs/core/src/auth.rs`.
  • Tui: hide Code mode footer label (#10063)
    Title
    Hide Code mode footer label/cycle hint; add Plan footer-collapse
    snapshots
    
    Summary
    - Keep Code mode internal naming but suppress the footer mode label +
    cycle hint when Code is active.
    - Only show the cycle hint when a non‑Code mode indicator is present.
    - Add Plan-mode footer collapse snapshot coverage (empty + queued,
    across widths) and update existing footer collapse snapshots for the new
    Code behavior.
    
    Notes
    - The test run currently fails in codex-cloud-requirements on
    origin/main due to a stale auth.mode field; no fix is included in this
    PR to keep the diff minimal.
    
    Codex author
    `codex resume 019c0296-cfd4-7193-9b0a-6949048e4546`
  • Plan mode: stream proposed plans, emit plan items, and render in TUI (#9786)
    ## Summary
    - Stream proposed plans in Plan Mode using `<proposed_plan>` tags parsed
    in core, emitting plan deltas plus a plan `ThreadItem`, while stripping
    tags from normal assistant output.
    - Persist plan items and rebuild them on resume so proposed plans show
    in thread history.
    - Wire plan items/deltas through app-server protocol v2 and render a
    dedicated proposed-plan view in the TUI, including the “Implement this
    plan?” prompt only when a plan item is present.
    
    ## Changes
    
    ### Core (`codex-rs/core`)
    - Added a generic, line-based tag parser that buffers each line until it
    can disprove a tag prefix; implements auto-close on `finish()` for
    unterminated tags. `codex-rs/core/src/tagged_block_parser.rs`
    - Refactored proposed plan parsing to wrap the generic parser.
    `codex-rs/core/src/proposed_plan_parser.rs`
    - In plan mode, stream assistant deltas as:
      - **Normal text** → `AgentMessageContentDelta`
      - **Plan text** → `PlanDelta` + `TurnItem::Plan` start/completion  
      (`codex-rs/core/src/codex.rs`)
    - Final plan item content is derived from the completed assistant
    message (authoritative), not necessarily the concatenated deltas.
    - Strips `<proposed_plan>` blocks from assistant text in plan mode so
    tags don’t appear in normal messages.
    (`codex-rs/core/src/stream_events_utils.rs`)
    - Persist `ItemCompleted` events only for plan items for rollout replay.
    (`codex-rs/core/src/rollout/policy.rs`)
    - Guard `update_plan` tool in Plan Mode with a clear error message.
    (`codex-rs/core/src/tools/handlers/plan.rs`)
    - Updated Plan Mode prompt to:  
      - keep `<proposed_plan>` out of non-final reasoning/preambles  
      - require exact tag formatting  
      - allow only one `<proposed_plan>` block per turn  
      (`codex-rs/core/templates/collaboration_mode/plan.md`)
    
    ### Protocol / App-server protocol
    - Added `TurnItem::Plan` and `PlanDeltaEvent` to core protocol items.
    (`codex-rs/protocol/src/items.rs`, `codex-rs/protocol/src/protocol.rs`)
    - Added v2 `ThreadItem::Plan` and `PlanDeltaNotification` with
    EXPERIMENTAL markers and note that deltas may not match the final plan
    item. (`codex-rs/app-server-protocol/src/protocol/v2.rs`)
    - Added plan delta route in app-server protocol common mapping.
    (`codex-rs/app-server-protocol/src/protocol/common.rs`)
    - Rebuild plan items from persisted `ItemCompleted` events on resume.
    (`codex-rs/app-server-protocol/src/protocol/thread_history.rs`)
    
    ### App-server
    - Forward plan deltas to v2 clients and map core plan items to v2 plan
    items. (`codex-rs/app-server/src/bespoke_event_handling.rs`,
    `codex-rs/app-server/src/codex_message_processor.rs`)
    - Added v2 plan item tests.
    (`codex-rs/app-server/tests/suite/v2/plan_item.rs`)
    
    ### TUI
    - Added a dedicated proposed plan history cell with special background
    and padding, and moved “• Proposed Plan” outside the highlighted block.
    (`codex-rs/tui/src/history_cell.rs`, `codex-rs/tui/src/style.rs`)
    - Only show “Implement this plan?” when a plan item exists.
    (`codex-rs/tui/src/chatwidget.rs`,
    `codex-rs/tui/src/chatwidget/tests.rs`)
    
    <img width="831" height="847" alt="Screenshot 2026-01-29 at 7 06 24 PM"
    src="https://github.com/user-attachments/assets/69794c8c-f96b-4d36-92ef-c1f5c3a8f286"
    />
    
    ### Docs / Misc
    - Updated protocol docs to mention plan deltas.
    (`codex-rs/docs/protocol_v1.md`)
    - Minor plumbing updates in exec/debug clients to tolerate plan deltas.
    (`codex-rs/debug-client/src/reader.rs`, `codex-rs/exec/...`)
    
    ## Tests
    - Added core integration tests:
      - Plan mode strips plan from agent messages.
      - Missing `</proposed_plan>` closes at end-of-message.  
      (`codex-rs/core/tests/suite/items.rs`)
    - Added unit tests for generic tag parser (prefix buffering, non-tag
    lines, auto-close). (`codex-rs/core/src/tagged_block_parser.rs`)
    - Existing app-server plan item tests in v2.
    (`codex-rs/app-server/tests/suite/v2/plan_item.rs`)
    
    ## Notes / Behavior
    - Plan output no longer appears in standard assistant text in Plan Mode;
    it streams via `PlanDelta` and completes as a `TurnItem::Plan`.
    - The final plan item content is authoritative and may diverge from
    streamed deltas (documented as experimental).
    - Reasoning summaries are not filtered; prompt instructs the model not
    to include `<proposed_plan>` outside the final plan message.
    
    ## Codex Author
    `codex fork 019bec2d-b09d-7450-b292-d7bcdddcdbfb`
  • feat: refactor CodexAuth so invalid state cannot be represented (#10208)
    Previously, `CodexAuth` was defined as follows:
    
    
    https://github.com/openai/codex/blob/d550fbf41afc09d7d7b5ac813aea38de07b2a73f/codex-rs/core/src/auth.rs#L39-L46
    
    But if you looked at its constructors, we had creation for
    `AuthMode::ApiKey` where `storage` was built using a nonsensical path
    (`PathBuf::new()`) and `auth_dot_json` was `None`:
    
    
    https://github.com/openai/codex/blob/d550fbf41afc09d7d7b5ac813aea38de07b2a73f/codex-rs/core/src/auth.rs#L212-L220
    
    By comparison, when `AuthMode::ChatGPT` was used, `api_key` was always
    `None`:
    
    
    https://github.com/openai/codex/blob/d550fbf41afc09d7d7b5ac813aea38de07b2a73f/codex-rs/core/src/auth.rs#L665-L671
    
    https://github.com/openai/codex/pull/10012 took things further because
    it introduced a new `ChatgptAuthTokens` variant to `AuthMode`, which is
    important in when invoking `account/login/start` via the app server, but
    most logic _internal_ to the app server should just reason about two
    `AuthMode` variants: `ApiKey` and `ChatGPT`.
    
    This PR tries to clean things up as follows:
    
    - `LoginAccountParams` and `AuthMode` in `codex-rs/app-server-protocol/`
    both continue to have the `ChatgptAuthTokens` variant, though it is used
    exclusively for the on-the-wire messaging.
    - `codex-rs/core/src/auth.rs` now has its own `AuthMode` enum, which
    only has two variants: `ApiKey` and `ChatGPT`.
    - `CodexAuth` has been changed from a struct to an enum. It is a
    disjoint union where each variant (`ApiKey`, `ChatGpt`, and
    `ChatGptAuthTokens`) have only the associated fields that make sense for
    that variant.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/10208).
    * #10224
    * __->__ #10208
  • Fetch Requirements from cloud (#10167)
    Load requirements from Codex Backend. It only does this for enterprise
    customers signed in with ChatGPT.
    
    Todo in follow-up PRs:
    * Add to app-server and exec too
    * Switch from fail-open to fail-closed on failure
  • Conversation naming (#8991)
    Session renaming:
    - `/rename my_session`
    - `/rename` without arg and passing an argument in `customViewPrompt`
    - AppExitInfo shows resume hint using the session name if set instead of
    uuid, defaults to uuid if not set
    - Names are stored in `CODEX_HOME/sessions.jsonl`
    
    Session resuming:
    - codex resume <name> lookup for `CODEX_HOME/sessions.jsonl` first entry
    matching the name and resumes the session
    
    ---------
    
    Co-authored-by: jif-oai <jif@openai.com>
  • feat: add output to /ps (#10154)
    <img width="599" height="238" alt="Screenshot 2026-01-29 at 13 24 57"
    src="https://github.com/user-attachments/assets/1e9a5af2-f649-476c-b310-ae4938814538"
    />
  • feat(tui): route employee feedback follow-ups to internal link (#10198)
    ## Problem
    OpenAI employees were sent to the public GitHub issue flow after
    `/feedback`, which is the wrong follow-up path internally.
    
    ## Mental model
    After feedback upload completes, we render a follow-up link/message.
    That link should be audience-aware but must not change the upload
    pipeline itself.
    
    ## Non-goals
    - Changing how feedback is captured or uploaded
    - Changing external user behavior
    
    ## Tradeoffs
    We detect employees via the authenticated account email suffix
    (`@openai.com`). If the email is unavailable (e.g., API key auth), we
    default to the external behavior.
    
    ## Architecture
    - Introduce `FeedbackAudience` and thread it from `App` -> `ChatWidget`
    -> `FeedbackNoteView`
    - Gate internal messaging/links on `FeedbackAudience::OpenAiEmployee`
    - Internal follow-up link is now `http://go/codex-feedback-internal`
    - External GitHub URL remains byte-for-byte identical
    
    ## Observability
    No new telemetry; this only changes rendered follow-up instructions.
    
    ## Tests
    - `just fmt`
    - `cargo test -p codex-tui --lib`
  • tui: add feature-gated /plan slash command to switch to Plan mode (#10103)
    ## Summary
    Adds a simple `/plan` slash command in the TUI that switches the active
    collaboration mode to Plan mode. The command is only available when the
    `collaboration_modes` feature is enabled.
    
    ## Changes
    - Add `plan_mask` helper in `codex-rs/tui/src/collaboration_modes.rs`
    - Add `SlashCommand::Plan` metadata in
    `codex-rs/tui/src/slash_command.rs`
    - Implement and hard-gate `/plan` dispatch in
    `codex-rs/tui/src/chatwidget.rs`
    - Hide `/plan` when collaboration modes are disabled in
    `codex-rs/tui/src/bottom_pane/slash_commands.rs`
    - Update command popup tests in
    `codex-rs/tui/src/bottom_pane/command_popup.rs`
    - Add a focused unit test for `/plan` in
    `codex-rs/tui/src/chatwidget/tests.rs`
    
    ## Behavior notes
    - `/plan` is now a no-op if `Feature::CollaborationModes` is disabled.
    - When enabled, `/plan` switches directly to Plan mode without opening
    the picker.
    
    ## Codex author
    `codex resume 019c05da-d7c3-7322-ae2c-3ca38d0ef702`
  • feat(app-server): support external auth mode (#10012)
    This enables a new use case where `codex app-server` is embedded into a
    parent application that will directly own the user's ChatGPT auth
    lifecycle, which means it owns the user’s auth tokens and refreshes it
    when necessary. The parent application would just want a way to pass in
    the auth tokens for codex to use directly.
    
    The idea is that we are introducing a new "auth mode" currently only
    exposed via app server: **`chatgptAuthTokens`** which consist of the
    `id_token` (stores account metadata) and `access_token` (the bearer
    token used directly for backend API calls). These auth tokens are only
    stored in-memory. This new mode is in addition to the existing `apiKey`
    and `chatgpt` auth modes.
    
    This PR reuses the shape of our existing app-server account APIs as much
    as possible:
    - Update `account/login/start` with a new `chatgptAuthTokens` variant,
    which will allow the client to pass in the tokens and have codex
    app-server use them directly. Upon success, the server emits
    `account/login/completed` and `account/updated` notifications.
    - A new server->client request called
    `account/chatgptAuthTokens/refresh` which the server can use whenever
    the access token previously passed in has expired and it needs a new one
    from the parent application.
    
    I leveraged the core 401 retry loop which typically triggers auth token
    refreshes automatically, but made it pluggable:
    - **chatgpt** mode refreshes internally, as usual.
    - **chatgptAuthTokens** mode calls the client via
    `account/chatgptAuthTokens/refresh`, the client responds with updated
    tokens, codex updates its in-memory auth, then retries. This RPC has a
    10s timeout and handles JSON-RPC errors from the client.
    
    Also some additional things:
    - chatgpt logins are blocked while external auth is active (have to log
    out first. typically clients will pick one OR the other, not support
    both)
    - `account/logout` clears external auth in memory
    - Ensures that if `forced_chatgpt_workspace_id` is set via the user's
    config, we respect it in both:
    - `account/login/start` with `chatgptAuthTokens` (returns a JSON-RPC
    error back to the client)
    - `account/chatgptAuthTokens/refresh` (fails the turn, and on next
    request app-server will send another `account/chatgptAuthTokens/refresh`
    request to the client).
  • [Codex][CLI] Show model-capacity guidance on 429 (#10118)
    ###### Problem
    Users get generic 429s with no guidance when a model is at capacity.
    ###### Solution
    Detect model-cap headers, surface a clear “try a different model”
    message, and keep behavior non‑intrusive (no auto‑switch).
    ###### Scope
    CLI/TUI only; protocol + error mapping updated to carry model‑cap info.
    ###### Tests
          - just fmt
          - cargo test -p codex-tui
    - cargo test -p codex-core --lib
    shell_snapshot::tests::try_new_creates_and_deletes_snapshot_file --
    --nocapture (ran in isolated env)
          - validate local build with backend
         
    <img width="719" height="845" alt="image"
    src="https://github.com/user-attachments/assets/1470b33d-0974-4b1f-b8e6-d11f892f4b54"
    />
  • updating comment to better indicate intent of skipping quit in the main slash command menu (#10186)
    Updates comment indicating intent for skipping `quit` in the main slash
    command dropdown.
  • Better handling skill depdenencies on ENV VAR. (#9017)
    An experimental flow for env var skill dependencies. Skills can now
    declare required env vars in SKILL.md; if missing, the CLI prompts the
    user to get the value, and Core will store it in memory (eventually to a
    local persistent store)
    <img width="790" height="169" alt="image"
    src="https://github.com/user-attachments/assets/cd928918-9403-43cb-a7e7-b8d59bcccd9a"
    />
  • feat: reduce span exposition (#10171)
    This only avoids the creation of duplicates spans
  • feat: add log db (#10086)
    Add a log DB. The goal is just to store our logs in a `.sqlite` DB to
    make it easier to crawl them and drop the oldest ones.
  • fix: ignore key release events during onboarding (#10131)
    ## Summary
    - guard onboarding key handling to ignore KeyEventKind::Release
    - handle key events at the onboarding screen boundary to avoid
    double-triggering widgets
    
    ## Related
    - https://github.com/ratatui/ratatui/issues/347
    
    ## Testing
    - cd codex-rs && just fmt
    - cd codex-rs && cargo test -p codex-tui
  • fix(tui) reorder personality command (#10134)
    ## Summary
    Reorder it down the list
    
    ## Testing 
    - [x] Tests pass
  • [connectors] Support connectors part 2 - slash command and tui (#9728)
    - [x] Support `/apps` slash command to browse the apps in tui.
    - [x] Support inserting apps to prompt using `$`.
    - [x] Lots of simplification/renaming from connectors to apps.
  • removing quit from dropdown menu, but not autocomplete [cli] (#10128)
    Currently we have both `\quit` and `\exit` which do the same thing. This
    removes `\quit` from the slash command menu but allows it to still be an
    autocomplete option & working for those used to that command.
    
    `/quit` autocomplete:
    <img width="232" height="108" alt="Screenshot 2026-01-28 at 4 32 53 PM"
    src="https://github.com/user-attachments/assets/d71e079f-77f6-4edc-9590-44a01e2a4ff5"
    />
    
    slash command menu:
    <img width="425" height="191" alt="Screenshot 2026-01-28 at 4 32 36 PM"
    src="https://github.com/user-attachments/assets/a9458cff-1784-4ce0-927d-43ad13d2a97c"
    />
  • chore(config) personality as a feature (#10116)
    ## Summary
    Sets up an explicit Feature flag for `/personality`, so users can now
    opt in to it via `/experimental`. #10114 also updates the config
    
    ## Testing
    - [x] Tested locally
  • Added tui.notifications_method config option (#10043)
    This PR adds a new `tui.notifications_method` config option that accepts
    values of "auto", "osc9" and "bel". It defaults to "auto", which
    attempts to auto-detect whether the terminal supports OSC 9 escape
    sequences and falls back to BEL if not.
    
    The PR also removes the inconsistent handling of notifications on
    Windows when WSL was used.
  • allow elevated sandbox to be enabled without base experimental flag (#10028)
    elevated flag = elevated sandbox
    experimental flag = non-elevated sandbox
    both = elevated
  • file-search: improve file query perf (#9939)
    switch nucleo-matcher for nucleo and use a "file search session" w/ live
    updating query instead of a single hermetic run per query.
  • Refine request_user_input TUI interactions and option UX (#10025)
    ## Summary
    Overhaul the ask‑user‑questions TUI to support “Other/None” answers,
    better notes handling, improved option selection
    UX, and a safer submission flow with confirmation for unanswered
    questions.
    
    Multiple choice (number keys for quick selection, up/down or jk for
    cycling through options):
    <img width="856" height="169" alt="Screenshot 2026-01-27 at 7 22 29 PM"
    src="https://github.com/user-attachments/assets/cabd1b0e-25e0-4859-bd8f-9941192ca274"
    />
    
    Tab to add notes:
    <img width="856" height="197" alt="Screenshot 2026-01-27 at 7 22 45 PM"
    src="https://github.com/user-attachments/assets/a807db5e-e966-412c-af91-6edc60062f35"
    />
    
    Freeform (also note enter tooltip is highlighted on last question to
    indicate questions UI will be exited upon submission):
    <img width="854" height="112" alt="Screenshot 2026-01-27 at 7 23 13 PM"
    src="https://github.com/user-attachments/assets/2e7b88bf-062b-4b9f-a9da-c9d8c8a59643"
    />
    
    Confirmation dialogue (submitting with unanswered questions):
    <img width="854" height="126" alt="Screenshot 2026-01-27 at 7 23 29 PM"
    src="https://github.com/user-attachments/assets/93965c8f-54ac-45bc-a660-9625bcd101f8"
    />
    
    ## Key Changes
    - **Options UI refresh**
    - Render options as numbered entries; allow number keys to select &
    submit.
    - Remove “Option X/Y” header and allow the question UI height to expand
    naturally.
    - Keep spacing between question, options, and notes even when notes are
    visible.
    - Hide the title line and render the question prompt in cyan **only when
    uncommitted**.
    
    - **“Other / None of the above” support**
      - Wire `isOther` to add “None of the above”.
      - Add guidance text: “Optionally, add details in notes (tab).”
    
    - **Notes composer UX**
    - Remove “Notes” heading; place composer directly under the selected
    option.
    - Preserve pending paste placeholders across question navigation and
    after submission.
      - Ctrl+C clears notes **only when the notes composer has focus**.
      - Ctrl+C now triggers an immediate redraw so the clear is visible.
    
    - **Committed vs uncommitted state**
      - Introduce a unified `answer_committed` flag per question.
    - Editing notes (including adding text or pastes) marks the answer
    uncommitted.
    - Changing the option highlight (j/k, up/down) marks the answer
    uncommitted.
      - Clearing options (Backspace/Delete) also clears pending notes.
      - Question prompt turns cyan only when the answer is uncommitted.
    
    - **Submission safety & confirmation**
      - Only submit notes/freeform text once explicitly committed.
    - Last-question submit with unanswered questions shows a confirmation
    dialog.
      - Confirmation options:
        1. **Proceed** (default)
        2. **Go back**
      - Description reflects count: “Submit with N unanswered question(s).”
      - Esc/Backspace in confirmation returns to first unanswered question.
      - Ctrl+C in confirmation interrupts and exits the overlay.
    
    - **Footer hints**
    - Cyan highlight restored for “enter to submit answer” / “enter to
    submit all”.
    
    ## Codex author
    `codex fork 019c00ed-323a-7000-bdb5-9f9c5a635bd9`
  • [skills] Auto install MCP dependencies when running skils with dependency specs. (#9982)
    Auto install MCP dependencies when running skils with dependency specs.
  • Restore image attachments/text elements when recalling input history (Up/Down) (#9628)
    **Summary**
    - Up/Down input history now restores image attachments and text elements
    for local entries.
    - Composer history stores rich local entries (text + text elements +
    local image paths) while persistent history remains text-only.
    - Added tests to verify history recall rehydrates image placeholders and
    attachments in both `tui` and `tui2`.
    
    **Changes**
    - `tui/src/bottom_pane/chat_composer_history.rs`: store `HistoryEntry`
    (text + elements + image paths) for local history; adapt navigation +
    tests.
    - `tui2/src/bottom_pane/chat_composer_history.rs`: same as above.
    - `tui/src/bottom_pane/chat_composer.rs`: record rich history entries
    and restore them on Up/Down; update Ctrl+C history and tests.
    - `tui2/src/bottom_pane/chat_composer.rs`: same as above.
  • tui: stabilize shortcut overlay snapshots on WSL (#9359)
    Fixes #9361
    
    ## Context
    Split out from #9059 per review:
    https://github.com/openai/codex/pull/9059#issuecomment-3757859033
    
    ## Summary
    The shortcut overlay renders different paste-image bindings on WSL
    (Ctrl+Alt+V) vs non-WSL (Ctrl+V), which makes snapshot tests
    non-deterministic when run under WSL.
    
    ## Changes
    - Gate WSL detection behind `cfg(not(test))` so snapshot tests are
    deterministic across environments.
    - Add a focused unit test that still asserts the WSL-specific
    paste-image binding.
    
    ## Testing
    - `just fmt`
    - `just fix -p codex-tui`
    - `just fix -p codex-tui2`
    - `cargo test -p codex-tui`
    - `cargo test -p codex-tui2`
  • Ask user question UI footer improvements (#9949)
    ## Summary
    
    Polishes the `request_user_input` TUI overlay
    
    Question 1 (unanswered)
    <img width="853" height="167" alt="Screenshot 2026-01-27 at 1 30 09 PM"
    src="https://github.com/user-attachments/assets/3c305644-449e-4e8d-a47b-d689ebd8702c"
    />
    
    Tab to add notes
    <img width="856" height="198" alt="Screenshot 2026-01-27 at 1 30 25 PM"
    src="https://github.com/user-attachments/assets/0d2801b0-df0c-49ae-85af-e6d56fc2c67c"
    />
    
    Question 2 (unanswered)
    <img width="854" height="168" alt="Screenshot 2026-01-27 at 1 30 55 PM"
    src="https://github.com/user-attachments/assets/b3723062-51f9-49c9-a9ab-bb1b32964542"
    />
    
    Ctrl+p or h to go back to q1 (answered)
    <img width="853" height="195" alt="Screenshot 2026-01-27 at 1 31 27 PM"
    src="https://github.com/user-attachments/assets/c602f183-1c25-4c51-8f9f-e565cb6bd637"
    />
    
    Unanswered freeform
    <img width="856" height="126" alt="Screenshot 2026-01-27 at 1 31 42 PM"
    src="https://github.com/user-attachments/assets/7e3d9d8b-820b-4b9a-9ef2-4699eed484c5"
    />
    
    ## Key changes
    
    - Footer tips wrap at tip boundaries (no truncation mid‑tip); footer
    height scales to wrapped tips.
    - Keep tooltip text as Esc: interrupt in all states.
    - Make the full Tab: add notes tip cyan/bold when applicable; hide notes
    UI by default.
    - Notes toggling/backspace:
    - Tab opens notes when an option is selected; Tab again clears notes and
    hides the notes UI.
        - Backspace in options clears the current selection.
        - Backspace in empty notes closes notes and returns to options.
    - Selection/answering behavior:
    - Option questions highlight a default option but are not answered until
    Enter.
    - Enter no longer auto‑selects when there’s no selection (prevents
    accidental answers).
        - Notes submission can commit the selected option when present.
    - Freeform questions require Enter with non‑empty text to mark answered;
    drafts are not submitted unless committed.
    - Unanswered cues:
        - Skipped option questions count as unanswered.
        - Unanswered question titles are highlighted for visibility.
    - Typing/navigation in options:
        - Typing no longer opens notes; notes are Tab‑only.
    - j/k move option selection; h/l switch questions (Ctrl+n/Ctrl+p still
    work).
    
    ## Tests
    
    - Added unit coverage for:
        - tip‑level wrapping
        - focus reset when switching questions with existing drafts
        - backspace clearing selection
        - backspace closing empty notes
        - typing in options does not open notes
        - freeform draft submission gating
        - h/l question navigation in options
    - Updated snapshots, including narrow footer wrap.
    
    ## Why
    
    These changes make the ask‑user‑question overlay:
    
    - safer (no silent auto‑selection or accidental freeform submission),
    - clearer (tips wrap cleanly and unanswered states stand out),
    - more ergonomic (Tab explicitly controls notes; backspace acts like
    undo/close).
    
    ## Codex author
    `codex fork 019bfc3c-2c42-7982-9119-fee8b9315c2f`
    
    ---------
    
    Co-authored-by: Ahmed Ibrahim <aibrahim@openai.com>
  • Clarify external editor env var message (#10030)
    ### Motivation
    - Improve UX by making it explicit that `VISUAL`/`EDITOR` must be set
    before launching Codex, not during a running session.
    
    ### Description
    - Update the external editor error text in `codex-rs/tui/src/app.rs` to:
    `"Cannot open external editor: set $VISUAL or $EDITOR before starting
    Codex."` and run `just fmt` to apply formatting.
    
    ### Testing
    - Ran `just fmt` successfully; attempted `cargo test -p codex-tui` but
    it failed due to network errors when fetching git dependencies (tests
    did not complete).
    
    ------
    [Codex
    Task](https://chatgpt.com/codex/tasks/task_i_6972c2c984948329b1a37d5c5839aff3)
  • Fix: Render MCP image outputs regardless of ordering (#9815)
    ## What?
    - Render an MCP image output cell whenever a decodable image block
    exists in `CallToolResult.content` (including text-before-image or
    malformed image before valid image).
    
    ## Why?
    - Tool results that include caption text before the image currently drop
    the image output cell.
    - A malformed image block can also suppress later valid image output.
    
    ## How?
    - Iterate `content` and return the first successfully decoded image
    instead of only checking the first block.
    - Add unit tests that cover text-before-image ordering and
    invalid-image-before-valid.
    
    ## Before
    ```rust
    let image = match result {
        Ok(mcp_types::CallToolResult { content, .. }) => {
            if let Some(mcp_types::ContentBlock::ImageContent(image)) = content.first() {
                // decode image (fails -> None)
            } else {
                None
            }
        }
        _ => None,
    }?;
    ```
    ## After
    ```rust
    let image = result
        .as_ref()
        .ok()?
        .content
        .iter()
        .find_map(decode_mcp_image)?;
    ```
    
    ## Risk / Impact
    - Low: only affects image cell creation for MCP tool results; no change
    for non-image outputs.
    
    ## Tests
    - [x] `just fmt`
    - [x] `cargo test -p codex-tui`
    - [x] Rerun after branch update (2026-01-27): `just fmt`, `cargo test -p
    codex-tui`
    
    Manual testing
    
    # Manual testing: MCP image tool result rendering (Codex TUI)
    
    # Build the rmcp stdio test server binary:
    cd codex-rs
    cargo build -p codex-rmcp-client --bin test_stdio_server
    
    # Register the server as an MCP server (absolute path to the built binary):
    codex mcp add mcpimg -- /Users/joshka/code/codex-pr-review/codex-rs/target/debug/test_stdio_server
    
    # Then in Codex TUI, ask it to call:
    - mcpimg.image_scenario({"scenario":"image_only"})
    - mcpimg.image_scenario({"scenario":"text_then_image","caption":"Here is the image:"})
    - mcpimg.image_scenario({"scenario":"invalid_base64_then_image"})
    - mcpimg.image_scenario({"scenario":"invalid_image_bytes_then_image"})
    - mcpimg.image_scenario({"scenario":"multiple_valid_images"})
    - mcpimg.image_scenario({"scenario":"image_then_text","caption":"Here is the image:"})
    - mcpimg.image_scenario({"scenario":"text_only","caption":"Here is the image:"})
    
    # Expected:
    # - You should see an extra history cell: "tool result (image output)" when the
    #   tool result contains at least one decodable image block (even if earlier
    #   blocks are text or invalid images).
    
    
    Fixes #9814
    
    ---------
    
    Co-authored-by: Josh McKinney <joshka@openai.com>
  • remove sandbox globals. (#9797)
    Threads sandbox updates through OverrideTurnContext for active turn
    Passes computed sandbox type into safety/exec