## TL;DR
- New `Ctrl+O` shortcut on top of the existing `/copy` command, allowing
users to copy the latest agent response without having to cancel a plan
or type `/copy`
- Copy server clipboard to the client over SSH (OSC 52)
- Fixes linux copy behavior: a clipboard handle has to be kept alive
while the paste happens for the contents to be preserved
- Uses arboard as primary mechanism on Windows, falling back to
PowerShell copy clipboard function
- Works with resumes, rolling back during a session, etc.
Tested on macOS, Linux/X11, Windows WSL2, Windows cmd.exe, Windows
PowerShell, Windows VSCode PowerShell, Windows VSCode WSL2, SSH (macOS
-> macOS).
## Problem
The TUI's `/copy` command was fragile. It relied on a single
`last_copyable_output` field that was bluntly cleared on every rollback
and thread reconfiguration, making copied content unavailable after
common operations like backtracking. It also had no keyboard shortcut,
requiring users to type `/copy` each time. The previous clipboard
backend mixed platform selection policy with low-level I/O in a way that
was hard to test, and it did not keep the Linux clipboard owner alive —
meaning pasted content could vanish once the process that wrote it
dropped its `arboard::Clipboard`.
This addresses the text-copy failure modes reported in #12836, #15452,
and #15663: native Linux clipboard access failing in remote or
unreachable-display environments, copy state going blank even after
visible assistant output, and local Linux X11 reporting success while
leaving the clipboard empty.
## Shortcut rationale
The copy hotkey is `Ctrl+O` rather than `Alt+C` because Alt/Option
combinations are not delivered consistently by macOS terminal emulators.
Terminal.app and iTerm2 can treat Option as text input or as a
configurable Meta/Esc prefix, and Option+C may be consumed or
transformed before the TUI sees an `Alt+C` key event. `Ctrl+O` is a
stable control-key chord in Terminal.app, iTerm2, SSH, and the existing
cross-platform terminal stack.
## Mental model
Agent responses are now tracked as a bounded, ordinal-indexed history
(`agent_turn_markdowns: Vec<AgentTurnMarkdown>`) rather than a single
nullable string. Each completed agent turn appends an entry keyed by its
ordinal (the number of user turns seen so far). Rollbacks pop entries
whose ordinal exceeds the remaining turn count, then use the visible
transcript cells as a best-effort fallback if the ordinal history no
longer has a surviving entry. This means `/copy` and `Ctrl+O` reflect
the most recent surviving agent response after a backtrack, instead of
going blank.
The clipboard backend was rewritten as `clipboard_copy.rs` with a
strategy-injection design: `copy_to_clipboard_with` accepts closures for
the OSC 52, arboard, and WSL PowerShell paths, making the selection
logic fully unit-testable without touching real clipboards. On Linux,
the `Clipboard` handle is returned as a `ClipboardLease` stored on
`ChatWidget`, keeping X11/Wayland clipboard ownership alive for the
lifetime of the TUI. When native copy fails under WSL, the backend now
tries the Windows clipboard through PowerShell before falling back to
OSC 52.
## Non-goals
- This change does not introduce rich-text (HTML) clipboard support; the
copied content is raw markdown.
- It does not add a paste-from-history picker or multi-entry clipboard
ring.
- WSL support remains a best-effort fallback, not a new configuration
surface or guarantee for every terminal/host combination.
## Tradeoffs
- **Bounded history (256 entries)**: `MAX_AGENT_COPY_HISTORY` caps
memory. For sessions with thousands of turns this silently drops the
oldest entries. The cap is generous enough for realistic sessions.
- **`saw_copy_source_this_turn` flag**: Prevents double-recording when
both `AgentMessage` and `TurnComplete.last_agent_message` fire for the
same turn. The flag is reset on turn start and on turn complete,
creating a narrow window where a race between the two events could
theoretically skip recording. In practice the protocol delivers them
sequentially.
- **Transcript fallback on rollback**:
`last_agent_markdown_from_transcript` walks the visible transcript cells
to reconstruct plain text when the ordinal history has been fully
truncated. This path uses `AgentMessageCell::plain_text()` which joins
rendered spans, so it reconstructs display text rather than the original
raw markdown. It keeps visible text copyable after rollback, but
responses with markdown-specific syntax can diverge from the original
source.
- **Clipboard fallback ordering**: SSH still uses OSC 52 exclusively
because native/PowerShell clipboard access would target the wrong
machine. Local sessions try native clipboard first, then WSL PowerShell
when running under WSL, then OSC 52. This adds one process-spawn
fallback for WSL users but keeps the normal desktop and SSH paths
simple.
## Architecture
```
chatwidget.rs
├── agent_turn_markdowns: Vec<AgentTurnMarkdown> // ordinal-indexed history
├── last_agent_markdown: Option<String> // always == last entry's markdown
├── completed_turn_count: usize // incremented when user turns enter history
├── saw_copy_source_this_turn: bool // dedup guard
├── clipboard_lease: Option<ClipboardLease> // keeps Linux clipboard owner alive
│
├── record_agent_markdown(&str) // append/update history entry
├── truncate_agent_turn_markdowns_to_turn_count() // rollback support
├── copy_last_agent_markdown() // public entry point (slash + hotkey)
└── copy_last_agent_markdown_with(fn) // testable core
clipboard_copy.rs
├── copy_to_clipboard(text) -> Result<Option<ClipboardLease>>
├── copy_to_clipboard_with(text, ssh, wsl, osc52_fn, arboard_fn, wsl_fn)
├── ClipboardLease { _clipboard on linux }
├── arboard_copy(text) // platform-conditional native clipboard path
├── wsl_clipboard_copy(text) // WSL PowerShell fallback
├── osc52_copy(text) // /dev/tty -> stdout fallback
├── SuppressStderr // macOS stderr redirect guard
├── is_ssh_session()
└── is_wsl_session()
app_backtrack.rs
├── last_agent_markdown_from_transcript() // reconstruct from visible cells
└── truncate call sites in trim/apply_confirmed_rollback
```
## Observability
- `tracing::warn!` on native clipboard failure before OSC 52 fallback.
- `tracing::debug!` on `/dev/tty` open/write failure before stdout
fallback.
- History cell messages: "Copied last message to clipboard", "Copy
failed: {error}", "No agent response to copy" appear in the TUI
transcript.
## Tests
- `clipboard_copy.rs`: Unit tests cover OSC 52 encoding roundtrip,
payload size rejection, writer output, SSH-only OSC52 routing, non-WSL
native-to-OSC52 fallback, WSL native-to-PowerShell fallback, WSL
PowerShell-to-OSC52 fallback, and all-error reporting via strategy
injection.
- `chatwidget/tests/slash_commands.rs`: Updated existing `/copy` tests
to use `last_agent_markdown_text()` accessor. Added coverage for the
Linux clipboard lease lifecycle, missing
`TurnComplete.last_agent_message` fallback through completed assistant
items, replayed legacy agent messages, stale-output prevention after
rollback, and the `Ctrl+O` no-output hotkey path.
- `app_backtrack.rs`: Added
`agent_group_count_ignores_context_compacted_marker` verifying that
info-event cells don't inflate the agent group count.
---------
Co-authored-by: Felipe Coury <felipe.coury@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This is a follow-up to https://github.com/openai/codex/pull/15922. That
previous PR deleted the old `tui` directory and left the new
`tui_app_server` directory in place. This PR renames `tui_app_server` to
`tui` and fixes up all references.
This is the part 1 of 2 PRs that will delete the `tui` /
`tui_app_server` split. This part simply deletes the existing `tui`
directory and marks the `tui_app_server` feature flag as removed. I left
the `tui_app_server` feature flag in place for now so its presence
doesn't result in an error. It is simply ignored.
Part 2 will rename the `tui_app_server` directory `tui`. I did this as
two parts to reduce visible code churn.
### Preliminary /plugins TUI menu
- Adds a preliminary /plugins menu flow in both tui and tui_app_server.
- Fetches plugin list data asynchronously and shows loading/error/cached
states.
- Limits this first pass to the curated ChatGPT marketplace.
- Shows available plugins with installed/status metadata.
- Supports in-menu search over plugin display name, plugin id, plugin
name, and marketplace label.
- Opens a plugin detail view on selection, including summaries for
Skills, Apps, and MCP Servers, with back navigation.
### Testing
- Launch codex-cli with plugins enabled (`--enable plugins`).
- Run /plugins and verify:
- loading state appears first
- plugin list is shown
- search filters results
- selecting a plugin opens detail view, with a list of
skills/connectors/MCP servers for the plugin
- back action returns to the list.
- Verify disabled behavior by running /plugins without plugins enabled
(shows “Plugins are disabled” message).
- Launch with `--enable tui_app_server` (and plugins enabled) and repeat
the same /plugins flow; behavior should match.
## Problem
When multiple Codex sessions are open at once, terminal tabs and windows
are hard to distinguish from each other. The existing status line only
helps once the TUI is already focused, so it does not solve the "which
tab is this?" problem.
This PR adds a first-class `/title` command so the terminal window or
tab title can carry a short, configurable summary of the current
session.
## Screenshot
<img width="849" height="320" alt="image"
src="https://github.com/user-attachments/assets/8b112927-7890-45ed-bb1e-adf2f584663d"
/>
## Mental model
`/statusline` and `/title` are separate status surfaces with different
constraints. The status line is an in-app footer that can be denser and
more detailed. The terminal title is external terminal metadata, so it
needs short, stable segments that still make multiple sessions easy to
tell apart.
The `/title` configuration is an ordered list of compact items. By
default it renders `spinner,project`, so active sessions show
lightweight progress first while idle sessions still stay easy to
disambiguate. Each configured item is omitted when its value is not
currently available rather than forcing a placeholder.
## Non-goals
This does not merge `/title` into `/statusline`, and it does not add an
arbitrary free-form title string. The feature is intentionally limited
to a small set of structured items so the title stays short and
reviewable.
This also does not attempt to restore whatever title the terminal or
shell had before Codex started. When Codex clears the title, it clears
the title Codex last wrote.
## Tradeoffs
A separate `/title` command adds some conceptual overlap with
`/statusline`, but it keeps title-specific constraints explicit instead
of forcing the status line model to cover two different surfaces.
Title refresh can happen frequently, so the implementation now shares
parsing and git-branch orchestration between the status line and title
paths, and caches the derived project-root name by cwd. That keeps the
hot path cheap without introducing background polling.
## Architecture
The TUI gets a new `/title` slash command and a dedicated picker UI for
selecting and ordering terminal-title items. The chosen ids are
persisted in `tui.terminal_title`, with `spinner` and `project` as the
default when the config is unset. `status` remains available as a
separate text item, so configurations like `spinner,status` render
compact progress like `⠋ Working`.
`ChatWidget` now refreshes both status surfaces through a shared
`refresh_status_surfaces()` path. That shared path parses configured
items once, warns on invalid ids once, synchronizes shared cached state
such as git-branch lookup, then renders the footer status line and
terminal title from the same snapshot.
Low-level OSC title writes live in `codex-rs/tui/src/terminal_title.rs`,
which owns the terminal write path and last-mile sanitization before
emitting OSC 0.
## Security
Terminal-title text is treated as untrusted display content before Codex
emits it. The write path strips control characters, removes invisible
and bidi formatting characters that can make the title visually
misleading, normalizes whitespace, and caps the emitted length.
References used while implementing this:
- [xterm control
sequences](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
- [WezTerm escape sequences](https://wezterm.org/escape-sequences.html)
- [CWE-150: Improper Neutralization of Escape, Meta, or Control
Sequences](https://cwe.mitre.org/data/definitions/150.html)
- [CERT VU#999008 (Trojan Source)](https://kb.cert.org/vuls/id/999008)
- [Trojan Source disclosure site](https://trojansource.codes/)
- [Unicode Bidirectional Algorithm (UAX
#9)](https://www.unicode.org/reports/tr9/)
- [Unicode Security Considerations (UTR
#36)](https://www.unicode.org/reports/tr36/)
## Observability
Unknown configured title item ids are warned about once instead of
repeatedly spamming the transcript. Live preview applies immediately
while the `/title` picker is open, and cancel rolls the in-memory title
selection back to the pre-picker value.
If terminal title writes fail, the TUI emits debug logs around set and
clear attempts. The rendered status label intentionally collapses richer
internal states into compact title text such as `Starting...`, `Ready`,
`Thinking...`, `Working...`, `Waiting...`, and `Undoing...` when
`status` is configured.
## Tests
Ran:
- `just fmt`
- `cargo test -p codex-tui`
At the moment, the red Windows `rust-ci` failures are due to existing
`codex-core` `apply_patch_cli` stack-overflow tests that also reproduce
on `main`. The `/title`-specific `codex-tui` suite is green.
### Motivation
- Interrupting a running turn (Ctrl+C / Esc) currently also terminates
long‑running background shells, which is surprising for workflows like
local dev servers or file watchers.
- The existing cleanup command name was confusing; callers expect an
explicit command to stop background terminals rather than a UI clear
action.
- Make background‑shell termination explicit and surface a clearer
command name while preserving backward compatibility.
### Description
- Renamed the background‑terminal cleanup slash command from `Clean`
(`/clean`) to `Stop` (`/stop`) and kept `clean` as an alias in the
command parsing/visibility layer, updated the user descriptions and
command popup wiring accordingly.
- Updated the unified‑exec footer text and snapshots to point to `/stop`
(and trimmed corresponding snapshot output to match the new label).
- Changed interrupt behavior so `Op::Interrupt` (Ctrl+C / Esc interrupt)
no longer closes or clears tracked unified exec / background terminal
processes in the TUI or core cleanup path; background shells are now
preserved after an interrupt.
- Updated protocol/docs to clarify that `turn/interrupt` (or
`Op::Interrupt`) interrupts the active turn but does not terminate
background terminals, and that `thread/backgroundTerminals/clean` is the
explicit API to stop those shells.
- Updated unit/integration tests and insta snapshots in the TUI and core
unified‑exec suites to reflect the new semantics and command name.
### Testing
- Ran formatting with `just fmt` in `codex-rs` (succeeded).
- Ran `cargo test -p codex-protocol` (succeeded).
- Attempted `cargo test -p codex-tui` but the build could not complete
in this environment due to a native build dependency that requires
`libcap` development headers (the `codex-linux-sandbox` vendored build
step); install `libcap-dev` / make `libcap.pc` available in
`PKG_CONFIG_PATH` to run the TUI test suite locally.
- Updated and accepted the affected `insta` snapshots for the TUI
changes so visual diffs reflect the new `/stop` wording and preserved
interrupt behavior.
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_69b39c44b6dc8323bd133ae206310fae)
## Summary
- update the /fast slash command description to mention fastest
inference
- mention the 3X plan usage tradeoff in the help copy
## Testing
- cargo test -p codex-tui slash_command (currently blocked by an
unrelated latest-main codex-tui compile error in chatwidget.rs:
refresh_queued_user_messages missing)
---------
Co-authored-by: Codex <noreply@openai.com>
- add a local Fast mode setting in codex-core (similar to how model id
is currently stored on disk locally)
- send `service_tier=priority` on requests when Fast is enabled
- add `/fast` in the TUI and persist it locally
- feature flag
## Summary
- add a dedicated /audio picker for realtime microphone and speaker
selection
- persist realtime audio choices and prompt to restart only local audio
when voice is live
- add snapshot coverage for the new picker surfaces
## Validation
- cargo test -p codex-tui
- cargo insta accept
- just fix -p codex-tui
- just fmt
- Add a hidden `realtime_conversation` feature flag and `/realtime`
slash command for start/stop live voice sessions.
- Reuse transcription composer/footer UI for live metering, stream mic
audio, play assistant audio, render realtime user text events, and
force-close on feature disable.
---------
Co-authored-by: Codex <noreply@openai.com>
## Summary
Adds syntax highlighting to the TUI for fenced code blocks in markdown
responses and file diffs, plus a `/theme` command with live preview and
persistent theme selection. Uses syntect (~250 grammars, 32 bundled
themes, ~1 MB binary cost) — the same engine behind `bat`, `delta`, and
`xi-editor`. Includes guardrails for large inputs, graceful fallback to
plain text, and SSH-aware clipboard integration for the `/copy` command.
<img width="1554" height="1014" alt="image"
src="https://github.com/user-attachments/assets/38737a79-8717-4715-b857-94cf1ba59b85"
/>
<img width="2354" height="1374" alt="image"
src="https://github.com/user-attachments/assets/25d30a00-c487-4af8-9cb6-63b0695a4be7"
/>
## Problem
Code blocks in the TUI (markdown responses and file diffs) render
without syntax highlighting, making it hard to scan code at a glance.
Users also have no way to pick a color theme that matches their terminal
aesthetic.
## Mental model
The highlighting system has three layers:
1. **Syntax engine** (`render::highlight`) -- a thin wrapper around
syntect + two-face. It owns a process-global `SyntaxSet` (~250 grammars)
and a `RwLock<Theme>` that can be swapped at runtime. All public entry
points accept `(code, lang)` and return ratatui `Span`/`Line` vectors or
`None` when the language is unrecognized or the input exceeds safety
guardrails.
2. **Rendering consumers** -- `markdown_render` feeds fenced code blocks
through the engine; `diff_render` highlights Add/Delete content as a
whole file and Update hunks per-hunk (preserving parser state across
hunk lines). Both callers fall back to plain unstyled text when the
engine returns `None`.
3. **Theme lifecycle** -- at startup the config's `tui.theme` is
resolved to a syntect `Theme` via `set_theme_override`. At runtime the
`/theme` picker calls `set_syntax_theme` to swap themes live; on cancel
it restores the snapshot taken at open. On confirm it persists `[tui]
theme = "..."` to config.toml.
## Non-goals
- Inline diff highlighting (word-level change detection within a line).
- Semantic / LSP-backed highlighting.
- Theme authoring tooling; users supply standard `.tmTheme` files.
## Tradeoffs
| Decision | Upside | Downside |
| ------------------------------------------------ |
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------
|
| syntect over tree-sitter / arborium | ~1 MB binary increase for ~250
grammars + 32 themes; battle-tested crate powering widely-used tools
(`bat`, `delta`, `xi-editor`). tree-sitter would add ~12 MB for 20-30
languages or ~35 MB for full coverage. | Regex-based; less structurally
accurate than tree-sitter for some languages (e.g. language injections
like JS-in-HTML). |
| Global `RwLock<Theme>` | Enables live `/theme` preview without
threading Theme through every call site | Lock contention risk
(mitigated: reads vastly outnumber writes, single UI thread) |
| Skip background / italic / underline from themes | Terminal BG
preserved, avoids ugly rendering on some themes | Themes that rely on
these properties lose fidelity |
| Guardrails: 512 KB / 10k lines | Prevents pathological stalls on huge
diffs or pastes | Very large files render without color |
## Architecture
```
config.toml ─[tui.theme]─> set_theme_override() ─> THEME (RwLock)
│
┌───────────────────────────────────────────┘
│
markdown_render ─── highlight_code_to_lines(code, lang) ─> Vec<Line>
diff_render ─── highlight_code_to_styled_spans(code, lang) ─> Option<Vec<Vec<Span>>>
│
│ (None ⇒ plain text fallback)
│
/theme picker ─── set_syntax_theme(theme) // live preview swap
─── current_syntax_theme() // snapshot for cancel
─── resolve_theme_by_name(name) // lookup by kebab-case
```
Key files:
- `tui/src/render/highlight.rs` -- engine, theme management, guardrails
- `tui/src/diff_render.rs` -- syntax-aware diff line wrapping
- `tui/src/theme_picker.rs` -- `/theme` command builder
- `tui/src/bottom_pane/list_selection_view.rs` -- side content panel,
callbacks
- `core/src/config/types.rs` -- `Tui::theme` field
- `core/src/config/edit.rs` -- `syntax_theme_edit()` helper
## Observability
- `tracing::warn` when a configured theme name cannot be resolved.
- `Config::startup_warnings` surfaces the same message as a TUI banner.
- `tracing::error` when persisting theme selection fails.
## Tests
- Unit tests in `highlight.rs`: language coverage, fallback behavior,
CRLF stripping, style conversion, guardrail enforcement, theme name
mapping exhaustiveness.
- Unit tests in `diff_render.rs`: snapshot gallery at multiple terminal
sizes (80x24, 94x35, 120x40), syntax-highlighted wrapping, large-diff
guardrail, rename-to-different-extension highlighting, parser state
preservation across hunk lines.
- Unit tests in `theme_picker.rs`: preview rendering (wide + narrow),
dim overlay on deletions, subtitle truncation, cancel-restore, fallback
for unavailable configured theme.
- Unit tests in `list_selection_view.rs`: side layout geometry, stacked
fallback, buffer clearing, cancel/selection-changed callbacks.
- Integration test in `lib.rs`: theme warning uses the final
(post-resume) config.
## Cargo Deny: Unmaintained Dependency Exceptions
This PR adds two `cargo deny` advisory exceptions for transitive
dependencies pulled in by `syntect v5.3.0`:
| Advisory | Crate | Status |
|----------|-------|--------|
| RUSTSEC-2024-0320 | `yaml-rust` | Unmaintained (maintainer
unreachable) |
| RUSTSEC-2025-0141 | `bincode` | Unmaintained (development ceased;
v1.3.3 considered complete) |
**Why this is safe in our usage:**
- Neither advisory describes a known security vulnerability. Both are
"unmaintained" notices only.
- `bincode` is used by syntect to deserialize pre-compiled syntax sets.
Again, these are **static vendored artifacts** baked into the binary at
build time. No user-supplied bincode data is ever deserialized. - Attack
surface is zero for both crates; exploitation would require a
supply-chain compromise of our own build artifacts.
- These exceptions can be removed when syntect migrates to `yaml-rust2`
and drops `bincode`, or when alternative crates are available upstream.
There is an edge case where a directory is not readable by the sandbox.
In practice, we've seen very little of it, but it can happen so this
slash command unlocks users when it does.
Future idea is to make this a tool that the agent knows about so it can
be more integrated.
1. Move Windows Sandbox NUX to right after trust directory screen
2. Don't offer read-only as an option in Sandbox NUX.
Elevated/Legacy/Quit
3. Don't allow new untrusted directories. It's trust or quit
4. move experimental sandbox features to `[windows]
sandbox="elevated|unelevatd"`
5. Copy tweaks = elevated -> default, non-elevated -> non-admin
With this PR we do not close the unified exec processes (i.e. background
terminals) at the end of a turn unless:
* The user interrupt the turn
* The user decide to clean the processes through `app-server` or
`/clean`
I made sure that `codex exec` correctly kill all the processes
## Summary
- Adds a new `/statusline` command to configure TUI footer status line
- Introduces reusable `MultiSelectPicker` component with keyboard
navigation, optional ordering and toggle support
- Implement status line setup modal that persist configuration to
config.toml
## Status Line Items
The following items can be displayed in the status line:
- **Model**: Current model name (with optional reasoning level)
- **Context**: Remaining/used context window percentage
- **Rate Limits**: 5-day and weekly usage limits
- **Git**: Current branch (with optimized lookups)
- **Tokens**: Used tokens, input/output token counts
- **Session**: Session ID (full or shortened prefix)
- **Paths**: Current directory, project root
- **Version**: Codex version
## Features
- Live preview while configuring status line items
- Fuzzy search filtering in the picker
- Intelligent truncation when items don't fit
- Items gracefully omit when data is unavailable
- Configuration persists to `config.toml`
- Validates and warns about invalid status line items
## Test plan
- [x] Run `/statusline` and verify picker UI appears
- [x] Toggle items on/off and verify live preview updates
- [x] Confirm selection persists after restart
- [x] Verify truncation behavior with many items selected
- [x] Test git branch detection in and out of git repos
---------
Co-authored-by: Josh McKinney <joshka@openai.com>
## 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 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>
## 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`
- [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.
## 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
We've recently standardized a [feature maturity
model](https://developers.openai.com/codex/feature-maturity) that we're
using in our docs and support forums to communicate expectations to
users. This PR updates the internal stage names and descriptions to
match.
This change involves a simple internal rename and updates to a few
user-visible strings. No functional change.
## Summary
Adds /personality selector in the TUI, which leverages the new core
interface in #9644
Notes:
- We are doing some of our own state management for model_info loading
here, but not sure if that's ideal. open to opinions on simpler
approach, but would like to avoid blocking on a larger refactor
- Right now, the `/personality` selector just hides when the model
doesn't support it. we can update this behavior down the line
## Testing
- [x] Tested locally
- [x] Added snapshot tests
## Summary
Adds the `/permissions` command, with a (usually) shorter set of
permissions. `/approvals` still exists, for backwards compatibility.
<img width="863" height="309" alt="Screenshot 2026-01-20 at 4 12 51 PM"
src="https://github.com/user-attachments/assets/c49b5ba5-bc47-46dd-9067-e1a5670328fe"
/>
## Testing
- [x] updated unit tests
- [x] Tested locally
Implemented /fork to fork the current session directly (no picker),
handling it via a new ForkCurrentSession app event in both tui and tui2.
Updated slash command descriptions/tooltips and adjusted the fork tests
accordingly. Removed the unused in-session fork picker event.
Elevated Sandbox NUX:
* prompt for elevated sandbox setup when agent mode is selected (via
/approvals or at startup)
* prompt for degraded sandbox if elevated setup is declined or fails
* introduce /elevate-sandbox command to upgrade from degraded
experience.
See snapshots for view of edge cases
This is still named `UnifiedExecSessions` for consistency across the
code but should be renamed to `BackgroundTerminals` in a follow-up
Example:
<img width="945" height="687" alt="Screenshot 2025-12-18 at 20 12 53"
src="https://github.com/user-attachments/assets/92f39ff2-243c-4006-b402-e3fa9e93c952"
/>
## Summary
- add the `/exit` slash command alongside `/quit` and reuse shared exit
handling
- refactor the chat widget to funnel quit, exit, logout, and shutdown
flows through a common `request_exit` helper
- add focused unit tests that confirm both `/quit` and `/exit` send an
`ExitRequest`
## Testing
- `just fmt`
- `just fix -p codex-tui`
- `cargo test -p codex-tui`
------
https://chatgpt.com/codex/tasks/task_i_6903d5a8f47c8321bf180f031f2fa330
## Summary
- add a debug-only `/rollout` slash command that prints the rollout file
path or reports when none is known
- surface the new command in the slash command metadata and cover it
with unit tests
<img width="539" height="99" alt="image"
src="https://github.com/user-attachments/assets/688e1334-8a06-4576-abb8-ada33b458661"
/>