universelabSCI · CLI · BIO
ESEspañol

Canopy

The TUI — Canopy Hub

Last updated

Running canopy with no arguments opens the Canopy Hub, a full-screen ratatui interface for managing everything in real time.

Creating agents

Ctrl+N / n opens the NewAgentDialog with three modes:

  1. Interactive — PTY session with full vt100 emulation, 24-bit color and support for interactive applications.
  2. Background — cron-scheduled or file-watcher-triggered agent.
  3. Terminal — raw shell session with per-session command history (TOML-backed), cross-session autocomplete search, and a Warp-like input mode.

The dialog includes a CLI picker, model picker, seed identity selector (◀ None / SeedName ▶), directory browser, and a yolo-mode toggle.

For Interactive, Enter on the finished form opens a second Mode step instead of launching straight away:

  • New session — exactly the launch the form always did: the launchpad opens (or the session starts directly when a new seed is being planted).
  • Resume a session — lists the finished sessions that belong to the selected harness and directory, or the harness’s own session_list_cmd output when it defines one. Enter on a session launches it resumed, skipping the mission step; a harness with nothing to resume shows No sessions to resume for <harness> in <dir> and New session stays usable.
    • Picking a finished canopy session relaunches that same session: it keeps its id and its name — even when canopy never recorded the harness’s own conversation id — so hooks and scheduled sends addressed to it still reach it afterwards, and session_list shows one row, never the old one plus a newly-named one. Only when another live session already holds the name does the resumed session take a fresh name (keeping its id); the header then names both. A conversation picked out of the harness’s own list that canopy has never seen starts a fresh session with a new id and a new generated name, as before.
    • When canopy recorded the harness’s own conversation id, the relaunch resumes that exact conversation (muse resume <id>); otherwise it falls back to the harness’s most-recent resume, and only one session per harness and directory may take that fallback at a restart — the rest fail with an explanation instead of colliding on one conversation. A resume whose harness exits immediately keeps its session row as an error card with the harness’s output tail attached until dismissed, so the failure is never silent. Closing a session yourself never leaves such a card.
    • ↑↓ selects, Enter confirms, and Esc returns to the form with every value kept. Background and Terminal launch from the form as before — the Mode step is Interactive-only.

Note: The Session/Mode choice is no longer a row on the form itself. Resume is handled entirely on the separate Mode step, so the form never shows a “Resume

” entry that could be mistaken for the launch button.

Interactive sessions

  • Copying text — drag inside a focused session pane to select and copy; the key-hint row shows drag to copy, so no keyboard modifier is needed. A Ctrl+C within 3 s of that copy is taken as the copy reflex and swallowed, and the pane shows already copied.
  • Sizing the side panel — drag the activity panel’s left border to make it wider or narrower (between 24 columns and the terminal width minus 40). The border takes the accent colour while dragged, and the session resizes once on release. While a paired device sizes the session narrower, the panel fills the freed strip on its own and cannot be dragged; when the device lets go or disconnects, the panel returns to the TUI’s own layout.
  • Interrupting a session — a platform whose registry entry sets ctrl_c_exits = true (opencode) exits on a single Ctrl+C. To protect the copy reflex, the first Ctrl+C of such a session is held back and the pane shows Ctrl+C again within 1 s to send it to <platform>; press it twice within that second to forward the interrupt. Every other platform (claude, …) keeps forwarding Ctrl+C unchanged.
  • Scrolling up — the scrollback keeps everything the session wrote, including output an inline TUI scrolled off the top of the screen, so scrolling up shows the transcript instead of stale rows.
  • Overlays keep the panel live behind them — opening the prompt builder, the knowledge list or viewer, context transfer, RAG transfer, the launchpad or the new-agent form never replaces the panel behind it: a focused session keeps its live PTY with its own colours (no accent tint, no plain-text log), home and the graph view keep what they were showing, and the focus rail with the Focus/Preview pill keep naming the focus underneath — including when one overlay opens on top of another.
  • Surviving TUI restarts — sessions live in the daemon, not the TUI: quitting (F10, closing the window) leaves every running session running, and reopening the TUI reattaches to them live, with screen and scrollback intact; a session that ended while the TUI was closed is not reopened. Only closing a session explicitly ends it. Restarting the daemon itself (canopy daemon restart) does end its sessions — they are its children — but the new daemon respawns every session the database still lists as active as soon as it starts, under the same id and name, resuming the harness conversation where the platform supports it. A terminal comes back in its working directory with a clean pane and a fresh prompt — no stored output is replayed into it. While the TUI runs, a dropped daemon connection is retried on its own (0.5 s doubling up to 5 s) and the sidebar re-lists what the daemon still has; sessions started or closed from another TUI appear in, or leave, the sidebar live. When the daemon speaks an incompatible session protocol the footer shows canopy daemon speaks session protocol <n>; restart the daemon (canopy daemon restart) and nothing attaches until the restarted daemon matches.
  • Failed launches stay in the TUI — when opening a session fails (the daemon is down or restarting, the hub is unreachable, the mission write fails), the footer shows Could not open session: <cause> and the TUI keeps running instead of exiting; the same error is appended to ~/.canopy/logs/tui.log. Fix the cause and reopen the dialog to try again.

Startup checks and diagnostics

  • Daemon version check — at startup the TUI compares its own version with the one the daemon recorded (the same version canopy daemon status prints). On a mismatch a blocking notice names both versions; when the daemon’s version cannot be read at all the notice says so and names the TUI’s version. R restarts the daemon and re-checks, Esc dismisses the notice for this run.
  • Panic log — if the TUI itself panics, the terminal is restored (raw mode off, alternate screen left, mouse capture and bracketed paste disabled) and the panic, with its backtrace, is appended to ~/.canopy/logs/tui-panic.log.

Terminal sessions

  • Warp-like input — keystrokes accumulate in the input box and are sent to the shell on Enter, with per-session history (↑↓) and cross-session autocomplete.
  • While a command runs the box withdraws — from Enter until the shell is back at an idle prompt, every keystroke (and paste) goes straight to the running program, so wizards, y/n questions, and long commands just work. The box returns empty at the next prompt.
  • Ctrl+W toggles native mode as a manual override in both directions; the next submit or prompt return re-asserts the automatic rule.

Monitoring

  • Agent status colors — teal for working (recent output), blue for idle, red for failed, gray for exited. A running session whose daemon connection dropped shows dim gray until it reconnects. The sidebar shows a colored gutter bar per agent.
  • Focus pill placement — the Focus/Preview pill (the Auto-follow/ Manual label in graph view) sits right-aligned on the panel’s first row, after the session name (or the graph name in graph view), so it never covers the text there. When the panel is too narrow for both, the pill is dropped and the name stays. The classic (bordered) theme is unchanged: the label keeps its place in the border title.
  • Split groups — side-by-side horizontal/vertical views to watch multiple agents simultaneously.
  • System dashboard — CPU, memory, disk, GPU (NVIDIA/Linux/macOS) and temperatures with amber/red alert thresholds. Under WSL, metrics come from the Windows host via PowerShell. With no reading the CPU row omits the temperature span; agent status reports the reading and its cpu_temp_source. Where ACPI thermal zones report nothing on Windows/WSL, canopy doctor notes (informational): run LibreHardwareMonitor (WMI on) to expose CPU temperature.
  • Live graph view — real-time graph rendering of the running graph’s current spec, auto-following the active node. When the engine moves to a new node, the view centres that node’s whole box (a tall collapsed ensemble included) in the panel; your own scroll is left alone until the next node change. Manual node inspection shows per-node run info (status, elapsed time, iteration; a node parked on a named mutex shows waiting with a waiting for: line naming each resource and who holds it). Esc toggles between auto-follow and manual mode.
  • Output pane — t opens a read-only viewer beside the live graph that streams the highlighted node’s stdout/stderr as the engine writes it (agent nodes and check nodes alike). In auto-follow the pane retargets node by node as the graph advances; in manual mode it shows whichever node you select. Enter in manual mode toggles the pane on the selected node (in auto-follow Enter still enters manual mode). The layout tiles at 140 columns or wider (graph left, pane right) and stacks below that. PageUp/PageDown scroll by half a screen, End returns to the live edge, and ,/. step through an ensemble’s members. Opening, closing or switching the pane never writes to a run.
  • Crews face — pin a panel face with the pinned_panel_face setting (F1 → Settings → TUI Appearance); crews is shown by pinning crews there (never selected automatically). It lists every harness/model pair in use with its effective availability — ● available, ○ unavailable, ◐ expired — its reason and expiry, and how many graphs and crews use it, plus every crew with its member and binding counts. Click the panel to focus it, then Tab switches between the Pairs and Crews sections. In Pairs, Space toggles the selected pair, e marks it unavailable with a reason and an optional YYYY-MM-DD HH:MM expiry, and p marks its whole provider prefix unavailable (showing how many pairs that covers first). In Crews, Enter expands a crew to its ordered members and bindings, a adds a member from the model picker, d removes one (refused when it would empty the crew), and Shift+↑/Shift+↓ reorder members. Every roster and crew write goes through the daemon — the face never touches SQLite — and the view refreshes without blocking while a call is in flight.
  • Idle visuals — Brian’s Brain cellular automaton, drawn as Braille glyphs and seeded by the cursor at a fixed step rate, plus animated kaomoji status messages (whimsg). With no live event, messages come from curated forest-voice phrases — idle, busy or scrolling to match what the TUI is doing — and about a quarter of them are deliberate jokes. When something just happened, a real-event message takes over instead, naming the agent, graph, or spec involved (a failure never gets a joke). The header title (harness-canopy) and those kaomoji share one slow sweep: a dark highlight travels across the letters, then the text rests for 2 seconds before the next sweep.

Gamification

Canopy tracks 28 achievement-style missions across six categories:

Category Missions Examples
Environment 4 Firefly Catcher, Harness Master, Canopy Explorer, Multitasker
Intelligence 6 World Connector, Data Architect, Deep Searcher, Digital Archeologist
Projects 3 Project Polyglot, The Gardener, Data Hoarder
Graph 4 Automation Engineer, Pipeline Pilot, Parallel Vision, Graph Survivor
Seeds 4 First Bloom, Identity Evolved, The Orchard, Deep Roots
SysInfo 7 Full Throttle, Nuclear Winter, VRAM Squeezer, YOLO Pilot

Missions are checked automatically during normal TUI operation — no manual action required. Progress is persisted in the database and visible in the TUI’s gamification panel. canopy missions [--json] lists the same missions read-only from the command line (see CLI Reference).

Two missions to know by their exact challenge: The Gardener — update descriptions or tags for 5+ different projects (via the project tool’s update action); Parallel Vision — run a graph with a parallel ensemble. On systems with no CPU temperature sensor (such as WSL), Nuclear Winter shows as not measurable on this system.

Press F1 to open the Canopy hub, a centered tabbed dialog: Settings edits every registry setting in place (with the value’s apply mode and status), Keys is a grouped reference of every shortcut the TUI handles, Missions & stats lists all the missions above — unlocked ones in full colour, locked ones dimmed — Shelf lists the files agents have sent (see below), and Remote is everything canopy remote does (see below). Shift+←/Shift+→ switch tabs, as everywhere else in Canopy; Tab/Shift+Tab and 1/2/3/4/5 also switch. F1 closes the hub from any state; Esc closes it from the top level of every tab. F1 works everywhere — including inside a focused interactive session whose harness has claimed the keyboard (alternate screen or the Kitty protocol) — the key opens the hub instead of being forwarded to the session.

In Settings, the ↑/↓ keys move within the focused column and → or Enter step from the category list into its properties (← goes back, keeping the same category selected). At the properties level Enter edits a number, duration, string, format or path list, Space toggles a bool or cycles an enum forward, and r resets the selected setting to its default. A path list edits with a (add a folder), d (delete) and Shift+↑/Shift+↓ (reorder). / filters every group by key or description. Each change goes through the daemon; a refused value keeps the old one and the daemon’s message shows in the status line. ←/→ never change a value.

In Remote (F1, then 5), ↑/↓ (or j/k) move over the GitHub, Remote control, Relay and Pair rows plus one row per paired device and one row per trusted GitHub profile. Enter on GitHub starts the device flow while signed out (the row shows the user code and https://github.com/login/device until it completes or expires); Enter on Remote control flips the relay switch (disabled while signed out); Relay only reports the phase (connected, connecting, token rejected: sign in again, relay rejected: see remote status, off); Enter on Pair a device shows the fresh code with its link, QR and countdown in an overlay (Esc hides it, the code stays valid). Each device row shows its label, id, paired time, last seen time and live while a peer is open; d disconnects its live peer at once (the pairing stays), r revokes it behind a y/n confirmation (Enter also confirms, Esc cancels); both report success or the exact error inline. Each trusted row shows a guest login with its grant (all sessions or the named sessions) and whether it may upload; t trusts a new profile through a form (username, comma-separated sessions, an all-sessions toggle flipped with a, an upload toggle flipped with u; Tab moves, Enter saves, Esc cancels), Enter on a trusted row edits its grant, and r forgets it behind a y/n confirmation — removing or narrowing a grant kicks the login’s live guest peers at once. While any device is live the sidebar header shows ⇄ n.

Session shelf

  • Sending — an agent delivers files to its session with the MCP tool session_send_file: 1..10 absolute paths per call, up to 20 MiB each. Any type is accepted; PNG, JPEG, WebP, GIF and SVG are recognised from the file’s content (never its extension) for display. A trusted remote guest with the upload grant sends files into its granted sessions through the shelf_put_* protocol frames; the session gets one 📎 the user attached … line and the file lands with origin user.
  • Reading — an agent lists a shelf with session_list_files and reads one file with session_read_file (text as UTF-8, binary as base64 capped at 1 MiB).
  • Listing — the hub’s Shelf tab (F1, then 4) lists the files agents have sent — the host shelf plus one per session, merged newest first — with arrival time, sender, name, size and caption per row.
  • Opening — Enter (or Space) opens the selected file with the system viewer — open on macOS, xdg-open on Linux, wslview (or explorer.exe with the path translated by wslpath) under WSL. c copies its absolute path, and d deletes it after a y/n confirmation. A viewer that fails or is missing raises a toast naming the file and the error — which carries the path — instead of crashing the TUI.
  • Confirming — while the y/n prompt is armed every key answers it: y or Enter deletes, n or Esc cancels, and nothing else moves — not even a tab switch.
  • Arrivals — a file sent while the TUI runs joins the list live and shows a 📎 <name> — <from> toast for five seconds, wherever you are in the TUI.
  • Lifetime — a session’s files are kept until 7 days after the session is removed, and the whole shelf is capped at 1 GiB: when a new file would exceed it, the oldest files are pruned first.

Projects sidebar

The sidebar shows sections for the selected project:

  • Graphs — active graphs with spec progress (done/total) and current spec indicator. Selecting a graph opens the live graph view.
  • Backlog — standalone specs tagged to the project’s workdir, not yet assigned to any graph. A spec with a type shows its code as a badge before the name ([FIX-7] open F1 from inside a session), followed by its type and scope dimmed ((fix:tui)). Legacy specs without a type show the name alone.
  • History — completed and failed graphs for the project.

Sections are automatically filtered by the selected project’s workdir.

Knowledge tab

The sidebar’s Knowledge tab lists the registered projects, most recently active first — a project’s last activity is the newest of its registration, its indexing, its knowledge entries and its project history, so the projects you are actually working on stay at the top instead of being buried under old ones. Each row’s first meta line shows how long ago that was, before the project hash.

Typing a plain letter filters the list the way a file browser does: the filter matches project names case-insensitively, the list keeps its recency order, and the selection jumps to the first match. The panel title shows the live / filter, and No project matches "<filter>" appears when nothing does. Backspace removes the last character, and Esc clears a non-empty filter instead of leaving for the Home screen. Because every letter has to be typable on this one list, the single-letter shortcuts it used to carry don’t apply there: arrows, Enter and Esc still do, the relation dialog moves to Ctrl+R, and leaving the Knowledge tab clears the filter.

Once a project is entered, its Knowledge tab lists every fact and pattern of that project as a row: a [fact]/[pattern] badge in its kind colour, the title in bold, and how long ago the entry was last updated. ↑↓ moves the selection and the list scrolls to keep it visible (showing n / total on its first row), and clicking a row selects it. Enter opens a read-only viewer for the selected entry — ↑↓ and PageUp/PageDown scroll the body, e closes the viewer and opens the entry for editing, and Esc returns to the list with the same row still selected. The list’s own keys keep working on the selected row: / filters, n creates, e edits and F4 deletes.

Moving context around

  • Scheduled delivery — Shift+Enter (requires Kitty keyboard enhancement protocol support) opens a time picker to send the prompt at a chosen hour instead of immediately. Falls back to Ctrl+S on terminals without Kitty protocol. Schedules can also be made from the remote.
  • Context transfer — a two-step modal (preview → agent picker) transfers conversation context, prompts and output between agents while preserving session state and scrollback.
  • RAG transfer — send semantic search results to another agent as injected context.
  • Prompt builder — structured prompt templates with configurable sections (instruction, context, resources, examples) and @-mention agent references. Press Ctrl+L to recall the last prompt you sent in the current project, restoring the full prompt text, tools, and preset selection. The Tools picker (opened from the Tools section in the section-add menu) lists on-disk [skill]:/[global]: skills first, then canopy’s dynamic skills as [canopy]:<name> (name-sorted, local cache only — no network). A name present both on disk and in canopy’s store appears under both prefixes, so you pick which one to reference. Selecting a [canopy]: entry renders canopy skill <name> — load it with the skill tool's get action (name="<name>") in the built prompt’s tools block, so the agent fetches it over MCP; on-disk skills keep their plain skill:<name> entry. Ctrl+V (or Alt+V, for terminals that bind Ctrl+V to text paste) attaches the clipboard image, and pasting or dropping image-file paths attaches those files instead of inserting text; attachments ride along as <image path="…"/> lines in a trailing attachments block every harness CLI can open. Attached images are stored on the target session’s shelf as prompt-<timestamp>-<n>.<ext> (so they also show in the hub Shelf tab), at most 10 per prompt and 20 MiB each — past either limit an inline error appears and nothing attaches. They render as [img 1 · 120 KB] chips under the sections: Tab reaches the row, ←/→ move along it and Backspace/Delete removes the focused chip (the shelf copy stays). Attachments persist with the draft across close/reopen, recall and scheduled sends.
  • Canopy protocol — once per session — the builder prepends the [CANOPY] session-start protocol to the first prompt of an interactive session, never to the ones that follow. That fact is stored with the session id in the database, so it survives restarting canopy and resuming the session: a conversation that already carries the protocol does not receive it again, and a resumed conversation keeps its session id, so the flag travels with it. When the protocol is not due, the builder shows nothing extra about it — to resend the block on purpose, add the Canopy protocol section from the section picker (Ctrl+A): it inserts a locked, read-only section holding the protocol text, the prompt then carries the block exactly once at the very top (before the per-turn workspace context), and that send counts as the session’s one delivery. The section can be taken out again with the remove-section picker (Ctrl+X) like any other section.
  • Launchpad — start new sessions with previous-mission recovery and auto-injected context.

Graphs in the TUI

The graph editor allows inline editing of node config JSON with validation. See Graphs for the engine itself.

Wherever a spec is named — the live graph view, the sync panel’s graph face, Activity lines and the node-tail waiting line — a spec with a type shows its code first ([FIX-7] name), like the Backlog badge above. Legacy specs without a type show the name alone.

Notifications

Native desktop notifications for task completions, failures, watcher triggers, and graph lifecycle events (graph started, spec completed with progress, graph finished with outcome, blocker reported, completion hook failure). Platform auto-detected: WSL (PowerShell toasts), macOS (osascript), Linux (notify-send). Set notifications.enabled = false to silence them.

A message delivered into this session by a graph hook or by an agent’s send_to_session trigger raises one too, naming the sender instead of showing a raw id. Hook message delivered reads <graph name> · <event>: message delivered to this session (for example canopy-tui-polish · on_completed: message delivered to this session); Agent message delivered reads <agent id> (<trigger>): message delivered to this session. When the graph or agent no longer exists, the first 8 characters of its id stand in for the name.

Colour and tinting

The TUI speaks the same palette as the web surfaces. Both themes — classic (bordered, Canopy green) and modern (borderless) — paint the big views and the alert dialogs on meaning-tinted grounds instead of one flat green slab. In classic those grounds are the operator’s dark palette: tints of the same lightness, differing only in hue, plus a neutral stage that big views stand on.

  • Prompt Builder — the window sits on the info tint, its section fields on the near-black stage ground, and the focused section on the selection colour. Borders and the title keep the theme’s own colours, and the text on the new grounds stays at 4.5:1 contrast or better. The Normal / Raw tab strip paints the selected tab as the accent pill (dark ink on the theme’s accent colour, bold) with the other tab dimmed.
  • Graph view — the canvas sits on the same info tint. Nodes and edges keep their status colours, which stay at 3:1 or better against it.
  • Alert dialogs — confirmations of destructive actions are tinted for warning: deleting a project, permanently deleting a graph, and resetting a graph. A failed graph action is reported on the error tint. Every dialog still spells out what it does in words, so the tint is never the only signal; the quit and archive confirmations keep the neutral dialog ground.
  • Sidebar — the sidebar is painted on its own theme surface, not on the terminal’s default background: the Live / Automation / Knowledge tab strip, the agent and group rows, and the project graph cards all sit on the theme’s sidebar background. On a terminal whose own background differs (a light or purple one, say), no part of the sidebar shows that foreign colour. The active tab keeps its accent colour and inactive tabs stay dimmed, both at readable contrast against that surface.
  • Hub — the F1 hub’s tab strip paints the active tab as the accent pill (dark ink on the theme’s accent colour) and the remaining tabs dimmed, so the selected tab never reads as dark text on the dark selection raise. The selected row of a hub list is the same accent pill: the Settings path lists and directory picker, a Shelf file, a Remote row or paired device, and the pairing code in the Remote overlay.

The header, footer and session panes are unchanged.

Appearance and behaviour settings

The [tui] settings (see canopy config list) tune the interface without a restart. Each keeps its default until you set it. All of them — and every other registry setting — can also be edited in place from the hub’s Settings tab (F1), which writes through the daemon and shows each value’s apply mode.

Setting Default What it does
tui.datetime_format %Y-%m-%d %H:%M chrono format for dates and times (validated by formatting a fixed instant).
tui.time_format %H:%M chrono format for time-only labels (validated the same way).
tui.panel_dwell_seconds 10 Seconds a panel event holds the right panel (0 disables the dwell).
tui.brians_brain true false keeps the home banner static.
tui.atmosphere true false disables fireflies and dandelions.
tui.scroll_step 3 Terminal scroll step, in lines.
tui.page_step 15 Terminal page step, in lines.
tui.animations reduced Animation budget: full (20 fps), reduced (8 fps, pauses on focus loss or 45 s idle), off (static frame).

Both animation toggles off is the master switch: the header spinner, sweep, dialog waves and pulse indicators freeze, and an idle TUI performs no redraws at all (near-zero CPU when nothing changes). tui.animations = "off" freezes them the same way; "reduced" (the default) additionally pauses all background animation while the terminal reports focus lost or after 45 s without key or mouse input, resuming on focus gain or the next input.