diff --git a/CLAUDE.md b/CLAUDE.md index 2172b054..a4d76b18 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,9 +22,9 @@ hive-c0re/ host daemon + CLI (one binary, subcommand-dispatched) src/manager_server.rs manager-privileged socket (ManagerRequest) src/agent_server.rs per-sub-agent socket listener (long-poll Recv) src/broker.rs sqlite Message store + intra-process broadcast - channel (`MessageEvent`) for `recv_blocking` + + channel (`MessageEvent`) for `recv_blocking_batch` + the dashboard forwarder; hourly vacuum of - delivered>30d + acked>30d src/dashboard_events.rs unified wire-facing event channel feeding `/dashboard/stream`. Carries broker `Sent` / `Delivered` (mirrored by the forwarder task diff --git a/docs/turn-loop.md b/docs/turn-loop.md index f5900ca4..5d5c3b2d 100644 --- a/docs/turn-loop.md +++ b/docs/turn-loop.md @@ -8,8 +8,8 @@ claude has access to in return. Each agent harness (`hive-ag3nt serve` or `hive-m1nd serve`) runs: 1. Long-poll `Recv` on its socket. The host-side broker - (`broker.rs::recv_blocking`) returns immediately if there's a - pending message, otherwise waits up to 30 s for a broker `Sent` + (`broker.rs::recv_blocking_batch`) returns immediately if there's + a pending message, otherwise waits up to 30 s for a broker `Sent` event for this recipient. 2. Pop one message. Peek the remaining inbox depth with `Status`. 3. Emit `LiveEvent::TurnStart { from, body, unread }` onto the SSE diff --git a/docs/web-ui.md b/docs/web-ui.md index 8e9135e0..a200c1aa 100644 --- a/docs/web-ui.md +++ b/docs/web-ui.md @@ -91,7 +91,13 @@ the previous process's socket release resolves itself. 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. **M1ND H4S QU3STI0NS** — pending operator-targeted `ask` +4. **M3T4 1NPUTS** — inputs in `meta/flake.lock` the operator can + selectively `nix flake update` (hyperhive first, then + `agent-` rows). 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`. +5. **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 @@ -101,16 +107,23 @@ the previous process's socket release resolves itself. with `[cancelled]`. Questions with a `ttl_seconds` show a `⏳ MM:SS` chip; the host-side watchdog auto-cancels with `[expired]` when the deadline fires. -5. **0PER4T0R 1NB0X** — recent messages addressed to `operator`, +6. **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`. +7. **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. +8. **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. -6. **P3NDING APPR0VALS** — the queue. The R3QU3ST SP4WN form - lives at the top of this section since submitting it - immediately queues an approval that lands directly below. -7. **MESS4GE FL0W** — live broker tail wrapped in a +9. **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 @@ -266,6 +279,10 @@ not ours. - `GET /api/reminders` — list pending reminders for the dashboard's queued-reminders panel. - `POST /cancel-reminder/{id}` — hard-delete a pending reminder. +- `POST /retry-reminder/{id}` — re-arm a reminder whose delivery + failed (clears the failure state so the scheduler retries). +- `POST /meta-update` — `nix flake update` the selected + `meta/flake.lock` inputs, then rebuild the affected agents. - `GET /dashboard/stream` — unified live event channel: broker `sent` / `delivered`, plus the mutation events listed below. Each frame carries `seq`. diff --git a/hive-c0re/src/dashboard_events.rs b/hive-c0re/src/dashboard_events.rs index 4d5346e6..943032f1 100644 --- a/hive-c0re/src/dashboard_events.rs +++ b/hive-c0re/src/dashboard_events.rs @@ -16,7 +16,7 @@ //! and re-emits each `MessageEvent` as a `DashboardEvent::Sent` / //! `DashboardEvent::Delivered` with a freshly-stamped seq. Keeping the //! broker's intra-process channel separate avoids coupling the broker -//! (used by `recv_blocking` inside the harness loop) to dashboard +//! (used by `recv_blocking_batch` inside the harness loop) to dashboard //! presentation concerns. //! //! New mutation kinds (approval added/resolved, question added/answered, diff --git a/hive-c0re/src/main.rs b/hive-c0re/src/main.rs index edd6e051..028a7d53 100644 --- a/hive-c0re/src/main.rs +++ b/hive-c0re/src/main.rs @@ -148,9 +148,10 @@ async fn main() -> Result<()> { tokio::spawn(async move { forge::ensure_all().await; }); - // Periodic broker vacuum: drop delivered messages older than - // 30 days. Undelivered messages are always kept (still in - // flight). Runs hourly; first sweep happens immediately. + // Periodic broker vacuum: drop fully-acked messages older + // than 30 days. Delivered-but-unacked rows (recoverable via + // requeue_inflight) and undelivered rows are always kept. + // Runs hourly; first sweep happens immediately. let vacuum_coord = coord.clone(); tokio::spawn(async move { let interval_secs = 3600u64; @@ -178,7 +179,7 @@ async fn main() -> Result<()> { // channel with a freshly-stamped seq, so the dashboard SSE // sees broker messages + future mutation events on one // stream with one monotonic seq. The broker's intra-process - // channel (used by `recv_blocking`) stays untouched. + // channel (used by `recv_blocking_batch`) stays untouched. spawn_broker_to_dashboard_forwarder(coord.clone()); let dash_coord = coord.clone(); tokio::spawn(async move {