diff --git a/docs/web-ui/README.md b/docs/web-ui/README.md
index 800d868f..d3c2a99d 100644
--- a/docs/web-ui/README.md
+++ b/docs/web-ui/README.md
@@ -12,7 +12,7 @@ what you actually do here.
Everything starts at the **dashboard**, served at `/`. A pages menu
(the `☰` button at the left of the tab strip) links to the other
-surfaces — Flow, Stats, the API docs and the swarm UI — and each of
+surfaces — Stats, the API docs and the swarm UI — and each of
those pages links back to the dashboard.
The dashboard is where you'll spend most of your time. It's a single
@@ -26,9 +26,8 @@ page with five tabs:
- **K3PT ST4T3** — kept state from agents with no container
(tombstones), with a purge button per entry.
-Two things live on their own page instead, reached from the pages
-menu: **Flow** (`/flow.html`, the raw live message stream across the
-whole swarm) and **Stats** (`/stats.html`, swarm-wide usage stats).
+Usage stats live on their own page, reached from the pages menu:
+**Stats** (`/stats.html`, swarm-wide usage stats).
Each agent also has its own page — a full terminal view of that
agent's session, reachable by selecting its name anywhere in the
diff --git a/docs/web-ui/css-vars.md b/docs/web-ui/css-vars.md
index 4bd90e65..90ba5f9c 100644
--- a/docs/web-ui/css-vars.md
+++ b/docs/web-ui/css-vars.md
@@ -12,7 +12,7 @@ swap touches only the first:
what the app references and it **never changes on a swap**.
Both files live in `frontend/packages/shared/src/`. Per-page stylesheets
-(`common.css`, `dashboard.css`, `flow.css`, `agent.css`)
+(`common.css`, `dashboard.css`, `agent.css`)
reference the **semantic** names and must **not** redeclare them or reach
for the raw `--baseNN` slots directly. (`base.css` holds only the shared
`body` typography — it references the palette but no longer defines it.)
diff --git a/docs/web-ui/dashboard.md b/docs/web-ui/dashboard.md
index 0a308514..f525f82b 100644
--- a/docs/web-ui/dashboard.md
+++ b/docs/web-ui/dashboard.md
@@ -12,21 +12,21 @@ hive-c0re serves the dashboard at `/`.
It has a fixed chrome header at the top and a `` that shows exactly
one tab pane at a time. The URL hash (`#agents`, `#rebuild`, `#meta`,
`#buildlogs`, `#kept`) drives which pane is
-active; hash changes don't reload the page. FL0W and ST4TS
-are standalone pages reached from the pages menu, not from the
+active; hash changes don't reload the page. ST4TS
+is a standalone page reached from the pages menu, not from the
dashboard tab strip.
**Chrome header** (fixed, overlays the active tab pane):
- **Pages menu**: a `☰` button at the left of the chrome (`links-menu.js`)
- opening a popover with links to **flow** (`/flow.html`), **stats**
+ opening a popover with links to **stats**
(`/stats.html`), **api docs** (`/api/docs/`) and **swarm** (the swarm
UI at the swarm apex domain, opened in a new tab). The swarm entry
comes from `state.swarm_public_url`, which hive-c0re reads from
`HIVE_SWARM_PUBLIC_URL` (set from `services.hyperhive.swarm.domain`);
with no domain set there is no swarm entry. The standalone pages link back to `/`.
- **Tab strip**: `◆ 4G3NTS ◆`, `◆ R3BU1LD QU3U3 ◆`, `◆ M3T4 1NPUTS ◆`,
- `◆ BUILD L0GS ◆`, `◆ K3PT ST4T3 ◆`. In-page tabs only — FL0W / ST4TS
- live on their own pages, reached from the pages menu. Count pills on
+ `◆ BUILD L0GS ◆`, `◆ K3PT ST4T3 ◆`. In-page tabs only — ST4TS
+ lives on its own page, reached from the pages menu. Count pills on
4G3NTS (stale containers), R3BU1LD QU3U3 (rebuilds in flight) and
K3PT ST4T3 (kept-state entries).
- **Banner-thin** (`░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░`)
@@ -46,10 +46,6 @@ dashboard tab strip.
to ` / // h1ve-c0re` once `hive_name` / `swarm_name`
arrive in the state snapshot.
-The FL0W page uses a slim header (a `← dashboard` back-link + the page
-title) rather than the dashboard tab strip — it's a standalone surface,
-not a tab pane.
-
## 4G3NTS tab
**C0NTAINERS** — live containers as a flat list, one row per
@@ -212,7 +208,7 @@ entry with that id pre-expanded.
Hive-wide turn statistics, aggregated across every agent's
`hyperhive-turn-stats.sqlite` for the selected window. Not a dashboard
tab — a standalone page reached from the dashboard's pages menu,
-same minimal chrome as `/flow.html`. Distinct from
+minimal chrome (a back-link and a title). Distinct from
each agent's own `/stats` page (which carries the per-agent trend
charts): ST4TS is the swarm-level rollup.
@@ -249,42 +245,6 @@ the model id, longest match wins) mapping to
`{ input, output, cache_read, cache_write }` USD-per-million-token
prices. Models not covered fall back to hive-c0re's built-in estimate.
-## FL0W page (`/flow.html`)
-
-A dedicated full-page terminal (not a tab pane — a separate HTML
-page). Slim chrome: a `← dashboard` back-link, the FL0W
-title, and the agent-filter select (see below). No dashboard tab
-strip — FL0W is a standalone surface reached from the pages menu.
-
-**MESS4GE FL0W** — live broker tail wrapped in a `.terminal-wrap`.
-Cold load backfills the last ~200 messages from `/api/dashboard/history`;
-live frames arrive on `/api/dashboard/stream`. Each row is one broker
-event — `sent` or `delivered` — with `from → to: body`. When a `sent`
-and `delivered` event for the same message arrive within 3 seconds
-(immediate delivery to a live recipient), the page upgrades the row in
-place (arrow becomes green ✓, title reads "sent + delivered") instead of
-rendering two near-identical lines — genuine delivery latency (recipient
-was busy) still appears as a second row. Each row carries `data-from` /
-`data-to` attributes; an **agent filter select in the FL0W header** narrows
-the timeline to messages involving the chosen agent (matched on `from` OR
-`to`), with non-matching rows hidden (`.flow-hidden` class). The selection
-persists in localStorage across reloads; new rows pick up the active filter
-at render time. The dropdown populates from the live container list and
-stays current on add/remove; a saved selection survives even if that agent
-isn't currently listed.
-
-The row is a `flex-wrap: wrap` container holding ts / arrow / from / sep
-/ to chips inline; the **body wraps to its own full-width line below** the
-chips (`flex: 1 1 100%`) so long timestamps and agent names never push the
-body into a narrow trailing column. `min-width: 0` keeps
-`word-break: break-word` effective so the body doesn't force the row wider
-than its container. Sticky-bottom autoscroll + "↓ N new" pill. Below the
-stream sits a terminal-style compose box: `@name` picks the recipient
-(sticky via localStorage; autocomplete from the live container list,
-Tab/Enter to confirm; `@*` broadcasts). `POST /api/op-send` drops
-`{from:"operator", to, body}` into the broker; the resulting SSE frame
-re-renders the terminal row. The operator addresses the root agent as `@root`.
-
## Container row
A full-height **square agent icon** (5em, capped) on the left. The
@@ -577,11 +537,6 @@ below — some endpoints aren't in it yet.
not yet delivered and sets `acked_at = now`. Returns
`{ "marked": N }`. Agent name validated against
`[a-z0-9_-]`, 1-63 chars; 400 on bad input.
-- `POST /api/op-send` (`to=`, `body=`) — drop an
- operator-authored message into ``'s inbox. `to=*` fans
- out to every registered agent. Returns 200; the broker
- `Sent` event re-renders the message-flow terminal without a
- snapshot refetch. Used by the compose textbox under MESS4GE FL0W.
- `GET /api/build-logs/{agent}?limit=N` — most-recent build log
headers for one agent, newest first. Returns
`Vec` (JSON): `id`, `agent`, `kind`, `cmdline`,
@@ -621,8 +576,7 @@ below — some endpoints aren't in it yet.
The client trusts that list and linkifies only those tokens,
so directories, missing files, and forbidden subtrees never
become anchors. No probe endpoint, no client-side regex
- heuristics. Historical messages get the same treatment on
- `/api/dashboard/history` backfill.
+ heuristics.
- `GET /api/reminders` — list pending reminders for the
dashboard's queued-reminders panel.
- `GET /api/stats-hive?window=<1h|4h|24h|3d|7d|30d|all>` — hive-wide
@@ -639,9 +593,6 @@ below — some endpoints aren't in it yet.
- `GET /api/dashboard/stream` — unified live event channel:
broker `sent` / `delivered`, plus the mutation events listed
below. Each frame carries `seq`.
-- `GET /api/dashboard/history` — last ~200 broker messages
- (wrapped as `{ seq, events }`) for the message-flow
- terminal's backfill on page load.
## Dashboard event channel
@@ -652,9 +603,7 @@ payload):
- `sent` / `delivered` — broker traffic, mirrored from the
intra-process channel by a forwarder task. Both carry `id: i64`
(the broker row id) and `in_reply_to: Option` for thread
- rendering. The dashboard message-flow terminal renders reply
- rows with a `↳ reply` tag that scroll-highlights the parent
- row on selection. Used by the message-flow terminal renderer.
+ rendering.
- `transient_set` (name, transient_kind, since_unix) /
`transient_cleared` (name) — lifecycle action spinners. The
client ticks the elapsed-seconds badge off `since_unix`
@@ -696,7 +645,7 @@ payload):
forms that mutate non-event-derived state** (PURG3 +
meta-update, since tombstones + meta_inputs aren't event-
shaped yet). Every other section — transients,
-containers, message flow —
+containers —
derives from `/api/dashboard/stream` after the initial snapshot,
maintaining its own client-side store and applying events on
top.
diff --git a/docs/web-ui/shape.md b/docs/web-ui/shape.md
index 45635d89..6e161de5 100644
--- a/docs/web-ui/shape.md
+++ b/docs/web-ui/shape.md
@@ -19,15 +19,15 @@
from `@hive/shared` (was `hive-fr0nt`); the dashboard ships one
CSS bundle per page (`colors.css` + `theme.css` + `common.css`,
loaded by every page, plus a page-specific bundle —
- `dashboard.css` / `flow.css` /
+ `dashboard.css` /
`stats.css`);
`common.css` inlines `@hive/shared`'s
`base.css` + `terminal.css` + `tabs.css` + `chrome.css` +
`pill.css` via esbuild's `@import` resolution.
`terminal.js` exports `{ create, linkify }` as ES module
members; a back-compat shim in the IIFE bodies still exposes
- a `window.HiveTerminal` global for callers that need it. The dashboard's
- `#msgflow` and the per-agent `#live` log are both backed by
+ a `window.HiveTerminal` global for callers that need it. The
+ per-agent `#live` log is backed by
this terminal — sticky-bottom autoscroll, "↓ N new" pill,
history backfill, SSE plumbing all live there. Each page
registers a kind→renderer map; unknown kinds fall through to
@@ -51,10 +51,8 @@
dashboard stream carries broker `Sent` / `Delivered` (mirrored
by a forwarder task from the broker's intra-process channel)
plus mutation events (`transient_set` / `transient_cleared`). Each frame carries a `seq`. The
- matching backfill endpoint is `GET /api/dashboard/history` (last
- ~200 broker messages wrapped in `{ seq, events }`) on the
- dashboard and `GET /events/history` (last 2000 `LiveEvent`s
- also wrapped in `{ seq, events }`) on the agent.
+ agent's matching backfill endpoint is `GET /events/history` (last 2000
+ `LiveEvent`s wrapped in `{ seq, events }`).
**One unified channel**: browsers cap concurrent SSE
connections per origin (~6 in Chrome). Using one channel per
domain would exhaust this budget on a live hive; dispatching
@@ -87,9 +85,8 @@
## Shared terminal pane
-The shared terminal factory in `@hive/shared/terminal.js` backs both
-surfaces' scrollable log streams (`#msgflow` on the dashboard, `#live`
-on the per-agent page). The factory wires up
+The shared terminal factory in `@hive/shared/terminal.js` backs the
+per-agent page's scrollable log stream (`#live`). The factory wires up
sticky-bottom autoscroll, a "↓ N new" pill, history backfill, and
SSE replay. Pages register a `kind → renderer` map; unknown kinds
fall through to a JSON-dump note row. The factory ships three row
diff --git a/docs/web-ui/terminal-rendering.md b/docs/web-ui/terminal-rendering.md
index aae8b9af..85762198 100644
--- a/docs/web-ui/terminal-rendering.md
+++ b/docs/web-ui/terminal-rendering.md
@@ -69,7 +69,3 @@ read that when you need the specifics, this doc doesn't duplicate it.
`marked.parse()` through DOMPurify into a `
`. 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.
diff --git a/frontend/packages/dashboard/build.mjs b/frontend/packages/dashboard/build.mjs
index 18d6e14c..92632559 100644
--- a/frontend/packages/dashboard/build.mjs
+++ b/frontend/packages/dashboard/build.mjs
@@ -1,5 +1,5 @@
// esbuild build for @hive/dashboard: one JS+CSS bundle per page (the
-// dashboard at `/`, flow/stats at their own
+// dashboard at `/`, stats at its own
// `.html`), each named after its own entry point below — see the
// `entryPoints`/`for` lists for the exact map, not repeated here to
// avoid this comment drifting out of sync with the real build steps.
@@ -29,7 +29,7 @@ mkdirSync(staticDir(""), { recursive: true });
// follow-up once asset sizes warrant it). esbuild writes each entry
// to `static/.js` based on the entryPoint basename.
await build({
- entryPoints: [src("tabs.js"), src("flow.js"), src("stats.js")],
+ entryPoints: [src("tabs.js"), src("stats.js")],
outdir: staticDir(""),
bundle: true,
format: "esm",
@@ -58,7 +58,7 @@ await build({
// Stream-worker entry. Lives in a separate bundle: SharedWorker
// scripts run in a different global (`self` is the worker scope, no
-// `window`) so they can't be inlined into tabs.js / flow.js. Output is
+// `window`) so they can't be inlined into tabs.js / stats.js. Output is
// at `static/stream-worker.js`; common.js's `openStream` references
// `/static/stream-worker.js` as the SharedWorker URL. `format: 'iife'`
// matches the classic-script load (`new SharedWorker(url, name)` with
@@ -89,7 +89,6 @@ for (const entry of [
"theme.css",
"common.css",
"dashboard.css",
- "flow.css",
"stats.css",
]) {
await build({
@@ -101,7 +100,7 @@ for (const entry of [
});
}
-for (const html of ["index.html", "flow.html", "stats.html"]) {
+for (const html of ["index.html", "stats.html"]) {
copyFileSync(src(html), dist(html));
}
diff --git a/frontend/packages/dashboard/src/agents.js b/frontend/packages/dashboard/src/agents.js
index 62d4f72e..4e529a46 100644
--- a/frontend/packages/dashboard/src/agents.js
+++ b/frontend/packages/dashboard/src/agents.js
@@ -588,7 +588,7 @@ function buildContainerLi(c, opts) {
export function renderContainers(s) {
const root = $("containers-section");
// #containers-section only exists on the dashboard. tabs.js is the
- // bundle for that page only (flow.html loads flow.js instead).
+ // bundle for that page only.
// Belt-and-suspenders for any future page that adds tabs.js
// without a #containers-section — matches the
// no-op-when-target-absent convention the other renderers
diff --git a/frontend/packages/dashboard/src/common.css b/frontend/packages/dashboard/src/common.css
index be07df35..34e31ce3 100644
--- a/frontend/packages/dashboard/src/common.css
+++ b/frontend/packages/dashboard/src/common.css
@@ -337,148 +337,9 @@ code {
pages' sticky back-link bar) moved to @hive/shared/chrome.css, imported
at the top of this file. */
-/* `#msgflow` is a shared `.live` pane inside `.terminal-wrap`. The
- msgrow / msg-* rules below power the /flow.html full-page terminal. */
-.live .msgrow {
- display: flex;
- flex-wrap: wrap;
- align-items: baseline;
- gap: 0.5em;
- padding: 0.1em 0;
- text-indent: 0;
-}
-.live .msgrow .msg-body {
- flex: 1 1 100%;
- min-width: 0;
-}
-.live .msgrow.sent .msg-arrow {
- color: var(--cyan);
-}
-.live .msgrow.delivered .msg-arrow {
- color: var(--green);
-}
-/* Reply-thread rendering: indented border-left + muted reply tag. */
-.live .msgrow.msg-reply {
- padding-left: 1.2em;
- border-left: 2px solid var(--border);
- margin-left: 0.6em;
-}
-.msg-reply-tag {
- color: var(--muted);
- font-size: 0.8em;
- white-space: nowrap;
- order: -1;
-}
-.msg-reply-tag a {
- color: var(--muted);
- text-shadow: none;
- font-weight: normal;
-}
-.msg-reply-tag a:hover {
- color: var(--fg);
-}
-@keyframes msg-highlight-fade {
- from {
- background: color-mix(in srgb, var(--purple) 18%, transparent);
- }
- to {
- background: transparent;
- }
-}
-.msg-highlight {
- animation: msg-highlight-fade 1.5s ease-out forwards;
-}
-.msg-ts {
- color: var(--muted);
- font-size: 0.85em;
-}
-.msg-arrow {
- font-weight: bold;
-}
-.msg-from {
- color: var(--amber);
-}
-.msg-sep {
- color: var(--muted);
-}
-.msg-to {
- color: var(--pink);
-}
-.msg-body {
- color: var(--fg);
- white-space: pre-wrap;
- word-break: break-word;
-}
-
-/* ─── operator compose box ──────────────────────────────────────────
- Sits inside `.terminal-wrap` on both /flow.html and the dashboard
- CALL tab. Dashed top separator mirrors the agent terminal prompt. */
-.op-compose {
- position: relative;
- display: flex;
- align-items: flex-start;
- gap: 0.6em;
- padding: 0.55em 0.8em;
- border-top: 1px dashed var(--purple-dim);
-}
-.op-compose-prompt {
- color: var(--purple);
- text-shadow: 0 0 4px currentColor;
- font-weight: bold;
- white-space: nowrap;
- user-select: none;
- padding-top: 0.15em;
-}
-.op-compose-input {
- flex: 1;
- background: transparent;
- color: var(--fg);
- border: none;
- font-family: inherit;
- font-size: 1em;
- line-height: 1.4;
- resize: none;
- overflow: hidden;
- max-height: 8em;
- padding: 0;
-}
-.op-compose-input:focus {
- outline: none;
-}
-.op-compose-input::placeholder {
- color: var(--muted);
-}
-.op-compose-suggest {
- position: absolute;
- left: 3em;
- bottom: 100%;
- z-index: 50;
- background: var(--bg-elev);
- border: 1px solid var(--purple-dim);
- border-radius: 4px;
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
- list-style: none;
- margin: 0;
- padding: 0.3em 0;
- min-width: 12em;
- max-height: 14em;
- overflow-y: auto;
-}
-.op-compose-suggest .item {
- padding: 0.35em 0.9em;
- cursor: pointer;
- font-size: 0.9em;
- color: var(--muted);
-}
-.op-compose-suggest .item.active,
-.op-compose-suggest .item:hover {
- background: var(--border);
- color: var(--purple);
-}
-
/* ─── side panel ─────────────────────────────────────────────────
Long content (file previews, diffs, applied config) opens in
- a drawer that swipes in from the right — used by both /flow.html and
+ a drawer that swipes in from the right on
the dashboard. The drawer chrome itself (backdrop, drawer, resize
handle, header, title, close button) is now the shared
`` element (@hive/shared/side-panel.js); what's left
@@ -566,8 +427,8 @@ hive-side-panel .md td {
carries the horizontal gutter. Full-width chrome (the tab strip, the
footer) then spans edge-to-edge for free,
with no negative-margin breakout hacks. Only matches on pages that opt
- in by adding the wrapper (index.html); FL0W / ST4TS
- are full-bleed with their own `.-main` padding. */
+ in by adding the wrapper (index.html); ST4TS
+ is full-bleed with its own `.-main` padding. */
.page-content {
padding: 0 1.5em;
}
diff --git a/frontend/packages/dashboard/src/common.js b/frontend/packages/dashboard/src/common.js
index 3d1449fd..fc536038 100644
--- a/frontend/packages/dashboard/src/common.js
+++ b/frontend/packages/dashboard/src/common.js
@@ -1,6 +1,6 @@
-// Shared dashboard helpers used by both index.html (./tabs.js) and
-// flow.html (./flow.js): pure DOM helpers, the side-panel singleton,
-// and the path-link / file-preview infrastructure for the side panel.
+// Shared dashboard helpers used by index.html (./tabs.js): pure DOM helpers,
+// the side-panel singleton, and the path-link / file-preview infrastructure
+// for the side panel.
import { linkify as termLinkify } from "@hive/shared/terminal.js";
import { el } from "@hive/shared/dom.js";
diff --git a/frontend/packages/dashboard/src/dashboard.css b/frontend/packages/dashboard/src/dashboard.css
index 9dae6251..25858573 100644
--- a/frontend/packages/dashboard/src/dashboard.css
+++ b/frontend/packages/dashboard/src/dashboard.css
@@ -180,8 +180,6 @@ body.dashboard-shell {
display: none;
}
-/* FL0W is a separate page (`/flow.html`) — its full-viewport
- styling lives in flow.css. */
.banner {
text-align: center;
margin: 0 0 1em 0;
diff --git a/frontend/packages/dashboard/src/flow.css b/frontend/packages/dashboard/src/flow.css
deleted file mode 100644
index e76d5094..00000000
--- a/frontend/packages/dashboard/src/flow.css
+++ /dev/null
@@ -1,111 +0,0 @@
-/* ─── /flow.html — full-page chat ─────────────────────────────────
- See docs/web-ui/dashboard.md::FL0W page for the page-vs-pane rationale.
- Shape mirrors the per-agent live page (frosted-glass header +
- composer, full-viewport terminal). */
-
-:root {
- /* Approximate height of the flow chrome (the slim back-link header).
- The slug banner lives at the page footer on the dashboard, omitted
- on /flow.html since the full-viewport terminal has no normal-flow
- footer position. */
- --flow-header-h: 3.6em;
- --flow-composer-h: 3.6em;
- --flow-frost-bg: color-mix(in srgb, var(--bg) 74%, transparent);
- --flow-frost-blur: blur(12px) saturate(140%);
-}
-
-body.flow-shell {
- /* Override the dashboard's centred-max-width layout — flow owns
- the whole viewport. */
- max-width: none;
- margin: 0;
- padding: 0;
- height: 100vh;
- overflow: hidden;
- background:
- radial-gradient(
- ellipse 80% 60% at 50% 0%,
- color-mix(in srgb, var(--purple) 6%, transparent) 0%,
- transparent 60%
- ),
- var(--bg);
-}
-
-.flow-main {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- overflow: hidden;
-}
-/* flow.html now has a slim header (back link only) instead of the full
- tabbar — reduce the top padding to match the smaller header height. */
-body.flow-shell .flow-main-slim {
- padding-top: calc(var(--flow-header-h) + 0.4em);
-}
-.flow-main .terminal-wrap {
- position: absolute;
- inset: 0;
- border: 0;
- background: transparent;
- box-shadow: none;
- border-radius: 0;
- margin: 0;
- padding: 0;
-}
-.flow-main .live {
- position: absolute;
- inset: 0;
- height: auto;
- max-height: none;
- padding-top: calc(var(--flow-header-h) + 0.8em);
- padding-bottom: calc(var(--flow-composer-h) + 0.8em);
- scroll-padding-top: calc(var(--flow-header-h) + 0.8em);
- scroll-padding-bottom: calc(var(--flow-composer-h) + 0.8em);
- overflow: auto;
-}
-/* Tail pill (↓ N new): bottom offset clears the floating composer.
- Pill is anchored on .flow-main (not .terminal-wrap) so the
- backdrop-filter stacking context doesn't trap its z-index — see
- docs/web-ui/agent.md::Per-agent page (Terminal-wrap) for the same
- gotcha on the agent page. */
-.flow-main .tail-pill {
- bottom: calc(var(--flow-composer-h) + 0.6em);
- z-index: 35;
-}
-
-.flow-composer {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- z-index: 30;
- min-height: var(--flow-composer-h);
- background: var(--flow-frost-bg);
- -webkit-backdrop-filter: var(--flow-frost-blur);
- backdrop-filter: var(--flow-frost-blur);
- border-top: 1px solid var(--purple-dim);
- box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.35);
- padding: 0.4em 1em;
-}
-
-/* Agent filter: a select in the FL0W header narrows the
- timeline to messages involving one agent. Non-matching rows get
- `.flow-hidden`. */
-.flow-filter {
- font-family: inherit;
- font-size: 0.85em;
- background: var(--bg-elev);
- color: var(--amber);
- border: 1px solid var(--border);
- border-radius: 4px;
- padding: 0.25em 0.5em;
- margin-left: auto;
-}
-.flow-filter:focus {
- outline: 1px solid var(--purple);
-}
-.msgrow.flow-hidden {
- display: none;
-}
diff --git a/frontend/packages/dashboard/src/flow.html b/frontend/packages/dashboard/src/flow.html
deleted file mode 100644
index bb13ddba..00000000
--- a/frontend/packages/dashboard/src/flow.html
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
- hyperhive // FL0W
-
-
-
-
-
-
-
-
-
- ← dashboard
- FL0W
-
-
-
-
-
-
-
-
connecting…
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/packages/dashboard/src/flow.js b/frontend/packages/dashboard/src/flow.js
deleted file mode 100644
index c721f695..00000000
--- a/frontend/packages/dashboard/src/flow.js
+++ /dev/null
@@ -1,479 +0,0 @@
-// /flow.html entry point. Owns the full-page broker terminal and the
-// @-mention compose box. Pulls shared infrastructure (DOM helpers,
-// side panel, path linkification) from `./common.js`. FL0W is the pure
-// event firehose.
-//
-// Does NOT contain the dashboard's tab renderers, mutation-event
-// dispatchers, or refreshState — that's `./tabs.js`, loaded only by
-// the dashboard. The flow page runs purely on the broker stream + an
-// initial /api/state fetch (compose autocomplete needs the live
-// container list).
-
-import { create as termCreate } from "@hive/shared/terminal.js";
-import { $, appendLinkified, openStream, initStickyTop } from "./common.js";
-import { el } from "@hive/shared/dom.js";
-import { epochSec } from "./util.js";
-
-(() => {
- initStickyTop();
-
- // ─── local containers cache (for compose autocomplete) ──────────────────
- // The compose box's @-mention completion suggests known agent names.
- // the dashboard (tabs.js) maintains the canonical `containersState`
- // from /api/state + SSE; here we keep a small local mirror updated
- // by the same `container_state_changed` / `container_removed` events
- // the dashboard would handle.
- const flowContainers = new Map();
- fetch("/api/state")
- .then((r) => (r.ok ? r.json() : null))
- .then((s) => {
- if (!s || !Array.isArray(s.containers)) return;
- for (const c of s.containers) flowContainers.set(c.name, c);
- populateAgentFilter();
- })
- .catch(() => {
- /* graceful: compose just shows `*` and nothing else */
- });
-
- // ─── agent filter ───────────────────────────────────────────────
- // A select in the FL0W header narrows the timeline to messages involving
- // one agent (matched on `from` OR `to`). Each rendered row carries
- // `data-from` / `data-to`; non-matching rows get `.flow-hidden`. New rows
- // pick up the active filter at render time (see renderMsg); changing the
- // filter re-scans existing rows. Selection persists in localStorage so a
- // reload / tab-switch keeps the view. Uses `$('msgflow')` for row access
- // so it works regardless of the message-flow IIFE's local scope.
- let agentFilter = localStorage.getItem("flow-agent-filter") || "";
- function rowMatchesFilter(from, to) {
- return !agentFilter || from === agentFilter || to === agentFilter;
- }
- function applyAgentFilter() {
- const flow = $("msgflow");
- if (!flow) return;
- for (const row of flow.children) {
- const { from, to } = row.dataset;
- if (from === undefined && to === undefined) continue; // non-message row
- row.classList.toggle("flow-hidden", !rowMatchesFilter(from, to));
- }
- }
- function populateAgentFilter() {
- const sel = $("flow-agent-filter");
- if (!sel) return;
- const names = [...flowContainers.keys()].sort();
- sel.replaceChildren();
- sel.append(el("option", { value: "" }, "all agents"));
- for (const n of names) sel.append(el("option", { value: n }, n));
- // Preserve a saved selection even if that agent isn't in the live
- // container list (yet / anymore) so the filter doesn't silently reset.
- if (agentFilter && !names.includes(agentFilter)) {
- sel.append(el("option", { value: agentFilter }, agentFilter));
- }
- sel.value = agentFilter;
- }
- {
- const sel = $("flow-agent-filter");
- if (sel) {
- sel.addEventListener("change", () => {
- agentFilter = sel.value;
- if (agentFilter) localStorage.setItem("flow-agent-filter", agentFilter);
- else localStorage.removeItem("flow-agent-filter");
- applyAgentFilter();
- });
- }
- }
-
- // ─── message flow: shared terminal pane ────────────────────────────────
- // Scroll, pill, backfill + SSE plumbing live in @hive/shared/terminal.
- // What stays here is the broker-message renderer + the page-local
- // side effect (banner pulse).
- (() => {
- const flow = $("msgflow");
- if (!flow) return;
- flow.replaceChildren();
- const tsFmt = (ts) => new Date(ts).toISOString().slice(11, 19);
- // Pulse the page banner whenever a broker event lands. The
- // `.banner` element lives in the dashboard's
-
@@ -271,7 +271,7 @@
diff --git a/frontend/packages/dashboard/src/links-menu.js b/frontend/packages/dashboard/src/links-menu.js
index 6707ca85..b92f1239 100644
--- a/frontend/packages/dashboard/src/links-menu.js
+++ b/frontend/packages/dashboard/src/links-menu.js
@@ -1,5 +1,5 @@
// Pages menu in the dashboard chrome: one button that opens a popover
-// linking the standalone pages (flow, stats, API docs) and the swarm UI.
+// linking the standalone pages (stats, API docs) and the swarm UI.
// Modelled on the swarm UI's `LinksMenu`. The swarm entry only exists
// when `/api/state` carries `swarm_public_url`, so a hive without a
// configured swarm domain shows no dead link.
@@ -10,7 +10,6 @@ import { el } from "@hive/shared/dom.js";
/** Menu entries for a state snapshot. `external` opens in a new tab. */
export function menuLinks(state) {
const links = [
- { icon: "📡", label: "flow", url: "/flow.html" },
{ icon: "📊", label: "stats", url: "/stats.html" },
{ icon: "🧬", label: "api docs", url: "/api/docs/" },
];
diff --git a/frontend/packages/dashboard/src/stats.css b/frontend/packages/dashboard/src/stats.css
index e8bd8000..3b4a3ccc 100644
--- a/frontend/packages/dashboard/src/stats.css
+++ b/frontend/packages/dashboard/src/stats.css
@@ -1,6 +1,6 @@
/* ─── /stats.html — hive-wide turn-stats rollup ────────────────────
- Extracted from the dashboard ST4TS tab. Same minimal-
- chrome pattern as /flow.html: the back-link header +
+ Extracted from the dashboard ST4TS tab. Minimal chrome:
+ the back-link header +
title (.page-header / .page-back / .page-title) live in
@hive/shared/chrome.css.
`.hive-stats-table` lives in common.css. Only the ST4TS-specific
diff --git a/frontend/packages/dashboard/src/stats.html b/frontend/packages/dashboard/src/stats.html
index 45c03776..5230fb5a 100644
--- a/frontend/packages/dashboard/src/stats.html
+++ b/frontend/packages/dashboard/src/stats.html
@@ -11,7 +11,7 @@
-
diff --git a/frontend/packages/dashboard/src/tabs.js b/frontend/packages/dashboard/src/tabs.js
index efae029d..4c625619 100644
--- a/frontend/packages/dashboard/src/tabs.js
+++ b/frontend/packages/dashboard/src/tabs.js
@@ -5,9 +5,7 @@
// docs/web-ui/shape.md::Shape (shared by both) for the broader contract.
//
// Pure helpers (DOM, side panel, path linkification)
-// live in `./common.js`; the flow-page surface (broker
-// terminal, @-mention composer) lives in `./flow.js` — `flow.html` and
-// `index.html` each load their own bundle.
+// live in `./common.js`.
// 4G3NTS (containers) domain lives in `./agents.js`.
import { marked } from "marked";
@@ -46,7 +44,7 @@ import { renderLinksMenu } from "./links-menu.js";
// mdNode (in common.js) reads `window.marked` for the markdown side
// panel preview path. Set it here on the dashboard entry so file
-// previews work; flow.js does the same for the flow-page entry.
+// previews work.
window.marked = marked;
(() => {
@@ -206,12 +204,10 @@ window.marked = marked;
// looked up against `MUTATION_HANDLERS`; unknown kinds (broker
// `sent` / `delivered`, anything new the backend adds) silently
// no-op. On (re)connect we kick a refreshState() to recover events
- // lost during the disconnect window (same pattern as flow.js's
- // onStreamOpen).
+ // lost during the disconnect window.
//
- // Both the dashboard and /flow.html subscribe to `/api/dashboard/stream`
- // and filter client-side — the dashboard ignores broker traffic
- // and FL0W ignores mutation events.
+ // The dashboard subscribes to `/api/dashboard/stream` and ignores broker
+ // traffic client-side.
const MUTATION_HANDLERS = {
transient_set: applyTransientSet,
transient_cleared: applyTransientCleared,
@@ -276,11 +272,9 @@ window.marked = marked;
// ─── tab routing ───────────────────────────────────────────────────────
// Hash-based: `#agents` / `#rebuild` / `#meta` / `#buildlogs` / `#kept`
// activate the matching pane on the dashboard. Empty
- // hash defaults to 4G3NTS. FL0W and ST4TS are NOT tabs —
- // they're separate pages (`/flow.html`, `/stats.html`) reached from
- // the pages menu (links-menu.js). Tab routing only
- // applies when the tab DOM is present (e.g. not on the flow page
- // itself, where these elements don't exist and the loop no-ops).
+ // hash defaults to 4G3NTS. ST4TS is NOT a tab — it is a separate page
+ // (`/stats.html`) reached from the pages menu (links-menu.js). Tab routing
+ // only applies when the tab DOM is present.
// The shared hash-routed tab strip (@hive/shared/tabs.js) owns the
// tab/pane toggle + aria-selected + routing; `activateTab` runs the
// per-tab side-effects via the strip's onShow. Constructed below (after
@@ -337,7 +331,4 @@ window.marked = marked;
// — no per-renderer hookup needed.
refreshTabCounts();
setInterval(refreshTabCounts, 1000);
-
- // Flow-specific code (broker terminal init + the @-mention composer)
- // lives in ./flow.js — loaded only by /flow.html.
})();
diff --git a/frontend/packages/shared/src/chrome.css b/frontend/packages/shared/src/chrome.css
index 03e50650..e5bc0d4e 100644
--- a/frontend/packages/shared/src/chrome.css
+++ b/frontend/packages/shared/src/chrome.css
@@ -1,6 +1,6 @@
/* ─── page chrome: back-link header ────────────────────────────────
The minimal sticky "frosted bar" header used by every standalone page
- reached from the dashboard's pages menu — /flow, /stats. A back
+ reached from the dashboard's pages menu — /stats. A back
link to the dashboard on the left, then a page title and, on /stats, a
sub-tab nav. The operator dashboard uses its own richer
sticky chrome (`.dashboard-chrome`); these classes cover the simple
diff --git a/hive-c0re/src/dashboard/misc_api.rs b/hive-c0re/src/dashboard/misc_api.rs
index 845303f2..e4d6d4d4 100644
--- a/hive-c0re/src/dashboard/misc_api.rs
+++ b/hive-c0re/src/dashboard/misc_api.rs
@@ -1,8 +1,8 @@
-//! Remaining single-endpoint dashboard handlers: mark-all-read, operator
-//! compose (`op-send`), spawn-request and hive-wide turn stats.
+//! Remaining single-endpoint dashboard handlers: mark-all-read,
+//! spawn-request and hive-wide turn stats.
use axum::{
- extract::{Form, Path as AxumPath, State},
+ extract::{Path as AxumPath, State},
http::StatusCode,
response::{IntoResponse, Response},
};
@@ -82,67 +82,3 @@ pub(super) async fn post_mark_all_read(
Err(e) => error_response(&format!("mark-all-read {name} failed: {e:#}")),
}
}
-
-/// Operator-side compose form on the dashboard terminal. Drops a
-/// message into the broker as `{from: "operator", to, body}`. Same
-/// shape that per-agent web UIs use via `OperatorMsg`, but here the
-/// operator picks the recipient explicitly with `@name`. No
-/// validation that `to` resolves to a known agent — broker accepts
-/// arbitrary recipients (and the agent's inbox grows whether or not
-/// they exist, which is fine for spawn-then-greet flows).
-#[derive(Deserialize, ToSchema)]
-pub(super) struct OpSendForm {
- to: String,
- body: String,
-}
-
-/// Operator compose: drop a message into the
-/// broker addressed to `to` (or `*` to broadcast).
-#[utoipa::path(
- post,
- path = "/api/op-send",
- request_body(content = OpSendForm, content_type = "application/x-www-form-urlencoded"),
- responses(
- (status = 200, description = "message sent", body = String),
- (status = 500, description = "missing to/body, or the broker send failed"),
- ),
- tag = "misc_api"
-)]
-pub(super) async fn post_op_send(
- State(state): State,
- Form(form): Form,
-) -> Response {
- let to = form.to.trim().to_owned();
- let body = form.body.trim().to_owned();
- if to.is_empty() {
- return error_response("op-send: `to` required");
- }
- if body.is_empty() {
- return error_response("op-send: `body` required");
- }
- if to == "*" {
- let errors = state
- .coord
- .broadcast_send(hive_sh4re::manager::OPERATOR_RECIPIENT, &body);
- if !errors.is_empty() {
- return error_response(&format!(
- "op-send broadcast partial fail: {}",
- errors.join("; ")
- ));
- }
- } else if let Err(e) = state.coord.broker.send(&hive_sh4re::inbox::Message {
- from: hive_sh4re::manager::trusted_sender(hive_sh4re::manager::OPERATOR_RECIPIENT),
- to: to.clone(),
- body,
- in_reply_to: None,
- }) {
- return error_response(&format!("op-send to {to} failed: {e:#}"));
- }
- // 200 instead of 303 → the client doesn't refetch /api/state. The
- // broker `send` already emitted a `MessageEvent` which the
- // dashboard channel forwarder mirrors as `DashboardEvent::Sent`,
- // and the page's terminal derives from that stream — so the
- // operator's send shows up the same way an agent's send does, with
- // no full-state refresh in between.
- (axum::http::StatusCode::OK, "ok").into_response()
-}
diff --git a/hive-c0re/src/dashboard/mod.rs b/hive-c0re/src/dashboard/mod.rs
index 6cd46850..d2e90c07 100644
--- a/hive-c0re/src/dashboard/mod.rs
+++ b/hive-c0re/src/dashboard/mod.rs
@@ -117,7 +117,6 @@ fn app(coord: Arc) -> axum::Router {
.routes(routes!(state_files::get_state_file))
.routes(routes!(misc_api::api_stats_hive))
.routes(routes!(misc_api::post_mark_all_read))
- .routes(routes!(misc_api::post_op_send))
.routes(routes!(build_logs::get_build_logs_all))
.routes(routes!(build_logs::get_build_log_for_node))
.routes(routes!(build_logs::get_build_log_raw_for_node))
@@ -137,7 +136,6 @@ fn app(coord: Arc) -> axum::Router {
.routes(routes!(meta_inputs::post_meta_update))
.routes(routes!(build_logs::get_build_log_stream))
.routes(routes!(state_snapshot::dashboard_stream))
- .routes(routes!(state_snapshot::dashboard_history))
.routes(routes!(state_snapshot::jobq_graph))
.routes(routes!(state_snapshot::jobq_rollup))
.split_for_parts();
@@ -371,7 +369,6 @@ mod router_build_probe {
.routes(routes!(state_files::get_state_file))
.routes(routes!(misc_api::api_stats_hive))
.routes(routes!(misc_api::post_mark_all_read))
- .routes(routes!(misc_api::post_op_send))
.routes(routes!(build_logs::get_build_logs_all))
.routes(routes!(build_logs::get_build_log_for_node))
.routes(routes!(build_logs::get_build_log_raw_for_node))
@@ -391,7 +388,6 @@ mod router_build_probe {
.routes(routes!(meta_inputs::post_meta_update))
.routes(routes!(build_logs::get_build_log_stream))
.routes(routes!(state_snapshot::dashboard_stream))
- .routes(routes!(state_snapshot::dashboard_history))
.routes(routes!(state_snapshot::jobq_graph))
.routes(routes!(state_snapshot::jobq_rollup));
}
diff --git a/hive-c0re/src/dashboard/state_snapshot.rs b/hive-c0re/src/dashboard/state_snapshot.rs
index 960020a7..1518b70f 100644
--- a/hive-c0re/src/dashboard/state_snapshot.rs
+++ b/hive-c0re/src/dashboard/state_snapshot.rs
@@ -1,17 +1,14 @@
//! `/api/state` cold-load snapshot plus the dashboard's live read side:
//! the `StateSnapshot` shape and its view builders, the
-//! `/api/dashboard/stream` SSE channel, and the `/api/dashboard/history`
-//! backfill. SPA shape + SSE channels: docs/web-ui/shape.md.
+//! `/api/dashboard/stream` SSE channel. SPA shape + SSE channels:
+//! docs/web-ui/shape.md.
use std::convert::Infallible;
use axum::{
extract::State,
http::HeaderMap,
- response::{
- IntoResponse, Response,
- sse::{Event, KeepAlive, Sse},
- },
+ response::sse::{Event, KeepAlive, Sse},
};
use serde::{Deserialize, Serialize};
use tokio_stream::wrappers::BroadcastStream;
@@ -20,9 +17,9 @@ use utoipa::IntoParams;
use crate::container_view::ContainerView;
+use super::AppState;
use super::meta_inputs::{MetaInputView, read_meta_inputs};
use super::tombstones::{TombstoneView, build_tombstone_views};
-use super::{AppState, error_response, scan_validated_paths};
#[allow(clippy::struct_excessive_bools)]
#[derive(Serialize)]
@@ -351,105 +348,14 @@ pub(super) async fn jobq_rollup(
axum::Json(state.coord.job_queue.state_rollup())
}
-/// Response body for `/api/dashboard/history`. No `ToSchema` — its
-/// `events` field wraps [`crate::dashboard_events::DashboardEvent`],
-/// which doesn't derive `ToSchema` either (a large enum with many
-/// variants; see that type's doc comment for why annotating it is
-/// out of scope here). The `responses(...)` doc below spells out the
-/// shape in prose instead of a `body = ...` reference.
-#[derive(Serialize)]
-struct DashboardHistoryBody {
- seq: u64,
- events: Vec,
-}
-
-#[utoipa::path(
- get,
- path = "/api/dashboard/history",
- responses(
- (status = 200, description = "`{ seq, events }` — up to the last 200 \
- broker messages as `DashboardEvent::Sent`/`Delivered` JSON, plus \
- `seq`: the dashboard channel's high-water mark at fetch time \
- (used by clients to dedupe against buffered live SSE frames)"),
- ),
- tag = "state_snapshot"
-)]
-pub(super) async fn dashboard_history(State(state): State) -> Response {
- // Backfill source for the dashboard terminal. Returns up to ~200
- // historical broker messages (no other event kinds are persisted)
- // converted to `DashboardEvent::Sent` JSON so the client can replay
- // through the same dispatch path as live frames. Wrapped in
- // `{ seq, events }`: the seq is the dashboard channel's high-water
- // mark at fetch time. Clients use it to dedupe their buffered live
- // SSE traffic (drop anything with `seq <= history_seq`) so a frame
- // that lands between SSE-subscribe and history-fetch isn't shown
- // twice and isn't lost. Historical rows carry `seq = 0`; the
- // boundary seq is what closes the dedupe window.
- const HISTORY_LIMIT: u64 = 200;
- let seq = state.coord.current_seq();
- match state.coord.broker.recent_all(HISTORY_LIMIT) {
- Ok(mut messages) => {
- messages.reverse();
- let events: Vec = messages
- .into_iter()
- .map(|m| match m {
- crate::broker::MessageEvent::Sent {
- id,
- from,
- to,
- body,
- at,
- in_reply_to,
- } => {
- let file_refs = scan_validated_paths(&body);
- crate::dashboard_events::DashboardEvent::Sent {
- seq: 0,
- id,
- from,
- to,
- body,
- at: hive_sh4re::wire_time::from_secs(at),
- in_reply_to,
- file_refs,
- }
- }
- crate::broker::MessageEvent::Delivered {
- id,
- from,
- to,
- body,
- at,
- in_reply_to,
- } => {
- let file_refs = scan_validated_paths(&body);
- crate::dashboard_events::DashboardEvent::Delivered {
- seq: 0,
- id,
- from,
- to,
- body,
- at: hive_sh4re::wire_time::from_secs(at),
- in_reply_to,
- file_refs,
- }
- }
- })
- .collect();
- axum::Json(DashboardHistoryBody { seq, events }).into_response()
- }
- Err(e) => error_response(&format!("dashboard/history failed: {e:#}")),
- }
-}
-
/// `/dashboard/stream` query string. Today's only field is `kinds`:
/// a comma-separated allow-list of event-`kind` strings.
/// Empty / absent ⇒ no filter (current behaviour, all variants
/// forwarded). Set ⇒ only the named kinds reach the subscriber,
/// non-matches are skipped before the JSON serialise cost.
///
-/// Useful for narrow pages (e.g. `flow.js` only cares about `sent`
-/// / `delivered` / `container_state_changed` / `container_removed`)
-/// that want to drop the dispatch overhead on every unrelated mutation.
+/// Useful for narrow pages that only care about a few kinds and want to
+/// drop the dispatch overhead on every unrelated mutation.
#[derive(Deserialize, Default, IntoParams)]
pub(super) struct DashboardStreamQuery {
/// Comma-separated event kinds to forward. Each token is
@@ -464,8 +370,7 @@ pub(super) struct DashboardStreamQuery {
params(DashboardStreamQuery),
responses(
(status = 200, description = "server-sent event stream; each event's \
- `data` is a JSON-serialised `DashboardEvent` (seq-tagged; pair \
- with `/api/dashboard/history` to backfill + dedupe on connect)",
+ `data` is a JSON-serialised `DashboardEvent` (seq-tagged)",
body = String, content_type = "text/event-stream"),
),
tag = "state_snapshot"
diff --git a/hive-c0re/src/stores/broker.rs b/hive-c0re/src/stores/broker.rs
index ded2ad59..60003c25 100644
--- a/hive-c0re/src/stores/broker.rs
+++ b/hive-c0re/src/stores/broker.rs
@@ -269,38 +269,6 @@ impl Broker {
.map_err(Into::into)
}
- /// Latest `limit` messages across every recipient, newest-first.
- /// Backs the dashboard's message-flow backfill so a reload doesn't
- /// blank the operator's view of recent traffic. Returns each row as
- /// a [`MessageEvent::Sent`] so the dashboard's live renderer (which
- /// already speaks `MessageEvent`) can replay history through the
- /// same code path. We don't synthesise `Delivered` events here —
- /// the recv-side acks live in a different table column and would
- /// double-render on backfill; the live stream picks them up
- /// immediately on the first new `recv`.
- pub fn recent_all(&self, limit: u64) -> Result> {
- let conn = self.conn.lock().unwrap();
- let limit_i = i64::try_from(limit.min(i64::MAX as u64)).unwrap_or(i64::MAX);
- let mut stmt = conn.prepare(
- "SELECT id, sender, recipient, body, sent_at, in_reply_to
- FROM messages
- ORDER BY id DESC
- LIMIT ?1",
- )?;
- let rows = stmt.query_map(params![limit_i], |row| {
- Ok(MessageEvent::Sent {
- id: row.get(0)?,
- from: row.get(1)?,
- to: row.get(2)?,
- body: row.get(3)?,
- at: row.get(4)?,
- in_reply_to: row.get(5)?,
- })
- })?;
- rows.collect::>>()
- .map_err(Into::into)
- }
-
/// Number of undelivered messages addressed to `recipient`. Non-mutating
/// — used by the harness to surface "N unread" in tool-result status
/// lines without popping the queue.
diff --git a/nix/packages/frontend.nix b/nix/packages/frontend.nix
index c31b3d45..66627fc2 100644
--- a/nix/packages/frontend.nix
+++ b/nix/packages/frontend.nix
@@ -15,10 +15,10 @@
#
# $out/dashboard/ the hive-c0re dashboard assets (full layout in
# frontend/packages/dashboard/build.mjs):
-# index.html (operator SPA, served at /) flow.html
+# index.html (operator SPA, served at /)
# stats.html favicon.svg
-# static/{tabs,flow,stats,stream-worker}.js{,.map}
-# static/{colors,theme,common,dashboard,flow,stats}.css
+# static/{tabs,stats,stream-worker}.js{,.map}
+# static/{colors,theme,common,dashboard,stats}.css
# $out/agent/ the per-agent default UI (layered with
# services.hyperhive.agent.frontend.extraFiles at activation time)
# index.html stats.html screen.html