The dashboard cold-loaded its derived stores (approvals, questions,
containers, …) from /api/state once, then relied solely on live SSE
events. Events that fired during a disconnect window (reconnect,
hive-c0re restart) are never replayed, so the dashboard drifted stale
until a manual reload.
- terminal.js: add onStreamOpen, fired on every EventSource open
(initial + reconnect); the dashboard wires it to refreshState() so
every connection epoch re-syncs the authoritative snapshot.
- terminal.js: seq-dedupe only event kinds that actually appeared in
the history replay. Mutation events are never in /dashboard/history,
so deduping them against the broker-history seq wrongly dropped ones
that fired between the /api/state snapshot and the history fetch.
- app.js: make applyApprovalResolved / applyQuestionResolved
idempotent (guard the history unshift by id) so a re-sync
overlapping a live event can't double a history row.
closes#163
layout
- unified prefix-column for every row kind: padding-left + negative
text-indent so the glyph (→ ← · ◆ ✓ ✗ ⌁ !) sits in the same column
whether the row is flat or a <details>. wraps hang under the body,
not under the glyph.
- expandable rows drop the directional glyph from their summary text;
the ▸/▾ disclosure marker from CSS sits in the prefix column instead,
and the row's colour still carries cyan = outbound, muted = inbound.
- turn-start / turn-end de-weighted: bold/margin/tint dropped, the
coloured left rule alone marks the boundary.
note classification
- stderr lines render orange with a `!` glyph (was muted `·`)
- operator-initiated notes (cancel/compact/model/new-session) render
mauve italic (was muted `·` indistinguishable from harness chatter)
- catch-all .sys row escalates to orange `!` so unrecognised stream-json
shapes surface for follow-up instead of hiding in muted noise
message-bearing rows
- send / ask / answer tool_use rich renderers default-open with the
body inline; new ask + answer renderers (previously fell through to
the generic JSON dump). recv tool_result also default-open, keyed by
tracking tool_use_id → name across the stream so we know which
result came from which tool.
- assistant text rows render markdown.
- bodies use vendored marked v4.0.2 (hive-fr0nt::MARKED_JS); falls
back to plain text when the asset doesn't load.
extra-mcp tool pretty-print
- generic args formatter replaces the raw JSON dump for unknown tools
(single-string field → `name k: "v"`; single dict / multi-field →
trimmed `k: v · k: v …` summary)
dashboard .live .msgrow gets a text-indent: 0 reset so the new
hanging-indent metrics from TERMINAL_CSS don't leak into the flex-grid
broker rows.