Terminal, plain and print

Choose interactive, plain, print or RPC output.

Manual for v0.10.2. Stable release: v0.10.2.

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.

Tern's TERM=stencil-term is recognized on Unix and VT-enabled Windows consoles, so its own terminal identifier does not force the plain frontend. TERM_PROGRAM=tern then selects native surface negotiation; --tern off keeps the ordinary formatted ANSI UI. This detection support does not qualify every Tern version's native protocol or Windows terminal rendering.

If a capable terminal unexpectedly shows plain output, check TERM, NO_COLOR, OCTET_COLOR and the plain configuration/--plain flag. TERM=dumb, unknown terminal identifiers and redirected input/output retain conservative fallback; forcing --color always does not enable cursor control. NO_COLOR suppresses styling without disabling the interactive layout. Preserve the terminal's real identifier rather than globally overriding TERM to hide a detection problem.

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.

A finished tool's tool_execution_end result and its toolResult message carry Pi's details: a Pi-compatible tool's own details, otherwise an extension tool's non-model-visible metadata. Structured content and media parts are included when the tool returned them. get_state reports messageCount without projecting the history, so it stays fast on long resumed sessions.

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.

Tab identity and activity#

The interactive frontend reports app=octet and its actual host-owned activity using OSC 7501 v0.4: idle, working, blocked (permission, question, or auth), done, and error. Provider waits/retries remain working, not blocked. Tool/extension input is a question, including private input; a secret field alone does not prove a login. ChatGPT browser/device authorization reports auth while awaiting the user, then working while exchanging/saving and reloading the model catalog. The login operation reports done only once Codex models are ready, or error if reload fails. Only authoritative run or foreground operation settlement reports done/error; cancellation reports idle. Ordinary reports and pickers do not block an otherwise running agent.

Reports are deduplicated across frames and renderer replacement, not across separate foreground operations that finish between paints. Suspension and exit retire live working/blocked activity to idle, but preserve done/error for the terminal to acknowledge. Resumption reasserts live activity. Changing between primary and alternate screens does not clear the terminal's record. Progress is indeterminate: no percentage, completion metric, prompt, tool argument, private approval detail, error text, login URL or OAuth code is sent. OSC 2 still carries the existing session/window title, independently of activity.

No detection query, input reader or startup wait is added. Unsupported terminals ignore the OSC. Plain, print, RPC and redirected output remain free of these controls. A multiplexer must forward/support the protocol; octet does not change shell or multiplexer configuration.

Terminal Native identity/status octet logo in tab
OSC 7501 terminal Stable app=octet; full activity states/kinds The protocol identifies an app; it does not carry an image.
Superlogical Rex Real local Rex Beta build 1541 server recorded app=octet with idle, working, blocked/permission, done and error. Quitting live working/blocked activity reports idle; done/error survive child exit. The installed block-glyph catalog has no octet asset, and no supported runtime icon registration route was found. A branded mapping belongs in Rex and should use the canonical mark. app= identifies the app; it is not image data.
Tern Existing TSP hello.app=octet identifies a listening surface's pane; OSC 2 keeps the session title. Tern's documented OSC 9;4 channel additionally receives indeterminate working, paused user gates, error, or inactive. OSC 7501 is also emitted; Tern support for that newer protocol is not assumed. Its built-in app/icon catalog must add/map octet's mark. A welcome image is not a tab icon. See Tern.

Rex and Tern choose their own tab presentation. App-side wire/PTY tests do not qualify a specific terminal version's pixels, notifications, or interaction. Rex Beta build 1541's server-side records were checked using task-owned blocks and a frozen diagnostic octet candidate, not a released build. Its rendered tab glyph, unfocused-tab indicators and OS notification delivery were not observed. No terminal plugin, global theme, proprietary escape sequence, or replacement logo is installed.

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. Terminal bracketed paste into a Pi custom editor normalizes CR and CRLF line endings to newlines, without submitting the draft. The original byte bound and rejection of other terminal controls remain enforced. 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. Switching surfaces with /settings tui fullscreen or /settings tui inline immediately reapplies that policy: auto starts or stops capture with the surface, while app, terminal, and off keep their explicit ownership.

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. Reflow under a stationary pointer does not expand the selected range on release; a displaced release still counts as movement when terminals coalesce drag events. Ctrl+C keeps its draft-clear/interrupt meaning. Composer, footer and separator rows are not transcript text. Shell selections include the retained, progress-normalized, sanitized command output, rather than mapping output rows to the command header. Each wrapped output row maps only its visible fragment, including wide Unicode text; selecting a continuation does not jump back to the first row. Compact-tail selection starts at the retained source boundary, not at matching text in omitted output. Elision labels and empty-output placeholders are not copyable source. Shift+drag remains your terminal's native selection override where the terminal supports it.

Literal whitespace-delimited HTTP(S) URLs in shell output retain their complete targets across wrapping, without interpreting output as Markdown. Encoded targets over 2 KiB stay plain; decoration also has a 64 KiB wrapped OSC-payload budget per shell output render, after which additional URLs stay plain. 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. Captured clicks also work without terminal OSC 8 support; unsupported hyperlink controls are omitted from terminal output, not from Octet's retained hit targets.

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.

Fullscreen image crops keep the original tool gutter when their first row scrolls above the viewport. Only the visible image rows are placed, leaving the composer and footer outside the placement. Terminal-specific pixels still require separate qualification; protocol and retained-row tests are not Rex or Ghostty verification.

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.