Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rust terminal frontend boundary

FieldDecision
Current frontend policyRust is the sole interactive terminal frontend; pure installs retain non-TUI interfaces
Runtime boundaryRust owns terminal presentation; Python owns agent semantics and durability
Historical decisionRC2 Rust-default trial on 2026-09-16; see RC2 checklist

wisp, wisp tui, and wisp --mode tui launch Rust; auto and rust are equivalent selectors. Native wheels for macOS arm64 and Linux glibc 2.28+ x86_64 bundle the binary. Pure-wheel installs retain print, JSON, RPC, and SDK interfaces but report an actionable missing-binary error for interactive startup. Source development uses a matching binary selected by an absolute WISP_RUST_TUI_BINARY path.

Rust decides how frontend state is presented. Python decides what is allowed, what is durable, and what commands and events mean.

Process topology

flowchart LR
  Launcher[Python launcher] -->|selects and supervises| Rust[Rust TUI]
  Rust -->|spawns| Backend[Python JSONL-RPC backend]
  Rust <-->|typed commands and events| Backend
  Backend --> Host[RPC command host]
  Host --> Session[CodingSession]
  Session --> Harness[AgentHarness]
  Harness --> Loop[run_agent_loop]

The Python launcher resolves the exact native executable, passes the Python interpreter and backend command, and remains alive as an external supervisor. Rust owns input, rendering, backend protocol exchange, and graceful shutdown. The launcher restores terminal state and cleans up the shared process group if Rust exits abruptly. Missing or incompatible binaries fail startup with guidance; the launcher does not select another terminal renderer.

Subsystem ownership

AreaOwnerFrontend boundary
Agent loop, providers, tools, MCP, and managed processesPythonRust receives typed events; it does not execute tools or decide provider policy.
Harness transcript, steering, follow-ups, and cancellationPythonRust projects authoritative run and queue state.
Durable sessions, replay, compaction, and branchingPythonRust requests snapshots through RPC; it never reads JSONL files.
Trust, protected paths, approvals, and credentialsPythonRust collects input and presents decisions, but Python validates and stores them.
Model catalog, configuration, project-file discovery, and updatesPythonRust renders backend-provided state and sends typed requests.
Terminal input, composer, overlays, layout, scrollback, and themesRust TUIPresentation state is disposable and cannot change backend policy.
CLI print/JSON, RPC, and SDK interfacesPythonThese interfaces use the same runtime without depending on Rust presentation.
Binary selection and fail-safe process cleanupPython launcherRust attempts graceful cleanup; the launcher enforces the process boundary.

Wire boundary and compatibility

The live boundary carries typed commands, events, capability snapshots, bounded previews, and explicit user prompts or answers. It never gives Rust direct ownership of session JSONL, credential files, provider SDK objects, tool executors, or approval policy. Python reads historical session formats and projects current-version data for Rust.

The Rust frontend and Python package are exact-version peers. Generated Rust transfer types follow the committed live schema; the launcher and handshake reject package or RPC protocol mismatch before ordinary interaction. The current live contract is RPC v9; events carry no separate schema version. Historical schema bundles remain immutable; see Compatibility and versioning.

Lifecycle and failure ownership

Failure or transitionOwner and outcome
Missing, corrupt, or incompatible Rust binaryPython launcher reports an actionable non-zero error with installation or source-build guidance.
Backend spawn or protocol failureRust stops accepting commands, restores the terminal, and reports failure; launcher verifies process cleanup.
Rust panic, abort, or abrupt terminationLauncher restores a known terminal baseline and terminates the supervised process group within a deadline.
Normal quit or signalRust requests graceful backend shutdown; launcher enforces the cleanup deadline.

The Rust frontend bounds handshake, event admission, shutdown, and task joins. Neither frontend ownership nor backend EOF alone is treated as a fail-safe cleanup guarantee.

RC2 decision history

The RC2 release PR authorized a Rust-default trial on native-wheel installations. It supersedes the default hold in #470 for this candidate only. Textual was retained as the selectable fallback during that trial. The subsequent retirement replaced that fallback with the existing prompt-toolkit fullscreen renderer. The later Rust-only retirement removed the Python terminal renderers; neither change moved the agent runtime to Rust. The RC2 trial did not itself publish a stable release.

The dated RC2 checklist and acceptance evidence record the original migration gates. Later interaction and memory measurements supersede its unmeasured performance questions. Those reports compare the former Textual frontend with Rust under specific workloads; they do not describe a currently selectable Textual renderer. Rust still retains full saved transcript history, with memory growing with session size.

Rust command interaction

The Rust frontend draws the virtual conversation and composer before clearing and painting the active popup rectangle. Modal geometry does not resize the background transcript. The same derived view priority controls painting and input, while individual views keep their existing asynchronous state. Only the focused editor places the cursor. Existing redraw coalescing and bounded row caches remain in effect; keeping the background current does not require continuous idle painting.

Approval/trust states dismiss ordinary inspection views and suppress retained connection/session views until the decision settles. Backend updates continue while a view is suppressed. Rendering readiness is invalidated on actionable catalog changes, navigation, resize, and decision transitions, so a hidden or replaced choice cannot be activated before it is drawn. At 30×8 a popup can occupy the terminal; the compact decision layout below 11 rows retains its existing accessibility priority.

/help lists commands implemented by the Rust frontend, using the backend’s descriptions and ordering. Arrow keys and Page Up/Down scroll help; Escape or Ctrl+C closes it, and r refreshes discovery. Unsupported catalog commands produce a notice when typed. Discovery runs in the background; its failure does not block prompts or explicitly typed supported commands.

Typing a slash prefix opens completion above the composer. Up/Down selects a command; Tab or Enter fills a partial command without executing it. Enter on an exact command executes it. Completion preserves existing arguments. Escape dismisses completion; Shift+Enter and Ctrl+J insert newlines. Multiline pastes and slash-prefixed prose remain prompt text. A lone unknown slash word is treated as a command attempt, so /tmp reports an unknown command while /tmp/file remains literal text. Commands are handled before steering and follow-up queues.

/plan and /build change the current process’s mode only while idle. The header shows mode after startup state discovery or successful configuration acknowledgement. /quit, /exit, and :q exit through normal backend shutdown, including cancellation of active work.