diff --git a/CLAUDE.md b/CLAUDE.md index 4405782d..eb4dbb56 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -354,8 +354,6 @@ docs/ web-ui/agent.md (header, terminal, composer, inbox, live view, per-agent endpoints, stats) turn-loop.md claude invocation, wake prompt, MCP tool surface - tools/ per-group tool docs (bash.md, lifecycle.md, - scheduling.md, matrix.md) approvals.md approval flow, manager policy, helper events persistence.md sqlite dbs, retention, state dir layout terminal-rendering.md per-agent terminal row taxonomy (as built) diff --git a/docs/tools/bash.md b/docs/tools/bash.md deleted file mode 100644 index d49719af..00000000 --- a/docs/tools/bash.md +++ /dev/null @@ -1,65 +0,0 @@ -# Bash execution tools - -Background shell execution via `hive-bash-mcp`. Tools land as -`mcp__bash__` (the MCP server name is `bash`, not `hyperhive`). -Available on every agent unconditionally — `harness-base.nix` always -injects bash into `hyperhive.extraMcpServers` (with `allowedTools = -["*"]`), so `mcp__bash__*` is in `--allowedTools` for every claude -invocation regardless of tool groups. - -## Tools - -### `run(cmd, timeout_secs?, wait_seconds?)` - -Submit a shell command for background execution (`sh -c `). -Stdout and stderr stream to `harness/bash-tasks/.{out,err}`. -When the task completes (or times out, or the process errors), the -harness fires a wake with `from: "bash-task-"` and the exit code -+ last stdout lines in the body; handle it on a future turn. - -- `timeout_secs` — kill the task after N seconds and mark it - `timed_out`. Omit for no timeout (runs until natural exit). -- `wait_seconds` — inline poll before returning (capped at 30). - When the task finishes within the window the full status is - returned immediately and no wake is fired; when the window expires - the task keeps running and the normal `task started: id=` - response is returned. **Defaults to 3** — pass `wait_seconds: 0` - to disable inline waiting and always get the immediate response. - -Exposed as `mcp__bash__run`. - -### `status(id, wait_seconds?)` - -Poll the status of a task submitted with `run`. Returns: - -- `status` — `pending` / `running` / `done` / `timed_out` / `interrupted` -- `exit_code` — set when done -- run duration -- last 4 KiB of stdout and stderr (full output in the `.out` / `.err` files) - -`wait_seconds` — optional inline poll (capped at 30): when the task -finishes within the window the full status is returned immediately. -Useful to avoid a separate round-trip when the task is expected to -finish soon. - -Tasks marked `interrupted` had their process killed by a harness -restart; a best-effort wake was still sent so the agent is not -silently blocked. - -Exposed as `mcp__bash__status`. - -## Namespace note - -`run` and `status` live in the `bash` MCP server, not `hyperhive`. So -the tool names in claude are `mcp__bash__run` and `mcp__bash__status`. -The `Bash` built-in tool is blocked — all shell execution goes through -this structured path so tasks get task-id tracking and structured output. - -## Relationship to the `execution` tool group - -`ToolGroup::Execution` exists and appears in `AGENT_DEFAULT`, but its -`tools()` returns `["run", "status"]` which the harness expands to -`mcp__hyperhive__run` / `mcp__hyperhive__status` — tools that don't -exist in the hyperhive MCP server (dead entries). Removing `execution` -from an agent's groups has no effect on bash availability. Bash is -registered separately via the `extraMcpServers` path described above. diff --git a/docs/tools/lifecycle.md b/docs/tools/lifecycle.md deleted file mode 100644 index c2135445..00000000 --- a/docs/tools/lifecycle.md +++ /dev/null @@ -1,91 +0,0 @@ -# Lifecycle and approvals tools - -Two tool groups govern agent lifecycle management and config changes. -Both are scoped to **direct children only** (topology-enforced: the -server rejects any name that is not a direct child of the calling -agent per `topology.json`). Privileged agents (e.g. ruth) may operate -on any sub-agent — the topology scope applies to all others. - -## `lifecycle` tool group - -No operator approval required. Direct children only. - -### `kill(name)` - -Graceful stop. Container state is preserved; recreating the agent -reuses prior config and credentials. - -### `start(name)` - -Start a stopped sub-agent. - -### `restart(name)` - -Stop + start in one call. - -### `update(name)` - -Rebuild: re-applies the current hyperhive flake + `agent.nix`, -then restarts. Idempotent — safe to call repeatedly. Used in response -to `needs_update` system events. - -### `list_containers()` - -List all **descendant** containers (children + their subtrees) with -running status. Topology-scoped to descendants only. - -## `approvals` tool group - -Config changes and new-agent spawns route through the operator -approval queue. Direct children only (topology-enforced). - -### `request_init_config(name, description?)` - -Step 1 of spawning a new direct child agent. Queues an `InitConfig` -approval; on operator approve, hive-c0re seeds the proposed config -repo at `/agents//config/agent.nix` with a default template and -delivers a `config_ready` system event. Then edit `agent.nix`, commit, -and call `request_apply_commit` with the commit sha — the first -`ApplyCommit` on a freshly-init'd config creates the container. - -Fails if a proposed config repo for `name` already exists. -`name` is ≤ 9 characters. The operator can also spawn an empty agent -via the dashboard `◆ R3QU3ST SP4WN` button, which routes via -`HostRequest::RequestSpawn`. - -### `request_apply_commit(agent, commit_ref, description?)` - -Step 2 of spawning (or updating an existing child's config). Submit a -commit sha from the child's proposed config repo for operator -approval. On approve, hive-c0re rebuilds the container with the -pinned commit. - -`commit_ref` must be a 7-40 char hex sha (branch/tag names are -rejected — the approval pins the exact commit). `agent` must be a -direct child. Topology-enforced. - -### `request_update_meta_inputs(inputs?, description?)` - -Queue an approval to run `nix flake update [inputs...]` on the meta -flake. Pass specific input names (e.g. `["bitburner-agent"]`) or omit -/ pass `[]` for all inputs. Returns immediately; the lock update runs -on operator approval. - -Does NOT trigger container rebuilds — call `update(name)` on affected -agents after the approval resolves. - -## Boundary summary - -| Operation | Requires approval? | Scope | -|---|---|---| -| `kill` / `start` / `restart` / `update` | No | Direct children | -| `list_containers` | No | All descendants | -| `request_init_config` | Yes (InitConfig) | New direct child only | -| `request_apply_commit` | Yes (ApplyCommit) | Direct children | -| `request_update_meta_inputs` | Yes (MetaUpdate) | Meta flake (global) | - -## See also - -- [`docs/approvals.md`](../approvals.md) — full approval flow, kinds, - helper events (`config_ready`, `approval_resolved`), flake.lock - validation. diff --git a/docs/tools/matrix.md b/docs/tools/matrix.md deleted file mode 100644 index 3e0d460f..00000000 --- a/docs/tools/matrix.md +++ /dev/null @@ -1,62 +0,0 @@ -# Matrix MCP tools and extra MCP servers - -## Built-in matrix MCP (`mcp__matrix__*`) - -When `hyperhive.matrix.enable = true` and the host-level matrix -tuwunel is configured, the harness auto-injects `hive-matrix-mcp` as -a second stdio MCP server. Tools land as `mcp__matrix__`: - -### Messaging - -- `send_message(room, body)` — send a markdown message; `room` - accepts `!id:server` or `#alias:server`, daemon resolves either -- `send_dm(user_id, body)` — send a direct message to a matrix user -- `send_reply(room, event_id, body)` — threaded reply to a specific - event -- `send_reaction(room, event_id, key)` — react to a message with an - emoji key - -### Reading - -- `read_room(room, limit?)` — recent timeline events -- `list_rooms()` — enumerate joined rooms - (`{ id, canonical_alias, name, member_count }` per room) -- `list_room_members(room)` — members of a room - -### Receipts - -- `mark_read(room, event_id)` — advance the read receipt - -## Architecture - -The daemon (`hive-matrix-daemon`) holds the long-running matrix-sdk -`Client` + sync loop; the stdio bridge (`hive-matrix-mcp`) is spawned -per turn and forwards tool calls over `/run/hive-matrix.sock`. Both -silently exit when `/matrix-token` is absent (account not yet -provisioned). - -Incoming room events wake the agent via `AgentRequest::Wake` with -`from: "matrix"` and a teaser body -(`[matrix] in : …`). - -See [`docs/matrix.md`](../matrix.md) for the homeserver setup, -provisioning flow, and federation config. - -## Extra MCP servers (per-agent) - -Each agent's NixOS config can declare additional MCP servers via -`hyperhive.extraMcpServers. = { 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.`) -and `--allowedTools` (as `mcp____`). - -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..packages.${pkgs.system}.default` — the resolved -sha lands in the agent's own `flake.lock` and rolls up to meta's. - -`allowedTools` defaults to `["*"]`, which expands to -`mcp____*` (every tool from that server auto-approved). Restrict -to specific tool names when you want finer control. diff --git a/docs/tools/scheduling.md b/docs/tools/scheduling.md deleted file mode 100644 index 358389e1..00000000 --- a/docs/tools/scheduling.md +++ /dev/null @@ -1,68 +0,0 @@ -# Scheduling and diagnostics tools - -## `scheduling` tool group - -Scheduled prompts fan a message body out to one or more agent inboxes -at a future time, optionally recurring. All scheduling ops go through -the operator approval queue (even self-targeted schedules — use -`remind` for unapproved self-wake). Authorization for read/cancel/edit -ops: you can act on schedules you own or any owned by a sub-agent in -your topology subtree. - -### `request_schedule_prompt(targets, body, first_fire_at_unix, interval_seconds?, description?)` - -Queue an operator-approval for a scheduled prompt. On approve, -`body` is fanned out to each agent in `targets` at -`first_fire_at_unix` (Unix timestamp). Recurring when `interval_seconds` -is set, one-shot otherwise. - -Catch-up clamp: if hive-c0re is down across multiple intervals, only -ONE delayed fire happens on resume (per recurring schedule). The -skipped-cycle count surfaces in the per-target `last_result` for -audit. - -### `edit_schedule(id, body?, description?, interval_seconds?, next_fire_at_unix?, targets_add?, targets_remove?)` - -Partial-update a schedule. Pass only the fields to change; absent -fields are left alone. `targets_add` / `targets_remove` mutate the -recipient list in the same transaction — re-adding a previously -cancelled target drops its tombstone and starts fresh. Clearing -`interval_seconds` to null flips recurring → one-shot. Refuses -cancelled rows (terminal state). - -### `cancel_schedule(id, targets?)` - -Cancel a schedule. Omit `targets` / pass empty to cancel the whole -schedule; pass a list to cancel just those recipients (the schedule -auto-cancels when every target is removed). - -### `fire_schedule_now(id)` - -Fire a scheduled prompt out of band immediately. Recurring schedules -keep their cadence — the manual fire is additive. One-shot schedules -are consumed by the manual fire and cancelled afterwards. - -### `list_schedules()` - -Snapshot every schedule (active + cancelled-but-not-reaped): id, -owner, body, per-target `last_fired_at` + `last_result`, -`next_fire_at_unix`, `interval_seconds`. Use to look up an id before -cancelling, or to audit upcoming wake-ups across the swarm. - -## `diagnostics` tool group - -### `get_logs(agent, lines?)` - -Fetch recent journal lines for a sub-agent container. Useful for -diagnosing MCP-registration failures, startup crashes, plugin install -errors, or any harness issue you can't see from inside the container. - -Pass the plain logical agent name (e.g. `"gui"`) — hive-c0re resolves -the machine name (`h-`). `lines` defaults to 50, host-capped at 500. - -## See also - -- `remind` (no-approval self-wake path) — documented in - [`docs/turn-loop.md`](../turn-loop.md). -- [`docs/approvals.md`](../approvals.md) — approval flow for - `request_schedule_prompt`. diff --git a/docs/turn-loop.md b/docs/turn-loop.md index 88ac9b82..929aefb8 100644 --- a/docs/turn-loop.md +++ b/docs/turn-loop.md @@ -5,8 +5,9 @@ claude has access to in return. ## The loop -Each agent harness (`hive serve`, role set via `$HIVE_ROLE` — always -`"agent"`, one binary) runs: +Each agent harness (`hive serve`, with role picked from `$HIVE_ROLE` +— `"agent"` for sub-agents, `"manager"` for the manager — one +binary, not two) runs: 1. Long-poll `Recv` on its socket. The host-side broker (`broker.rs::recv_blocking_batch`) returns immediately if there's @@ -291,75 +292,115 @@ The harness ships an embedded MCP server (rmcp 1.7). Claude launches it as a stdio child via `--mcp-config`. The hyperhive socket name is `hyperhive`, so the tools land in claude as `mcp__hyperhive__`. -Tool access is gated by tool groups (`HIVE_TOOL_GROUPS`). The default -preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and -`execution`. Privileged groups (`lifecycle`, `approvals`, `scheduling`, -`diagnostics`) are opt-in via the P3RM1SS10NS tab. +### Sub-agent tools -### Core tools (always available) - -**Messaging** (`messaging` group): `send(to, body, in_reply_to?)`, -`recv(wait_seconds?, max?)`, `ask(question, options?, multi?, -ttl_seconds?, to?)`, `answer(id, answer)`. - -- `send` — message a peer (logical name) or the operator - (`to: "operator"`). Use `to: ""` to address the topology - parent without hardcoding the label; the broker resolves the - sentinel at delivery time. Optional `in_reply_to: i64` links the - message to a prior id for thread rendering. -- `recv` — drain inbox. Without `wait_seconds` (or `0`) returns - immediately. Positive value parks the turn up to that many seconds - (cap 180) — incoming messages wake instantly. `max` (default 1, cap - 32) drains up to N rows; `wait_seconds` applies to the first, then - drains up to `max` total. -- `ask` — surface a structured question to the operator (default) or - a peer agent (`to: ""`). Non-blocking — returns a question - id; the answer arrives as a `question_answered` system event in the - asker's inbox. `options` is advisory; `multi=true` renders as - checkboxes; `ttl_seconds` auto-cancels with answer `[expired]`. -- `answer` — respond to a `question_asked` event routed to this - agent. Strict authorisation: only the declared target can answer. - -**Inbox** (`inbox` group): `get_loose_ends()`, -`cancel_loose_end(kind, id)`, `remind(message, delay_seconds? | -at_unix_timestamp?)`, `request_next_turn()`. - -- `get_loose_ends` — list pending questions (asked/owed) and - scheduled reminders. Each row carries an id + kind for +- `send(to, body, in_reply_to?)` — message a peer (logical agent + name), another agent, or the operator (recipient `operator`, + surfaces in the dashboard inbox). Use `to: ""` to + address the agent's topology parent without knowing its label; + the broker resolves the sentinel at delivery time (falls back + to `operator` for root agents). Optional `in_reply_to: i64` + links this message to a prior message id for thread rendering + in the dashboard message flow and the per-agent inbox. +- `recv(wait_seconds?, max?)` — drain inbox messages. Without + `wait_seconds` (or with `0`) returns immediately, a cheap + "anything pending?" peek. Positive value parks the turn up + to that many seconds (cap 180) — incoming messages wake + 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?)` — + surface a structured question. Same shape as the manager's; + recipient defaults to the operator (dashboard) but can be set + to a peer agent name via `to: ""`. Answer routes back + to the asker's own inbox as `HelperEvent::QuestionAnswered` + via `coord.notify_agent`. For peer questions the recipient + sees a `HelperEvent::QuestionAsked` event and replies with + `answer(id, answer)`. +- `answer(id, answer)` — respond to a `question_asked` event + routed to this agent. Authorisation is strict: only the + declared target (or the operator via the dashboard) can + 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` — withdraw a `question` (posts `[cancelled by - ]`), hard-delete a `reminder`, or cancel a pending `approval` - row. Agents may only cancel rows they own; the `approval` kind is - further restricted to the root agent (`ruth`) server-side. -- `remind` — schedule a reminder in this agent's own inbox. Large - payloads spill to `/agents//state/reminders/`. Pending count - capped at 50 per agent (`HIVE_REMIND_MAX_PENDING_PER_AGENT`). -- `request_next_turn` — ask the harness to start another turn - immediately after this one ends, even if the inbox is empty. - Next turn fires with `from: "self"` and `body: "continue"`. +- `cancel_loose_end(kind, id)` — withdraw a `question` + (posts `[cancelled by ]` to unblock the asker), a + `reminder` (hard-delete before fire), or (manager only) an + `approval` (transitions to `Cancelled`; sub-agents refused with a + clear error). 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//state/reminders/` with the inbox message a + short pointer. Each agent's pending-reminder count is capped + (default 50, override via `HIVE_REMIND_MAX_PENDING_PER_AGENT`); + scheduling a new one fails if the cap is already hit. +- `set_status(text)` — set a free-text status string visible on + the operator dashboard. Persisted to + `{state_dir}/hyperhive-status`; survives harness restarts. Pass + an empty string to clear. Validated: must be a single line and + ≤ 200 Unicode characters; the server rejects multi-line or + over-length text with a clear error. +- `get_agent_meta(name?)` — fetch identity + status metadata for + an agent: `{ name, role, hyperhive_rev, running, status_text, + status_set_at, hive_name?, swarm_name? }`. Pass `name` to query + a peer (e.g. check whether a sub-agent is idle before sending it + work). Omit `name` to get your own identity stamp — replaces the + previous `whoami` tool. `running` is `true` when the container is + up. When `running` is `false` the host clears `status_text` / + `status_set_at` (they would be stale snapshots from before the + container stopped) before serving the response. Status fields are + also `None` when the target has never called `set_status` or has + cleared it. `hive_name` and `swarm_name` are present when + `services.hyperhive.hiveName` / `services.hyperhive.swarmName` + are configured on the host; omitted in single-hive deployments. +- `request_next_turn()` — ask the harness to start another turn + immediately after this one ends, even if the inbox is empty. Use for + multi-turn tasks (long builds, sequential steps) where you want to + continue without waiting for an external message. The next turn starts + with `from: "self"` and `body: "continue"`. No-op if new inbox + messages arrive before this turn ends. No args. +- `run(cmd, timeout_secs?)` — submit a shell command for + background execution (`sh -c `). Returns a task ID immediately; + the command runs asynchronously in a harness-managed tokio task. Stdout + and stderr stream to `harness/bash-tasks/.{out,err}`. When the + task completes (or times out, or the process errors), the harness wakes + the agent with a summary body — handle on a future turn. Optional + `timeout_secs`: pass a value for a deadline, or omit for no timeout + (runs until natural exit). Requires the `execution` tool group. + Exposed as `mcp__bash__run`. +- `status(id)` — poll the status of a task submitted with + `run`. Returns status (`pending`/`running`/`done`/`timed_out`/ + `interrupted`), exit code, run duration, and the last 4 KiB of stdout + and stderr (full output in the `.out`/`.err` files). Tasks marked + `interrupted` had their process killed by a harness restart; a best- + effort wake was still sent so the agent is not silently blocked. + Exposed as `mcp__bash__status`. -**Meta** (`meta` group): `set_status(text)`, `get_agent_meta(name?)`. +**Agent lifecycle + config tools** (direct children only; requires +`lifecycle` or `approvals` tool group): -- `set_status` — set a free-text status string visible on the - dashboard. Single line, ≤ 200 chars. Persisted to - `{state_dir}/hyperhive-status`. Pass `""` to clear. -- `get_agent_meta` — fetch identity + status metadata for an agent: - `{ name, hyperhive_rev, running, status_text, status_set_at, - hive_name?, swarm_name? }`. Omit `name` to query self. - -### Privileged tools (by tool group) - -- **Bash execution** (`execution`) — background shell tasks. See - [`docs/tools/bash.md`](tools/bash.md). -- **Lifecycle + config** (`lifecycle`, `approvals`) — manage child - agents, spawn new ones, apply config commits. See - [`docs/tools/lifecycle.md`](tools/lifecycle.md). -- **Scheduling + diagnostics** (`scheduling`, `diagnostics`) — - scheduled prompts, `get_logs`. See - [`docs/tools/scheduling.md`](tools/scheduling.md). -- **Matrix MCP + extra servers** — `mcp__matrix__*` tools and - per-agent extra MCP config. See - [`docs/tools/matrix.md`](tools/matrix.md). +- `kill(name)`, `start(name)`, `restart(name)`, `update(name)` — manage + a direct child sub-agent (graceful stop, start, stop+start, rebuild). + No approval required. Topology-enforced: `name` must be a direct child + per `topology.json`. Server rejects all other agent names. Requires + `lifecycle` tool group. +- `list_containers()` — list all descendant containers with running + status. Topology-enforced (descendants only). Requires `lifecycle` + tool group. +- `request_init_config(name, description?)` — step 1 of spawning a new + direct child agent. Queues an `InitConfig` approval; on operator + approve, hive-c0re seeds the proposed config repo with a default + `agent.nix` template. `name` must be a direct child. Topology-enforced. + Fails if config already exists. Requires `approvals` tool group. +- `request_apply_commit(agent, commit_ref, description?)` — step 2 of + spawning a new direct child (or updating an existing child's config). + Submit a commit sha from the child's proposed config repo for operator + approval. `agent` must be a direct child. Topology-enforced. `commit_ref` + must be a 7-40 char hex sha. Requires `approvals` tool group. ### Waking the agent from inside the container @@ -370,22 +411,170 @@ socket at `/run/hive/mcp.sock`. Two equivalent paths: - **Shell out to `hive wake --from