Moves the TermMsg rendering pipeline (Row.tsx, termMsg.ts, linkify.tsx,
markdown.ts) from @hive/agent into @hive/shared, so swarm-ui becomes a
second consumer of it instead of forking a copy -- CSS was already
shared (@hive/shared/terminal.css). marked+dompurify move from
@hive/agent's deps to @hive/shared's; swarm-ui picks them up
transitively, no new direct dep there.
New swarm-ui route /agents/:name/term (AgentTermPage), linked from
AgentsPage's detail panel via a "terminal" badge next to "link matrix
account". Consumes GET /api/agents/{name}/term/stream: unlike
@hive/agent's own useLiveStream (TermEnvelope-wrapped, history/backfill
dance), the swarm relay forwards one bare TermMsg per SSE event with no
envelope and no history endpoint -- useSwarmTermStream is a much
smaller hook for that shape (EventSource -> parse -> coalesce, nothing
to buffer/dedupe/backfill against).
Verified against a live SSE mock (screenshots in /agents/iris/state/screenshots/
3801-agents-detail-panel-terminal-badge.png and
3801-agent-term-page-live-rows.png -- real rows rendering through the
shared Row component, not just a build/typecheck pass).
71 lines
3.4 KiB
Markdown
71 lines
3.4 KiB
Markdown
# 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 it was fetched. 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.
|