9.5 KiB
Per-agent terminal: row taxonomy + inconsistencies
Snapshot of how the per-agent web UI's live pane renders each
event kind today, written up so the next coherence pass has a
reference to work from. Source of truth lives in
hive-ag3nt/assets/app.js (renderStream,
fmtToolUse, renderRichToolUse, renderToolResult,
renderTaskEvent) + hive-fr0nt/assets/terminal.css (the
shared .live .<class> styling).
Row taxonomy
| CSS class | Prefix glyph | Color | Triggered by | Source |
|---|---|---|---|---|
.turn-start |
◆ TURN ← <from> |
amber, bold, top-margin, amber left rule | LiveEvent::TurnStart |
harness wake |
.turn-body |
(inline under turn-start) | fg dimmed 85% | same | the wake-prompt body |
.turn-end-ok |
✓ turn ok |
green, green left rule | LiveEvent::TurnEnd { ok: true } |
harness |
.turn-end-fail |
✗ turn fail — note |
red, red left rule | LiveEvent::TurnEnd { ok: false } |
harness |
.text |
none | fg/white, indented | claude assistant.content[].text |
stream-json |
.thinking |
· or · thinking … |
muted, italic | claude assistant.content[].thinking |
stream-json |
.tool-use |
→ Name args… |
cyan | assistant.content[].tool_use |
stream-json |
.tool-use <details> |
→ Name path · +N |
cyan, body is diff | rich tool_use (Write/Edit) | renderRichToolUse |
.tool-use <details> |
→ send → to · headline |
cyan, body is text | mcp__hyperhive__send | renderRichToolUse |
.tool-result |
← <txt> |
muted | short user.content[].tool_result (≤120c) |
stream-json |
.tool-result-block <details> |
▸ ← Nl · headline |
muted, body is text | long tool_result (>120c) |
stream-json |
.tool-use |
⌁ task <id> started · <desc> [type] |
cyan | claude Task-tool subagent event | renderTaskEvent |
.turn-end-ok / .turn-end-fail / .tool-result |
⌁ task <id> ✓/✗/◌ <status> · <desc> · → <output_file> |
green / red / muted | claude Task-tool result | renderTaskEvent |
.note |
· <text> |
muted | LiveEvent::Note (harness chatter, /cancel /compact /new-session, stderr lines, etc.) |
harness |
.sys |
· {json…} |
muted | anything renderStream doesn't recognise |
catch-all |
.result |
(defined, never emitted today) | green | — | — |
| Banner shimmer | mauve | turn in flight (ref-counted) | setBannerActive |
Where the inconsistencies live
-
Glyph vocabulary drifts:
- tool_use uses
→ - tool_result uses
← - thinking uses
· - notes use
· - turn-end ok/fail use
✓ / ✗ - turn-start uses
◆ - task events use
⌁ - The
·glyph is overloaded across thinking, notes, sys.
- tool_use uses
-
What gets a
<details>block vs a flat row is per-tool ad-hoc: Write/Edit always expand, send always expands, every other tool_use is flat regardless of input size.tool_resultis flat if ≤120 chars otherwise<details>. -
Stderr handling: claude's stderr lines come through as
LiveEvent::Notewithtext: "stderr: <line>"— they render as muted· stderr: …, identical styling to harness notes about /compact / /model. No red-tinted "this is an error" affordance for stderr. -
Catch-all
.sysrows are visually identical to.noterows — both muted, both·prefix. They look like normal notes despite usually being "an event renderStream couldn't classify". Unmatched stream shapes (rate limit warnings under odd type/subtype combos, future claude additions, etc.) silently fall through. -
.tool-useranges from one-liner (Read foo.md) to multi-page collapsed diff — same color, same prefix glyph, very different visual weight. A small marker on the collapsed<details>summary would help (the▸is present in.tool-result-blocksummaries but absent in tool-use<details>summaries). -
Cancel/compact/new-session notes are styled the same as autonomous harness chatter; nothing flags them as "operator initiated."
-
Turn-start / turn-end are visually overweight: the triggering event row + the closing row both get bold text, top margins, and a full coloured background tint. The coloured left rule alone already says "this is a turn boundary" — the heavy chrome adds noise without information. Drop the bold/margin/tint, keep the left rule.
-
Left-alignment is incoherent across row types: the
▸disclosure marker on expandable rows doesn't sit in the same column as flat-row prefix glyphs (→,←,·), so the prefix column wanders by row kind. Expandable tool_use (→ Name …) and expandable tool_result (▸ ← Nl …) use different layouts from each other too. Pick one prefix column and align every row kind into it; the disclosure marker should be visually inside that column, not pushed to the side. -
Continuation lines aren't inset: when a row's body wraps (e.g.
→ foo bar baz quux …), the second visual line starts at column 0 rather than under thefoffoo, so wrapped content blurs into the next row. Want atext-indent/ hanging-indent rule so continuation lines align with the start of the body, not the prefix glyph. -
Most message-bearing rows are collapsed by default even when the body is the whole point:
send,recv,ask, and friends hide their text behind a<details>summary. The summary headline is rarely enough context. Want these expanded by default, with collapse reserved for genuinely heavy payloads (multi-page diffs, long tool_result blocks). -
Tools from extra MCP servers aren't pretty-printed:
renderRichToolUseonly special-cases the built-in hyperhive tools (Write/Edit/send). Anything coming fromextraMcpServers(matrix, bitburner, …) falls through to the generic→ Name args…JSON dump. Either a plugin-style hook in the renderer map, or at minimum a nicer generic args-pretty-printer that handles common shapes (single string arg, single dict arg, etc.). -
No markdown rendering on message bodies:
send/recv/ask/answer/ agent text content all arrive as markdown-flavoured prose (bullets, fenced code, bold, links) but render as raw text. Want a minimal markdown pass — at least lists, fenced code, inline code, bold/italic, links — applied to message bodies and probably to theassistant.content[].text.textrow.
Suggested coherence pass
Pick one scheme and audit all renderers to match. A concrete proposal:
→cyan: outbound action (tool_use, send)←muted: inbound result (tool_result)◆amber: turn framing (turn_start)✓ / ✗: success / failure, green / red (turn_end, task_notification)⌁mauve: subagent / background event (task_*)·muted: ambient note, italic for thinking!orange: caught error (stderr lines, .sys catch-all that landed something the renderer didn't recognise)
Plus: every tool_use <details> summary gets ▸ so collapsed
content is visually announced. Operator-initiated notes get a
distinct prefix (op· or similar) so they're easier to spot in
the scrollback.
The .sys catch-all should escalate visually — a louder
"unrecognised event" rendering surfaces silently-dropped event
shapes for future fix-up rather than hiding them in the muted
note stream.
Layout rules to apply uniformly
- One prefix column for every row kind — flat rows and
expandable rows alike. The disclosure marker (
▸) lives inside that column, not as a separate gutter, so glyph alignment doesn't shift between row types. - Hanging indent on wrapped bodies so continuation lines
start under the first character of the body, not under the
prefix glyph. Probably
display: gridwithgrid-template-columns: <prefix-col> 1frper row, ortext-indent/padding-leftwith negativetext-indent. - Turn boundaries are rule-only — drop the bold + margin
- tint on
.turn-start/.turn-body/.turn-end-*. The coloured left rule alone carries the boundary signal.
- tint on
- Default-expanded message rows:
send,recv,ask,answer, and short-ishtextrows render their body inline (no<details>). Reserve collapse for genuinely heavy bodies — multi-page diffs, long tool_results, thinking blocks past N lines.
Renderer surface for extra MCP tools
renderRichToolUse currently switches on hard-coded tool
names. Replace with a registry keyed by
mcp__<server>__<tool> (and the built-in claude tool names)
that maps to a (toolUse) -> Node renderer. Per-agent extra
MCPs can register their own renderers via a small JS hook
(loaded the same way extra-mcp.json is loaded server-side);
the fallback is a generic args-pretty-printer that handles
single-string and single-dict shapes nicely instead of dumping
raw JSON.
Markdown rendering
Apply a minimal markdown pass to message bodies (send /
recv / ask / answer) and to assistant text rows.
Scope: paragraphs, lists, fenced + inline code, bold/italic,
links. No HTML passthrough, no images, no tables —
explicitly bounded so we don't import a kitchen-sink parser
into the per-agent page. A small handwritten pass or a tiny
dep (e.g. micromark / marked) both work.
Dashboard side (not covered here)
The main dashboard's message-flow pane is a different beast:
broker messages render as .msgrow grid lines (ts / arrow /
from / → / to / body) with separate styling. The current file
focuses only on the per-agent terminal.