Commit Graph

3134 Commits

  • chore: update listMcpServerStatus to be non-blocking (#8151)
    ### Summary
    * Update `listMcpServerStatus` to be non-blocking by wrapping it with
    tokio:spawn.
  • feat: unified exec footer (#8117)
    # With `unified_exec`
    Known tools are correctly casted
    <img width="1150" height="312" alt="Screenshot 2025-12-16 at 19 27 28"
    src="https://github.com/user-attachments/assets/24150ee5-e88d-461b-a459-483c24784196"
    />
    If a session exit the turn, we render it with the "Ran ..."
    <img width="1168" height="355" alt="Screenshot 2025-12-16 at 19 27 58"
    src="https://github.com/user-attachments/assets/3f00b60c-2d57-4f9d-a201-9cc8388957cb"
    />
    If a session does not exit during the turn, it is closed at the end of
    the turn but this is not rendered
    <img width="642" height="342" alt="Screenshot 2025-12-16 at 19 34 37"
    src="https://github.com/user-attachments/assets/c2bd9283-7017-4915-ba73-c52199b0b28e"
    />
    
    # Without `unified_exec`
    No changes
    <img width="740" height="603" alt="Screenshot 2025-12-16 at 19 31 21"
    src="https://github.com/user-attachments/assets/ca5d90fe-a9b2-42ba-bcd7-3e98c4ed22e8"
    />
  • feat: experimental menu (#8071)
    This will automatically render any `Stage::Beta` features.
    
    The change only gets applied to the *next session*. This started as a
    bug but actually this is a good thing to prevent out of distribution
    push
    
    <img width="986" height="288" alt="Screenshot 2025-12-15 at 15 38 35"
    src="https://github.com/user-attachments/assets/78b7a71d-0e43-4828-a118-91c5237909c7"
    />
    
    
    <img width="509" height="109" alt="Screenshot 2025-12-15 at 17 35 44"
    src="https://github.com/user-attachments/assets/6933de52-9b66-4abf-b58b-a5f26d5747e2"
    />
  • feat: Constrain values for approval_policy (#7778)
    Constrain `approval_policy` through new `admin_policy` config.
    
    This PR will:
    1. Add a `admin_policy` section to config, with a single field (for now)
    `allowed_approval_policies`. This list constrains the set of
    user-settable `approval_policy`s.
    2. Introduce a new `Constrained<T>` type, which combines a current value
    and a validator function. The validator function ensures disallowed
    values are not set.
    3. Change the type of `approval_policy` on `Config` and
    `SessionConfiguration` from `AskForApproval` to
    `Constrained<AskForApproval>`. The validator function is set by the
    values passed into `allowed_approval_policies`.
    4. `GenericDisplayRow`: add a `disabled_reason: Option<String>`. When
    set, it disables selection of the value and indicates as such in the
    menu. This also makes it unselectable with arrow keys or numbers. This
    is used in the `/approvals` menu.
    
    Follow ups are:
    1. Do the same thing to `sandbox_policy`.
    2. Propagate the allowed set of values through app-server for the
    extension (though already this should prevent app-server from setting
    this values, it's just that we want to disable UI elements that are
    unsettable).
    
    Happy to split this PR up if you prefer, into the logical numbered areas
    above. Especially if there are parts we want to gavel on separately
    (e.g. admin_policy).
    
    Disabled full access:
    <img width="1680" height="380" alt="image"
    src="https://github.com/user-attachments/assets/1fb61c8c-1fcb-4dc4-8355-2293edb52ba0"
    />
    
    Disabled `--yolo` on startup:
    <img width="749" height="76" alt="image"
    src="https://github.com/user-attachments/assets/0a1211a0-6eb1-40d6-a1d7-439c41e94ddb"
    />
    
    CODEX-4087
  • feat: change ConfigLayerName into a disjoint union rather than a simple enum (#8095)
    This attempts to tighten up the types related to "config layers."
    Currently, `ConfigLayerEntry` is defined as follows:
    
    
    https://github.com/openai/codex/blob/bef36f4ae765f471d7cd69372fcf1b92c8f0367a/codex-rs/core/src/config_loader/state.rs#L19-L25
    
    but the `source` field is a bit of a lie, as:
    
    - for `ConfigLayerName::Mdm`, it is
    `"com.openai.codex/config_toml_base64"`
    - for `ConfigLayerName::SessionFlags`, it is `"--config"`
    - for `ConfigLayerName::User`, it is `"config.toml"` (just the file
    name, not the path to the `config.toml` on disk that was read)
    - for `ConfigLayerName::System`, it seems like it is usually
    `/etc/codex/managed_config.toml` in practice, though on Windows, it is
    `%CODEX_HOME%/managed_config.toml`:
    
    
    https://github.com/openai/codex/blob/bef36f4ae765f471d7cd69372fcf1b92c8f0367a/codex-rs/core/src/config_loader/layer_io.rs#L84-L101
    
    All that is to say, in three out of the four `ConfigLayerName`, `source`
    is a `PathBuf` that is not an absolute path (or even a true path).
    
    This PR tries to uplevel things by eliminating `source` from
    `ConfigLayerEntry` and turning `ConfigLayerName` into a disjoint union
    named `ConfigLayerSource` that has the appropriate metadata for each
    variant, favoring the use of `AbsolutePathBuf` where appropriate:
    
    ```rust
    pub enum ConfigLayerSource {
        /// Managed preferences layer delivered by MDM (macOS only).
        #[serde(rename_all = "camelCase")]
        #[ts(rename_all = "camelCase")]
        Mdm { domain: String, key: String },
        /// Managed config layer from a file (usually `managed_config.toml`).
        #[serde(rename_all = "camelCase")]
        #[ts(rename_all = "camelCase")]
        System { file: AbsolutePathBuf },
        /// Session-layer overrides supplied via `-c`/`--config`.
        SessionFlags,
        /// User config layer from a file (usually `config.toml`).
        #[serde(rename_all = "camelCase")]
        #[ts(rename_all = "camelCase")]
        User { file: AbsolutePathBuf },
    }
    ```
  • Add public skills + improve repo skill discovery and error UX (#8098)
    1. Adds SkillScope::Public end-to-end (core + protocol) and loads skills
    from the public cache directory
    2. Improves repo skill discovery by searching upward for the nearest
    .codex/skills within a git repo
    3. Deduplicates skills by name with deterministic ordering to avoid
    duplicates across sources
    4. Fixes garbled “Skill errors” overlay rendering by preventing pending
    history lines from being injected during the modal
    5. Updates the project docs “Skills” intro wording to avoid hardcoded
    paths
  • chore(apply-patch) unicode scenario (#8141)
    ## Summary
    Adds a unicode scenario, and fills in files on failing scenarios to
    ensure directory state is unchanged, for completeness
    
    ## Testing
    - [x] only changes tests
  • [app-server] add new RawResponseItem v2 event (#8152)
    ``codex/event/raw_response_item` (v1) -> `rawResponseItem/completed`
    (v1).
    
    test client log:
    ````
    < {
    <   "method": "codex/event/raw_response_item",
    <   "params": {
    <     "conversationId": "019b29f7-b089-7140-a535-3fe681562c15",
    <     "id": "0",
    <     "msg": {
    <       "item": {
    <         "arguments": "{\"command\":\"sed -n '1,160p' Cargo.toml\",\"workdir\":\"/Users/celia/code/codex/codex-rs\"}",
    <         "call_id": "call_DrqbdB2jPxezPWc19YVEEt3h",
    <         "name": "shell_command",
    <         "type": "function_call"
    <       },
    <       "type": "raw_response_item"
    <     }
    <   }
    < }
    < {
    <   "method": "rawResponseItem/completed",
    <   "params": {
    <     "item": {
    <       "arguments": "{\"command\":\"sed -n '1,160p' Cargo.toml\",\"workdir\":\"/Users/celia/code/codex/codex-rs\"}",
    <       "call_id": "call_DrqbdB2jPxezPWc19YVEEt3h",
    <       "name": "shell_command",
    <       "type": "function_call"
    <     },
    <     "threadId": "019b29f7-b089-7140-a535-3fe681562c15",
    <     "turnId": "0"
    <   }
    < }
    ```
  • Add user_agent header (#8149)
    add `user_agent` header and remove rust tool chain
  • include new windows binaries in npm package. (#8140)
    The Windows Elevated Sandbox uses two new binaries:
    
    codex-windows-sandbox-setup.exe
    codex-command-runner.exe
    
    This PR includes them when installing native deps and packaging for npm
  • chore: update listMcpServers to listMcpServerStatus (#8114)
    ### Summary
    * rename app server `listMcpServers` to `listMcpServerStatuses`.
  • docs: refine tui2 viewport roadmap (#8122)
    Update the tui2 viewport/history design doc with current status and a
    prioritized roadmap (scroll feel, selection/copy correctness, streaming
    wrap polish, terminal integration, and longer-term per-cell
    interactivity ideas).
  • chore(apply-patch) move invocation tests (#8111)
    ## Summary:
    This PR is a pure copy and paste of tests from lib.rs into
    invocation.rs, to colocate logic and tests.
    
    ## Testing
    - [x] Purely a test refactor
  • Revert "chore: review in read-only (#7593)" (#8127)
    This reverts commit 291b54a762.
    
    This commit was intended to prevent the model from making code changes
    during `/review`, which is sometimes does. Unfortunately, it has other
    unintended side effects that cause `/review` to fail in a variety of
    ways. See #8115 and #7815. We've therefore decided to revert this
    change.
  • feat(sdk): add xhigh reasoning effort support to TypeScript SDK (#8108)
    Add "xhigh" to the ModelReasoningEffort type to match the Rust backend
    which already supports this reasoning level for models like
    gpt-5.1-codex-max.
  • fix tui2 compile error (#8124)
    I'm not sure if this fix is ​​correct for the intended change in #7601,
    but at least the compilation error is fixed.
    
    regression: #7601
    
    ```
    error[E0004]: non-exhaustive patterns: `TuiEvent::Mouse(_)` not covered
       --> tui2/src/update_prompt.rs:57:19
        |
     57 |             match event {
        |                   ^^^^^ pattern `TuiEvent::Mouse(_)` not covered
        |
    note: `TuiEvent` defined here
       --> tui2/src/tui.rs:122:10
        |
    122 | pub enum TuiEvent {
        |          ^^^^^^^^
    ...
    126 |     Mouse(crossterm::event::MouseEvent),
        |     ----- not covered
        = note: the matched value is of type `TuiEvent`
    help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
        |
     64 ~                 },
     65 +                 TuiEvent::Mouse(_) => todo!()
        |
    ```
    
    Signed-off-by: Koichi Shiraishi <zchee.io@gmail.com>
  • Upgrade GitHub Actions for Node 24 compatibility (#8102)
    ## Summary
    
    Upgrade GitHub Actions to their latest versions to ensure compatibility
    with Node 24, as Node 20 will reach end-of-life in April 2026.
    
    ## Changes
    
    | Action | Old Version(s) | New Version | Release | Files |
    |--------|---------------|-------------|---------|-------|
    | `actions/setup-node` |
    [`v5`](https://github.com/actions/setup-node/releases/tag/v5) |
    [`v6`](https://github.com/actions/setup-node/releases/tag/v6) |
    [Release](https://github.com/actions/setup-node/releases/tag/v6) |
    ci.yml, rust-release.yml, sdk.yml, shell-tool-mcp-ci.yml,
    shell-tool-mcp.yml |
    
    ## Context
    
    Per [GitHub's
    announcement](https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/),
    Node 20 is being deprecated and runners will begin using Node 24 by
    default starting March 4th, 2026.
    
    ### Why this matters
    
    - **Node 20 EOL**: April 2026
    - **Node 24 default**: March 4th, 2026
    - **Action**: Update to latest action versions that support Node 24
    
    ### Security Note
    
    Actions that were previously pinned to commit SHAs remain pinned to SHAs
    (updated to the latest release SHA) to maintain the security benefits of
    immutable references.
    
    ### Testing
    
    These changes only affect CI/CD workflow configurations and should not
    impact application functionality. The workflows should be tested by
    running them on a branch before merging.
  • chore: mac codesign refactor (#8085)
    ### Summary
    Similar to our linux and windows codesign, moving mac codesign logic
    into its own files.
  • chore(apply-patch) move invocation parsing (#8110)
    lib.rs has grown quite large, and mixes two responsibilities:
    1. executing patch operations
    2. parsing apply_patch invocations via a shell command
    
    This PR splits out (2) into its own file, so we can work with it more
    easily. We are explicitly NOT moving tests in this PR, to ensure
    behavior stays the same and we can avoid losing coverage via merge
    conflicts. Tests are moved in a subsequent PR.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8110).
    * #8111
    * __->__ #8110
  • bug fixes and perf improvements for elevated sandbox setup (#8094)
    a few fixes based on testing feedback:
    * ensure cap_sid file is always written by elevated setup.
    * always log to same file whether using elevated sandbox or not
    * process potentially slow ACE write operations in parallel
    * dedupe write roots so we don't double process any
    * don't try to create read/write ACEs on the same directories, due to
    race condition
  • nit: trace span for regular task (#8053)
    Logs are too spammy
    
    ---------
    
    Co-authored-by: Anton Panasenko <apanasenko@openai.com>
  • feat: unified exec footer (#8067)
    <img width="452" height="205" alt="Screenshot 2025-12-15 at 17 54 44"
    src="https://github.com/user-attachments/assets/9ece0b1c-8387-4dfc-b883-c6a68ea1b663"
    />
  • refactor TUI event loop to enable dropping + recreating crossterm event stream (#7961)
    Introduces an `EventBroker` between the crossterm `EventStream` source
    and the consumers in the TUI. This enables dropping + recreating the
    `crossterm_events` without invalidating the consumer.
    
    Dropping and recreating the crossterm event stream enables us to fully
    relinquish `stdin` while the app keeps running. If the stream is not
    dropped, it will continue to read from `stdin` even when it is not
    actively being polled, potentially stealing input from other processes.
    See
    [here](https://www.reddit.com/r/rust/comments/1f3o33u/myterious_crossterm_input_after_running_vim/?utm_source=chatgpt.com)
    and [here](https://ratatui.rs/recipes/apps/spawn-vim/) for details.
    
    ### Tests
    Added tests for new `EventBroker` setup, existing tests pass, tested
    locally.
  • feat: if .codex is a sub-folder of a writable root, then make it read-only to the sandbox (#8088)
    In preparation for in-repo configuration support, this updates
    `WritableRoot::get_writable_roots_with_cwd()` to include the `.codex`
    subfolder in `WritableRoot.read_only_subpaths`, if it exists, as we
    already do for `.git`.
    
    As noted, currently, like `.git`, `.codex` will only be read-only under
    macOS Seatbelt, but we plan to bring support to other OSes, as well.
    
    Updated the integration test in `seatbelt.rs` so that it actually
    attempts to run the generated Seatbelt commands, verifying that:
    
    - trying to write to `.codex/config.toml` in a writable root fails
    - trying to write to `.git/hooks/pre-commit` in a writable root fails
    - trying to write to the writable root containing the `.codex` and
    `.git` subfolders succeeds
  • refactor(tui2): make transcript line metadata explicit (#8089)
    This is a pure refactor only change.
    
    Replace the flattened transcript line metadata from `Option<(usize,
    usize)>` to an explicit
    `TranscriptLineMeta::{CellLine { cell_index, line_in_cell }, Spacer}`
    enum.
    
    This makes spacer rows unambiguous, removes “tuple semantics” from call
    sites, and keeps the
    scroll anchoring model clearer and aligned with the viewport/history
    design notes.
    
    Changes:
    - Introduce `TranscriptLineMeta` and update `TranscriptScroll` helpers
    to consume it.
    - Update `App::build_transcript_lines` and downstream consumers
    (scrolling, row classification, ANSI rendering).
    - Refresh scrolling module docs to describe anchors + spacer semantics
    in context.
    - Add tests and docs about the behavior
    
    Tests:
    - just fmt
    - cargo test -p codex-tui2 tui::scrolling
    
    Manual testing:
    - Scroll the inline transcript with mouse wheel + PgUp/PgDn/Home/End,
    then resize the terminal while staying scrolled up; verify the same
    anchored content stays in view and you don’t jump to bottom
    unexpectedly.
    - Create a gap case (multiple non-continuation cells) and scroll so a
    blank spacer row is at/near the top; verify scrolling doesn’t get stuck
    on spacers and still anchors to nearby real lines.
    - Start a selection while the assistant is streaming; verify the view
    stops auto-following, the selection stays on the intended content, and
    subsequent scrolling still behaves normally.
    - Exit the TUI and confirm scrollback rendering still styles user rows
    as blocks (background padding) and non-user rows as expected.
  • chore(shell_command) fix freeform timeout output (#7791)
    ## Summary
    Adding an additional integration test for timeout_ms
    
    ## Testing
    - [x] these are tests
  • feat: merge remote models instead of destructing (#7997)
    - merge remote models instead of destructing
    - make config values have more precedent over remote values
  • WIP: Rework TUI viewport, history printing, and selection/copy (#7601)
    > large behavior change to how the TUI owns its viewport, history, and
    suspend behavior.
    > Core model is in place; a few items are still being polished before
    this is ready to merge.
    
    We've moved this over to a new tui2 crate from being directly on the tui
    crate.
    To enable use --enable tui2 (or the equivalent in your config.toml). See
    https://developers.openai.com/codex/local-config#feature-flags
    
    Note that this serves as a baseline for the changes that we're making to
    be applied rapidly. Tui2 may not track later changes in the main tui.
    It's experimental and may not be where we land on things.
    
    ---
    
    ## Summary
    
    This PR moves the Codex TUI off of “cooperating” with the terminal’s
    scrollback and onto a model
    where the in‑memory transcript is the single source of truth. The TUI
    now owns scrolling, selection,
    copy, and suspend/exit printing based on that transcript, and only
    writes to terminal scrollback in
    append‑only fashion on suspend/exit. It also fixes streaming wrapping so
    streamed responses reflow
    with the viewport, and introduces configuration to control whether we
    print history on suspend or
    only on exit.
    
    High‑level goals:
    
    - Ensure history is complete, ordered, and never silently dropped.
    - Print each logical history cell at most once into scrollback, even
    with resizes and suspends.
    - Make scrolling, selection, and copy match the visible transcript, not
    the terminal’s notion of
      scrollback.
    - Keep suspend/alt‑screen behavior predictable across terminals.
    
    ---
    
    ## Core Design Changes
    
    ### Transcript & viewport ownership
    
    - Treat the transcript as a list of **cells** (user prompts, agent
    messages, system/info rows,
      streaming segments).
    - On each frame:
    - Compute a **transcript region** as “full terminal frame minus the
    bottom input area”.
    - Flatten all cells into visual lines plus metadata (which cell + which
    line within that cell).
    - Use scroll state to choose which visual line is at the top of the
    region.
      - Clear that region and draw just the visible slice of lines.
    - The terminal’s scrollback is no longer part of the live layout
    algorithm; it is only ever written
      to when we decide to print history.
    
    ### User message styling
    
    - User prompts now render as clear blocks with:
      - A blank padding line above and below.
    - A full‑width background for every line in the block (including the
    prompt line itself).
    - The same block styling is used when we print history into scrollback,
    so the transcript looks
    consistent whether you are in the TUI or scrolling back after
    exit/suspend.
    
    ---
    
    ## Scrolling, Mouse, Selection, and Copy
    
    ### Scrolling
    
    - Scrolling is defined in terms of the flattened transcript lines:
      - Mouse wheel scrolls up/down by fixed line increments.
      - PgUp/PgDn/Home/End operate on the same scroll model.
    - The footer shows:
      - Whether you are “following live output” vs “scrolled up”.
      - Current scroll position (line / total).
    - When there is no history yet, the bottom pane is **pegged high** and
    gradually moves down as the
      transcript fills, matching the existing UX.
    
    ### Selection
    
    - Click‑and‑drag defines a **linear selection** over transcript
    line/column coordinates, not raw
      screen rows.
    - Selection is **content‑anchored**:
    - When you scroll, the selection moves with the underlying lines instead
    of sticking to a fixed
        Y position.
    - This holds both when scrolling manually and when new content streams
    in, as long as you are in
        “follow” mode.
    - The selection only covers the “transcript text” area:
      - Left gutter/prefix (bullets, markers) is intentionally excluded.
    - This keeps copy/paste cleaner and avoids including structural margin
    characters.
    
    ### Copy (`Ctrl+Y`)
    
    - Introduce a small clipboard abstraction (`ClipboardManager`‑style) and
    use a cross‑platform
      clipboard crate under the hood.
    - When `Ctrl+Y` is pressed and a non‑empty selection exists:
    - Re‑render the transcript region off‑screen using the same wrapping as
    the visible viewport.
    - Walk the selected line/column range over that buffer to reconstruct
    the exact text:
        - Includes spaces between words.
        - Preserves empty lines within the selection.
      - Send the resulting text to the system clipboard.
    - Show a short status message in the footer indicating success/failure.
    - Copy is **best‑effort**:
    - Clipboard failures (headless environment, sandbox, remote sessions)
    are handled gracefully via
        status messages; they do not crash the TUI.
    - Copy does *not* insert a new history entry; it only affects the status
    bar.
    
    ---
    
    ## Streaming and Wrapping
    
    ### Previous behavior
    
    Previously, streamed markdown:
    
    - Was wrapped at a fixed width **at commit time** inside the streaming
    collector.
    - Those wrapped `Line<'static>` values were then wrapped again at
    display time.
    - As a result, streamed paragraphs could not “un‑wrap” when the terminal
    width increased; they were
      permanently split according to the width at the start of the stream.
    
    ### New behavior
    
    This PR implements the first step from
    `codex-rs/tui/streaming_wrapping_design.md`:
    
    - Streaming collector is constructed **without** a fixed width for
    wrapping.
      - It still:
        - Buffers the full markdown source for the current stream.
        - Commits only at newline boundaries.
        - Emits logical lines as new content becomes available.
    - Agent message cells now wrap streamed content only at **display
    time**, based on the current
      viewport width, just like non‑streaming messages.
    - Consequences:
      - Streamed responses reflow correctly when the terminal is resized.
    - Animation steps are per logical line instead of per “pre‑wrapped”
    visual line; this makes some
    commits slightly larger but keeps the behavior simple and predictable.
    
    Streaming responses are still represented as a sequence of logical
    history entries (first line +
    continuations) and integrate with the same scrolling, selection, and
    printing model.
    
    ---
    
    ## Printing History on Suspend and Exit
    
    ### High‑water mark and append‑only scrollback
    
    - Introduce a **cell‑based high‑water mark** (`printed_history_cells`)
    on the transcript:
    - Represents “how many cells at the front of the transcript have already
    been printed”.
      - Completely independent of wrapped line counts or terminal geometry.
    - Whenever we print history (suspend or exit):
    - Take the suffix of `transcript_cells` beyond `printed_history_cells`.
      - Render just that suffix into styled lines at the **current** width.
      - Write those lines to stdout.
      - Advance `printed_history_cells` to cover all cells we just printed.
    - Older cells are never re‑rendered for scrollback. They stay in
    whatever wrapping they had when
    printed, which is acceptable as long as the logical content is present
    once.
    
    ### Suspend (`Ctrl+Z`)
    
    - On suspend:
      - Leave alt screen if active and restore normal terminal modes.
    - Render the not‑yet‑printed suffix of the transcript and append it to
    normal scrollback.
      - Advance the high‑water mark.
      - Suspend the process.
    - On resume (`fg`):
      - Re‑enter the TUI mode (alt screen + input modes).
    - Clear the viewport region and fully redraw from in‑memory transcript
    and state.
    
    This gives predictable behavior across terminals without trying to
    maintain scrollback live.
    
    ### Exit
    
    - On exit:
      - Render any remaining unprinted cells once and write them to stdout.
    - Add an extra blank line after the final Codex history cell before
    printing token usage, so the
        transcript and usage info are visually separated.
    - If you never suspended, exit prints the entire transcript exactly
    once.
    - If you suspended one or more times, exit prints only the cells
    appended after the last suspend.
    
    ---
    
    ## Configuration: Suspend Printing
    
    This PR also adds configuration to control **when** we print history:
    
    - New TUI config option to gate printing on suspend:
      - At minimum:
    - `print_on_suspend = true` – current behavior: print new history at
    each suspend *and* on exit.
        - `print_on_suspend = false` – only print on exit.
    - Default is tuned to preserve current behavior, but this can be
    revisited based on feedback.
    - The config is respected in the suspend path:
    - If disabled, suspend only restores terminal modes and stops rendering
    but does not print new
        history.
      - Exit still prints the full not‑yet‑printed suffix once.
    
    This keeps the core viewport logic agnostic to preference, while letting
    users who care about
    quiet scrollback opt out of suspend printing.
    
    ---
    
    ## Tradeoffs
    
    What we gain:
    
    - A single authoritative history model (the in‑memory transcript).
    - Deterministic viewport rendering independent of terminal quirks.
    - Suspend/exit flows that:
      - Print each logical history cell exactly once.
      - Work across resizes and different terminals.
      - Interact cleanly with alt screen and raw‑mode toggling.
    - Consistent, content‑anchored scrolling, selection, and copy.
    - Streaming messages that reflow correctly with the viewport width.
    
    What we accept:
    
    - Scrollback may contain older cells wrapped differently than newer
    ones.
    - Streaming responses appear in scrollback as a sequence of blocks
    corresponding to their streaming
      structure, not as a single retroactively reflowed paragraph.
    - We do not attempt to rewrite or reflow already‑printed scrollback.
    
    For deeper rationale and diagrams, see
    `docs/tui_viewport_and_history.md` and
    `codex-rs/tui/streaming_wrapping_design.md`.
    
    ---
    
    ## Still to Do Before This PR Is Ready
    
    These are scoped to this PR (not long‑term future work):
    
    - [ ] **Streaming wrapping polish**
      - Double‑check all streaming paths use display‑time wrapping only.
      - Ensure tests cover resizing after streaming has started.
    
    - [ ] **Suspend printing config**
    - Finalize config shape and default (keep existing behavior vs opt‑out).
    - Wire config through TUI startup and document it in the appropriate
    config docs.
    
    - [x] **Bottom pane positioning**
    - Ensure the bottom pane is pegged high when there’s no history and
    smoothly moves down as the
    transcript fills, matching the current behavior across startup and
    resume.
    
    - [x] **Transcript mouse scrolling**
    - Re‑enable wheel‑based transcript scrolling on top of the new scroll
    model.
    - Make sure mouse scroll does not get confused with “alternate scroll”
    modes from terminals.
    
    - [x] **Mouse selection vs streaming**
    - When selection is active, stop auto‑scrolling on streaming so the
    selection remains stable on
        the selected content.
    - Ensure that when streaming continues after selection is cleared,
    “follow latest output” mode
        resumes correctly.
    
    - [ ] **Auto‑scroll during drag**
    - While the user is dragging a selection, auto‑scroll when the cursor is
    at/near the top or bottom
    of the transcript viewport to allow selecting beyond the current visible
    window.
    
    - [ ] **Feature flag / rollout**
    - Investigate gating the new viewport/history behavior behind a feature
    flag for initial rollout,
    so we can fall back to the old behavior if needed during early testing.
    
    - [ ] **Before/after videos**
      - Capture short clips showing:
        - Scrolling (mouse + keys).
        - Selection and copy.
        - Streaming behavior under resize.
        - Suspend/resume and exit printing.
      - Use these to validate UX and share context in the PR discussion.
  • chore(app-server): remove stubbed thread/compact API (#8086)
    We want to rely on server-side auto-compaction instead of having the
    client trigger context compaction manually. This API was stubbed as a
    placeholder and never implemented.
  • chore: persist comments in edit (#7931)
    This PR makes sure that inline comment is preserved for mcp server
    config and arbitrary key/value setPath config.
    
    ---------
    
    Co-authored-by: celia-oai <celia@openai.com>
  • Fix: Detect Bun global install via path check (#8004)
    ## Summary
    Restores ability to detect when Codex is installed globally via **Bun**,
    which was broken by c3e4f920b4. Fixes
    #8003.
    
    Instead of relying on `npm_config_user_agent` (which is only set when
    running via `bunx` or `bun run`), this adds a path-based check to see if
    the CLI wrapper is located in Bun's global installation directory.
    
    ## Regression Context
    Commit `c3e4f920b4e965085164d6ee0249a873ef96da77` removed the
    `BUN_INSTALL` environment variable checks to prevent false positives.
    However, this caused false negatives for genuine Bun global installs
    because `detectPackageManager()` defaults to NPM when no signal is
    found.
    
    ## Changes
    - Updated `codex-cli/bin/codex.js` to check if `__dirname` contains
    `.bun/install/global` (handles both POSIX and Windows paths).
    
    ## Verification
    Verified by performing a global install of the patched CLI (v0.69.0 to
    trigger the update prompt):
    
    1. Packed the CLI using `npm pack` in `codex-cli/` to create a release
    tarball.
    2. Installed globally via Bun: `bun install -g
    $(pwd)/openai-codex-0.0.0-dev.tgz`.
    3. Ran `codex`, confirmed it detected Bun (banner showed `bun install -g
    @openai/codex`), selected "Update now", and verified it correctly
    spawned `bun install -g` instead of `npm`.
    4. Confirmed the upgrade completed successfully using Bun.
    <img width="1038" height="813" alt="verifying installation via bun"
    src="https://github.com/user-attachments/assets/00c9301a-18f1-4440-aa95-82ccffba896c"
    />
    5. Verified installations via npm are unaffected.
    <img width="2090" height="842" alt="verifying installation via npm"
    src="https://github.com/user-attachments/assets/ccb3e031-b85c-4bbe-bac7-23b087c5b844"
    />
  • chore(ci): drop Homebrew origin/main workaround for macOS runners (#8084)
    ## Notes
    
    GitHub Actions macOS runners now ship a Homebrew version (5.0.5) that
    includes the fix that was needed in a change, so it's possible to remove
    the temporary CI step that forced using brew from origin/main (added in
    #7680).
    
    Proof of macOS GitHub Actions coming packaged with 5.0.5 - latest commit
    on `main`
    (https://github.com/openai/codex/actions/runs/20245177832/job/58123247999)
    - <img width="1286" height="136" alt="image"
    src="https://github.com/user-attachments/assets/8b25fd57-dad5-45c5-907c-4f4da6a36c3f"
    />
    
    `actions/runner-images` upgraded the macOS 14 image from pre-release to
    release today
    (https://github.com/actions/runner-images/releases/tag/macos-14-arm64%2F20251210.0045)
    
    - <img width="1076" height="793" alt="image"
    src="https://github.com/user-attachments/assets/357ea4bd-40b0-49c3-a6cd-e7d87ba6766d"
    />
  • docs: fix gpt-5.2 typo in config.md (#8079)
    Fix small typo in docs/config.md: `gpt5-2` -> `gpt-5.2`
  • exec-server: additional context for errors (#7935)
    Add a .context() on some exec-server errors for debugging CI flakes.
    
    Also, "login": false in the test to make the test not affected by user
    profile.
  • better name for windows sandbox features (#8077)
    `--enable enable...` is a bad look