# 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 .` styling). ## Row taxonomy | CSS class | Prefix glyph | Color | Triggered by | Source | |---|---|---|---|---| | `.turn-start` | `◆ TURN ← ` | 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` `
` | `→ Name path · +N` | cyan, body is diff | rich tool_use (Write/Edit) | renderRichToolUse | | `.tool-use` `
` | `→ send → to · headline` | cyan, body is text | mcp__hyperhive__send | renderRichToolUse | | `.tool-result` | `← ` | muted | short `user.content[].tool_result` (≤120c) | stream-json | | `.tool-result-block` `
` | `▸ ← Nl · headline` | muted, body is text | long `tool_result` (>120c) | stream-json | | `.tool-use` | `⌁ task started · [type]` | cyan | claude Task-tool subagent event | renderTaskEvent | | `.turn-end-ok` / `.turn-end-fail` / `.tool-result` | `⌁ task ✓/✗/◌ · · → ` | green / red / muted | claude Task-tool result | renderTaskEvent | | `.note` | `· ` | 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 1. **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. 2. **What gets a `
` 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_result` is flat if ≤120 chars otherwise `
`. 3. **Stderr handling**: claude's stderr lines come through as `LiveEvent::Note` with `text: "stderr: "` — they render as muted `· stderr: …`, identical styling to harness notes about /compact / /model. No red-tinted "this is an error" affordance for stderr. 4. **Catch-all `.sys` rows** are visually identical to `.note` rows — 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. 5. **`.tool-use` ranges 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 `
` summary would help (the `▸` is present in `.tool-result-block` summaries but absent in tool-use `
` summaries). 6. **Cancel/compact/new-session notes** are styled the same as autonomous harness chatter; nothing flags them as "operator initiated." 7. **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. 8. **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. 9. **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 the `f` of `foo`, so wrapped content blurs into the next row. Want a `text-indent` / hanging-indent rule so continuation lines align with the start of the body, not the prefix glyph. 10. **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 `
` 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). 11. **Tools from extra MCP servers aren't pretty-printed**: `renderRichToolUse` only special-cases the built-in hyperhive tools (Write/Edit/send). Anything coming from `extraMcpServers` (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.). 12. **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 the `assistant.content[].text` `.text` row. ## 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 `
` 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: grid` with `grid-template-columns: 1fr` per row, or `text-indent`/`padding-left` with negative `text-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. - **Default-expanded message rows**: `send`, `recv`, `ask`, `answer`, and short-ish `text` rows render their body inline (no `
`). 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____` (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.