3.4 KiB
Per-agent terminal: row taxonomy (as built)
The per-agent web UI's live pane renders one row per TermMsg
(hive-agent/src/term_msg.rs): {ts, icon?, level: debug|info|warn|error, summary, body?, body_format?: markdown|diff, coalesce_key?}. Classification
(icon, summary text, whether a tool call gets an expandable body) happens
server-side, once — term_msg.rs + stream_enrich.rs — and both
GET /api/events/history and GET /api/events/stream serve it identically
as TermEnvelope { seq?, msgs: TermMsg[] } frames
(hive-agent/src/web_ui/stream.rs).
ts is ISO 8601 UTC (2026-09-13T12:35:03Z) and is the source event's
time, stamped at classify time from the event's own stamp — so a row
replayed from sqlite says when it happened, not when a client fetched
it. It
lives on the row rather than on the envelope because the swarm queue
publishes rows bare, with no envelope around them
(hive-agent/src/swarm_term.rs); a subscriber there has nowhere else to
read the time from.
The frontend renders a TermMsg close to as-is
(frontend/packages/shared/src/terminal/Row.tsx — moved out of
packages/agent once swarm-ui became a second consumer of it): level
picks the
CSS colour (terminal.css's .live .level-*), an empty summary +
markdown body renders as a flat row with just the body (assistant
text), and any other bodied row is an expandable <details>, opened by
default according to the operator's expand-tool-output preference
(getExpandDetailsPref()) — uniformly, no per-tool override. There's no
separate client-side row model or classification step.
Layout
Every row is its own 2-column grid — icon column (.row-glyph, width
from the --row-icon-col custom property) then content column
(.row-content, or a markdown body's own .md class) — so an icon's
left edge lands in the same place regardless of row kind. A <details>
row grids its <summary> instead of itself (so the <pre> body below
can stack full-width), using the exact same grid-template-columns value,
with the disclosure caret leading the summary text rather than the icon.
One shared column definition, not two independently computed offsets
kept in sync by hand — see terminal.css's own comment on the incident
that motivated the change.
Levels
| Level | Colour | Roughly |
|---|---|---|
debug |
muted | thinking, coalesced ticks, ambient harness chatter |
info |
default fg | turn start/ok, assistant text, tool calls + results |
warn |
amber, left rule | stderr, an unrecognised event shape, API retries |
error |
red, left rule | turn failed, a tool result with is_error: true |
The settings menu's "hide debug output" toggle (getHideDebugPref(),
@hive/shared/prefs.js) skips debug-level rows entirely rather than
muting them further — client-side only, same live-read-no-reload shape
as the expand-tool-output preference above.
Per-tool icon + summary formatting (what a Read/Edit/send call's row
actually says) lives in stream_enrich.rs's fmt_tool_use() family —
read that when you need the specifics, this doc doesn't duplicate it.
Markdown
frontend/packages/shared/src/terminal/markdown.ts's renderMarkdown() runs
marked.parse() through DOMPurify into a <div class="md">. Applied to
any TermMsg with body_format: "markdown".
Dashboard side (not covered here)
The main dashboard's message-flow pane is a different shape: broker
messages render as .msgrow grid lines, not agent-terminal rows.