Terminal, plain and print

Choose interactive, plain, print or RPC output.

Manual for v0.9.5. Stable release: v0.9.5.

Documentation · Commands and keys · Themes

sh
octet --safe-mode                           # interactive
octet --plain --safe-mode                   # plain, in order
octet -p "Explain this function" --no-tools # final answer on stdout
octet --mode rpc                            # JSONL for automation

Choose a mode#

Mode Use it for
octet Streaming, tools, pickers, branching, steering and native scrollback.
octet --plain Basic terminals, logs, accessibility tools, or no cursor control.
octet -p "prompt" (or --print) The final answer on stdout, for scripts.
octet --mode rpc JSONL commands and responses over stdin and stdout.

On native Windows, the console (Windows Terminal or conhost), not TERM, picks the frontend. See Windows.

The first three share one agent loop, providers, sessions, safety policy and cancellation. Print mode still has tools, so add tool limits if you want none. Plain and print send readiness diagnostics to stderr.

Cache warming defaults to streaming; idle also runs while the retained host waits for input, without delaying input, reload or shutdown. Refresh usage is billed to the session, never the current assistant turn or its cache-hit/throughput/context metrics. show_cache_miss_notices = true in user config opts into brief ordinary cache-miss/refresh notices (default false). /session, /cache and /cache-warming show scheduler state, expected savings, miss penalty and refresh costs even with notices off. Plain/print notices stay on stderr, and print stdout stays response-only.

The startup card says permissions: full access in bold red by default. With --safe-mode it says safe mode (blue in the default theme) and octet asks before running bash or changing files. Neither is a sandbox.

RPC mode

RPC isn't a terminal UI. Its messages use type fields, not the native host's hello envelope, and --mode rpc can't be combined with --print. It's independent of native-host protocol 1 and extension API 0.4. The framing is Pi-compatible, but that doesn't mean every Pi command or feature works, and it hasn't been tested against a pinned Pi release.

Startup keeps routine session lookup, replay and extension-loading progress off screen. You can type while startup work finishes, and the welcome card and saved conversation appear when octet is ready. Setup prompts, errors, and cancel or shutdown diagnostics stay visible. Fresh sessions skip the replay worker entirely, and resumed sessions still restore their history. --models inventory discovery also runs after the shell owns input, not before the first paint.

Fresh-start composer rows are reserved before the welcome, without provisional model branding. The welcome is static and fits the pane height. Typing and late update hints do not move it or clear saved history. Cursor placement is part of the synchronized frame; Working and Thinking animations remain active. Enter before model/session resolution keeps the draft without submitting or queuing it. Submit after resolution.

Window title and background detection

After session resolution, the interactive renderer sets the terminal window title to octet or octet · <session name> (via OSC 2). It updates on rename and session changes, and nothing is written to plain, print or RPC output. When auto-theme detection needs an OSC 11 background-color reply, the probe starts after the first ready frame. A changed background triggers a repaint, and a timeout or an unsupported reply keeps the initial theme.

Input and active work#

Type / for commands and @ for file mentions (.gitignore is respected). A ./, ../, ~/ or absolute path token starts filesystem completion. Up and Down pick a visible path or mention suggestion, and Tab inserts the selected result. Directory completion stays open, and spaces are backslash-escaped. Without a visible completion menu, the arrows move through the editor as usual. Multiline editing, bracketed paste and large-paste chips work. A pasted or dropped file needs an attachment chip before you send it: a typed path is just text. Undo and redo each keep at most 64 snapshots / 4 MiB, oldest first. An edit too big for that budget starts a new undo boundary instead of keeping an unbounded copy.

While octet works:

  • Enter queues live steering for the next model boundary.
  • Ctrl+S queues a local follow-up. Follow-ups run one at a time, in order, after normal completion. Both kinds of pending input share the bounded hint above the input.
  • Option+Up (Alt+Up) recalls the newest editable queued steering message or follow-up into an empty input, keeping attachment and paste chips. A recalled steering message is withdrawn from the agent before it's saved, so it's never also delivered. Once the agent has claimed it at a model boundary, the recall is refused and it stays queued. Recall never submits, never interrupts and never overwrites a draft.
  • Escape first closes the current panel or slash popup. With the input focused, it interrupts active work and sends the oldest queued follow-up after the run settles. It never submits an unqueued draft.
  • Ctrl+C clears a draft. With no draft, it aborts active work without sending anything, and does nothing when idle. A Ctrl+C abort also cancels a send that Escape had armed. Failures and closing leave follow-ups unsent.
  • Ctrl+D closes octet from any input, after active work and child processes are cleaned up.
  • Shift+Enter adds a newline if your terminal reports enhanced keys.

Pi custom editors use the same native submission rules, including slash and extension commands. Their resolved Enter/follow-up keybindings are honored, and a rejected submission retains the genuine Pi Editor's draft, pastes and undo. The custom editor occupies the composer slot, not the screen: Octet keeps the transcript, chrome, queue indicators and one native slash popup. Native, executable extension and Pi commands share that popup; later name collisions are owner-qualified (/extension:name). Ctrl+G returns from explicit fullscreen views only, never unmounting the editor or footer.

Native clipboard reads during active work don't block input, cancellation or run progress. A pending read is dropped when the run settles or is cancelled, or when the draft is cleared, sent, steered, recalled or consumed by a command. Results are also dropped after text or cursor edits in between, or if the input loses focus, so a late clipboard result can't land in a replacement draft or another input. See all keys.

Scrolling and rendering#

sh
octet --color auto
octet --mouse app   # always capture the mouse, including inline

By default octet uses the fullscreen (alternate-screen) surface with mouse = "auto". Fullscreen has no terminal scrollback, so auto captures the wheel/trackpad and drag selection there: scrolling and selecting work with no flag, as in Pi. --tui-mode inline (or tui_mode = "inline") keeps octet on the primary screen, where auto leaves mouse reporting off so your terminal keeps drag selection and wheel/touchpad history. --mouse terminal and --mouse off never capture the mouse on either surface; --mouse app always does. Inline, the renderer follows the height of the content, not a fixed full-screen composer and footer; it loads the complete resumed branch and appends to native scrollback. Explicit --mouse app (or mouse = "app" in configuration) opts inline sessions into the bounded semantic viewport as well, and lets a resumed session load newest-first. Explicit CLI choices take precedence over configured mouse policy.

A pi theme changes presentation, not viewport ownership. Native Tern surfaces negotiate their own inline layout and leave ANSI mouse reporting off, including with --mouse app, so Tern owns touchpad scrolling and selection without Shift. If native rendering fails, the ANSI fallback restores the requested mouse policy. --tern off uses the ordinary ANSI policy even inside Tern. Native pickers and report overlays do not change the ordinary ANSI launch default.

With capture on (the fullscreen default, or --mouse app), wheel/touchpad events scroll the transcript by three rows per event (Alt/Option: fifteen); bursts accumulate without changing composer history. Click-drag highlights transcript text without Shift. Release copies it, or use Ctrl+Shift+C to copy the current selection again; Ctrl+C keeps its draft-clear/interrupt meaning. Composer, footer and separator rows are not transcript text. Shift+drag remains your terminal's native selection override where the terminal supports it.

With capture on, a plain left-click on an HTTP(S) transcript link opens it in an available system browser on release, without Shift or Ctrl. Dragging a link selects/copies instead; moving away and back does not activate it. Resize, scrolling, focus changes and intervening input cancel the pending click. Octet never launches local-file or application-protocol links this way. With capture off, including native Tern, link gestures remain terminal-owned.

Copy writes to an available local clipboard helper and emits OSC 52 to the terminal, so SSH/remote sessions can reach the client clipboard. The terminal must permit OSC 52; inside tmux enable set -g set-clipboard on. Remote transport sends a UTF-8-safe prefix of at most 49,152 source bytes (64 KiB base64 payload), using BEL termination. The retained selection and local clipboard copy are not truncated by that transport limit. Capture explicitly requests cell coordinates (SGR 1006), clearing inherited pixel reporting (1016) and alternate-scroll arrow emulation (1007); octet does not infer cells from pixel reports.

Inline, PageUp pins the view above the tail while output grows, and shows when there's new output. PageDown returns to live output. Wheel history that octet doesn't capture stays with your terminal, because portable protocols can't report its position.

The renderer uses a complete retained frame, synchronized frames, and exact first-to-last changed-range repainting. Completions, panels, reports, and streamed Markdown participate in the same algorithm; visible changes do not force history replay. Dimension changes and canonical changes above the old viewport clear and replay the complete native transcript, so saved and live output remain complete. This can reset your terminal's reading position, and replay work grows with history. PageUp and explicit --mouse app instead use a separate bounded semantic viewport. Resize bursts settle after 75 ms of quiet (150 ms maximum); width changes reflow the transcript, while height-only changes reuse its wrapping. Away-and-back resizes also rebuild native history. The hardware composer cursor stays visible inside synchronized frames through panels, resizing, and renderer resumes. Rendering details.

Layouts adapt between wide and narrow terminals and fall back from truecolor to 256 colors, 16 colors and no color, and from Unicode to ASCII. Markdown shows highlighted code, tables, task lists and links, plus bounded summaries of tool intent and lifecycle. Control sequences in untrusted text are sanitized. The vendored sexy-tui-rs renderer uses #![forbid(unsafe_code)]. Themes.

Reasoning and progress#

/thinking can change an explicitly qualified Responses route without stopping the root task. A queued label lasts until the durable next-response boundary. The effective choice is separate from the pinned wire baseline, and neither is a provider acknowledgement. Other routes still change at an idle boundary. See the control qualification.

Reasoning is collapsed by default. Ctrl+O expands what octet kept.

Each run shows a shimmering Working row, and a trailing Working (<elapsed> • esc to interrupt) stays until the run settles. Retry status keeps the interrupt hint but leaves out the run-elapsed counter, even after its backoff countdown reaches zero, so two clocks never compete on one row. While the model reasons, the bold Thinking label shimmers more quietly than Working on supported terminals. It shows the latest Markdown heading from the reasoning (a # heading or a standalone bold line), with a dim expand hint. Ordinary reasoning text never becomes a label. With no heading, only the hint shows.

text
• Thinking
  └ Verifying the implementation (ctrl+o to expand)

Reasoning and activity dots keep a solid, fixed-size glyph. The Working dot shares the label's phase and blinks as the shimmer crosses it, then returns to its resting colour. The Thinking dot keeps its model colour. Tool and shell dots turn green on success or red on failure, and assistant-response dots stay steady. To change the level, see Selecting reasoning.

Shimmer details

By default the shimmer is a smooth, asymmetric raised-cosine band with a central glint, on known dark and light TrueColor and ANSI256 terminals. Activity shimmer is foreground-only and parks briefly after crossing the whole label. Set OCTET_SHIMMER=classic for the old stepped field. ANSI16, unknown-background, reduced-motion and no-color paths keep the compatibility or static behavior. Expanded reasoning keeps its indent, with no margin dot or extra first bullet, and completed reasoning disappears again once collapsed.

Tool output and workers#

Bash and PowerShell commands wrap to the available width. In terse mode, they show up to six rendered command rows, then a count of hidden rows. Generic tool input previews use the same six-row limit for their selected argument, preserving newlines. Ctrl+O expands the full retained input and collapses it again. That doesn't change tool execution or the separate output preview.

--show-images (or show_images = true) shows tool-result images inline. It's off by default and works on Kitty-compatible terminals. Others fall back to text. It never loads a URL or path, copied, plain and print output never contain image data, and it doesn't upload anything or replace attachment consent. The model must still support the format.

Ctrl+O and /verbose [on|off] reveal reasoning, compaction, subagent activity, bounded search and shell output, and edit and write diffs. They can't recover output that was discarded when it was captured. Raw arguments, unsanitized failures and extension-rendered payloads stay out of the transcript and out of copy. A failed run keeps failed · <duration> and a short reason with credentials removed.

Tool results the model needs are saved and sent to the provider. Live progress is neither. Tool presentation.

While workers are active, a bounded, tool-like Subagents block in the transcript updates in place. It no longer stays pinned above the input. Its heading counts worker states, and up to four child lines show active tasks with input and output tokens. Ctrl+O keeps disclosure. /subagents opens the live worker list, including during a run, with the complete roster (up to 32) and failure details. Enter opens a worker's read-only transcript and Ctrl+X stops the selected worker. /subagents stop <name-or-id|all> also works during a run; other worker operations wait for idle. /extensions manages extension enablement and configuration instead. Finished workers' usage is added once to the main session's ledger, so cost limits count it. Worker display.

How worker activity is counted and shown
  • List rows keep state, model and available metrics. The tools column counts tool calls, not model turns.
  • Host limit_reached counts as failed. interrupted, shutdown, detached and awaiting_approval count as stopped. Detached and approval-parked workers stay recoverable, not successful, and their exact states and reasons stay inspectable after the block settles.
  • Raw first-party orchestration calls and results, including errors, stay out of the interactive transcript, live and on replay, and worker state or reason transitions add no automatic notices. This doesn't change worker outcomes, model-visible errors, durable results, approval prompts, or ordinary tool and run failures.
  • Resume starts a fresh telemetry roster rather than replaying raw calls. Already committed child cost isn't added again when telemetry refreshes.
  • The block's input tokens sum uncached, cache-read and cache-write usage, and output estimates are marked until usage settles. Tool-call counts, priced spend, transient phases and tool identities stay in the inspector. The nonblocking 250 ms refresh keeps its last fenced snapshot on failure, and no extension replaces the cumulative footer.