readme: voice pass — opener hook, why-this-exists framing, section breaks
This commit is contained in:
parent
c423ce9e39
commit
61f296fc83
1 changed files with 76 additions and 36 deletions
112
README.md
112
README.md
|
|
@ -1,11 +1,27 @@
|
||||||
# hyperhive
|
# hyperhive
|
||||||
|
|
||||||
Multi-Claude-Code-agent orchestration on **nixos-containers**.
|
> a swarm of claude-code agents, each in its own nspawn cage, gossiping
|
||||||
|
> over unix sockets. config changes flow as git commits, the operator
|
||||||
|
> approves them in a browser, every deploy is a tag. cyberpunk-themed
|
||||||
|
> dashboard included. 💜⚡
|
||||||
|
|
||||||
A host-side Rust daemon (`hive-c0re`) spawns nspawn-isolated agent
|
A host-side Rust daemon (`hive-c0re`) spawns nspawn-isolated agent
|
||||||
containers and brokers messages between them. A manager agent (`hm1nd`)
|
containers, runs each one's claude turn loop, and brokers messages
|
||||||
coordinates the swarm and gates lifecycle changes on user approval via git
|
between them. A privileged manager agent (`hm1nd`) drives the swarm —
|
||||||
commits, surfaced through a vibec0re-styled HTTP dashboard.
|
proposing new agents, editing their NixOS modules, escalating
|
||||||
|
ambiguous decisions to the operator. Every lifecycle change (spawn,
|
||||||
|
config edit, destroy) is gated on a human ◆ APPR0VE click in the
|
||||||
|
dashboard. Every approved change lands as a fast-forward on a git
|
||||||
|
tag, so the deploy history is just `git log`.
|
||||||
|
|
||||||
|
**Why this exists:** claude code is great in one window. claude code
|
||||||
|
is *exponentielle* across many — but only if you can keep the agents
|
||||||
|
from stepping on each other, give them durable identity across
|
||||||
|
restarts, and stop them from eating production. hyperhive is the
|
||||||
|
substrate: identity = unix socket, communication = sqlite-backed
|
||||||
|
broker, config = git, deploys = tagged commits, blast radius =
|
||||||
|
container. The operator stays in the loop without becoming the
|
||||||
|
bottleneck.
|
||||||
|
|
||||||
```
|
```
|
||||||
host (NixOS, runs hive-c0re.service)
|
host (NixOS, runs hive-c0re.service)
|
||||||
|
|
@ -41,31 +57,49 @@ host (NixOS, runs hive-c0re.service)
|
||||||
on a hashed :8100-8999
|
on a hashed :8100-8999
|
||||||
```
|
```
|
||||||
|
|
||||||
Each turn: harness pops one inbox message (Recv long-polls server-side and
|
## The turn loop
|
||||||
wakes on a broker Sent event) → builds a wake prompt → spawns
|
|
||||||
`claude --print --continue --output-format stream-json --mcp-config …` →
|
|
||||||
streams JSON events into the per-agent SSE bus + a sqlite history db →
|
|
||||||
claude drives any further `recv`/`send` itself via the embedded MCP server.
|
|
||||||
|
|
||||||
Operator surface per agent: terminal-themed live tail with a textarea
|
One message in, one turn out. The harness pops a single inbox message
|
||||||
prompt; slash commands `/help` `/clear` `/cancel` `/compact`
|
(`Recv` long-polls server-side, wakes the instant a broker `Sent`
|
||||||
`/model <name>` `/new-session`; granular state badge (idle / thinking
|
event fires) → builds a wake prompt → spawns
|
||||||
/ compacting / offline) with age timer + last-turn duration chip +
|
`claude --print --continue --output-format stream-json --mcp-config …`
|
||||||
model chip; cancel-turn + new-session buttons in the state row;
|
→ streams JSON events into the per-agent SSE bus + a sqlite history db
|
||||||
sticky-bottom auto-scroll with "↓ N new" pill; event history
|
→ claude drives any further `recv`/`send` itself via the embedded MCP
|
||||||
backfilled on page load; collapsible inbox + collapsible journald
|
server. `--continue` preserves the prior session so context spans turns
|
||||||
viewer + collapsible `agent.nix` viewer per agent on the dashboard;
|
without rebuilding from history every wake. If the turn dies mid-stream,
|
||||||
deployed-sha chip per container (read from meta's `flake.lock`).
|
the next wake picks up clean — sessions are durable but cheap to discard.
|
||||||
|
|
||||||
Operator surface on the dashboard itself: a terminal compose box
|
## Operator surfaces
|
||||||
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
|
**Per agent (`:8100-8999`):** terminal-themed live tail with a
|
||||||
`/agents/<name>/config/` — `agent.nix` is a plain NixOS module function
|
textarea prompt; slash commands `/help` `/clear` `/cancel`
|
||||||
|
`/compact` `/model <name>` `/new-session`; granular state badge
|
||||||
|
(idle / thinking / compacting / offline) with age timer +
|
||||||
|
last-turn duration chip + model chip; cancel-turn + new-session
|
||||||
|
buttons in the state row; sticky-bottom auto-scroll with "↓ N new"
|
||||||
|
pill; event history backfilled on page load; collapsible inbox +
|
||||||
|
collapsible journald viewer + collapsible `agent.nix` viewer per
|
||||||
|
agent on the dashboard; deployed-sha chip per container (read
|
||||||
|
from meta's `flake.lock`).
|
||||||
|
|
||||||
|
**Dashboard itself (`:7000`):** the swarm-wide view — every
|
||||||
|
container with status chips, every pending approval with inline
|
||||||
|
diff, every open question with operator-answer affordance, every
|
||||||
|
recent message on a unified live stream. A terminal compose box
|
||||||
|
under the message-flow lets the operator drop messages into any
|
||||||
|
agent's inbox: `@name` picks the recipient with auto-complete
|
||||||
|
from the live container list, sticky across sends, POSTs
|
||||||
|
`/op-send` which lands in the broker as
|
||||||
|
`{from:"operator", to:<name>, body}` — same shape any sub-agent
|
||||||
|
sees as a regular inbox message. No special channel, no
|
||||||
|
out-of-band notification system. If the operator says it, the
|
||||||
|
broker carries it.
|
||||||
|
|
||||||
|
## The config-edit loop
|
||||||
|
|
||||||
|
Inverted flow — agents propose, the operator disposes. The manager
|
||||||
|
edits files under `/agents/<name>/config/` — `agent.nix` is a plain
|
||||||
|
NixOS module function
|
||||||
`{ config, pkgs, lib, ... }: { ... }`, and arbitrary sibling files in
|
`{ config, pkgs, lib, ... }: { ... }`, and arbitrary sibling files in
|
||||||
the commit are preserved → commits → submits the sha via
|
the commit are preserved → commits → submits the sha via
|
||||||
`request_apply_commit`. Hive-c0re immediately fetches that commit from
|
`request_apply_commit`. Hive-c0re immediately fetches that commit from
|
||||||
|
|
@ -82,20 +116,26 @@ tag carrying the operator's note.
|
||||||
|
|
||||||
Meta's git log is the swarm-wide deploy audit trail (one commit per
|
Meta's git log is the swarm-wide deploy audit trail (one commit per
|
||||||
successful deploy). Per-agent applied repos carry the tag-rich state
|
successful deploy). Per-agent applied repos carry the tag-rich state
|
||||||
machine for inside-baseball decisions. The manager sees both — proposed
|
machine for inside-baseball decisions. The manager sees both —
|
||||||
repos ship with an `applied` remote pre-wired, and `/meta/` is RO-bound
|
proposed repos ship with an `applied` remote pre-wired, and `/meta/`
|
||||||
inside the container — so `git fetch applied`,
|
is RO-bound inside the container — so `git fetch applied`,
|
||||||
`git show applied/refs/tags/deployed/<id>`, `git log /meta`,
|
`git show applied/refs/tags/deployed/<id>`, `git log /meta`,
|
||||||
`cat /meta/flake.lock` all just work without constructing paths by
|
`cat /meta/flake.lock` all just work without constructing paths by
|
||||||
hand. See [`docs/approvals.md`](docs/approvals.md) for the full state
|
hand. See [`docs/approvals.md`](docs/approvals.md) for the full state
|
||||||
machine + lock-flow walkthrough.
|
machine + lock-flow walkthrough.
|
||||||
For decisions any agent (manager or sub) needs structured signal on,
|
|
||||||
`ask(question, options?, multi?, ttl_seconds?, to?)` queues a question:
|
## Structured Q&A
|
||||||
default recipient is the operator (dashboard renders a free-text /
|
|
||||||
checkbox / radio form), or pass `to: "<agent>"` to route a structured
|
Agents don't have to guess. `ask(question, options?, multi?,
|
||||||
peer question into another agent's inbox. The answer arrives later as
|
ttl_seconds?, to?)` queues a structured question — default recipient
|
||||||
a `HelperEvent::QuestionAnswered { id, question, answer, answerer }`
|
is the operator (dashboard renders a free-text / checkbox / radio
|
||||||
in the asker's inbox. Peer recipients respond via `answer(id, answer)`.
|
form), or pass `to: "<agent>"` to route the question into a peer
|
||||||
|
agent's inbox instead. The answer arrives later as a
|
||||||
|
`HelperEvent::QuestionAnswered { id, question, answer, answerer }`
|
||||||
|
in the asker's inbox; peer recipients respond via `answer(id, answer)`.
|
||||||
|
Plus `remind(message, delay_seconds | at_unix_timestamp, file_path?)`
|
||||||
|
for self-scheduled wake-ups when an agent wants to nudge itself later
|
||||||
|
without holding a connection open.
|
||||||
|
|
||||||
## Host config
|
## Host config
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue