diff --git a/docs/web-ui/dashboard.md b/docs/web-ui/dashboard.md index 1d19c2c3..e84a5039 100644 --- a/docs/web-ui/dashboard.md +++ b/docs/web-ui/dashboard.md @@ -100,6 +100,17 @@ operator, with amber pulsing border. Anatomy of each card: - **◆ answ3red (N)** — collapsible `
` below the pending list; shows the last 20 resolved questions with their answers. +**0PER4T0R 1NB0X** — messages agents have sent to `to="operator"` but +the operator hasn't read yet. Cold-loaded from `/api/operator-inbox` on +tab activation + page load; appended live from the broker `sent` stream +(deduped on row id). Each row shows sender · timestamp · body (with +file-path linkification). A `✓ mark all read` button on the right acks +all rows via `POST /api/agent/operator/mark-all-read` (reuses the existing +mark-read endpoint). Unread count folds into the Y3R C4LL tab pill so +messages are visible from any tab even while inactive. Backed by +`GET /api/operator-inbox` → `{ messages: [...] }` (id, from, body, at, +in_reply_to, file_refs). + ## SYST3M tab Passive / rare-interaction state. @@ -422,20 +433,45 @@ mark-all read). FL0W stays the pure event firehose. **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`. 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 the body always gets the full row -width down to the content edge — long timestamps + agent names -used to push the body ~30ch in and force awkward narrow-column -wraps. `min-width: 0` keeps `word-break: break-word` effective so -the body doesn't force the row wider than its container. 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 the terminal row. Manager is addressed as `@root`. +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 row is upgraded 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 the body always gets the full row width down to +the content edge — long timestamps + agent names used to push the body ~30ch +in and force awkward narrow-column wraps. `min-width: 0` keeps +`word-break: break-word` effective so the body doesn't force the row wider +than its container. 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 the terminal row. Manager is addressed as `@root`. + +## Home page (`/`, `/home.html`) + +The H0M3 hub is the primary landing page (served at `/` by default). A +responsive grid of link tiles — Dashboard, Flow, Logs, Matrix (when enabled) +— each pointing to their respective surfaces. The page is a pure portal with +no tab-bar or SSE subscriptions. Typography + colours inherit from the shared +theme (Catppuccin Mocha via `common.css` + `theme.css`). The Matrix tile is +hidden until `home.js` confirms `matrix_gui_enabled` (same gating as the +dashboard's M4TR1X tab); `home.js` also fills the swarm/hive identity line +at the top. Dashboard is now served at `/dashboard.html` (route swap completed +in #1464 step 2); the home page at `/` replaces the old dashboard root. All +dashboard sub-pages include a `← Home` back-link for navigation. ## L0GS page (`/logs.html`) @@ -799,6 +835,13 @@ that's a browser-level decision, not ours. 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. +- `GET /api/operator-inbox` — list unread messages addressed to + `to="operator"` (broker rows with `acked_at = NULL`). Cold-loaded + for the Y3R C4LL tab's ◆ 1NB0X ◆ section on page load + tab activation; + live updates fed from the broker `sent` stream. Returns + `{ messages: [{ id, from, body, at, in_reply_to, file_refs }, …] }`, + newest-first. Reuses `/api/agent/operator/mark-all-read` to ack (filters + are identical so every listed row is exactly what mark-read clears). - `POST /op-send` (`to=`, `body=`) — drop an operator-authored message into ``'s inbox. `to=*` fans out to every registered agent. Returns 200; the broker