From af4021f336a7c7af9ec541d99d2ae6f695bbb4e9 Mon Sep 17 00:00:00 2001 From: damocles Date: Thu, 4 Jun 2026 21:41:42 +0200 Subject: [PATCH 1/6] fix(#1088): sync system.md and mcp.rs tool descriptions against implementation --- hive-ag3nt/prompts/system.md | 3 ++- hive-ag3nt/src/mcp.rs | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/hive-ag3nt/prompts/system.md b/hive-ag3nt/prompts/system.md index ce9118e2..bf0c2f8d 100644 --- a/hive-ag3nt/prompts/system.md +++ b/hive-ag3nt/prompts/system.md @@ -7,7 +7,7 @@ Tools (hyperhive surface): - (some agents only) **extra MCP tools** surfaced as `mcp____` — 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: ""`). 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 operator calls `ask(to: "", ...)`. 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(agent?)` — 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 to list your own threads — cheap server-side sweep useful at turn start. Pass `agent: ""` to inspect a peer agent's threads. Direct child agents are always accessible. For non-children, the `query_agent_state` capability is required — without it the request is rejected with an error. - `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 ]` answer so the waiter unblocks), `"reminder"` (hard-deleted before it fires), or `"approval"` (withdraws a pending approval you submitted that got superseded — root agent only; the server rejects this kind for all other callers). `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. Each agent's pending-reminder count is capped (default 50) — the tool will error if the cap is already reached. - `mcp__hyperhive__set_status(text)` — set a free-text status visible on the operator dashboard. **Call this at the start of every task** to say what you're working on (e.g. `"processing matrix messages"`, `"fixing #319 model priority"`, `"idle"`). Single line, ≤200 chars — the dashboard renders this as a short chip, so longer multi-line text is rejected. Pass an empty string to clear. Persists across harness restarts. @@ -27,6 +27,7 @@ Tools (hyperhive surface): - `mcp__hyperhive__edit_schedule(id, body?, description?, interval_seconds?, next_fire_at_unix?, targets_add?, targets_remove?)` — *(requires `scheduling` tool group)* partial-update a schedule's mutable fields. Pass only the fields you want to change. `targets_add` / `targets_remove` mutate the recipient list in the same transaction. Refuses cancelled rows. Same authorization as `cancel_schedule`. - `mcp__hyperhive__list_schedules()` — *(requires `scheduling` tool group)* snapshot every schedule in the queue. Returns id, owner, body, target set with per-target `last_fired_at` + `last_result`, `next_fire_at_unix`, recurring `interval_seconds`. - `mcp__hyperhive__get_logs(agent, lines?)` — *(requires `diagnostics` tool group)* fetch recent journal lines for a sub-agent container. Pass the plain logical agent name; `lines` defaults to 50 (capped at 500). +- `mcp__hyperhive__get_host_journal(unit?, container?, lines?, priority?, grep?, since?, until?)` — *(requires `read_host_journal` capability)* fetch recent lines from the host journal. All filters optional — omit to get the last N host journal lines. `unit`: filter to a systemd unit (e.g. `hive-c0re.service`). `container`: nspawn machine name verbatim (e.g. `h-iris`). Omit for host journal. Agent containers use the `h-` prefix (e.g. `h-iris`); infrastructure containers use their full name (e.g. `hive-ci`, `hive-forge`, `hive-matrix`, `hive-gateway`). `lines`: how many lines (default 30, max 100). `priority`: minimum syslog level enum. `grep`: regex matched against log message fields (journalctl --grep). `since`: show entries on or newer than this (e.g. `-1h`, `2024-01-01 12:00:00`). `until`: show entries on or older than this. Need new packages, env vars, or other NixOS config for yourself? You can't edit your own config directly — ask a peer agent with the `approvals` tool group, or contact the operator directly. Config repos live at `/agents/{label}/config/` (read-only inside your container). All changes flow through operator-approved commits. diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 3a8f2bb0..719346c8 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -859,7 +859,11 @@ impl AgentServer { you scheduled (hard-deleted before it fires). `kind` is `\"question\"` or \ `\"reminder\"`; `id` is the row id from the matching `get_loose_ends` entry \ or the `question_queued` reply you got when you submitted. Auth: you can only \ - cancel rows where you're the asker / owner. Returns `ok` or an error string." + cancel rows where you're the asker / owner. Returns `ok` or an error string.\n\ + Agents with the `approvals` tool group can also pass `kind: \"approval\"` to \ + withdraw a pending approval they submitted (before the operator acts on it). \ + The root agent (`ruth`) may cancel any approval; non-root agents are restricted \ + to their own approvals and the server rejects `approval` kind otherwise." )] async fn cancel_loose_end(&self, Parameters(args): Parameters) -> String { let log = format!("{args:?}"); From 29bc40a1d7d7531f8485b833eb42ce4e98a92e73 Mon Sep 17 00:00:00 2001 From: damocles Date: Thu, 4 Jun 2026 21:48:16 +0200 Subject: [PATCH 2/6] docs: document get_host_journal in scheduling.md and turn-loop.md --- docs/tools/scheduling.md | 23 +++++++++++++++++++++++ docs/turn-loop.md | 5 +++++ 2 files changed, 28 insertions(+) diff --git a/docs/tools/scheduling.md b/docs/tools/scheduling.md index 358389e1..325b5c64 100644 --- a/docs/tools/scheduling.md +++ b/docs/tools/scheduling.md @@ -60,6 +60,29 @@ 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. +## `read_host_journal` capability + +Capability-gated (not a tool group) — the operator enables it in the +P3RM1SS10NS C4P4B1L1T13S section. Unlike tool groups this is not +configurable from `agent.nix`. + +### `get_host_journal(unit?, container?, lines?, priority?, grep?, since?, until?)` + +Fetch recent lines from the **host** journal (requires +`read_host_journal` capability). Useful when you need visibility +outside your own container — infrastructure services, hive-c0re +lifecycle events, or another container's boot log. + +- `unit` — filter to a systemd unit (e.g. `hive-c0re.service`). +- `container` — nspawn machine name verbatim. Agent containers use + the `h-` prefix (e.g. `h-iris`); infrastructure containers + use their full name (e.g. `hive-ci`, `hive-forge`, `hive-matrix`, + `hive-gateway`). Omit for the host journal. +- `lines` — how many lines to return (default 30, max 100). +- `priority` — minimum syslog level (`emerg` … `debug`). +- `grep` — regex matched against log message fields (`journalctl --grep`). +- `since` / `until` — time bounds (e.g. `-1h`, `2024-01-01 12:00:00`). + ## See also - `remind` (no-approval self-wake path) — documented in diff --git a/docs/turn-loop.md b/docs/turn-loop.md index fa26aa26..f6644d14 100644 --- a/docs/turn-loop.md +++ b/docs/turn-loop.md @@ -356,6 +356,11 @@ at_unix_timestamp?)`, `request_next_turn()`. - **Scheduling + diagnostics** (`scheduling`, `diagnostics`) — scheduled prompts, `get_logs`. See [`docs/tools/scheduling.md`](tools/scheduling.md). +- **Capability-gated** — `get_host_journal` (requires + `read_host_journal` capability set via the P3RM1SS10NS tab; + orthogonal to tool groups). Full list of capabilities and their + effects in [`docs/conventions.md#capabilities`](../docs/conventions.md). + Also documented in [`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). From 457a3e3ae5fc73de12d931fa6ed4af65a1ed89f0 Mon Sep 17 00:00:00 2001 From: damocles Date: Thu, 4 Jun 2026 21:57:42 +0200 Subject: [PATCH 3/6] docs: add running field to get_agent_meta description in system.md and mcp.rs --- hive-ag3nt/prompts/system.md | 2 +- hive-ag3nt/src/mcp.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hive-ag3nt/prompts/system.md b/hive-ag3nt/prompts/system.md index bf0c2f8d..304787eb 100644 --- a/hive-ag3nt/prompts/system.md +++ b/hive-ag3nt/prompts/system.md @@ -11,7 +11,7 @@ Tools (hyperhive surface): - `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 ]` answer so the waiter unblocks), `"reminder"` (hard-deleted before it fires), or `"approval"` (withdraws a pending approval you submitted that got superseded — root agent only; the server rejects this kind for all other callers). `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. Each agent's pending-reminder count is capped (default 50) — the tool will error if the cap is already reached. - `mcp__hyperhive__set_status(text)` — set a free-text status visible on the operator dashboard. **Call this at the start of every task** to say what you're working on (e.g. `"processing matrix messages"`, `"fixing #319 model priority"`, `"idle"`). Single line, ≤200 chars — the dashboard renders this as a short chip, so longer multi-line text is rejected. Pass an empty string to clear. Persists across harness restarts. -- `mcp__hyperhive__get_agent_meta(name?)` — fetch identity + status metadata for an agent: canonical `name`, current `hyperhive_rev`, plus self-reported `status` text (set via `set_status`) and how long ago it was set. Also returns the hive + swarm display names (`hive_name`, `swarm_name`) when the operator has configured `services.hyperhive.{hiveName, swarmName}`; both lines omitted when unset. Pass `name` to query a peer (e.g. check whether iris is idle before pinging them). Omit `name` to get your own trustworthy identity stamp — useful for state files, commit messages, cross-agent attribution that won't drift across renames or session-continue boundaries where the system-prompt label could be stale. +- `mcp__hyperhive__get_agent_meta(name?)` — fetch identity + status metadata for an agent: canonical `name`, current `hyperhive_rev`, plus self-reported `status` text (set via `set_status`) and how long ago it was set. Also returns `running: bool` (whether the container is up — when `false`, `status_text`/`status_set_at` are stale pre-stop values) and the hive + swarm display names (`hive_name`, `swarm_name`) when the operator has configured `services.hyperhive.{hiveName, swarmName}`; both lines omitted when unset. Pass `name` to query a peer (e.g. check whether iris is idle before pinging them). Omit `name` to get your own trustworthy identity stamp — useful for state files, commit messages, cross-agent attribution that won't drift across renames or session-continue boundaries where the system-prompt label could be stale. - `mcp__hyperhive__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 (the harness already loops immediately on pending messages). No args. - `mcp__hyperhive__restart(name)` — *(requires `lifecycle` tool group)* restart a direct child sub-agent (stop + start). The server enforces topology: the call is rejected unless `name` is a direct child of yours per `topology.json`. No approval required. - `mcp__hyperhive__kill(name)` — *(requires `lifecycle` tool group)* stop a direct child sub-agent (graceful). Direct children only — server enforces topology. State dir kept; recreating reuses prior config + credentials. No approval required. diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 719346c8..5db88a56 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -833,9 +833,13 @@ impl AgentServer { #[tool( description = "Fetch identity + status metadata for an agent. Returns canonical \ - `name`, the current `hyperhive_rev` hive-c0re is \ - running against, and the target's self-reported `status` text (set via \ - `set_status`) plus how long ago it was set. Pass `name` to query a peer (e.g. \ + `name`, the current `hyperhive_rev` hive-c0re is running against, `running` \ + (bool — whether the container is currently up; when false, `status_text` and \ + `status_set_at` are stale pre-stop values and should not be treated as live), \ + and the target's self-reported `status` text (set via `set_status`) plus how \ + long ago it was set. Also returns the hive + swarm display names (`hive_name`, \ + `swarm_name`) when the operator has configured `services.hyperhive.{hiveName, \ + swarmName}`; both lines omitted when unset. Pass `name` to query a peer (e.g. \ check whether iris is idle before pinging them); omit `name` to get your own \ identity stamp — handy for state files / commit messages / cross-agent \ attribution that won't drift across renames or session-continue boundaries \ From 4d4bfb2b33fdb01089e770c3c6f4a86bdb5d14f7 Mon Sep 17 00:00:00 2001 From: damocles Date: Thu, 4 Jun 2026 21:55:16 +0200 Subject: [PATCH 4/6] chore: remove stale TODO in stats.rs (ReminderRollup RPC already implemented) --- hive-ag3nt/src/stats.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hive-ag3nt/src/stats.rs b/hive-ag3nt/src/stats.rs index 301d1c50..7f044fbd 100644 --- a/hive-ag3nt/src/stats.rs +++ b/hive-ag3nt/src/stats.rs @@ -275,7 +275,7 @@ fn snapshot(path: &Path, window: Window) -> Result { result_mix: top_n(result_totals, 20), models, duration_summary, - reminder_stats: None, // TODO: fetch via ReminderRollup RPC + reminder_stats: None, // filled in by api_stats in web_ui.rs via fetch_reminder_stats RPC }) } From 32d815df81cb4cdeceb801fd9a396c9927fef038 Mon Sep 17 00:00:00 2001 From: damocles Date: Fri, 5 Jun 2026 00:45:37 +0200 Subject: [PATCH 5/6] =?UTF-8?q?docs:=20clarify=20execution=20tool=20group?= =?UTF-8?q?=20is=20vestigial=20=E2=80=94=20bash=20always=20available=20via?= =?UTF-8?q?=20extraMcpServers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conventions.md b/docs/conventions.md index 7060e205..8a73a381 100644 --- a/docs/conventions.md +++ b/docs/conventions.md @@ -280,7 +280,7 @@ binary flavor. | `messaging` | `send`, `recv`, `ask`, `answer` | | `meta` | `set_status`, `get_agent_meta` | | `inbox` | `get_loose_ends`, `cancel_loose_end`, `remind`, `request_next_turn` | -| `execution` | `run`, `status` (`mcp__bash__run`, `mcp__bash__status`) | +| `execution` | vestigial — `mcp__bash__run` / `mcp__bash__status` are always available unconditionally via `extraMcpServers`; this group's entries expand to non-existent `mcp__hyperhive__run` / `mcp__hyperhive__status` and have no effect. See `docs/tools/bash.md`. | | `lifecycle` | `kill`, `start`, `restart`, `update` *(privileged)* | | `approvals` | `request_init_config`, `request_apply_commit`, `request_update_meta_inputs` *(privileged)* | | `scheduling` | `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, `edit_schedule`, `list_schedules` *(privileged)* | From 2fff9485b99f251712afcc420cb0e445b57ff09d Mon Sep 17 00:00:00 2001 From: damocles Date: Fri, 5 Jun 2026 00:47:03 +0200 Subject: [PATCH 6/6] fix: cancel_loose_end approval kind is root-agent-only, no own-approval carve-out --- hive-ag3nt/src/mcp.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 5db88a56..f6ee89db 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -864,10 +864,9 @@ impl AgentServer { `\"reminder\"`; `id` is the row id from the matching `get_loose_ends` entry \ or the `question_queued` reply you got when you submitted. Auth: you can only \ cancel rows where you're the asker / owner. Returns `ok` or an error string.\n\ - Agents with the `approvals` tool group can also pass `kind: \"approval\"` to \ - withdraw a pending approval they submitted (before the operator acts on it). \ - The root agent (`ruth`) may cancel any approval; non-root agents are restricted \ - to their own approvals and the server rejects `approval` kind otherwise." + `kind` may also be `\"approval\"` to withdraw a pending approval you submitted \ + (before the operator acts on it) — root agent (`ruth`) only; the server rejects \ + `approval` kind for all other callers." )] async fn cancel_loose_end(&self, Parameters(args): Parameters) -> String { let log = format!("{args:?}");