Commit Graph
1200 Commits
Author SHA1 Message Date
Felipe Coury 55c961040c fix(tui): address syntax-theme review feedback 2026-02-21 19:21:19 -03:00
Felipe Coury 44a7ba0dda feat(tui): harden theme lock recovery and ordering 2026-02-21 19:21:19 -03:00
Felipe Coury 18d62ac80f feat(tui): harden theme fallback contracts and diff wrapping 2026-02-21 19:21:19 -03:00
Felipe Coury 75c819e54a docs(tui): theme picker, highlight lifecycle, diff rendering contracts 2026-02-21 19:21:19 -03:00
Felipe Coury 5986719f96 fix(tui): resolve clippy -D warnings findings 2026-02-21 19:21:19 -03:00
Felipe Coury 801ae5e979 feat(tui): reuse diff renderer for theme preview 2026-02-21 19:21:19 -03:00
Felipe Coury 89891de55a feat(tui): diff visual gallery snapshots with syntax-highlighted cases 2026-02-21 19:21:19 -03:00
Felipe Coury 46021a3591 fix(tui): wrap syntax spans before first overflowing wide character 2026-02-21 19:21:19 -03:00
Felipe Coury c510eec9f8 feat(tui): fallback to active theme when configured theme is unavailable 2026-02-21 19:21:18 -03:00
Felipe Coury 8c4bfa78f7 feat(tui): width-aware theme subtitle fallback and 94x35 regression test 2026-02-21 19:21:18 -03:00
Felipe Coury f72e7bf529 feat(tui): live theme override and dynamic CODEX_HOME paths
set_theme_override now updates the runtime theme immediately instead
of only setting the OnceLock, enabling live switching from /theme.
Replace hardcoded ~/.codex/themes references with resolved
$CODEX_HOME/themes paths in warnings, schema docs, and picker subtitle.
2026-02-21 19:21:17 -03:00
Felipe Coury 987db6aaf6 fix(tui): only fire on_selection_changed when actual item changes
Extract selected_actual_idx() helper and guard move_up/move_down so
the callback is skipped when wrapping doesn't change the selected
item (e.g. single-item lists). Adds a test for the single-item case.
2026-02-21 19:21:17 -03:00
Felipe Coury 0066e85f0f fix(tui): preserve syntect parser state across diff hunk lines
Highlight each hunk as a single block instead of per-line so that
multiline constructs (e.g. strings spanning multiple lines) get
correct syntax coloring. Adds a regression test.
2026-02-21 19:21:17 -03:00
Felipe Coury 197131762b tui: harden theme picker cancel and custom theme handling 2026-02-21 19:21:17 -03:00
Felipe Coury 85e1e7bf5a tui: sync theme picker state and validate custom themes 2026-02-21 19:21:16 -03:00
Felipe Coury f4ac1f09b3 tui: refine theme preview alignment in wide and narrow layouts 2026-02-21 19:20:48 -03:00
Felipe Coury 8ca13e5d56 tui: fix theme preview layout and terminal background rendering 2026-02-21 19:20:48 -03:00
Felipe Coury ea57beb959 feat(tui): add /theme picker with live syntax-highlighted preview
Add a `/theme` slash command that opens an interactive picker listing
all bundled and custom (.tmTheme) syntax themes. The picker shows a
live-preview code snippet below the list that re-highlights as the
user navigates, and restores the original theme on cancel.

Key changes:
- ListSelectionView gains `footer_content`, `on_selection_changed`,
  and `on_cancel` to support rich preview and lifecycle callbacks
- highlight.rs: swap global Theme from OnceLock to RwLock for live
  swapping; add resolve/list/set/current helpers for theme management
- New theme_picker module with ThemePreviewRenderable and picker builder
- diff_render: make DiffLineType and helpers pub(crate); fix rename
  highlighting to use destination extension
- markdown_render: fix CRLF double-newlines and info-string metadata
  parsing for fenced code blocks
2026-02-21 19:20:48 -03:00
Felipe Coury 976dd6831e fix(tui): configure syntax theme from final config, not initial
`set_theme_override` was called with `initial_config.tui_theme` before
onboarding/resume/fork could reload config. Because the theme state
lives in a `OnceLock`, it was permanently locked to the initial value.

Move the call to after the last possible config reload so the theme
and its warning always reflect the final session config.
2026-02-21 19:20:48 -03:00
Felipe Coury d32e05d4e5 fix(tui): treat tabs as visible width, preserve theme warning on resume
- Tabs in syntax-highlighted diff spans now count as 4 columns instead
  of zero, so tab-indented code wraps correctly in the diff view.

- Move theme warning injection to after the last possible config reload
  (session resume/fork) so the ⚠ banner is not silently discarded.
2026-02-21 19:20:48 -03:00
Felipe Coury 06d4c95d35 style(tui): rustfmt 2026-02-21 19:20:48 -03:00
Felipe Coury 3cd1301409 fix(tui): trim CRLF in highlighted spans, enforce diff size guardrails
Two fixes from code review:

1. `trim_end_matches('\n')` left a stray `\r` on CRLF inputs, which
   propagated into rendered code blocks and diffs. Now strips both
   `\r` and `\n`.

2. Unified diff highlighting called `highlight_code_to_styled_spans`
   per line, bypassing the global size guardrails (max bytes/lines).
   On large patches this triggered thousands of parser initializations.
   Added a pre-check on aggregate patch size that skips highlighting
   when limits are exceeded.
2026-02-21 19:20:48 -03:00
Felipe Coury e7d4e828c3 feat(tui): support custom .tmTheme files from ~/.codex/themes/
Extend theme resolution so `theme = "foo"` checks
~/.codex/themes/foo.tmTheme before falling back to auto-detection.
Surface unresolved theme names as a ⚠ startup warning in the TUI
via the existing startup_warnings pipeline.
2026-02-21 19:20:47 -03:00
Felipe Coury 32ff9e89ed fix(tui): fix fallback phantom line and diff wrap at span boundaries
Two fixes:

1. highlight.rs: The fallback path used split('\n') which produces a
   phantom empty trailing element for inputs ending in '\n' (as
   pulldown-cmark emits for code blocks). Switch to lines() which
   handles trailing newlines correctly.

2. diff_render.rs: wrap_styled_spans only flushed the current line
   when col >= max_cols AND the current span had remaining content.
   This missed the case where one span ends exactly at max_cols and
   the next span has content — the next span's first character was
   appended to the already-full line. Remove the !remaining.is_empty()
   guard so the line flushes at span boundaries too.
2026-02-21 19:20:47 -03:00
Felipe Coury f00f7d49dc fix(tui): preserve trailing blank lines in fenced code blocks
trim_end_matches('\n') stripped all trailing newlines from the code
block buffer before highlighting, discarding intentional blank lines
authored inside fences. pulldown-cmark appends exactly one trailing
'\n' to the text content, and LinesWithEndings handles it correctly
as a line terminator, so no trimming is needed. Remove the trim to
preserve user-authored blank lines faithfully.
2026-02-21 19:20:47 -03:00
Felipe Coury 82fc47e317 feat(tui): add theme config setting, suppress underline from themes
Add `[tui] theme = "..."` config option that lets users override the
auto-detected syntax highlighting theme with any of the 32 bundled
two-face themes by kebab-case name. Invalid names log a warning and
fall back to adaptive detection.

Also suppress FontStyle::UNDERLINE in convert_style — themes like
Dracula mark type scopes with underline which produces distracting
underlines on type/module names in terminal output.
2026-02-21 19:20:47 -03:00
Felipe Coury 9878b734a0 refactor(tui): remove normalize_lang, delegate to two-face directly
two-face's ~250 language syntax set resolves almost all names and
extensions on its own. Remove the redundant normalize_lang layer and
keep only 3 patched aliases (golang, python3, shell) that two-face
cannot resolve. This enables highlighting for any language in bat's
syntax set (elixir, haskell, scala, dart, php, html, css, etc.)
without needing explicit entries.
2026-02-21 19:20:47 -03:00
Felipe Coury 5f92cb4e58 feat(tui): use two-face for extended syntax highlighting and adaptive theme
Replace syntect's default ~40 language set with two-face's bat-sourced
~250 language set, adding support for TypeScript, TSX, Kotlin, Swift,
and Zig. Auto-detect terminal background to pick CatppuccinMocha (dark)
or CatppuccinLatte (light). Suppress italic modifier by default since
many terminals render it poorly.
2026-02-21 19:20:47 -03:00
Felipe Coury 252b574c65 fix(tui): update exec approval snapshot for syntect bash highlighting
The exec approval modal's command line now gets syntax-colored spans
from syntect (base16-ocean.dark) instead of tree-sitter-bash.
2026-02-21 19:20:47 -03:00
Felipe Coury 1ddc3d7b00 fix(tui): wrap syntax-highlighted diff lines, add highlight guardrails
Integrate syntax highlighting into diff rendering and markdown code
blocks, with proper line wrapping that preserves styled spans.

- Add detect_lang_for_path() for extension-based language detection
- Syntax-aware diff rendering for Add/Delete/Update file changes
- wrap_styled_spans() splits long highlighted lines across display rows
- Buffer fenced code blocks in markdown_render for batch highlighting
- Add snapshot tests for highlighted diff wrapping
- Replace code_block_unhighlighted test with 3 syntax color assertions
2026-02-21 19:20:47 -03:00
Felipe Coury 776e4b0aa8 feat(tui): add multi-language syntax highlighting via syntect
Replace tree-sitter-bash and tree-sitter-highlight with syntect for
code highlighting. Syntect's HighlightLines API resolves overlapping
spans internally, significantly simplifying the implementation.

- Add syntect workspace dependency, remove tree-sitter-bash/highlight from tui
- Rewrite highlight.rs with syntect singletons (SyntaxSet + base16-ocean.dark theme)
- Normalize language aliases (js→javascript, py→python, rs→rust, etc.)
- Multi-strategy syntax lookup: token, name, case-insensitive name, extension
- Add guardrails: skip highlighting for inputs >512KB or >10K lines
- Comprehensive tests for style conversion, language resolution, content preservation
2026-02-21 19:20:47 -03:00
Eric Traut a6b2bacb5b Prevent replayed runtime events from forcing active status (#12420)
Fixes #11852

Resume replay was applying transient runtime events (`TurnStarted`,
`StreamError`) as if they were live, which could leave the TUI stuck in
a stale `Working` / `Reconnecting...` state after resuming an
interrupted reconnect.

This change makes replay transcript-oriented for these events by:
- skipping retry-status restoration for replayed non-stream events
- ignoring replayed `TurnStarted` for task-running state
- ignoring replayed `StreamError` for reconnect/status UI

Also adds TUI regression tests and snapshot coverage for the interrupted
reconnect replay case.
2026-02-21 11:55:03 -08:00
Michael Bolin 1af2a37ada chore: remove codex-core public protocol/shell re-exports (#12432)
## Why

`codex-rs/core/src/lib.rs` re-exported a broad set of types and modules
from `codex-protocol` and `codex-shell-command`. That made it easy for
workspace crates to import those APIs through `codex-core`, which in
turn hides dependency edges and makes it harder to reduce compile-time
coupling over time.

This change removes those public re-exports so call sites must import
from the source crates directly. Even when a crate still depends on
`codex-core` today, this makes dependency boundaries explicit and
unblocks future work to drop `codex-core` dependencies where possible.

## What Changed

- Removed public re-exports from `codex-rs/core/src/lib.rs` for:
- `codex_protocol::protocol` and related protocol/model types (including
`InitialHistory`)
  - `codex_protocol::config_types` (`protocol_config_types`)
- `codex_shell_command::{bash, is_dangerous_command, is_safe_command,
parse_command, powershell}`
- Migrated workspace Rust call sites to import directly from:
  - `codex_protocol::protocol`
  - `codex_protocol::config_types`
  - `codex_protocol::models`
  - `codex_shell_command`
- Added explicit `Cargo.toml` dependencies (`codex-protocol` /
`codex-shell-command`) in crates that now import those crates directly.
- Kept `codex-core` internal modules compiling by using `pub(crate)`
aliases in `core/src/lib.rs` (internal-only, not part of the public
API).
- Updated the two utility crates that can already drop a `codex-core`
dependency edge entirely:
  - `codex-utils-approval-presets`
  - `codex-utils-cli`

## Verification

- `cargo test -p codex-utils-approval-presets`
- `cargo test -p codex-utils-cli`
- `cargo check --workspace --all-targets`
- `just clippy`
2026-02-20 23:45:35 -08:00
pakrym-oai a87c9c3299 Collapse waited message (#12430)
<img width="1349" height="148" alt="image"
src="https://github.com/user-attachments/assets/98c96523-4cec-4bb1-9998-59d38e0bebb8"
/>
2026-02-20 23:32:59 -08:00
Charley Cunningham 4c1744afb2 Improve Plan mode reasoning selection flow (#12303)
Addresses https://github.com/openai/codex/issues/11013

## Summary
- add a Plan implementation path in the TUI that lets users choose
reasoning before switching to Default mode and implementing
- add Plan-mode reasoning scope handling (Plan-only override vs
all-modes default), including config/schema/docs plumbing for
`plan_mode_reasoning_effort`
- remove the hardcoded Plan preset medium default and make the reasoning
popup reflect the active Plan override as `(current)`
- split the collaboration-mode switch notification UI hint into #12307
to keep this diff focused

If I have `plan_mode_reasoning_effort = "medium"` set in my
`config.toml`:
<img width="699" height="127" alt="Screenshot 2026-02-20 at 6 59 37 PM"
src="https://github.com/user-attachments/assets/b33abf04-6b7a-49ed-b2e9-d24b99795369"
/>

If I don't have `plan_mode_reasoning_effort` set in my `config.toml`:
<img width="704" height="129" alt="Screenshot 2026-02-20 at 7 01 51 PM"
src="https://github.com/user-attachments/assets/88a086d4-d2f1-49c7-8be4-f6f0c0fa1b8d"
/>

## Codex author
`codex resume 019c78a2-726b-7fe3-adac-3fa4523dcc2a`
2026-02-20 20:08:56 -08:00
Ahmed Ibrahim 6817f0be8a Wire realtime api to core (#12268)
- Introduce `RealtimeConversationManager` for realtime API management 
- Add `op::conversation` to start conversation, insert audio, insert
text, and close conversation.
- emit conversation lifecycle and realtime events.
- Move shared realtime payload types into codex-protocol and add core
e2e websocket tests for start/replace/transport-close paths.

Things to consider:
- Should we use the same `op::` and `Events` channel to carry audio? I
think we should try this simple approach and later we can create
separate one if the channels got congested.
- Sending text updates to the client: we can start simple and later
restrict that.
- Provider auth isn't wired for now intentionally
2026-02-20 19:06:35 -08:00
Felipe Coury a5d0757ed1 fix(tui): queued-message edit shortcut unreachable in some terminals (#12240)
## Problem

The TUI's "edit queued message" shortcut (Alt+Up) is either silently
swallowed or recognized as another key combination by Apple Terminal,
Warp, and VSCode's integrated terminal on macOS. Users in those
environments see the hint but pressing the keys does nothing.

## Mental model

When a model turn is in progress the user can still type follow-up
messages. These are queued and displayed below the composer with a hint
line showing how to pop the most recent one back into the editor. The
hint text and the actual key handler must agree on which shortcut is
used, and that shortcut must actually reach the TUI—i.e. it must not be
intercepted by the host terminal.

Three terminals are known to intercept Alt+Up: Apple Terminal (remaps it
to cursor movement), Warp (consumes it for its own command palette), and
VSCode (maps it to "move line up"). For these we use Shift+Left instead.

<p align="center">
<img width="283" height="182" alt="image"
src="https://github.com/user-attachments/assets/4a9c5d13-6e47-4157-bb41-28b4ce96a914"
/>
</p>

| macOS Native Terminal | Warp | VSCode Terminal |
|---|---|---|
| <img width="1557" height="1010" alt="SCR-20260219-kigi"
src="https://github.com/user-attachments/assets/f4ff52f8-119e-407b-a3f3-52f564c36d70"
/> | <img width="1479" height="1261" alt="SCR-20260219-krrf"
src="https://github.com/user-attachments/assets/5807d7c4-17ae-4a2b-aa27-238fd49d90fd"
/> | <img width="1612" height="1312" alt="SCR-20260219-ksbz"
src="https://github.com/user-attachments/assets/1cedb895-6966-4d63-ac5f-0eea0f7057e8"
/> |

## Non-goals

- Making the binding user-configurable at runtime (deferred to a broader
keybinding-config effort).
- Remapping any other shortcuts that might be terminal-specific.

## Tradeoffs

- **Exhaustive match instead of a wildcard default.** The
`queued_message_edit_binding_for_terminal` function explicitly lists
every `TerminalName` variant. This is intentional: adding a new terminal
to the enum will produce a compile error, forcing the author to decide
which binding that terminal should use.

- **Binding lives on `ChatWidget`, hint lives on `QueuedUserMessages`.**
The key event handler that actually acts on the press is in
`ChatWidget`, but the rendered hint text is inside `QueuedUserMessages`.
These are kept in sync by `ChatWidget` calling
`bottom_pane.set_queued_message_edit_binding(self.queued_message_edit_binding)`
during construction. A mismatch would show the wrong hint but would not
lose data.

## Architecture

```mermaid
  graph TD
      TI["terminal_info().name"] --> FN["queued_message_edit_binding_for_terminal(name)"]
      FN --> KB["KeyBinding"]
      KB --> CW["ChatWidget.queued_message_edit_binding<br/><i>key event matching</i>"]
      KB --> BP["BottomPane.set_queued_message_edit_binding()"]
      BP --> QUM["QueuedUserMessages.edit_binding<br/><i>rendered in hint line</i>"]

      subgraph "Special terminals (Shift+Left)"
          AT["Apple Terminal"]
          WT["Warp"]
          VS["VSCode"]
      end

      subgraph "Default (Alt+Up)"
          GH["Ghostty"]
          IT["iTerm2"]
          OT["Others…"]
      end

      AT --> FN
      WT --> FN
      VS --> FN
      GH --> FN
      IT --> FN
      OT --> FN
```

No new crates or public API surface. The only cross-crate dependency
added is `codex_core::terminal::{TerminalName, terminal_info}`, which
already existed for telemetry.

## Observability

No new logging. Terminal detection already emits a `tracing::debug!` log
line at startup with the detected terminal name, which is sufficient to
diagnose binding mismatches.

## Tests

- Existing `alt_up_edits_most_recent_queued_message` test is preserved
and explicitly sets the Alt+Up binding to isolate from the host
terminal.
- New parameterized async tests verify Shift+Left works for Apple
Terminal, Warp, and VSCode.
- A sync unit test asserts the mapping table covers the three special
terminals (Shift+Left) and that iTerm2 still gets Alt+Up.
  
Fixes #4490
2026-02-20 16:56:41 -08:00
Charley Cunningham 021e39b303 Show model/reasoning hint when switching modes (#12307)
## Summary
- show an info message when switching collaboration modes changes the
effective model or reasoning
- include the target mode in the message (for example `... for Plan
mode.`)
- add TUI tests for model-change and reasoning-only change notifications
on mode switch

<img width="715" height="184" alt="Screenshot 2026-02-20 at 2 01 40 PM"
src="https://github.com/user-attachments/assets/18d1beb3-ab87-4e1c-9ada-a10218520420"
/>
2026-02-20 15:22:10 -08:00
pakrym-oai 1bb7989b20 Add ability to attach extra files to feedback (#12370)
Allow clients to provide extra files.
2026-02-20 22:26:14 +00:00
viyatb-oaiandCodex 28c0089060 fix(network-proxy): add unix socket allow-all and update seatbelt rules (#11368)
## Summary
Adds support for a Unix socket escape hatch so we can bypass socket
allowlisting when explicitly enabled.

## Description
* added a new flag, `network.dangerously_allow_all_unix_sockets` as an
explicit escape hatch
* In codex-network-proxy, enabling that flag now allows any absolute
Unix socket path from x-unix-socket instead of requiring each path to be
explicitly allowlisted. Relative paths are still rejected.
* updated the macOS seatbelt path in core so it enforces the same Unix
socket behavior:
  * allowlisted sockets generate explicit network* subpath rules
  * allow-all generates a broad network* (subpath "/") rule

---------

Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-02-20 10:56:57 -08:00
viyatb-oai e8afaed502 Refactor network approvals to host/protocol/port scope (#12140)
## Summary
Simplify network approvals by removing per-attempt proxy correlation and
moving to session-level approval dedupe keyed by (host, protocol, port).
Instead of encoding attempt IDs into proxy credentials/URLs, we now
treat approvals as a destination policy decision.

- Concurrent calls to the same destination share one approval prompt.
- Different destinations (or same host on different ports) get separate
prompts.
- Allow once approves the current queued request group only.
- Allow for session caches that (host, protocol, port) and auto-allows
future matching requests.
- Never policy continues to deny without prompting.

Example:
- 3 calls: 
  - a.com (line 443)
  - b.com (line 443)
  - a.com (line 443)
=> 2 prompts total (a, b), second a waits on the first decision.
- a.com:80 is treated separately from a.com line 443

## Testing
- `just fmt` (in `codex-rs`)
- `cargo test -p codex-core tools::network_approval::tests`
- `cargo test -p codex-core` (unit tests pass; existing
integration-suite failures remain in this environment)
2026-02-20 10:39:55 -08:00
jif-oai 5a30cd3f92 feat: better agent picker in TUI (#12332)
<img width="486" height="112" alt="Screenshot 2026-02-20 at 15 04 52"
src="https://github.com/user-attachments/assets/0d744f58-d902-4638-aeaf-27e7389ccd73"
/>
2026-02-20 15:40:34 +00:00
jif-oai 4d60c803ba feat: cleaner TUI for sub-agents (#12327)
<img width="760" height="496" alt="Screenshot 2026-02-20 at 14 31 25"
src="https://github.com/user-attachments/assets/1983b825-bb47-417e-9925-6f727af56765"
/>
2026-02-20 15:26:33 +00:00
jif-oai 0f9eed3a6f feat: add nick name to sub-agents (#12320)
Adding random nick name to sub-agents. Used for UX

At the same time, also storing and wiring the role of the sub-agent
2026-02-20 14:39:49 +00:00
Matthew Zeng 18bd6d2d71 [apps] Store apps tool cache in disk to reduce startup time. (#11822)
We now write MCP tools from installed apps to disk cache so that they
can be picked up instantly at startup. We still do a fresh fetch from
remote MCP server but it's non blocking unless there's a cache miss.

- [x] Store apps tool cache in disk to reduce startup time.
2026-02-19 22:06:51 -08:00
sayan-oai d54999d006 client side modelinfo overrides (#12101)
TL;DR
Add top-level `model_catalog_json` config support so users can supply a
local model catalog override from a JSON file path (including adding new
models) without backend changes.

### Problem
Codex previously had no clean client-side way to replace/overlay model
catalog data for local testing of model metadata and new model entries.

### Fix
- Add top-level `model_catalog_json` config field (JSON file path).
- Apply catalog entries when resolving `ModelInfo`:
  1. Base resolved model metadata (remote/fallback)
  2. Catalog overlay from `model_catalog_json`
3. Existing global top-level overrides (`model_context_window`,
`model_supports_reasoning_summaries`, etc.)

### Note
Will revisit per-field overrides in a follow-up

### Tests
Added tests
2026-02-19 10:38:57 -08:00
Jack Mousseau 3a951f8096 Restore phase when loading from history (#12244) 2026-02-19 09:56:56 -08:00
jif-oai 0dcf8d9c8f Enable default status line indicators in TUI config (#12015)
Default statusline to something
<img width="307" height="83" alt="Screenshot 2026-02-17 at 18 16 12"
src="https://github.com/user-attachments/assets/44e16153-0aa2-4c1a-9b4a-02e2feb8b7f6"
/>
2026-02-18 09:51:15 +00:00
won-openai 189f592014 got rid of experimental_mode for configtoml (#12077) 2026-02-17 21:10:30 -08:00
Owen Lin db4d2599b5 feat(core): plumb distinct approval ids for command approvals (#12051)
zsh fork PR stack:
- https://github.com/openai/codex/pull/12051 👈 
- https://github.com/openai/codex/pull/12052

With upcoming support for a fork of zsh that allows us to intercept
`execve` and run execpolicy checks for each subcommand as part of a
`CommandExecution`, it will be possible for there to be multiple
approval requests for a shell command like `/path/to/zsh -lc 'git status
&& rg \"TODO\" src && make test'`.

To support that, this PR introduces a new `approval_id` field across
core, protocol, and app-server so that we can associate approvals
properly for subcommands.
2026-02-18 01:55:57 +00:00