mirror of
https://github.com/openai/codex.git
synced 2026-11-03 08:22:30 +00:00
5906c6a658d6b91ccd0963ee6741bdb559b8f78e
28
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5906c6a658 |
chore: remove skill metadata from command approval payloads (#15906)
## Why This is effectively a follow-up to [#15812](https://github.com/openai/codex/pull/15812). That change removed the special skill-script exec path, but `skill_metadata` was still being threaded through command-approval payloads even though the approval flow no longer uses it to render prompts or resolve decisions. Keeping it around added extra protocol, schema, and client surface area without changing behavior. Removing it keeps the command-approval contract smaller and avoids carrying a dead field through app-server, TUI, and MCP boundaries. ## What changed - removed `ExecApprovalRequestSkillMetadata` and the corresponding `skillMetadata` field from core approval events and the v2 app-server protocol - removed the generated JSON and TypeScript schema output for that field - updated app-server, MCP server, TUI, and TUI app-server approval plumbing to stop forwarding the field - cleaned up tests that previously constructed or asserted `skillMetadata` ## Testing - `cargo test -p codex-app-server-protocol` - `cargo test -p codex-protocol` - `cargo test -p codex-app-server-test-client` - `cargo test -p codex-mcp-server` - `just argument-comment-lint` |
||
|
|
2c54d4b160 |
feat(tui): add terminal title support to tui app server (#15860)
## TR;DR
Replicates the `/title` command from `tui` to `tui_app_server`.
## Problem
The classic `tui` crate supports customizing the terminal window/tab
title via `/title`, but the `tui_app_server` crate does not. Users on
the app-server path have no way to configure what their terminal title
shows (project name, status, spinner, thread, etc.), making it harder to
identify Codex sessions across tabs or windows.
## Mental model
The terminal title is a *status surface* -- conceptually parallel to the
footer status line. Both surfaces are configurable lists of items, both
share expensive inputs (git branch lookup, project root discovery), and
both must be refreshed at the same lifecycle points. This change ports
the classic `tui`'s design verbatim:
1. **`terminal_title.rs`** owns the low-level OSC write path and input
sanitization. It strips control characters and bidi/invisible codepoints
before placing untrusted text (model output, thread names, project
paths) inside an escape sequence.
2. **`title_setup.rs`** defines `TerminalTitleItem` (the 8 configurable
items) and `TerminalTitleSetupView` (the interactive picker that wraps
`MultiSelectPicker`).
3. **`status_surfaces.rs`** is the shared refresh pipeline. It parses
both surface configs once per refresh, warns about invalid items once
per session, synchronizes the git-branch cache, then renders each
surface from the same `StatusSurfaceSelections` snapshot.
4. **`chatwidget.rs`** sets `TerminalTitleStatusKind` at each state
transition (Working, Thinking, Undoing, WaitingForBackgroundTerminal)
and calls `refresh_terminal_title()` whenever relevant state changes.
5. **`app.rs`** handles the three setup events (confirm/preview/cancel),
persists config via `ConfigEditsBuilder`, and clears the managed title
on `Drop`.
## Non-goals
- **Restoring the previous terminal title on exit.** There is no
portable way to read the terminal's current title, so `Drop` clears the
managed title rather than restoring it.
- **Sharing code between `tui` and `tui_app_server`.** The
implementation is a parallel copy, matching the existing pattern for the
status-line feature. Extracting a shared crate is future work.
## Tradeoffs
- **Duplicate code across crates.** The three core files
(`terminal_title.rs`, `title_setup.rs`, `status_surfaces.rs`) are
byte-for-byte copies from the classic `tui`. This was chosen for
consistency with the existing status-line port and to avoid coupling the
two crates at the dependency level. Future changes must be applied in
both places.
- **`status_surfaces.rs` is large (~660 lines).** It absorbs logic that
previously lived inline in `chatwidget.rs` (status-line refresh, git
branch management, project root discovery) plus all new terminal-title
logic. This consolidation trades file size for a single place where both
surfaces are coordinated.
- **Spinner scheduling on every refresh.** The terminal title spinner
(when active) schedules a frame every 100ms. This is the same pattern
the status-indicator spinner already uses; the overhead is a timer
registration, not a redraw.
## Architecture
```
/title command
-> SlashCommand::Title
-> open_terminal_title_setup()
-> TerminalTitleSetupView (MultiSelectPicker)
-> on_change: AppEvent::TerminalTitleSetupPreview -> preview_terminal_title()
-> on_confirm: AppEvent::TerminalTitleSetup -> ConfigEditsBuilder + setup_terminal_title()
-> on_cancel: AppEvent::TerminalTitleSetupCancelled -> cancel_terminal_title_setup()
Runtime title refresh:
state change (turn start, reasoning, undo, plan update, thread rename, ...)
-> set terminal_title_status_kind
-> refresh_terminal_title()
-> status_surface_selections() (parse configs, collect invalids)
-> refresh_terminal_title_from_selections()
-> terminal_title_value_for_item() for each configured item
-> assemble title string with separators
-> skip if identical to last_terminal_title (dedup OSC writes)
-> set_terminal_title() (sanitize + OSC 0 write)
-> schedule spinner frame if animating
Widget replacement:
replace_chat_widget_with_app_server_thread()
-> transfer last_terminal_title from old widget to new
-> avoids redundant OSC clear+rewrite on session switch
```
## Observability
- Invalid terminal-title item IDs in config emit a one-per-session
warning via `on_warning()` (gated by
`terminal_title_invalid_items_warned` `AtomicBool`).
- OSC write failures are logged at `tracing::debug` level.
- Config persistence failures are logged at `tracing::error` and
surfaced to the user via `add_error_message()`.
## Tests
- `terminal_title.rs`: 4 unit tests covering sanitization (control
chars, bidi codepoints, truncation) and OSC output format.
- `title_setup.rs`: 3 tests covering setup view snapshot rendering,
parse order preservation, and invalid-ID rejection.
- `chatwidget/tests.rs`: Updated test helpers with new fields; existing
tests continue to pass.
---------
Co-authored-by: Eric Traut <etraut@openai.com>
|
||
|
|
1ff39b6fa8 |
Wire remote app-server auth through the client (#14853)
For app-server websocket auth, support the two server-side mechanisms from PR #14847: - `--ws-auth capability-token --ws-token-file /abs/path` - `--ws-auth signed-bearer-token --ws-shared-secret-file /abs/path` with optional `--ws-issuer`, `--ws-audience`, and `--ws-max-clock-skew-seconds` On the client side, add interactive remote support via: - `--remote ws://host:port` or `--remote wss://host:port` - `--remote-auth-token-env <ENV_VAR>` Codex reads the bearer token from the named environment variable and sends it as `Authorization: Bearer <token>` during the websocket handshake. Remote auth tokens are only allowed for `wss://` URLs or loopback `ws://` URLs. Testing: - tested both auth methods manually to confirm connection success and rejection for both auth types |
||
|
|
b565f05d79 |
Fix quoted command rendering in tui_app_server (#15825)
When `tui_app_server` is enabled, shell commands in the transcript render as fully quoted invocations like `/bin/zsh -lc "..."`. The non-app-server TUI correctly shows the parsed command body. Root cause: The app-server stores `ThreadItem::CommandExecution.command` as a shell-quoted string. When `tui_app_server` bridges that item back into the exec renderer, it was passing `vec![command]` unchanged instead of splitting the string back into argv. That prevented `strip_bash_lc_and_escape()` from recognizing the shell wrapper, so the renderer displayed the wrapper literally. Solution: Add a shared command-string splitter that round-trips shell-quoted commands back into argv when it is safe to do so, while preserving non-roundtrippable inputs as a single string. Use that helper everywhere `tui_app_server` reconstructs exec commands from app-server payloads, including live command-execution items, replayed thread items, and exec approval requests. This restores the same command display behavior as the direct TUI path without breaking Windows-style commands that cannot be safely round-tripped. |
||
|
|
2794e27849 |
Add ReloadUserConfig to tui_app_server (#15806)
- Adds ReloadUserConfig to `tui_app_server` |
||
|
|
79359fb5e7 |
fix(tui_app_server): fix remote subagent switching and agent names (#15513)
## TL;DR This PR changes the `tui_app_server` _path_ in the following ways: - add missing feature to show agent names (shows only UUIDs today) - add `Cmd/Alt+Arrows` navigation between agent conversations ## Problem When the TUI connects to a remote app server, collab agent tool-call items (spawn, wait, delegate, etc.) render thread UUIDs instead of human-readable agent names because the `ChatWidget` never receives nickname/role metadata for receiver threads. Separately, keyboard next/previous agent navigation silently does nothing when the local `AgentNavigationState` cache has not yet been populated with subagent threads that the remote server already knows about. Both issues share a root cause: in the remote (app-server) code path the TUI never proactively fetches thread metadata. In the local code path this metadata arrives naturally via spawn events the TUI itself orchestrates, but in the remote path those events were processed by a different client and the TUI only sees the resulting collab tool-call notifications. ## Mental model Collab agent tool-call notifications reference receiver threads by id, but carry no nickname or role. The TUI needs that metadata in two places: 1. **Rendering** -- `ChatWidget` converts `CollabAgentToolCall` items into history cells. Without metadata, agent status lines show raw UUIDs. 2. **Navigation** -- `AgentNavigationState` tracks known threads for the `/agent` picker and keyboard cycling. Without entries for remote subagents, next/previous has nowhere to go. This change closes the gap with two complementary strategies: - **Eager hydration**: when any notification carries `receiver_thread_ids`, the TUI fetches metadata (`thread/read`) for threads it has not yet cached before the notification is rendered. - **Backfill on thread switch**: when the user resumes, forks, or starts a new app-server thread, the TUI fetches the full `thread/loaded/list`, walks the parent-child spawn tree, and registers every descendant subagent in both the navigation cache and the `ChatWidget` metadata map. A new `collab_agent_metadata` side-table in `ChatWidget` stores nickname/role keyed by `ThreadId`, kept in sync by `App` whenever it calls `upsert_agent_picker_thread`. The `replace_chat_widget` helper re-seeds this map from `AgentNavigationState` so that thread switches (which reconstruct the widget) do not lose previously discovered metadata. ## Non-goals - This change does not alter the local (non-app-server) collab code path. That path already receives metadata via spawn events and is unaffected. - No new protocol messages are introduced. The change uses existing `thread/read` and `thread/loaded/list` RPCs. - No changes to how `AgentNavigationState` orders or cycles through threads. The traversal logic is unchanged; only the population of entries is extended. ## Tradeoffs - **Extra RPCs on notification path**: `hydrate_collab_agent_metadata_for_notification` issues a `thread/read` for each unknown receiver thread before the notification is forwarded to rendering. This adds latency on the notification path but only fires once per thread (the result is cached). The alternative -- rendering first and backfilling names later -- would cause visible flicker as UUIDs are replaced with names. - **Backfill fetches all loaded threads**: `backfill_loaded_subagent_threads` fetches the full loaded-thread list and walks the spawn tree even when the user may only care about one subagent. This is simple and correct but O(loaded_threads) per thread switch. For typical session sizes this is negligible; it could become a concern for sessions with hundreds of subagents. - **Metadata duplication**: agent nickname/role is now stored in both `AgentNavigationState` (for picker/label) and `ChatWidget::collab_agent_metadata` (for rendering). The two are kept in sync through `upsert_agent_picker_thread` and `replace_chat_widget`, but there is no compile-time enforcement of this coupling. ## Architecture ### New module: `app::loaded_threads` Pure function `find_loaded_subagent_threads_for_primary` that takes a flat list of `Thread` objects and a primary thread id, then walks the `SessionSource::SubAgent` parent-child edges to collect all transitive descendants. Returns a sorted vec of `LoadedSubagentThread` (thread_id + nickname + role). No async, no side effects -- designed for unit testing. ### New methods on `App` | Method | Purpose | |--------|---------| | `collab_receiver_thread_ids` | Extracts `receiver_thread_ids` from `ItemStarted` / `ItemCompleted` collab notifications | | `hydrate_collab_agent_metadata_for_notification` | Fetches and caches metadata for unknown receiver threads before a notification is rendered | | `backfill_loaded_subagent_threads` | Bulk-fetches all loaded threads and registers descendants of the primary thread | | `adjacent_thread_id_with_backfill` | Attempts navigation, falls back to backfill if the cache has no adjacent entry | | `replace_chat_widget` | Replaces the widget and re-seeds its metadata map from `AgentNavigationState` | ### New state in `ChatWidget` `collab_agent_metadata: HashMap<ThreadId, CollabAgentMetadata>` -- a lookup table that rendering functions consult to attach human-readable names to collab tool-call items. Populated externally by `App` via `set_collab_agent_metadata`. ### New method on `AppServerSession` `thread_loaded_list` -- thin wrapper around `ClientRequest::ThreadLoadedList`. ## Observability - `tracing::warn` on invalid thread ids during hydration and backfill. - `tracing::warn` on failed `thread/read` or `thread/loaded/list` RPCs (with thread id and error). - No new metrics or feature flags. ## Tests - **`loaded_threads::tests::finds_loaded_subagent_tree_for_primary_thread`** -- unit test for the spawn-tree walk: verifies child and grandchild are included, unrelated threads are excluded, and metadata is carried through. - **`app::tests::replace_chat_widget_reseeds_collab_agent_metadata_for_replay`** -- integration test that creates a `ChatWidget`, replaces it via `replace_chat_widget`, replays a collab wait notification, and asserts the rendered history cell contains the agent name rather than a UUID. - **Updated snapshot** `app_server_collab_wait_items_render_history` -- the existing collab wait rendering test now sets metadata before sending notifications, so the snapshot shows `Robie [explorer]` / `Ada [reviewer]` instead of raw thread ids. --------- Co-authored-by: Eric Traut <etraut@openai.com> |
||
|
|
504aeb0e09 |
Use AbsolutePathBuf for cwd state (#15710)
Migrate `cwd` and related session/config state to `AbsolutePathBuf` so downstream consumers consistently see absolute working directories. Add test-only `.abs()` helpers for `Path`, `PathBuf`, and `TempDir`, and update branch-local tests to use them instead of `AbsolutePathBuf::try_from(...)`. For the remaining TUI/app-server snapshot coverage that renders absolute cwd values, keep the snapshots unchanged and skip the Windows-only cases where the platform-specific absolute path layout differs. |
||
|
|
c0ffd000dd |
Fix stale turn steering fallback in tui_app_server (#15714)
This PR adds code to recover from a narrow app-server timing race where a follow-up can be sent after the previous turn has already ended but before the TUI has observed that completion. Instead of surfacing turn/steer failed: no active turn to steer, the client now treats that as a stale active-turn cache and falls back to starting a fresh turn, matching the intended submit behavior more closely. This is similar to the strategy employed by other app server clients (notably, the IDE extension and desktop app). This race exists because the current app-server API makes the client choose between two separate RPCs, turn/steer and turn/start, based on its local view of whether a turn is still active. That view is replicated from asynchronous notifications, so it can be stale for a brief window. The server may already have ended the turn while the client still believes it is in progress. Since the choice is made client-side rather than atomically on the server, tui_app_server can occasionally send turn/steer for a turn that no longer exists. |
||
|
|
0f90a34676 |
Refresh mentions list after plugin install/uninstall (#15598)
Refresh mentions list after plugin install/uninstall to that $mentions are updated without requiring exiting/launching the client. |
||
|
|
67c1c7c054 |
chore(core) Add approvals reviewer to UserTurn (#15426)
## Summary Adds support for approvals_reviewer to `Op::UserTurn` so we can migrate `[CodexMessageProcessor::turn_start]` to use Op::UserTurn ## Testing - [x] Adds quick test for the new field Co-authored-by: Codex <noreply@openai.com> |
||
|
|
7b92a90612 |
Unify realtime stop handling in TUI (#15529)
## Summary - route /realtime, Ctrl+C, and deleted realtime meters through the same realtime stop path - keep generic transcription placeholder cleanup free of realtime shutdown side effects ## Testing - Ran - Relied on CI for verification; did not run local tests --------- Co-authored-by: Codex <noreply@openai.com> |
||
|
|
b5d0a5518d |
Plugins TUI install/uninstall (#15342)
- Add install/uninstall actions to the TUI plugins menu - Wire plugin install/uninstall through both TUI and `tui_app_server` - Refresh config/plugin state after changes so the UI updates immediately - Add a post-install app setup flow for plugins that require additional app auth <img width="1567" height="300" alt="Screenshot 2026-03-20 at 4 08 44 PM" src="https://github.com/user-attachments/assets/366bd31b-2ffd-4e80-b4a3-3a9a9c674a5f" /> <img width="445" height="240" alt="Screenshot 2026-03-20 at 4 08 54 PM" src="https://github.com/user-attachments/assets/613999ab-269a-4758-ab59-7c057a1742dc" /> <img width="797" height="219" alt="Screenshot 2026-03-20 at 4 09 07 PM" src="https://github.com/user-attachments/assets/b9679e60-40f5-49bb-ade0-2e40449c3fbf" /> <img width="499" height="235" alt="Screenshot 2026-03-20 at 4 09 24 PM" src="https://github.com/user-attachments/assets/261ce2fe-f356-4e99-8ac9-f29ed850bc75" /> Note/known issue: The /plugin install flow fails in `tui_app_server` because after a successful install it tries to trigger a ReloadUserConfig operation, but `tui_app_server` has not yet implemented transport for that operation, so it falls through to the generic “Not available in app-server TUI yet” stub. |
||
|
|
e838645fa2 |
tui: queue follow-ups during manual /compact (#15259)
## Summary - queue input after the user submits `/compact` until that manual compact turn ends - mirror the same behavior in the app-server TUI - add regressions for input queued before compact starts and while it is running Co-authored-by: Codex <noreply@openai.com> |
||
|
|
d1088158b8 |
fix: fall back to vendored bubblewrap when system bwrap lacks --argv0 (#15338)
## Why Fixes [#15283](https://github.com/openai/codex/issues/15283), where sandboxed tool calls fail on older distro `bubblewrap` builds because `/usr/bin/bwrap` does not understand `--argv0`. The upstream [bubblewrap v0.9.0 release notes](https://github.com/containers/bubblewrap/releases/tag/v0.9.0) explicitly call out `Add --argv0`. Flipping `use_legacy_landlock` globally works around that compatibility bug, but it also weakens the default Linux sandbox and breaks proxy-routed and split-policy cases called out in review. The follow-up Linux CI failure was in the new launcher test rather than the launcher logic: the fake `bwrap` helper stayed open for writing, so Linux would not exec it. This update also closes the user-visibility gap from review by surfacing the same startup warning when `/usr/bin/bwrap` is present but too old for `--argv0`, not only when it is missing. ## What Changed - keep `use_legacy_landlock` default-disabled - teach `codex-rs/linux-sandbox/src/launcher.rs` to fall back to the vendored bubblewrap build when `/usr/bin/bwrap` does not advertise `--argv0` support - add launcher tests for supported, unsupported, and missing system `bwrap` - write the fake `bwrap` test helper to a closed temp path so the supported-path launcher test works on Linux too - extend the startup warning path so Codex warns when `/usr/bin/bwrap` is missing or too old to support `--argv0` - mirror the warning/fallback wording across `codex-rs/linux-sandbox/README.md` and `codex-rs/core/README.md`, including that the fallback is the vendored bubblewrap compiled into the binary - cite the upstream `bubblewrap` release that introduced `--argv0` ## Verification - `bazel test --config=remote --platforms=//:rbe //codex-rs/linux-sandbox:linux-sandbox-unit-tests --test_filter=launcher::tests::prefers_system_bwrap_when_help_lists_argv0 --test_output=errors` - `cargo test -p codex-core system_bwrap_warning` - `cargo check -p codex-exec -p codex-tui -p codex-tui-app-server -p codex-app-server` - `just argument-comment-lint` |
||
|
|
b0236501e2 |
Remove legacy app-server notification handling from tui_app_server (#15390)
As part of moving the TUI onto the app server, we added some temporary handling of some legacy events. We've confirmed that these do not need to be supported, so this PR removes this support from the tui_app_server, allowing for additional simplifications in follow-on PRs. These events are needed only for very old rollouts. None of the other app server clients (IDE extension or app) support these either. ## Summary - stop translating legacy `codex/event/*` notifications inside `tui_app_server` - remove the TUI-side legacy warning and rollback buffering/replay paths that were only fed by those notifications - keep the lower-level app-server and app-server-client legacy event plumbing intact so PR #15106 can rebase on top and handle the remaining exec/lower-layer migration separately |
||
|
|
f7201e5a9f |
Initial plugins TUI menu - list and read only. tui + tui_app_server (#15215)
### 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.
|
||
|
|
fa2a2f0be9 |
Use released DotSlash package for argument-comment lint (#15199)
## Why The argument-comment lint now has a packaged DotSlash artifact from [#15198](https://github.com/openai/codex/pull/15198), so the normal repo lint path should use that released payload instead of rebuilding the lint from source every time. That keeps `just clippy` and CI aligned with the shipped artifact while preserving a separate source-build path for people actively hacking on the lint crate. The current alpha package also exposed two integration wrinkles that the repo-side prebuilt wrapper needs to smooth over: - the bundled Dylint library filename includes the host triple, for example `@nightly-2025-09-18-aarch64-apple-darwin`, and Dylint derives `RUSTUP_TOOLCHAIN` from that filename - on Windows, Dylint's driver path also expects `RUSTUP_HOME` to be present in the environment Without those adjustments, the prebuilt CI jobs fail during `cargo metadata` or driver setup. This change makes the checked-in prebuilt wrapper normalize the packaged library name to the plain `nightly-2025-09-18` channel before invoking `cargo-dylint`, and it teaches both the wrapper and the packaged runner source to infer `RUSTUP_HOME` from `rustup show home` when the environment does not already provide it. After the prebuilt Windows lint job started running successfully, it also surfaced a handful of existing anonymous literal callsites in `windows-sandbox-rs`. This PR now annotates those callsites so the new cross-platform lint job is green on the current tree. ## What Changed - checked in the current `tools/argument-comment-lint/argument-comment-lint` DotSlash manifest - kept `tools/argument-comment-lint/run.sh` as the source-build wrapper for lint development - added `tools/argument-comment-lint/run-prebuilt-linter.sh` as the normal enforcement path, using the checked-in DotSlash package and bundled `cargo-dylint` - updated `just clippy` and `just argument-comment-lint` to use the prebuilt wrapper - split `.github/workflows/rust-ci.yml` so source-package checks live in a dedicated `argument_comment_lint_package` job, while the released lint runs in an `argument_comment_lint_prebuilt` matrix on Linux, macOS, and Windows - kept the pinned `nightly-2025-09-18` toolchain install in the prebuilt CI matrix, since the prebuilt package still relies on rustup-provided toolchain components - updated `tools/argument-comment-lint/run-prebuilt-linter.sh` to normalize host-qualified nightly library filenames, keep the `rustup` shim directory ahead of direct toolchain `cargo` binaries, and export `RUSTUP_HOME` when needed for Windows Dylint driver setup - updated `tools/argument-comment-lint/src/bin/argument-comment-lint.rs` so future published DotSlash artifacts apply the same nightly-filename normalization and `RUSTUP_HOME` inference internally - fixed the remaining Windows lint violations in `codex-rs/windows-sandbox-rs` by adding the required `/*param*/` comments at the reported callsites - documented the checked-in DotSlash file, wrapper split, archive layout, nightly prerequisite, and Windows `RUSTUP_HOME` requirement in `tools/argument-comment-lint/README.md` |
||
|
|
2e22885e79 |
Split features into codex-features crate (#15253)
- Split the feature system into a new `codex-features` crate. - Cut `codex-core` and workspace consumers over to the new config and warning APIs. Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com> Co-authored-by: Codex <noreply@openai.com> |
||
|
|
7eb19e5319 |
Move terminal module to terminal-detection crate (#15216)
- Move core/src/terminal.rs and its tests into a standalone terminal-detection workspace crate. - Update direct consumers to depend on codex-terminal-detection and import terminal APIs directly. --------- Co-authored-by: Codex <noreply@openai.com> |
||
|
|
01df50cf42 |
Add thread/shellCommand to app server API surface (#14988)
This PR adds a new `thread/shellCommand` app server API so clients can implement `!` shell commands. These commands are executed within the sandbox, and the command text and output are visible to the model. The internal implementation mirrors the current TUI `!` behavior. - persist shell command execution as `CommandExecution` thread items, including source and formatted output metadata - bridge live and replayed app-server command execution events back into the existing `tui_app_server` exec rendering path This PR also wires `tui_app_server` to submit `!` commands through the new API. |
||
|
|
334164a6f7 |
feat(tui): restore composer history in app-server tui (#14945)
## Problem
The app-server TUI (`tui_app_server`) lacked composer history support.
Pressing Up/Down to recall previous prompts hit a stub that logged a
warning and displayed "Not available in app-server TUI yet." New
submissions were silently dropped from the shared history file, so
nothing persisted for future sessions.
## Mental model
Codex maintains a single, append-only history file
(`$CODEX_HOME/history.jsonl`) shared across all TUI processes on the
same machine. The legacy (in-process) TUI already reads/writes this file
through `codex_core::message_history`. The app-server TUI delegates most
operations to a separate process over RPC, but history is intentionally
*not* an RPC concern — it's a client-local file.
This PR makes the app-server TUI access the same history file directly,
bypassing the app-server process entirely. The composer's Up/Down
navigation and submit-time persistence now follow the same code paths as
the legacy TUI, with the only difference being *where* the call is
dispatched (locally in `App`, rather than inside `CodexThread`).
The branch is rebuilt directly on top of `upstream/main`, so it keeps
the
existing app-server restore architecture intact.
`AppServerStartedThread`
still restores transcript history from the server `Thread` snapshot via
`thread_snapshot_events`; this PR only adds composer-history support.
## Non-goals
- Adding history support to the app-server protocol. History remains
client-local.
- Changing the on-disk format or location of `history.jsonl`.
- Surfacing history I/O errors to the user (failures are logged and
silently swallowed, matching the legacy TUI).
## Tradeoffs
| Decision | Why | Risk |
|----------|-----|------|
| Widen `message_history` from `pub(crate)` to `pub` | Avoids
duplicating file I/O logic; the module already has a clean, minimal API
surface. | Other workspace crates can now call these functions — the
contract is no longer crate-private. However, this is consistent with
recent precedent: `590cfa617` exposed `mention_syntax` for TUI
consumption, `752402c4f` exposed plugin APIs (`PluginsManager`), and
`14fcb6645`/`edacbf7b6` widened internal core APIs for other crates.
These were all narrow, intentional exposures of specific APIs — not
broad "make internals public" moves. `1af2a37ad` even went the other
direction, reducing broad re-exports to tighten boundaries. This change
follows the same pattern: a small, deliberate API surface (3 functions)
rather than a wholesale visibility change. |
| Intercept `AddToHistory` / `GetHistoryEntryRequest` in `App` before
RPC fallback | Keeps history ops out of the "unsupported op" error path
without changing app-server protocol. | This now routes through a single
`submit_thread_op` entry point, which is safer than the original
duplicated dispatch. The remaining risk is organizational: future
thread-op submission paths need to keep using that shared entry point. |
| `session_configured_from_thread_response` is now `async` | Needs
`await` on `history_metadata()` to populate real `history_log_id` /
`history_entry_count`. | Adds an async file-stat + full-file newline
scan to the session bootstrap path. The scan is bounded by
`history.max_bytes` and matches the legacy TUI's cost profile, but
startup latency still scales with file size. |
## Architecture
```
User presses Up User submits a prompt
│ │
▼ ▼
ChatComposerHistory ChatWidget::do_submit_turn
navigate_up() encode_history_mentions()
│ │
▼ ▼
AppEvent::CodexOp Op::AddToHistory { text }
(GetHistoryEntryRequest) │
│ ▼
▼ App::try_handle_local_history_op
App::try_handle_local_history_op message_history::append_entry()
spawn_blocking { │
message_history::lookup() ▼
} $CODEX_HOME/history.jsonl
│
▼
AppEvent::ThreadEvent
(GetHistoryEntryResponse)
│
▼
ChatComposerHistory::on_entry_response()
```
## Observability
- `tracing::warn` on `append_entry` failure (includes thread ID).
- `tracing::warn` on `spawn_blocking` lookup join error.
- `tracing::warn` from `message_history` internals on file-open, lock,
or parse failures.
## Tests
- `chat_composer_history::tests::navigation_with_async_fetch` — verifies
that Up emits `Op::GetHistoryEntryRequest` (was: checked for stub error
cell).
- `app::tests::history_lookup_response_is_routed_to_requesting_thread` —
verifies multi-thread composer recall routes the lookup result back to
the originating thread.
-
`app_server_session::tests::resume_response_relies_on_snapshot_replay_not_initial_messages`
— verifies app-server session restore still uses the upstream
thread-snapshot path.
-
`app_server_session::tests::session_configured_populates_history_metadata`
— verifies bootstrap sets nonzero `history_log_id` /
`history_entry_count` from the shared local history file.
|
||
|
|
347c6b12ec | Removed remaining core events from tui_app_server (#14942) | ||
|
|
a265d6043e |
feat: add memory citation to agent message (#14821)
Client side to come |
||
|
|
0d1539e74c |
fix(linux-sandbox): prefer system /usr/bin/bwrap when available (#14963)
## Problem Ubuntu/AppArmor hosts started failing in the default Linux sandbox path after the switch to vendored/default bubblewrap in `0.115.0`. The clearest report is in [#14919](https://github.com/openai/codex/issues/14919), especially [this investigation comment](https://github.com/openai/codex/issues/14919#issuecomment-4076504751): on affected Ubuntu systems, `/usr/bin/bwrap` works, but a copied or vendored `bwrap` binary fails with errors like `bwrap: setting up uid map: Permission denied` or `bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted`. The root cause is Ubuntu's `/etc/apparmor.d/bwrap-userns-restrict` profile, which grants `userns` access specifically to `/usr/bin/bwrap`. Once Codex started using a vendored/internal bubblewrap path, that path was no longer covered by the distro AppArmor exception, so sandbox namespace setup could fail even when user namespaces were otherwise enabled and `uidmap` was installed. ## What this PR changes - prefer system `/usr/bin/bwrap` whenever it is available - keep vendored bubblewrap as the fallback when `/usr/bin/bwrap` is missing - when `/usr/bin/bwrap` is missing, surface a Codex startup warning through the app-server/TUI warning path instead of printing directly from the sandbox helper with `eprintln!` - use the same launcher decision for both the main sandbox execution path and the `/proc` preflight path - document the updated Linux bubblewrap behavior in the Linux sandbox and core READMEs ## Why this fix This still fixes the Ubuntu/AppArmor regression from [#14919](https://github.com/openai/codex/issues/14919), but it keeps the runtime rule simple and platform-agnostic: if the standard system bubblewrap is installed, use it; otherwise fall back to the vendored helper. The warning now follows that same simple rule. If Codex cannot find `/usr/bin/bwrap`, it tells the user that it is falling back to the vendored helper, and it does so through the existing startup warning plumbing that reaches the TUI and app-server instead of low-level sandbox stderr. ## Testing - `cargo test -p codex-linux-sandbox` - `cargo test -p codex-app-server --lib` - `cargo test -p codex-tui-app-server tests::embedded_app_server_start_failure_is_returned` - `cargo clippy -p codex-linux-sandbox --all-targets` - `cargo clippy -p codex-app-server --all-targets` - `cargo clippy -p codex-tui-app-server --all-targets` |
||
|
|
43ee72a9b9 |
fix(tui): implement /mcp inventory for tui_app_server (#14931)
## Problem
The `/mcp` command did not work in the app-server TUI (remote mode). On
`main`, `add_mcp_output()` called `McpManager::effective_servers()`
in-process, which only sees locally configured servers, and then emitted
a generic stub message for the app-server to handle. In remote usage,
that left `/mcp` without a real inventory view.
## Solution
Implement `/mcp` for the app-server TUI by fetching MCP server inventory
directly from the app-server via the paginated `mcpServerStatus/list`
RPC and rendering the results into chat history.
The command now follows a three-phase lifecycle:
1. Loading: `ChatWidget::add_mcp_output()` inserts a transient
`McpInventoryLoadingCell` and emits `AppEvent::FetchMcpInventory`. This
gives immediate feedback that the command registered.
2. Fetch: `App::fetch_mcp_inventory()` spawns a background task that
calls `fetch_all_mcp_server_statuses()` over an app-server request
handle. When the RPC completes, it sends `AppEvent::McpInventoryLoaded {
result }`.
3. Resolve: `App::handle_mcp_inventory_result()` clears the loading cell
and renders either `new_mcp_tools_output_from_statuses(...)` or an error
message.
This keeps the main app event loop responsive, so the TUI can repaint
before the remote RPC finishes.
## Notes
- No `app-server` changes were required.
- The rendered inventory includes auth, tools, resources, and resource
templates, plus transport details when they are available from local
config for display enrichment.
- The app-server RPC does not expose authoritative `enabled` or
`disabled_reason` state for MCP servers, so the remote `/mcp` view no
longer renders a `Status:` row rather than guessing from local config.
- RPC failures surface in history as `Failed to load MCP inventory:
...`.
## Tests
- `slash_mcp_requests_inventory_via_app_server`
- `mcp_inventory_maps_prefix_tool_names_by_server`
- `handle_mcp_inventory_result_clears_committed_loading_cell`
- `mcp_tools_output_from_statuses_renders_status_only_servers`
- `mcp_inventory_loading_snapshot`
|
||
|
|
78e8ee4591 |
fix(tui): restore remote resume and fork history (#14930)
## Problem
When the TUI connects to a **remote** app-server (via WebSocket), resume
and fork operations lost all conversation history.
`AppServerStartedThread` carried only the `SessionConfigured` event, not
the full `Thread` snapshot. After resume or fork, the chat transcript
was empty — prior turns were silently discarded.
A secondary issue: `primary_session_configured` was not cleared on
reset, causing stale session state after reconnection.
## Approach: TUI-side only, zero app-server changes
The app-server **already returns** the full `Thread` object (with
populated `turns: Vec<Turn>`) in its `ThreadStartResponse`,
`ThreadResumeResponse`, and `ThreadForkResponse`. The data was always
there — the TUI was simply throwing it away. The old
`AppServerStartedThread` struct only kept the `SessionConfiguredEvent`,
discarding the rich turn history that the server had already provided.
This PR fixes the problem entirely within `tui_app_server` (3 files
changed, 0 changes to `app-server`, `app-server-protocol`, or any other
crate). Rather than modifying the server to send history in a different
format or adding a new endpoint, the fix preserves the existing `Thread`
snapshot and replays it through the TUI's standard event pipeline —
making restored sessions indistinguishable from live ones.
## Solution
Add a **thread snapshot replay** path. When the server hands back a
`Thread` object (on start, resume, or fork),
`restore_started_app_server_thread` converts its historical turns into
the same core `Event` sequence the TUI already processes for live
interactions, then replays them into the event store so the chat widget
renders them.
Key changes:
- **`AppServerStartedThread` now carries the full `Thread`** —
`started_thread_from_{start,resume,fork}_response` clone the thread into
the struct alongside the existing `SessionConfiguredEvent`.
- **`thread_snapshot_events()`** walks the thread's turns and items,
producing `TurnStarted` → `ItemCompleted`* →
`TurnComplete`/`TurnAborted` event sequences that the TUI already knows
how to render.
- **`restore_started_app_server_thread()`** pushes the session event +
history events into the thread channel's store, activates the channel,
and replays the snapshot — used for initial startup, resume, and fork.
- **`primary_session_configured` cleared on reset** to prevent stale
session state after reconnection.
## Tradeoffs
- **`Thread` is cloned into `AppServerStartedThread`**: The full thread
snapshot (including all historical turns) is cloned at startup. For
long-lived threads this could be large, but it's a one-time cost and
avoids lifetime gymnastics with the response.
## Tests
- `restore_started_app_server_thread_replays_remote_history` —
end-to-end: constructs a `Thread` with one completed turn, restores it,
and asserts user/agent messages appear in the transcript.
- `bridges_thread_snapshot_turns_for_resume_restore` — unit: verifies
`thread_snapshot_events` produces the correct event sequence for
completed and interrupted turns.
## Test plan
- [ ] Verify `cargo check -p codex-tui-app-server` passes
- [ ] Verify `cargo test -p codex-tui-app-server` passes
- [ ] Manual: connect to a remote app-server, resume an existing thread,
confirm history renders in the chat widget
- [ ] Manual: fork a thread via remote, confirm prior turns appear
|
||
|
|
b77fe8fefe |
Apply argument comment lint across codex-rs (#14652)
## Why Once the repo-local lint exists, `codex-rs` needs to follow the checked-in convention and CI needs to keep it from drifting. This commit applies the fallback `/*param*/` style consistently across existing positional literal call sites without changing those APIs. The longer-term preference is still to avoid APIs that require comments by choosing clearer parameter types and call shapes. This PR is intentionally the mechanical follow-through for the places where the existing signatures stay in place. After rebasing onto newer `main`, the rollout also had to cover newly introduced `tui_app_server` call sites. That made it clear the first cut of the CI job was too expensive for the common path: it was spending almost as much time installing `cargo-dylint` and re-testing the lint crate as a representative test job spends running product tests. The CI update keeps the full workspace enforcement but trims that extra overhead from ordinary `codex-rs` PRs. ## What changed - keep a dedicated `argument_comment_lint` job in `rust-ci` - mechanically annotate remaining opaque positional literals across `codex-rs` with exact `/*param*/` comments, including the rebased `tui_app_server` call sites that now fall under the lint - keep the checked-in style aligned with the lint policy by using `/*param*/` and leaving string and char literals uncommented - cache `cargo-dylint`, `dylint-link`, and the relevant Cargo registry/git metadata in the lint job - split changed-path detection so the lint crate's own `cargo test` step runs only when `tools/argument-comment-lint/*` or `rust-ci.yml` changes - continue to run the repo wrapper over the `codex-rs` workspace, so product-code enforcement is unchanged Most of the code changes in this commit are intentionally mechanical comment rewrites or insertions driven by the lint itself. ## Verification - `./tools/argument-comment-lint/run.sh --workspace` - `cargo test -p codex-tui-app-server -p codex-tui` - parsed `.github/workflows/rust-ci.yml` locally with PyYAML --- * -> #14652 * #14651 |
||
|
|
db89b73a9c |
Move TUI on top of app server (parallel code) (#14717)
This PR replicates the `tui` code directory and creates a temporary parallel `tui_app_server` directory. It also implements a new feature flag `tui_app_server` to select between the two tui implementations. Once the new app-server-based TUI is stabilized, we'll delete the old `tui` directory and feature flag. |