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)* | 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). diff --git a/hive-ag3nt/prompts/system.md b/hive-ag3nt/prompts/system.md index ce9118e2..304787eb 100644 --- a/hive-ag3nt/prompts/system.md +++ b/hive-ag3nt/prompts/system.md @@ -7,11 +7,11 @@ 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. -- `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. @@ -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..f6ee89db 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 \ @@ -859,7 +863,10 @@ 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\ + `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:?}"); 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 }) }