From e9cce17828c2168000cc920f23459b33c8ee5ca2 Mon Sep 17 00:00:00 2001 From: lexis Date: Mon, 25 May 2026 00:45:56 +0200 Subject: [PATCH] docs: update web-ui.md for tabbed layout, topology tree, per-agent overhaul (#366 #373 #381) --- docs/web-ui.md | 378 +++++++++++++++++++++++++------------------------ 1 file changed, 194 insertions(+), 184 deletions(-) diff --git a/docs/web-ui.md b/docs/web-ui.md index a0a555d..ebd2882 100644 --- a/docs/web-ui.md +++ b/docs/web-ui.md @@ -100,100 +100,136 @@ Both bind their listeners with `SO_REUSEADDR` via exponential backoff capped at 2s) so an nspawn restart that races the previous process's socket release resolves itself. -## Dashboard sections (top to bottom) +## Dashboard layout -1. **Notification row** โ€” `๐Ÿ”” enable notifications` button when - permission ungranted; `๐Ÿ”• mute / ๐Ÿ”” unmute` toggle once granted; - inline "unsupported / blocked" message when applicable. Sits - under the banner. -2. **C0NTAINERS** โ€” live containers with their action surface. - Pulsing red banner at the top of this section if any two - sub-agents hash to the same port (`port_conflicts` from - `/api/state`): the operator must rename one of them and - rebuild. `lifecycle::{spawn,rebuild}` also preflight this and - refuse with a clear error message naming the conflicting agent. -3. **K3PT ST4T3** โ€” destroyed-but-state-kept tombstones (size + - age + claude-creds badge). Two actions: `โŠ• R3V1V3` (queues a - Spawn approval; existing state is reused), `PURG3` (wipes - state + applied dirs; `POST /purge-tombstone/{name}`). -4. **M3T4 1NPUTS** โ€” inputs in `meta/flake.lock` the operator can - selectively `nix flake update`, rendered as an indented tree: - every fetched input at every depth (`hyperhive`, - `hyperhive/nixpkgs`, `agent-`, `agent-/mcp-`, โ€ฆ), each - shown once at its shallowest path. `read_meta_inputs` walks the - lock graph with a `visited` set โ€” `follows` aliases and rev-less - nodes are skipped (issue #275). A `select all / select none` - control sits above the tree. Checking inputs + submitting bumps - the lock in `/meta/` and rebuilds the selected agents in - sequence; each outcome reaches the manager as a `rebuilt` - system event. - `POST /meta-update`. The lock bump + rebuild ripple runs in the - background; while it does, the panel shows a pulsing "โณ - meta-update running" banner and the update button is disabled - (snapshot field `meta_update_running`, live event - `meta_update_running`). -5. **R3BU1LD QU3U3** โ€” pending and recently-completed container - operations: rebuilds, meta-update cascades, and first-spawns. - One operation runs at a time; the worker drains FIFO. Each row - shows a state glyph (`โธ` queued / `โ–ถ` running / `โœ”` done / - `โœ–` failed / `โŠ˜` cancelled), kind glyph + verb (`โ†ป rebuild`, - `โ—† meta_update`, `โœจ spawn`, `๐Ÿ—‘ destroy`), agent name, source - chip (`manual | meta_update | auto_update | crash_recover`), - timing, and an optional reason / error. Meta-update cascade - rebuilds nest under their parent entry (`parent_id` grouping; - `rqe-child` CSS class). Dedup: re-enqueueing a still-queued op - for the same agent collapses into the existing entry. Running - entries tick elapsed seconds live (same pattern as the TTL - countdown). Cold-loaded from `/api/state.rebuild_queue`; live - updates via `rebuild_queue_changed` snapshot event. -6. **M1ND H4S QU3STI0NS** โ€” pending operator-targeted `ask` - questions, i.e. rows with `target IS NULL` (peer-to-peer - questions live in the same table but never surface here) - (amber pulsing border). Free-text fallback always rendered - alongside any option list; `multi=true` renders options as - checkboxes; submit merges selections + free text comma-joined. - Each row has a `โœ— CANC3L` button that resolves the question - with `[cancelled]`. Questions with a `ttl_seconds` show a - `โณ MM:SS` chip; the host-side watchdog auto-cancels with - `[expired]` when the deadline fires. -7. **QU3U3D R3M1ND3RS** โ€” reminders agents have scheduled for - themselves (via the `remind` tool) but not yet delivered. - Each row shows the owner, due time, and message; a `CANC3L` - button hard-deletes (`POST /cancel-reminder/{id}`) and a - `R3TRY` button re-arms one whose delivery failed - (`POST /retry-reminder/{id}`). Backed by `GET /api/reminders`. -8. **P3NDING APPR0VALS** โ€” the queue (see "Approval card" - below). The R3QU3ST SP4WN form lives at the top of this - section since submitting it immediately queues an approval - that lands directly below. -9. **0PER4T0R 1NB0X** โ€” recent messages addressed to `operator`, - derived client-side from the dashboard event stream (no longer - a snapshot field). Cold load seeds from - `/dashboard/history`'s 200-message backfill; subsequent - `sent` events with `to == "operator"` are appended live. Cap - 50, newest-first. -10. **MESS4GE FL0W** โ€” live broker tail wrapped in a - `.terminal-wrap` (same chrome as the per-agent terminal). - Cold load backfills the last ~200 messages from - `/dashboard/history`; live frames arrive on - `/dashboard/stream` and dispatch through - `HiveTerminal.create`. Each row is one broker event โ€” - `sent` or `delivered` โ€” with `from โ†’ to: body`; per-agent - thinking / tool calls / claude chatter stay out of this - view, only what passes through hive-c0re's broker. Sticky- - bottom auto-scroll + "โ†“ N new" pill match the per-agent - page. Below the stream sits a terminal-style compose box: - `@name` picks the recipient (sticky across sends via - localStorage; auto-complete from the live container list, - Tab/Enter to confirm; `@*` broadcasts to every registered - agent), starting a message with `@ body` retargets - in one stroke, plain text sends to the sticky recipient. - `POST /op-send` drops `{from:"operator", to, body}` into - the broker and returns 200; the resulting SSE frame - re-renders both the terminal row and the inbox section - (no `/api/state` refetch). Manager is addressed as - `@manager` (the broker recipient string), not `@hm1nd` - (the container name); the auto-complete swaps automatically. +The dashboard (`/`) has a fixed chrome header at the top and a +`
` that shows exactly one tab pane at a time. The URL hash +(`#swarm`, `#call`, `#system`) drives which pane is active; hash +changes don't reload the page. FL0W is a separate full-page +terminal at `/flow.html` โ€” its tab-strip entry is a cross-page +link (`โ—† FL0W โ—† โ†’`), not a pane swap. + +**Chrome header** (fixed, overlays the active tab pane): +- **Tab strip**: `โ—† SW4RM โ—†`, `โ—† Y3R C4LL โ—†`, `โ—† SYST3M โ—†`, and + `โ—† FL0W โ—† โ†’` (page link). Count pills on SW4RM (container count) + and Y3R C4LL (pending approvals + questions); FL0W pill mirrors + the operator inbox length (hidden when zero). +- **Notification controls**: `๐Ÿ”” enable notifications` when + permission ungranted; `๐Ÿ”• mute / ๐Ÿ”” unmute` toggle once granted. + Always visible in the chrome regardless of active tab. +- **Banner-thin** (`โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘`) + โ€” sits below the tab strip. + +### SW4RM tab + +**C0NTAINERS** โ€” live containers rendered as a depth-first +tree using `ContainerView.parent` (populated by `topology.rs`). +Each container's row is prefixed with ASCII tree glyphs (`โ”œโ”€`, +`โ””โ”€`, `โ”‚ ` continuation columns) showing the agent +parent/child hierarchy. When every container has `parent = null` +(flat topology) the tree collapses to a plain list with no +glyphs. Children are sorted alphabetically within each parent; +roots likewise. Cycles in the parent graph are tolerated โ€” +orphaned containers (not reachable from any root) are appended +as roots so no agent disappears. Pulsing red banner at the top +of this section if any two sub-agents hash to the same port +(`port_conflicts` from `/api/state`): the operator must rename +one of them and rebuild. `lifecycle::{spawn,rebuild}` also +preflight this and refuse with a clear error message naming the +conflicting agent. + +`โ†ป UPD4TE 4LL` button appears above the containers list when any +agent is stale. + +### Y3R C4LL tab + +Things blocked on operator decision โ€” approvals and questions +share a tab because they're the same concept ("something is +waiting on you"). + +**P3NDING APPR0VALS** โ€” the queue (see "Approval card" below). +The R3QU3ST SP4WN form lives at the top of this section. + +**M1ND H4S QU3STI0NS** โ€” pending operator-targeted `ask` +questions (amber pulsing border). Free-text fallback always +rendered alongside any option list; `multi=true` renders options +as checkboxes; submit merges selections + free text +comma-joined. Each row has a `โœ— CANC3L` button. Questions with +a `ttl_seconds` show a `โณ MM:SS` chip; the host-side watchdog +auto-cancels with `[expired]` when the deadline fires. + +### SYST3M tab + +Passive / rare-interaction state. + +**M3T4 1NPUTS** โ€” inputs in `meta/flake.lock` the operator can +selectively `nix flake update`, rendered as an indented tree: +every fetched input at every depth (`hyperhive`, +`hyperhive/nixpkgs`, `agent-`, `agent-/mcp-`, โ€ฆ), each +shown once at its shallowest path. `read_meta_inputs` walks the +lock graph with a `visited` set โ€” `follows` aliases and rev-less +nodes are skipped (issue #275). A `select all / select none` +control sits above the tree. Checking inputs + submitting bumps +the lock in `/meta/` and rebuilds the selected agents in +sequence; each outcome reaches the manager as a `rebuilt` +system event. `POST /meta-update`. While a lock-bump ripple runs, +the panel shows a pulsing "โณ meta-update running" banner and the +update button is disabled (snapshot field `meta_update_running`, +live event `meta_update_running`). + +**R3BU1LD QU3U3** โ€” pending and recently-completed container +operations: rebuilds, meta-update cascades, and first-spawns. +One operation runs at a time; the worker drains FIFO. Each row +shows a state glyph (`โธ` queued / `โ–ถ` running / `โœ”` done / +`โœ–` failed / `โŠ˜` cancelled), kind glyph + verb (`โ†ป rebuild`, +`โ—† meta_update`, `โœจ spawn`, `๐Ÿ—‘ destroy`), agent name, source +chip (`manual | meta_update | auto_update | crash_recover`), +timing, and an optional reason / error. Meta-update cascade +rebuilds nest under their parent entry (`parent_id` grouping; +`rqe-child` CSS class). Dedup: re-enqueueing a still-queued op +for the same agent collapses into the existing entry. Running +entries tick elapsed seconds live. Cold-loaded from +`/api/state.rebuild_queue`; live updates via `rebuild_queue_changed` +snapshot event. + +**QU3U3D R3M1ND3RS** โ€” reminders agents have scheduled for +themselves (via the `remind` tool) but not yet delivered. +Each row shows the owner, due time, and message; a `CANC3L` +button hard-deletes (`POST /cancel-reminder/{id}`) and a +`R3TRY` button re-arms one whose delivery failed +(`POST /retry-reminder/{id}`). Backed by `GET /api/reminders`. + +**K3PT ST4T3** โ€” destroyed-but-state-kept tombstones (size + +age + claude-creds badge). Two actions: `โŠ• R3V1V3` (queues a +Spawn approval; existing state is reused), `PURG3` (wipes +state + applied dirs; `POST /purge-tombstone/{name}`). + +### FL0W page (`/flow.html`) + +A dedicated full-page terminal (not a tab pane โ€” a separate HTML +page). Reuses the same `
` chrome +as the dashboard so the tab strip remains visible; SW4RM / Y3R +C4LL / SYST3M are cross-page links back to `/#`, and the +FL0W entry is marked active (`aria-current="page"`). + +**0PER4T0R 1NB0X** โ€” recent messages addressed to `operator`, +derived client-side from the dashboard event stream. Cold load +seeds from `/dashboard/history`'s 200-message backfill; subsequent +`sent` events with `to == "operator"` are appended live. Cap 50, +newest-first. + +**MESS4GE FL0W** โ€” live broker tail wrapped in a `.terminal-wrap`. +Cold load backfills the last ~200 messages from `/dashboard/history`; +live frames arrive on `/dashboard/stream`. Each row is one broker +event โ€” `sent` or `delivered` โ€” with `from โ†’ to: body`. Sticky- +bottom auto-scroll + "โ†“ N new" pill. Below the stream sits a +terminal-style compose box: `@name` picks the recipient (sticky via +localStorage; auto-complete from the live container list, Tab/Enter +to confirm; `@*` broadcasts). `POST /op-send` drops +`{from:"operator", to, body}` into the broker; the resulting SSE +frame re-renders both the terminal row and the inbox section. +Manager is addressed as `@manager` (the broker recipient string), +not `@hm1nd` (the container name). ### Container row @@ -436,101 +472,75 @@ Generalised form helpers: `form[data-confirm="โ€ฆ"]` pops ## Per-agent page -Layout, top to bottom: +Three fixed-position layers frame a full-viewport terminal: -- Banner (gradient shimmer while state=thinking). -- Title with `โ†‘ DASHB04RD` back-link (new tab) + `โ†ป R3BU1LD`. -- Meta links row: backend-supplied `StateSnapshot.links` (issue - #262) rendered as ` label โ†’`. Always includes `๐Ÿ“Š stats` - (`kind = Container`); `๐Ÿ–ฅ screen` when the VNC compositor is - enabled; `โฌก forge` (profile) + `โ†ณ config` (agent-configs - mirror, repo root since the agent doesn't know its own deployed - sha) when the agent has a forge account, both `kind = Forge`; - followed by any `hyperhive.dashboardLinks` extras - (`kind = External`) read from - `{state_dir}/hyperhive-dashboard-links.json`. The same list - feeds the dashboard card's icon strip via the host's - `GET /api/agent/{name}/links` passthrough proxy โ€” agent backend - is the single source of truth for what links it exposes. - Frontend resolves each `kind` (`container` โ†’ same-origin path, - `forge` โ†’ `http://host:3000`, `external` โ†’ absolute) via DOM - building, so agent-declared strings never reach `innerHTML`. -- Status section: empty when online (alive-badge in the state - row carries the signal), populated with the login form / - OAuth URL when `status` is `needs_login_*`. -- **State row**: alive badge + state badge + model chip + ctx - badge + last-turn timing + cancel-turn button + new-session - button. Every chip carries a `title=...` tooltip with the - detailed breakdown. - - Alive badge: `โ— alive` (green) / `โŠ˜ rate limited` (red, while - the harness is parked after a 429 โ€” clears automatically when - the sleep expires) / `โ—Œ needs login` (amber) / `โ—Œ logging in` / - `โ—‹ offline` / `โ€ฆ connecting`. Driven by - `LiveEvent::StatusChanged`; replaces the old "harness alive - โ€” turn loop running" paragraph so the state row carries - every reachability signal. +**Fixed-overlay header** (`
`): frosted +glass โ€” `backdrop-filter: blur` lets scrolled terminal rows show +through. Left to right: +- Agent icon (``). +- Title (`

`) + meta-nav (`