docs: refresh for the dashboard rework + recent harness commits

- web-ui.md: side panel, approval card + 3-way diff base, stats
  page, forge config links, removed agent.nix viewer, per-agent
  loose-ends inline answer.
- approvals.md: forge mirror section + diff base toggle.
- turn-loop.md: recv(max), get_logs, remind, loose-ends, whoami.
- agent.md / manager.md prompts: recv(max), remind, get_logs.
- CLAUDE.md: forge.rs / stats.rs / hive-forge.nix in the file
  map, scratchpad refresh.

also: forgejo migrations.ALLOW_LOCALNETWORKS = true so an in-hive
mirror of the hyperhive repo can import from a localhost source.
This commit is contained in:
müde 2026-05-20 11:34:43 +02:00
parent 94781ccd08
commit 6ab3810e18
7 changed files with 205 additions and 36 deletions

View file

@ -7,6 +7,8 @@ scratchpad.
- High-level project intro: **[README.md](README.md)**. - High-level project intro: **[README.md](README.md)**.
- Open work + backlog: **[TODO.md](TODO.md)**. - Open work + backlog: **[TODO.md](TODO.md)**.
- Deployment / ops / boundaries / gateway backlog:
**[TODO-ops.md](TODO-ops.md)**.
## File map ## File map
@ -69,6 +71,10 @@ hive-c0re/ host daemon + CLI (one binary, subcommand-dispatched)
prepare/finalize/abort, lock_update_* prepare/finalize/abort, lock_update_*
src/migrate.rs startup auto-migration from pre-meta layout src/migrate.rs startup auto-migration from pre-meta layout
(idempotent, marker-guarded phase 4) (idempotent, marker-guarded phase 4)
src/forge.rs optional Forgejo wiring: per-agent users +
tokens, the `agent-configs` org, and
`push_config` — mirrors each applied repo
into `agent-configs/<n>` on the local forge
src/dashboard.rs axum HTTP: static shell + /api/state JSON + actions src/dashboard.rs axum HTTP: static shell + /api/state JSON + actions
+ journald viewer + bind-with-retry (SO_REUSEADDR) + journald viewer + bind-with-retry (SO_REUSEADDR)
+ deployed_sha chip per container + + deployed_sha chip per container +
@ -107,6 +113,9 @@ hive-ag3nt/ in-container harness crate; produces TWO binaries
src/turn_stats.rs per-turn analytics sink (one sqlite row per src/turn_stats.rs per-turn analytics sink (one sqlite row per
turn at /state/hyperhive-turn-stats.sqlite); turn at /state/hyperhive-turn-stats.sqlite);
schema + best-effort writer schema + best-effort writer
src/stats.rs read-side aggregations over turn-stats.sqlite
backing the /stats page (bucketed Snapshot:
turns / duration / tokens / model mix)
src/events.rs LiveEvent + broadcast Bus + sqlite-backed history src/events.rs LiveEvent + broadcast Bus + sqlite-backed history
(/state/hyperhive-events.sqlite) + TurnState + (/state/hyperhive-events.sqlite) + TurnState +
model selection (persisted at /state/hyperhive-model) model selection (persisted at /state/hyperhive-model)
@ -116,7 +125,8 @@ hive-ag3nt/ in-container harness crate; produces TWO binaries
src/login_session.rs drives `claude auth login` over stdio pipes src/login_session.rs drives `claude auth login` over stdio pipes
src/bin/hive-ag3nt.rs sub-agent main (Serve + Mcp subcommands) src/bin/hive-ag3nt.rs sub-agent main (Serve + Mcp subcommands)
src/bin/hive-m1nd.rs manager main (Serve + Mcp subcommands) src/bin/hive-m1nd.rs manager main (Serve + Mcp subcommands)
assets/ index.html, agent.css, app.js (include_str!) assets/ index.html, agent.css, app.js, stats.html,
stats.js (include_str!)
prompts/ static role/tools/settings for claude (include_str!): prompts/ static role/tools/settings for claude (include_str!):
agent.md — sub-agent system prompt agent.md — sub-agent system prompt
manager.md — manager system prompt manager.md — manager system prompt
@ -126,7 +136,10 @@ hive-sh4re/ wire types (HostRequest/Response, AgentRequest/Response,
ManagerRequest/Response, Message, Approval, HelperEvent) ManagerRequest/Response, Message, Approval, HelperEvent)
nix/ nix/
modules/hive-c0re.nix systemd service + firewall + git wiring modules/hive-c0re.nix systemd service + firewall + git wiring;
imports hive-forge.nix
modules/hive-forge.nix optional in-container Forgejo
(`hyperhive.forge.enable`, default on)
templates/harness-base.nix shared scaffolding for sub-agents + manager templates/harness-base.nix shared scaffolding for sub-agents + manager
templates/agent-base.nix sub-agent nixosConfiguration templates/agent-base.nix sub-agent nixosConfiguration
templates/manager.nix manager nixosConfiguration templates/manager.nix manager nixosConfiguration
@ -185,6 +198,33 @@ read them à la carte.
In-flight or recent context that hasn't earned a section yet. In-flight or recent context that hasn't earned a section yet.
Prune freely. Prune freely.
- **Just landed:** dashboard side panel + forge-linked
config/approvals + per-bucket model stats. (a) Long content
(file previews, approval diffs, journald logs) opens in a
right-side slide-in panel instead of expanding inline;
markdown `.md` previews render through the vendored `marked`
bundle (`/static/marked.js` route added to the dashboard).
(b) the container-row `agent.nix` viewer + `/api/agent-config`
are gone — each row links to the agent's `agent-configs/<n>`
repo on the forge instead. (c) pending approvals render as a
card (identity / what-changed / decision actions) with a
`commit on forge` deep-link and a 3-way diff base toggle — vs
applied / vs last-approved / vs previous proposal — served by
the new `/api/approval-diff/{id}?base=` endpoint. (d) the
`/stats` page gained a per-bucket turns-by-model stacked bar
(`Snapshot.models` + `Bucket.model_counts`) since model
choice drives token cost. `/api/state` carries `forge_present`
so the links only show when the forge is up. Docs refreshed:
web-ui.md, approvals.md, turn-loop.md, agent.md, manager.md.
- **Just landed:** `hyperhive.forge.enable` (default **true**) —
the forge option moved out of `services.hive-forge.*` into the
`hyperhive.*` namespace, and `hive-c0re.nix` imports
`hive-forge.nix` so the forge ships with the standard core
install. Forgejo settings gained
`migrations.ALLOW_LOCALNETWORKS = true` so an in-hive mirror
of the hyperhive repo can import from a `localhost` source
(Forgejo blocks loopback/RFC-1918 migration sources by
default).
- **Just landed:** failed `nixos-container update` self-documents. - **Just landed:** failed `nixos-container update` self-documents.
`lifecycle::run` now appends the tail (40 lines) of the target `lifecycle::run` now appends the tail (40 lines) of the target
container's own journal to the bail message when an `update` container's own journal to the bail message when an `update`
@ -565,11 +605,9 @@ Prune freely.
agent_web_port + collision banner + spawn/rebuild preflight, agent_web_port + collision banner + spawn/rebuild preflight,
browser notifications, focus-preserving refresh, generalised browser notifications, focus-preserving refresh, generalised
<details data-restore-key> survival, prompt-on-submit pattern. <details data-restore-key> survival, prompt-on-submit pattern.
- **Open threads:** custom per-agent MCP tools (groundwork for - **Open threads:** two-step spawn, notes compaction,
moving bitburner-agent into hyperhive), two-step spawn, unprivileged containers, Bash allow-list, xterm.js. The
per-agent send allow-list, telemetry/charts, notes deployment / gateway / privsep cluster is tracked in
compaction, unprivileged containers, Bash allow-list, `TODO-ops.md`. (Landed since this note was first written:
xterm.js. **Known bug** (in TODO.md): question id=5 was extra per-agent MCP servers, per-agent send allow-list,
queued but didn't render — likely a `pending()` row-decode telemetry + the `/stats` page.)
error swallowed by `unwrap_or_default`; investigate by curl
/api/state | jq '.questions' + browser console.

View file

@ -28,7 +28,10 @@ happens after a decision lands.
force-push, or `rm -rf` the proposed repo and the queued force-push, or `rm -rf` the proposed repo and the queued
approval still points at an immutable git object inside approval still points at an immutable git object inside
applied. applied.
4. Operator sees the diff on the dashboard, clicks ◆ APPR0VE (or 4. Operator sees the proposal as a card on the dashboard — a
full multi-file diff, toggleable between three bases (vs the
running tree / vs the last approved proposal / vs the
previous queued proposal) — and clicks ◆ APPR0VE (or
`hive-c0re approve <id>` on the CLI). `hive-c0re approve <id>` on the CLI).
5. hive-c0re moves the working tree to `proposal/<id>` and runs 5. hive-c0re moves the working tree to `proposal/<id>` and runs
the build under a sequence of tags (see below). On success, the build under a sequence of tags (see below). On success,
@ -158,6 +161,30 @@ approval id to retry. Because tags are first-class git objects,
rejected and failed trees stay browsable forever — `git log rejected and failed trees stay browsable forever — `git log
--tags` in the applied repo is the audit trail. --tags` in the applied repo is the audit trail.
### Forge mirror
When the bundled `hive-forge` container is running — on by
default, `hyperhive.forge.enable` — hive-c0re mirrors every
agent's applied repo into a private `agent-configs` Forgejo
org. `forge::push_config(<name>)` pushes `applied/main` plus
every tag to `agent-configs/<name>` after each ref mutation:
the spawn that seeds `deployed/0`, every `request_apply_commit`
(which plants `proposal/<id>`), every approve / deny, and a
sweep at startup. Pushes are best-effort — a missing or stopped
forge never blocks a deploy.
The org is private and agents are not members, so only the
`core` user (a Forgejo site admin) can read it: an agent can't
reach another agent's config — or even its own — through the
forge. The tokenised push URL is passed inline to `git push`,
never written into `applied/<n>/.git/config`; that repo is
RO-bind-mounted into the manager, and a stored token would leak
core's admin credential to an agent.
The dashboard deep-links into this org — a `config repo` link
per container row and a `commit on forge` link per approval
card. See `docs/web-ui.md`.
### Manager view of applied + meta ### Manager view of applied + meta
The manager container gets three host-side bind mounts via The manager container gets three host-side bind mounts via

View file

@ -102,11 +102,14 @@ it as a stdio child via `--mcp-config`. The hyperhive socket name is
- `send(to, body)` — message a peer (logical agent name), another - `send(to, body)` — message a peer (logical agent name), another
agent, or the operator (recipient `operator`, surfaces in the agent, or the operator (recipient `operator`, surfaces in the
dashboard inbox). dashboard inbox).
- `recv(wait_seconds?)` — drain one inbox message. Without - `recv(wait_seconds?, max?)` — drain inbox messages. Without
`wait_seconds` (or with `0`) returns immediately, a cheap `wait_seconds` (or with `0`) returns immediately, a cheap
"anything pending?" peek. Positive value parks the turn up "anything pending?" peek. Positive value parks the turn up
to that many seconds (cap 180) — incoming messages wake to that many seconds (cap 180) — incoming messages wake
instantly, otherwise returns empty at the timeout. instantly, otherwise returns empty at the timeout. `max`
(default 1, server-side cap 32) drains up to N popped rows
in one round-trip; `wait_seconds` applies to the *first*
message, then the call drains up to `max` total.
- `ask(question, options?, multi?, ttl_seconds?, to?)` - `ask(question, options?, multi?, ttl_seconds?, to?)`
surface a structured question. Same shape as the manager's; surface a structured question. Same shape as the manager's;
recipient defaults to the operator (dashboard) but can be set recipient defaults to the operator (dashboard) but can be set
@ -119,6 +122,21 @@ it as a stdio child via `--mcp-config`. The hyperhive socket name is
routed to this agent. Authorisation is strict: only the routed to this agent. Authorisation is strict: only the
declared target (or the operator via the dashboard) can declared target (or the operator via the dashboard) can
answer. answer.
- `get_loose_ends()` — list everything still pending against
this agent: unanswered questions it asked / was asked, plus
reminders it scheduled. Each row carries an id + kind for
`cancel_loose_end`.
- `cancel_loose_end(kind, id)` — withdraw a `question`
(posts `[cancelled by <self>]` to unblock the asker) or a
`reminder` (hard-delete before fire). Sub-agents may only
cancel rows they own.
- `remind(message, due)` — schedule a reminder that lands in
this agent's own inbox at a future time (sender shows as
`reminder`). Large payloads spill to
`/agents/<self>/state/reminders/` with the inbox message a
short pointer.
- `whoami()``{ name, role, pronouns, hyperhive_rev }` for
self-identification without scraping the system prompt.
### Waking the agent from inside the container ### Waking the agent from inside the container
@ -191,6 +209,16 @@ meta's.
that was routed to the manager (a sub-agent did that was routed to the manager (a sub-agent did
`ask(to: "manager", ...)`). Surfaces in the asker's inbox as `ask(to: "manager", ...)`). Surfaces in the asker's inbox as
the same `question_answered` event. the same `question_answered` event.
- `get_logs(agent, lines?)` — fetch recent journal lines for a
sub-agent container (diagnose MCP-registration failures,
startup crashes, etc.). Pass the plain logical agent name;
hive-c0re resolves the machine name (`h-<name>`, manager
`hm1nd`). `lines` defaults to 50, host-capped at 500.
- `remind` / `get_loose_ends` / `cancel_loose_end` / `whoami`
same as the sub-agent tools above, but `get_loose_ends` is
hive-wide (every agent's pending questions + reminders, not
just the manager's) and `cancel_loose_end` may cancel any
agent's row.
The boundary: lifecycle ops on *existing* sub-agents The boundary: lifecycle ops on *existing* sub-agents
(`kill`/`start`/`restart`) are at the manager's discretion — no (`kill`/`start`/`restart`) are at the manager's discretion — no

View file

@ -54,12 +54,21 @@ soon as they blur.
**`<details>` open-state preservation:** any collapsible element **`<details>` open-state preservation:** any collapsible element
tagged with `data-restore-key="<stable-key>"` survives the tagged with `data-restore-key="<stable-key>"` survives the
refresh. `snapshotOpenDetails()` walks managed sections before refresh. `snapshotOpenDetails()` walks managed sections before
render, `restoreOpenDetails()` re-applies after. Used today for render, `restoreOpenDetails()` re-applies after. Long-content
the journald viewer (`journal:<container>`), the agent-config drill-ins (file previews, diffs, journald logs) now open in the
viewer (`agent-config:<name>`), and approval diff blocks **side panel** (see below) rather than expanding inline, so the
(`approval-diff:<id>`). Setting `.open = true` programmatically only restore-keyed `<details>` left is the answered-questions
also fires the `toggle` event, so any lazy-fetch wired to it history list.
re-runs cleanly on restore.
**Side panel (dashboard):** long content opens in a drawer that
swipes in from the right — a singleton `#side-panel` with a
titled header, a close button, and a scrollable body. Closes on
the button, a backdrop click, or `Escape`. `Panel.open(title,
node)` swaps the body; the JS builders for file previews,
approval diffs, and journald logs all render into it. Markdown
file previews (`.md` / `.markdown`) render through the vendored
`marked` bundle (`GET /static/marked.js`) into a `.md` block;
other files stay raw in a `<pre>`.
Both bind their listeners with `SO_REUSEADDR` via Both bind their listeners with `SO_REUSEADDR` via
`tokio::net::TcpSocket` plus a retry loop on `AddrInUse` (12 tries, `tokio::net::TcpSocket` plus a retry loop on `AddrInUse` (12 tries,
@ -136,21 +145,50 @@ Two-line layout (`assets/app.js::renderContainers`):
on sub-agents, `↺ R3ST4RT` + (sub-agents) `■ ST0P` when running, on sub-agents, `↺ R3ST4RT` + (sub-agents) `■ ST0P` when running,
`▶ ST4RT` when stopped. Buttons dim + disable while a transient `▶ ST4RT` when stopped. Buttons dim + disable while a transient
lifecycle action is in flight. lifecycle action is in flight.
- Plus two collapsible `<details>` blocks: - Line 3: drill-in triggers —
- `↳ logs · <container>` — lazy-fetches journald output via - `↳ logs · <container>` — opens the side panel and lazy-
`GET /api/journal/{name}?unit=...&lines=...` (`journalctl -M fetches journald via `GET /api/journal/{name}?unit=&lines=`
<container> -b --no-pager --output=short-iso`). A unit (`journalctl -M <container> -b --no-pager --output=short-iso`).
dropdown switches between the harness service (default) and A unit dropdown (harness service / full machine journal) and
the full machine journal; refresh button re-fetches. a refresh button live in the panel.
- `↳ agent.nix · <name>` — lazy-fetches the applied config - `↳ config repo ↗` — link to the agent's applied config repo
file via `GET /api/agent-config/{name}` (read-only mirror of on the bundled forge (`agent-configs/<name>`), opened in a
`/var/lib/hyperhive/applied/<name>/agent.nix`). Mutating new tab. Shown only when `forge_present`. Replaces the old
this still requires `request_apply_commit` + approval. one-file `agent.nix` viewer — the forge shows the full repo
with history. Mutating config still requires
`request_apply_commit` + approval.
`↻ UPD4TE 4LL` button appears above the containers list when any `↻ UPD4TE 4LL` button appears above the containers list when any
agent is stale. Banner pulses on each broker SSE event agent is stale. Banner pulses on each broker SSE event
(`pulseBanner` with a 4s grace timer). (`pulseBanner` with a 4s grace timer).
### Approval card
Each pending approval renders as a card (`assets/app.js::
renderApprovals`) with three stacked sections:
- **identity header** — glyph, `#id`, agent, kind chip, and (for
`apply_commit`) the short proposal sha as `<code>`.
- **what-changed body** — the manager's description, then
drill-in triggers: `↳ view diff` opens the diff in the side
panel; `↳ commit on forge ↗` deep-links the proposal commit
into `agent-configs/<agent>` (shown only when `forge_present`).
Spawn approvals show a one-line "container will be created"
note instead.
- **decision actions**`◆ APPR0VE` and `DENY`. Deny pops a
`prompt()` for an optional reason carried to the manager as
`HelperEvent::ApprovalResolved.note`.
The diff panel has a 3-way base toggle — **vs applied** (the
running tree, served instantly from the diff already on the
approval), **vs last-approved**, **vs previous proposal** — the
latter two fetched on click from `GET /api/approval-diff/{id}
?base=approved|previous`. Each line is classified client-side
(`+` / `-` / `@@` / `--- ` / `+++ ` → add / del / hunk / file).
A `pending · N` / `history · N` tab pair switches the section
between the live queue and the last 30 resolved approvals.
### Browser notifications ### Browser notifications
Pure frontend (`Notification` API). Three signals trigger them: Pure frontend (`Notification` API). Three signals trigger them:
@ -198,9 +236,13 @@ not ours.
the operator inbox without a snapshot refetch. Used by the the operator inbox without a snapshot refetch. Used by the
compose textbox under MESS4GE FL0W. compose textbox under MESS4GE FL0W.
- `GET /api/journal/{name}?unit=&lines=` — journalctl viewer for - `GET /api/journal/{name}?unit=&lines=` — journalctl viewer for
a managed container. a managed container; rendered in the side panel.
- `GET /api/agent-config/{name}` — read-only view of the applied - `GET /api/approval-diff/{id}?base=applied|approved|previous`
`agent.nix`. on-demand unified diff for an `ApplyCommit` approval against
the chosen base (running tree / last approved proposal /
previous queued proposal). Raw diff text, classified
client-side. `GET /static/marked.js` serves the vendored
`marked` bundle the side panel uses for markdown previews.
- `GET /api/state-file?path=<host-or-container-path>` — bounded - `GET /api/state-file?path=<host-or-container-path>` — bounded
text read of a file under the per-agent `state/` subtree or text read of a file under the per-agent `state/` subtree or
the shared `/var/lib/hyperhive/shared/`. Accepts the the shared `/var/lib/hyperhive/shared/`. Accepts the
@ -343,8 +385,16 @@ Layout, top to bottom:
- Inbox `<details>` block (collapsed): `inbox · N` — last 30 - Inbox `<details>` block (collapsed): `inbox · N` — last 30
messages addressed to this agent, fetched via messages addressed to this agent, fetched via
`AgentRequest::Recent { limit: 30 }`. (Separate from `AgentRequest::Recent { limit: 30 }`. (Separate from
`AgentRequest::Recv { wait_seconds }` which the harness uses `AgentRequest::Recv { wait_seconds, max }` which the harness
internally to long-poll the broker.) uses internally to long-poll the broker.)
- Loose-ends `<details>` block: `loose ends · N` — questions,
approvals, and reminders pending against this agent (the
`get_loose_ends` data, via `GET /api/loose-ends`). Question
rows carry an inline answer form (textarea — Enter submits,
Shift+Enter newlines); submitting POSTs cross-origin to the
core dashboard's `/answer-question/{id}` so the operator
answers *as operator*. The per-agent socket deliberately gets
no operator-authority path — see `TODO-ops.md`.
- Terminal-wrap: live event tail (sticky-bottom auto-scroll + - Terminal-wrap: live event tail (sticky-bottom auto-scroll +
`↓ N new` pill when not at bottom) followed by an `↓ N new` pill when not at bottom) followed by an
operator-input textarea acting as a prompt. operator-input textarea acting as a prompt.
@ -454,3 +504,19 @@ Bus events (new vocabulary on `/events/stream`):
totals). totals).
- `turn_state_changed { state, since_unix }` — drives the - `turn_state_changed { state, since_unix }` — drives the
state badge (`idle`/`thinking`/`compacting`). state badge (`idle`/`thinking`/`compacting`).
### Stats page
`GET /stats` is a separate per-agent page (served by the
harness, linked from the per-agent page's `📊 stats →` and from
each dashboard container row). Turn analytics, read-only, from
`/state/hyperhive-turn-stats.sqlite`. `GET /api/stats?window=
24h|7d|30d` returns a time-bucketed `Snapshot`; the page renders
it with Chart.js (vendored from a CDN). Charts: turns,
duration (p50 · p95 · avg), context tokens, token cost per
bucket, a **turns-by-model** stacked bar (model choice drives
token cost, so it sits directly under the cost chart), and
doughnuts for tool / wake-source / result mix. A summary chip
row carries window totals. `stats.rs` opens the sqlite db
read-only and degrades to an empty snapshot on any error — the
page is decorative, never authoritative.

View file

@ -2,13 +2,14 @@ You are hyperhive agent `{label}` in a multi-agent system. The operator (recipie
Tools (hyperhive surface): Tools (hyperhive surface):
- `mcp__hyperhive__recv(wait_seconds?)` — drain one more message from your inbox (returns `(empty)` if nothing pending). Without `wait_seconds` (or with `0`) it returns immediately — a cheap "anything pending?" peek you can sprinkle between tool calls. To **wait** for work when you have nothing else useful to do this turn, call with a long wait (e.g. `wait_seconds: 180`, the max) — incoming messages wake you instantly, otherwise the call returns empty at the timeout. That's strictly better than a fixed `sleep` shell command: lower latency on new work, no busy-loop. - `mcp__hyperhive__recv(wait_seconds?, max?)` — drain inbox messages (returns `(empty)` if nothing pending). Without `wait_seconds` (or with `0`) it returns immediately — a cheap "anything pending?" peek you can sprinkle between tool calls. To **wait** for work when you have nothing else useful to do this turn, call with a long wait (e.g. `wait_seconds: 180`, the max) — incoming messages wake you instantly, otherwise the call returns empty at the timeout. That's strictly better than a fixed `sleep` shell command: lower latency on new work, no busy-loop. `max` (default 1, cap 32) drains several queued messages in one call — the wake prompt tells you the pending count.
- `mcp__hyperhive__send(to, body)` — message a peer (by their name) or the operator (recipient `operator`, surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action). Some agents have a per-agent allow-list (`hyperhive.allowedRecipients` in their `agent.nix`) — if so the tool refuses recipients outside the list with a clear error; route through the manager (`send(to: "manager", …)`) which is always reachable. - `mcp__hyperhive__send(to, body)` — message a peer (by their name) or the operator (recipient `operator`, surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action). Some agents have a per-agent allow-list (`hyperhive.allowedRecipients` in their `agent.nix`) — if so the tool refuses recipients outside the list with a clear error; route through the manager (`send(to: "manager", …)`) which is always reachable.
- (some agents only) **extra MCP tools** surfaced as `mcp__<server>__<tool>` — these are agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. Treat them as first-class tools alongside the hyperhive surface; the operator already auto-approved them at deploy time. - (some agents only) **extra MCP tools** surfaced as `mcp__<server>__<tool>` — these are agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. Treat them as first-class tools alongside the hyperhive surface; the operator already auto-approved them at deploy time.
- `mcp__hyperhive__ask(question, options?, multi?, ttl_seconds?, to?)` — surface a structured question to the human operator (default, or `to: "operator"`) OR a peer agent (`to: "<agent-name>"`). Returns immediately with a question id — do NOT wait inline. When the recipient answers, a system message with event `question_answered { id, question, answer, answerer }` lands in your inbox; handle it on a future turn. Use this for clarifications, permission for risky actions, choice between options, or peer Q&A without burning regular inbox slots. `options` is advisory: a short fixed-choice list when applicable, otherwise leave empty for free text. `multi: true` lets the answerer pick multiple (checkboxes), answer comes back comma-joined. `ttl_seconds` auto-cancels with answer `[expired]` (and `answerer: "ttl-watchdog"`) when the decision becomes moot. - `mcp__hyperhive__ask(question, options?, multi?, ttl_seconds?, to?)` — surface a structured question to the human operator (default, or `to: "operator"`) OR a peer agent (`to: "<agent-name>"`). Returns immediately with a question id — do NOT wait inline. When the recipient answers, a system message with event `question_answered { id, question, answer, answerer }` lands in your inbox; handle it on a future turn. Use this for clarifications, permission for risky actions, choice between options, or peer Q&A without burning regular inbox slots. `options` is advisory: a short fixed-choice list when applicable, otherwise leave empty for free text. `multi: true` lets the answerer pick multiple (checkboxes), answer comes back comma-joined. `ttl_seconds` auto-cancels with answer `[expired]` (and `answerer: "ttl-watchdog"`) when the decision becomes moot.
- `mcp__hyperhive__answer(id, answer)` — answer a question that was routed to YOU. You'll see one in your inbox as a `question_asked { id, asker, question, options, multi }` system event when a peer or the manager calls `ask(to: "<your-name>", ...)`. The answer surfaces in the asker's inbox as a `question_answered` event. Strict authorisation: you can only answer questions where you are the declared target. - `mcp__hyperhive__answer(id, answer)` — answer a question that was routed to YOU. You'll see one in your inbox as a `question_asked { id, asker, question, options, multi }` system event when a peer or the manager calls `ask(to: "<your-name>", ...)`. The answer surfaces in the asker's inbox as a `question_answered` event. Strict authorisation: you can only answer questions where you are the declared target.
- `mcp__hyperhive__get_loose_ends()` — list your loose ends: unanswered questions where you're asker (waiting on someone) or target (owing a reply), plus reminders you've scheduled that haven't fired. No args, cheap server-side sweep. Useful at turn start to remember what's outstanding without scanning inbox archaeology. - `mcp__hyperhive__get_loose_ends()` — list your loose ends: unanswered questions where you're asker (waiting on someone) or target (owing a reply), plus reminders you've scheduled that haven't fired. No args, cheap server-side sweep. Useful at turn start to remember what's outstanding without scanning inbox archaeology.
- `mcp__hyperhive__cancel_loose_end(kind, id)` — cancel one of your own open threads. `kind` is `"question"` (the asker — you, in this case — gets a `[cancelled by <you>]` answer so the waiter unblocks) or `"reminder"` (hard-deleted before it fires). `id` from the matching `get_loose_ends` row or the original submission reply. - `mcp__hyperhive__cancel_loose_end(kind, id)` — cancel one of your own open threads. `kind` is `"question"` (the asker — you, in this case — gets a `[cancelled by <you>]` answer so the waiter unblocks) or `"reminder"` (hard-deleted before it fires). `id` from the matching `get_loose_ends` row or the original submission reply.
- `mcp__hyperhive__remind(message, delay_seconds? | at_unix_timestamp?, file_path?)` — schedule a message to land in your *own* inbox at a future time (sender shows as `reminder`). Set exactly one of `delay_seconds` (relative) or `at_unix_timestamp` (absolute). Use for self-paced follow-ups instead of blocking a whole turn on a long `recv` wait. A large `message` auto-spills to a file under `/agents/{label}/state/reminders/`; pass `file_path` to point at one yourself.
- `mcp__hyperhive__whoami()` — self-introspection: returns your canonical agent name (from socket identity, not the prompt-substituted label), role, and current hyperhive rev. No args. Use it when you want a trustworthy identity stamp for state files, commit messages, or cross-agent attribution that won't drift across renames. - `mcp__hyperhive__whoami()` — self-introspection: returns your canonical agent name (from socket identity, not the prompt-substituted label), role, and current hyperhive rev. No args. Use it when you want a trustworthy identity stamp for state files, commit messages, or cross-agent attribution that won't drift across renames.
Need new packages, env vars, or other NixOS config for yourself? You can't edit your own config directly — message the manager (recipient `manager`) describing what you need + why. The manager evaluates the request (it doesn't rubber-stamp), edits `/agents/{label}/config/agent.nix` on your behalf, commits, and submits an approval that the operator can accept on the dashboard; on approve hive-c0re rebuilds your container with the new config. Need new packages, env vars, or other NixOS config for yourself? You can't edit your own config directly — message the manager (recipient `manager`) describing what you need + why. The manager evaluates the request (it doesn't rubber-stamp), edits `/agents/{label}/config/agent.nix` on your behalf, commits, and submits an approval that the operator can accept on the dashboard; on approve hive-c0re rebuilds your container with the new config.

View file

@ -2,18 +2,20 @@ You are the hyperhive manager `{label}` in a multi-agent system. You coordinate
Tools (hyperhive surface): Tools (hyperhive surface):
- `mcp__hyperhive__recv(wait_seconds?)` — drain one more message from your inbox. Without `wait_seconds` (or with `0`) it returns immediately — a cheap inbox peek you can drop between actions. To **wait** when you have nothing else to do, call with a long wait (e.g. `wait_seconds: 180`, the max) — you'll wake instantly on new work, otherwise return after the timeout. Use that instead of ending the turn or sleeping in a Bash command. - `mcp__hyperhive__recv(wait_seconds?, max?)` — drain inbox messages. Without `wait_seconds` (or with `0`) it returns immediately — a cheap inbox peek you can drop between actions. To **wait** when you have nothing else to do, call with a long wait (e.g. `wait_seconds: 180`, the max) — you'll wake instantly on new work, otherwise return after the timeout. Use that instead of ending the turn or sleeping in a Bash command. `max` (default 1, cap 32) drains several queued messages in one call.
- `mcp__hyperhive__send(to, body)` — message an agent (by name), another peer, or the operator (`operator` surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action). - `mcp__hyperhive__send(to, body)` — message an agent (by name), another peer, or the operator (`operator` surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action).
- `mcp__hyperhive__request_spawn(name, description?)` — queue a brand-new sub-agent for operator approval (≤9 char name). Pass an optional `description` and it appears on the dashboard approval card — no need to send a separate message explaining the request. - `mcp__hyperhive__request_spawn(name, description?)` — queue a brand-new sub-agent for operator approval (≤9 char name). Pass an optional `description` and it appears on the dashboard approval card — no need to send a separate message explaining the request.
- `mcp__hyperhive__kill(name)` — graceful stop on a sub-agent. No approval required. - `mcp__hyperhive__kill(name)` — graceful stop on a sub-agent. No approval required.
- `mcp__hyperhive__start(name)` — start a stopped sub-agent. No approval required. - `mcp__hyperhive__start(name)` — start a stopped sub-agent. No approval required.
- `mcp__hyperhive__restart(name)` — stop + start a sub-agent. No approval required. - `mcp__hyperhive__restart(name)` — stop + start a sub-agent. No approval required.
- `mcp__hyperhive__update(name)` — rebuild a sub-agent (re-applies the current hyperhive flake + agent.nix, restarts the container). No approval required — idempotent. Use when you receive a `needs_update` system event. - `mcp__hyperhive__update(name)` — rebuild a sub-agent (re-applies the current hyperhive flake + agent.nix, restarts the container). No approval required — idempotent. Use when you receive a `needs_update` system event.
- `mcp__hyperhive__get_logs(agent, lines?)` — fetch recent journal lines for a sub-agent container. Use to diagnose MCP-server registration failures, startup crashes, or harness issues you can't see from inside. Pass the plain logical agent name; `lines` defaults to 50 (capped at 500).
- `mcp__hyperhive__request_apply_commit(agent, commit_ref, description?)` — submit a config change for any agent (`hm1nd` for self) for operator approval. Pass an optional `description` and it appears on the dashboard approval card so the operator knows what changed without opening the diff. At submit time hive-c0re fetches your commit into the agent's applied repo and pins it as `proposal/<id>`; from that moment your proposed-side commit can be amended or force-pushed freely without changing what the operator will build. - `mcp__hyperhive__request_apply_commit(agent, commit_ref, description?)` — submit a config change for any agent (`hm1nd` for self) for operator approval. Pass an optional `description` and it appears on the dashboard approval card so the operator knows what changed without opening the diff. At submit time hive-c0re fetches your commit into the agent's applied repo and pins it as `proposal/<id>`; from that moment your proposed-side commit can be amended or force-pushed freely without changing what the operator will build.
- `mcp__hyperhive__ask(question, options?, multi?, ttl_seconds?, to?)` — surface a structured question to the operator (default, or `to: "operator"`) OR a sub-agent (`to: "<agent-name>"`). Returns immediately with a question id; the answer arrives later as a system `question_answered { id, question, answer, answerer }` event in your inbox. Options are advisory: the dashboard always lets the operator type a free-text answer in addition. Set `multi: true` to render options as checkboxes (operator can pick multiple); the answer comes back as `, `-separated. Set `ttl_seconds` to auto-cancel after a deadline (capped at 6h server-side) — on expiry the answer is `[expired]` and `answerer` is `"ttl-watchdog"`. Do not poll inside the same turn — finish the current work and react when the event lands. - `mcp__hyperhive__ask(question, options?, multi?, ttl_seconds?, to?)` — surface a structured question to the operator (default, or `to: "operator"`) OR a sub-agent (`to: "<agent-name>"`). Returns immediately with a question id; the answer arrives later as a system `question_answered { id, question, answer, answerer }` event in your inbox. Options are advisory: the dashboard always lets the operator type a free-text answer in addition. Set `multi: true` to render options as checkboxes (operator can pick multiple); the answer comes back as `, `-separated. Set `ttl_seconds` to auto-cancel after a deadline (capped at 6h server-side) — on expiry the answer is `[expired]` and `answerer` is `"ttl-watchdog"`. Do not poll inside the same turn — finish the current work and react when the event lands.
- `mcp__hyperhive__answer(id, answer)` — answer a question that was routed to YOU (a sub-agent did `ask(to: "manager", ...)`). The triggering event in your inbox is `question_asked { id, asker, question, options, multi }`. The answer surfaces in the asker's inbox as a `question_answered` event. - `mcp__hyperhive__answer(id, answer)` — answer a question that was routed to YOU (a sub-agent did `ask(to: "manager", ...)`). The triggering event in your inbox is `question_asked { id, asker, question, options, multi }`. The answer surfaces in the asker's inbox as a `question_answered` event.
- `mcp__hyperhive__get_loose_ends()` — hive-wide loose ends: every pending approval + every unanswered question + every pending reminder across the swarm. Cheap server-side sweep, no args. Use to find stalled threads (sub-agent A asked B something three days ago and B never answered) before they rot. - `mcp__hyperhive__get_loose_ends()` — hive-wide loose ends: every pending approval + every unanswered question + every pending reminder across the swarm. Cheap server-side sweep, no args. Use to find stalled threads (sub-agent A asked B something three days ago and B never answered) before they rot.
- `mcp__hyperhive__cancel_loose_end(kind, id)` — cancel any question or reminder in the swarm (manager bypasses the owner check used on sub-agents). Use for hive-wide cleanup when a sub-agent is offline / can't withdraw its own ask / reminder. - `mcp__hyperhive__cancel_loose_end(kind, id)` — cancel any question or reminder in the swarm (manager bypasses the owner check used on sub-agents). Use for hive-wide cleanup when a sub-agent is offline / can't withdraw its own ask / reminder.
- `mcp__hyperhive__remind(message, delay_seconds? | at_unix_timestamp?, file_path?)` — schedule a message to land in your own inbox at a future time (sender shows as `reminder`). Set exactly one of `delay_seconds` (relative) or `at_unix_timestamp` (absolute). Good for deadline follow-ups — "check whether agent X answered the question I relayed". Large payloads auto-spill to a file under `/state/reminders/`; pass `file_path` to control the destination.
- `mcp__hyperhive__whoami()` — self-introspection: canonical name (`manager`), role, current hyperhive rev. No args. Useful for boot announcements and cross-agent attribution that won't drift across config reloads. - `mcp__hyperhive__whoami()` — self-introspection: canonical name (`manager`), role, current hyperhive rev. No args. Useful for boot announcements and cross-agent attribution that won't drift across config reloads.
Approval boundary: lifecycle ops on *existing* sub-agents (`kill`, `start`, `restart`) are at your discretion — no operator approval. *Creating* a new agent (`request_spawn`) and *changing* any agent's config (`request_apply_commit`) still go through the approval queue. The operator only signs off on changes; you run the day-to-day. Approval boundary: lifecycle ops on *existing* sub-agents (`kill`, `start`, `restart`) are at your discretion — no operator approval. *Creating* a new agent (`request_spawn`) and *changing* any agent's config (`request_apply_commit`) still go through the approval queue. The operator only signs off on changes; you run the day-to-day.

View file

@ -137,6 +137,13 @@ in
DEFAULT_BRANCH = "main"; DEFAULT_BRANCH = "main";
DEFAULT_PRIVATE = "private"; DEFAULT_PRIVATE = "private";
}; };
# Repo migrations / pull-mirrors fetch from the source
# URL *inside* Forgejo. hyperhive code is synced from
# `localhost` (and the host LAN), which Forgejo's
# migration guard blocks by default ("cannot import from
# disallowed hosts"). Allow loopback + RFC-1918 sources
# so an in-hive mirror of the hyperhive repo works.
migrations.ALLOW_LOCALNETWORKS = true;
log.LEVEL = "Warn"; log.LEVEL = "Warn";
# F3 (federation) computes its data dir relative to the # F3 (federation) computes its data dir relative to the
# forgejo binary, which lands in the read-only nix # forgejo binary, which lands in the read-only nix