docs: sync to current state of the world

claude.md scratchpad rewritten — folds in pronouns option,
extra MCP servers + flakeInputs forwarding, ask_operator
on sub-agents, dashboard compose box with @-mentions, new-
session button, cwd=/state for claude turns, meta-mutex +
stale-lock cleanup.

readme picks up the operator pronouns option example,
the dashboard compose box description, the new slash
commands list, the deployed-sha chip, the per-agent UI
gains new-session.

docs/web-ui.md gains:
- a fuller MESS4GE FL0W description that calls out the
  compose box, sticky @-mention recipient, /op-send, and
  the manager-name swap
- /op-send in the dashboard endpoint table
- new-session button + /new-session slash command in the
  per-agent surface
- compact endpoint now notes 'same session shape as a normal
  turn'

docs/turn-loop.md:
- new-session one-shot, cwd=/state with CLAUDE.md auto-load
  walking upward, operator-pronouns substitution
- sub-agent tool list grows ask_operator
- new 'Extra MCP servers (per-agent)' section documenting
  hyperhive.extraMcpServers + the flakeInputs forwarding
  pattern
This commit is contained in:
müde 2026-05-16 02:49:48 +02:00
parent 2d277038a7
commit 1278f880da
4 changed files with 137 additions and 37 deletions

View file

@ -123,31 +123,53 @@ In-flight or recent context that hasn't earned a section yet.
Prune freely. Prune freely.
- **Just landed:** meta-flake overhaul. Each agent's applied - **Just landed:** meta-flake overhaul. Each agent's applied
repo is a tiny module-only flake (`nixosModules.default = repo is a module-only flake (forwards every `inputs.*`
import ./agent.nix`); `agent.nix` is a plain NixOS module through to `agent.nix` as the `flakeInputs` module arg —
function — no extendModules, no hyperhive input visible to manager edits `inputs` to pull in external flakes like an
the manager. A single hive-c0re-owned repo at MCP server's own flake; the new sha lands in the agent's
`/var/lib/hyperhive/meta/` declares one input per agent own `flake.lock` and rolls up to meta's). A single
(pointing at that agent's applied repo via `git+file://`) hive-c0re-owned repo at `/var/lib/hyperhive/meta/`
and one `nixosConfigurations.<n>` output per agent, declares one input per agent and one
wrapping `inputs.agent-<n>.nixosModules.default` with the `nixosConfigurations.<n>` output, wrapping the agent's
identity + `HIVE_PORT` / `HIVE_LABEL` / `nixosModules.default` with identity + `HIVE_PORT` /
`HIVE_DASHBOARD_PORT` injection. Containers run against `HIVE_LABEL` / `HIVE_DASHBOARD_PORT` /
`meta#<n>`. Every approve runs `nix flake lock `HIVE_OPERATOR_PRONOUNS`. Containers run against
--update-input agent-<n>` (two-phase: prepare on the `meta#<n>`. Every approve uses two-phase staging
build path, finalize/abort on the result) — meta's git (prepare → build → finalize/abort) so meta's git log only
log is the system-wide deploy audit trail; failures and records successful deploys; failures + denials live as
denials live as annotated tags in applied. The manager annotated tags in applied. All meta operations
has `/applied` and `/meta` RO-bound and the `applied` serialize behind a tokio mutex; stale `.git/index.lock`
remote pre-wired in every proposed repo so `git fetch is cleared on hive-c0re startup. Manager has `/applied`
applied`, `git show applied/refs/tags/deployed/<id>`, + `/meta` RO-bound + the `applied` remote pre-wired in
`git -C /meta log --oneline`, `cat /meta/flake.lock` every proposed repo. Migration runs idempotently on
all just work. Migration runs idempotently on startup (`HIVE_SKIP_META_MIGRATION=1` skips). Operator
hive-c0re startup (`HIVE_SKIP_META_MIGRATION=1` skips it): pronouns are a NixOS module option
rewrites pre-meta applied flakes to module-only, wires (`services.hive-c0re.operatorPronouns`, default
the proposed remote, seeds meta, and repoints every `"she/her"`); the harness substitutes them into the
container at `meta#<n>` (guarded by a marker so the system prompt at boot.
expensive phase only runs once). - **Just landed:** per-agent extra MCP servers via the
`hyperhive.extraMcpServers.<key>` NixOS option in
`agent.nix`. Declares `{ command, args, env,
allowedTools }`; the module writes the whole map to
`/etc/hyperhive/extra-mcp.json`; the harness reads that
file and merges each entry into both `--mcp-config`
and `--allowedTools` (mapped to `mcp__<key>__<pattern>`).
Unblocks matrix / bitburner / any agent with rich
domain tooling — the agent flake's `inputs` block pulls
the external flake, `agent.nix` references it via
`flakeInputs.<name>.packages.${pkgs.system}.default`.
- **Just landed:** `mcp__hyperhive__ask_operator` is now on
the sub-agent surface too (not just the manager). Answer
routes back to whichever agent asked via
`coord.notify_agent`; the dashboard already shows the
asker on each question row.
- **Just landed:** dashboard now has a terminal-style
compose textbox under the message-flow stream — `@name`
picks the recipient (sticky in localStorage, auto-
completed from `containers[]`), POSTs `/op-send`. New
per-agent `↻ new session` button drops `--continue` for
one turn. Claude spawns with `cwd = /state` so relative
paths in tool calls land in the durable dir.
- **Just landed (prior overhaul still underneath):** tag- - **Just landed (prior overhaul still underneath):** tag-
driven config-apply. Two-repo split (proposed = manager driven config-apply. Two-repo split (proposed = manager
RW, applied = core-only); `request_apply_commit` fetches RW, applied = core-only); `request_apply_commit` fetches

View file

@ -36,7 +36,9 @@ host (NixOS, runs hive-c0re.service)
│ ask_operator) + web UI on :8000 │ ask_operator) + web UI on :8000
└── h-<name> hive-ag3nt serve : claude turn loop + └── h-<name> hive-ag3nt serve : claude turn loop +
MCP (send / recv) + web UI on a hashed :8100-8999 MCP (send / recv / ask_operator + agent-declared extras
via hyperhive.extraMcpServers) + web UI on a
hashed :8100-8999
``` ```
Each turn: harness pops one inbox message (Recv long-polls server-side and Each turn: harness pops one inbox message (Recv long-polls server-side and
@ -47,12 +49,20 @@ claude drives any further `recv`/`send` itself via the embedded MCP server.
Operator surface per agent: terminal-themed live tail with a textarea Operator surface per agent: terminal-themed live tail with a textarea
prompt; slash commands `/help` `/clear` `/cancel` `/compact` prompt; slash commands `/help` `/clear` `/cancel` `/compact`
`/model <name>`; granular state badge (idle / thinking / `/model <name>` `/new-session`; granular state badge (idle / thinking
compacting / offline) with age timer + last-turn duration chip + / compacting / offline) with age timer + last-turn duration chip +
model chip; cancel-turn button while thinking; sticky-bottom model chip; cancel-turn + new-session buttons in the state row;
auto-scroll with "↓ N new" pill; event history backfilled on page sticky-bottom auto-scroll with "↓ N new" pill; event history
load; collapsible inbox + collapsible journald viewer + collapsible backfilled on page load; collapsible inbox + collapsible journald
`agent.nix` viewer per agent on the dashboard. viewer + collapsible `agent.nix` viewer per agent on the dashboard;
deployed-sha chip per container (read from meta's `flake.lock`).
Operator surface on the dashboard itself: a terminal compose box
under the message-flow stream — `@name` picks the recipient with
auto-complete from the live container list, sticky across sends,
POSTs `/op-send` which drops the message into the broker as
`{from:"operator", to:<name>, body}`. Same shape any sub-agent
sees as a regular inbox message.
Config changes flow the other way: manager edits files under Config changes flow the other way: manager edits files under
`/agents/<name>/config/``agent.nix` is a plain NixOS module function `/agents/<name>/config/``agent.nix` is a plain NixOS module function
@ -102,6 +112,11 @@ Minimal `flake.nix` for a host that runs hive-c0re:
hyperhive.nixosModules.hive-c0re hyperhive.nixosModules.hive-c0re
({ ... }: { ({ ... }: {
services.hive-c0re.enable = true; services.hive-c0re.enable = true;
# Free-text operator pronouns — defaults to "she/her", threaded
# through to every agent's system prompt as HIVE_OPERATOR_PRONOUNS
# so claude refers to you naturally in third person.
# services.hive-c0re.operatorPronouns = "they/them";
# ... rest of your host config (hardware, networking, users, …) # ... rest of your host config (hardware, networking, users, …)
system.stateVersion = "25.11"; system.stateVersion = "25.11";
}) })

View file

@ -45,6 +45,18 @@ sessions in `~/.claude/projects/`, which is bind-mounted
persistently). Auto-compact and auto-memory are disabled via persistently). Auto-compact and auto-memory are disabled via
`--settings` because hyperhive owns compaction (`/compact` on `--settings` because hyperhive owns compaction (`/compact` on
overflow, retry once; operator can also force one via `/api/compact`). overflow, retry once; operator can also force one via `/api/compact`).
A one-shot `--continue` suppression is available via
`POST /api/new-session` (or `/new-session` slash command in the
per-agent terminal) — `Bus::take_skip_continue()` flips an
`AtomicBool` once per turn, the next claude invocation drops
`--continue`, every subsequent turn resumes normal behaviour.
The child runs with `cwd = /state` (when the bind exists; falls
back to the parent's cwd in dev), so any relative path in a tool
call (`Read foo.md`, `Bash ls`, `Write notes.md`) lands in the
agent's durable bind-mounted dir. CLAUDE.md auto-load walks
upward from `/state` — drop a per-agent CLAUDE.md there if you
want long-term hints that survive destroy/recreate.
The wake prompt is intentionally minimal: just the popped message's The wake prompt is intentionally minimal: just the popped message's
`from`/`body`, plus an inline `({unread} more pending — drain via `from`/`body`, plus an inline `({unread} more pending — drain via
@ -68,8 +80,11 @@ socket at `/run/hive/` once at startup:
- `claude-settings.json` — the `--settings` blob (auto-compact and - `claude-settings.json` — the `--settings` blob (auto-compact and
auto-memory off, effortLevel medium). auto-memory off, effortLevel medium).
- `claude-system-prompt.md` — rendered from - `claude-system-prompt.md` — rendered from
`hive-ag3nt/prompts/{agent,manager}.md` with `{label}` `hive-ag3nt/prompts/{agent,manager}.md` with `{label}` and
substituted. Passed via `--system-prompt-file`. `{operator_pronouns}` substituted. Pronouns come from
`HIVE_OPERATOR_PRONOUNS` env (set by the meta flake from
`services.hive-c0re.operatorPronouns`, default `she/her`).
Passed via `--system-prompt-file`.
The shared per-turn plumbing lives in `hive_ag3nt::turn::{write_mcp_config, The shared per-turn plumbing lives in `hive_ag3nt::turn::{write_mcp_config,
write_settings, write_system_prompt, run_turn, drive_turn, write_settings, write_system_prompt, run_turn, drive_turn,
@ -92,6 +107,25 @@ it as a stdio child via `--mcp-config`. The hyperhive socket name is
omitted). Agents use a long wait to park their turn waiting for omitted). Agents use a long wait to park their turn waiting for
work instead of busy-looping with short polls — they wake work instead of busy-looping with short polls — they wake
instantly when a message arrives. instantly when a message arrives.
- `ask_operator(question, options?, multi?, ttl_seconds?)`
surface a question on the dashboard. Same shape as the manager's;
answer routes back to the asker's own inbox as
`HelperEvent::OperatorAnswered` via `coord.notify_agent`.
### Extra MCP servers (per-agent)
Each agent's NixOS config can declare additional MCP servers via
`hyperhive.extraMcpServers.<key> = { command, args, env,
allowedTools }`. The module writes the map to
`/etc/hyperhive/extra-mcp.json`; the harness reads it at boot and
merges every entry into `--mcp-config` (under `mcpServers.<key>`)
and `--allowedTools` (as `mcp__<key>__<pattern>`). The agent's
flake.nix forwards every flake input to `agent.nix` as the
`flakeInputs` module arg, so external MCP-server flakes are pulled
in by adding them to `inputs.*` and referenced as
`flakeInputs.<name>.packages.${pkgs.system}.default` — the
resolved sha lands in the agent's own `flake.lock` and rolls up to
meta's.
### Manager tools (in addition to send/recv) ### Manager tools (in addition to send/recv)

View file

@ -74,7 +74,21 @@ the previous process's socket release resolves itself.
6. **P3NDING APPR0VALS** — the queue. The R3QU3ST SP4WN form 6. **P3NDING APPR0VALS** — the queue. The R3QU3ST SP4WN form
lives at the top of this section since submitting it lives at the top of this section since submitting it
immediately queues an approval that lands directly below. immediately queues an approval that lands directly below.
7. **MESS4GE FL0W** — live broker SSE tail. 7. **MESS4GE FL0W** — live broker SSE tail (newest-first).
Each row is one broker event — `sent` or `delivered` — with
`from → to: body`; per-agent thinking / tool calls / claude
chatter stay out of this view, only what passes through
hive-c0re's broker. Below the stream sits a terminal-style
compose box: `@name` picks the recipient (sticky across
sends via localStorage; auto-complete from the live
container list, Tab/Enter to confirm), starting a message
with `@<name> body` retargets in one stroke, plain text
sends to the sticky recipient. `POST /op-send` drops
`{from:"operator", to, body}` into the broker — same shape
any sub-agent sees as a regular inbox message. Manager is
addressed as `@manager` (the broker recipient string), not
`@hm1nd` (the container name); the auto-complete swaps
automatically.
### Container row ### Container row
@ -140,6 +154,9 @@ not ours.
the sentinel `[cancelled]`. Same code path as a real answer. the sentinel `[cancelled]`. Same code path as a real answer.
- `POST /request-spawn` — queue a Spawn approval. - `POST /request-spawn` — queue a Spawn approval.
- `POST /update-all` — rebuild every stale container. - `POST /update-all` — rebuild every stale container.
- `POST /op-send` (`to=<name>`, `body=<text>`) — drop an
operator-authored message into `<name>`'s inbox. Used by the
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.
- `GET /api/agent-config/{name}` — read-only view of the applied - `GET /api/agent-config/{name}` — read-only view of the applied
@ -158,7 +175,7 @@ Layout, top to bottom:
- Title with `↑ DASHB04RD` back-link (new tab) + `↻ R3BU1LD`. - Title with `↑ DASHB04RD` back-link (new tab) + `↻ R3BU1LD`.
- Status section (online / needs login / login-in-progress). - Status section (online / needs login / login-in-progress).
- **State row**: state badge + model chip + last-turn timing + - **State row**: state badge + model chip + last-turn timing +
cancel-turn button. cancel-turn button + new-session button.
- State badge: `💤 idle` / `🧠 thinking` / `📦 compacting` / - State badge: `💤 idle` / `🧠 thinking` / `📦 compacting` /
`○ offline` / `… booting`, with an age suffix (`12s`, `○ offline` / `… booting`, with an age suffix (`12s`,
`2m 14s`). Driven from `/api/state.turn_state` + `2m 14s`). Driven from `/api/state.turn_state` +
@ -170,6 +187,11 @@ Layout, top to bottom:
turn ends, computed from the state-since deltas. turn ends, computed from the state-since deltas.
- `■ cancel turn` button: visible only while state=thinking, - `■ cancel turn` button: visible only while state=thinking,
POSTs `/api/cancel`. POSTs `/api/cancel`.
- `↻ new session` button: always visible, amber. Confirms
via `window.confirm()` then POSTs `/api/new-session` to
arm a one-shot Bus flag — the next turn drops
`--continue`, starting a fresh claude session. Subsequent
turns resume normal `--continue`.
- 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
@ -238,6 +260,9 @@ Slash commands today:
Takes effect on the next turn; persisted to Takes effect on the next turn; persisted to
`/state/hyperhive-model` so the override survives harness `/state/hyperhive-model` so the override survives harness
restart / rebuild. restart / rebuild.
- `/new-session``POST /api/new-session` (confirms first).
Arms a one-shot on the Bus; next turn runs without
`--continue`, dropping the resume session entirely.
Unknown `/foo` shows an error row instead of being silently sent. Unknown `/foo` shows an error row instead of being silently sent.
@ -246,7 +271,11 @@ Unknown `/foo` shows an error row instead of being silently sent.
- `POST /send` — operator-injected message into this agent's inbox. - `POST /send` — operator-injected message into this agent's inbox.
- `POST /login/{start,code,cancel}` — claude OAuth login flow. - `POST /login/{start,code,cancel}` — claude OAuth login flow.
- `POST /api/cancel` — SIGINT the in-flight claude turn. - `POST /api/cancel` — SIGINT the in-flight claude turn.
- `POST /api/compact` — run `/compact` on the persistent session. - `POST /api/compact` — run `/compact` on the persistent session
(same MCP config + system prompt + allowed tools as a normal
turn — only the stdin payload differs).
- `POST /api/model` (`model=<name>`) — switch the model for - `POST /api/model` (`model=<name>`) — switch the model for
future turns. future turns.
- `POST /api/new-session` — arm a one-shot for the next turn to
drop `--continue`.
- `GET /events/history` — replay buffer for the terminal. - `GET /events/history` — replay buffer for the terminal.