## 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>
Codex CLI (Rust Implementation)
We provide Codex CLI as a standalone, native executable to ensure a zero-dependency install.
Installing Codex
Today, the easiest way to install Codex is via npm:
npm i -g @openai/codex
codex
You can also install via Homebrew (brew install --cask codex) or download a platform-specific release directly from our GitHub Releases.
Documentation quickstart
- First run with Codex? Start with
docs/getting-started.md(links to the walkthrough for prompts, keyboard shortcuts, and session management). - Want deeper control? See
docs/config.mdanddocs/install.md.
What's new in the Rust CLI
The Rust implementation is now the maintained Codex CLI and serves as the default experience. It includes a number of features that the legacy TypeScript CLI never supported.
Config
Codex supports a rich set of configuration options. Note that the Rust CLI uses config.toml instead of config.json. See docs/config.md for details.
Model Context Protocol Support
MCP client
Codex CLI functions as an MCP client that allows the Codex CLI and IDE extension to connect to MCP servers on startup. See the configuration documentation for details.
MCP server (experimental)
Codex can be launched as an MCP server by running codex mcp-server. This allows other MCP clients to use Codex as a tool for another agent.
Use the @modelcontextprotocol/inspector to try it out:
npx @modelcontextprotocol/inspector codex mcp-server
Use codex mcp to add/list/get/remove MCP server launchers defined in config.toml, and codex mcp-server to run the MCP server directly.
Notifications
You can enable notifications by configuring a script that is run whenever the agent finishes a turn. The notify documentation includes a detailed example that explains how to get desktop notifications via terminal-notifier on macOS. When Codex detects that it is running under WSL 2 inside Windows Terminal (WT_SESSION is set), the TUI automatically falls back to native Windows toast notifications so approval prompts and completed turns surface even though Windows Terminal does not implement OSC 9.
codex exec to run Codex programmatically/non-interactively
To run Codex non-interactively, run codex exec PROMPT (you can also pass the prompt via stdin) and Codex will work on your task until it decides that it is done and exits. If you provide both a prompt argument and piped stdin, Codex appends stdin as a <stdin> block after the prompt so patterns like echo "my output" | codex exec "Summarize this concisely" work naturally. Output is printed to the terminal directly. You can set the RUST_LOG environment variable to see more about what's going on.
Use codex exec --ephemeral ... to run without persisting session rollout files to disk.
Experimenting with the Codex Sandbox
To test to see what happens when a command is run under the sandbox provided by Codex, we provide the following subcommands in Codex CLI:
# macOS
codex sandbox macos [--full-auto] [--log-denials] [COMMAND]...
# Linux
codex sandbox linux [--full-auto] [COMMAND]...
# Windows
codex sandbox windows [--full-auto] [COMMAND]...
# Legacy aliases
codex debug seatbelt [--full-auto] [--log-denials] [COMMAND]...
codex debug landlock [--full-auto] [COMMAND]...
Selecting a sandbox policy via --sandbox
The Rust CLI exposes a dedicated --sandbox (-s) flag that lets you pick the sandbox policy without having to reach for the generic -c/--config option:
# Run Codex with the default, read-only sandbox
codex --sandbox read-only
# Allow the agent to write within the current workspace while still blocking network access
codex --sandbox workspace-write
# Danger! Disable sandboxing entirely (only do this if you are already running in a container or other isolated env)
codex --sandbox danger-full-access
The same setting can be persisted in ~/.codex/config.toml via the top-level sandbox_mode = "MODE" key, e.g. sandbox_mode = "workspace-write".
In workspace-write, Codex also includes ~/.codex/memories in its writable roots so memory maintenance does not require an extra approval.
Code Organization
This folder is the root of a Cargo workspace. It contains quite a bit of experimental code, but here are the key crates:
core/contains the business logic for Codex. Ultimately, we hope this to be a library crate that is generally useful for building other Rust/native applications that use Codex.exec/"headless" CLI for use in automation.tui/CLI that launches a fullscreen TUI built with Ratatui.cli/CLI multitool that provides the aforementioned CLIs via subcommands.
If you want to contribute or inspect behavior in detail, start by reading the module-level README.md files under each crate and run the project workspace from the top-level codex-rs directory so shared config, features, and build scripts stay aligned.