diff --git a/hive-ag3nt/Cargo.toml b/hive-ag3nt/Cargo.toml index bc33727c..ed5de53b 100644 --- a/hive-ag3nt/Cargo.toml +++ b/hive-ag3nt/Cargo.toml @@ -28,11 +28,10 @@ tracing-subscriber.workspace = true tempfile = "3" [[bin]] -# Unified harness binary (#598). Replaces the pre-#598 split into -# `hive-ag3nt` (sub-agent) + `hive-m1nd` (manager). Both code paths -# live here; the binary picks its role at startup from `HIVE_ROLE` -# (set by `harness-base.nix` from `hyperhive.role` — `"agent"` or -# `"manager"`). The privilege boundary is enforced server-side at -# the socket, so shipping both surfaces in one binary is safe. +# Unified harness binary: both `agent` and `manager` code paths live +# here; the binary picks its role at startup from `HIVE_ROLE` (set by +# `harness-base.nix` from `hyperhive.role`). The privilege boundary is +# enforced server-side at the socket, so shipping both surfaces in one +# binary is safe. See `docs/turn-loop.md::Harness binary shape`. name = "hive" path = "src/bin/hive.rs" diff --git a/hive-ag3nt/prompts/system.md b/hive-ag3nt/prompts/system.md index 0ef2fe0d..2cdb47b0 100644 --- a/hive-ag3nt/prompts/system.md +++ b/hive-ag3nt/prompts/system.md @@ -23,7 +23,7 @@ Tools (hyperhive surface): - `mcp__hyperhive__request_schedule_prompt(targets, body, first_fire_at_unix, interval_seconds?, description?)` — queue an approval for the operator to add a scheduled prompt. On approve hive-c0re inserts a schedule row and the worker fans `body` out to each agent in `targets` at `first_fire_at_unix` (recurring every `interval_seconds` if set, one-shot when absent). Even self-targeted schedules go through approval — the existing `remind` tool stays the quick no-approval self-wake path. Catch-up clamp: long downtime fires ONCE per recurring row on resume (skipped count surfaces in per-target `last_result`), not N stacked pulses. - `mcp__hyperhive__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 keeps firing for any remaining active targets, auto-cancels when every target is gone). Authorization: you can cancel schedules you own OR any owned by a sub-agent in your subtree per topology.json. - `mcp__hyperhive__fire_schedule_now(id)` — fire a scheduled prompt out of band. Runs the per-target fan-out once immediately. Recurring schedules keep their cadence intact (the manual fire is additive); one-shot schedules are CONSUMED by the manual fire (cancelled afterwards). Same authorization as `cancel_schedule`. -- `mcp__hyperhive__edit_schedule(id, body?, description?, interval_seconds?, next_fire_at_unix?, targets_add?, targets_remove?)` — partial-update a schedule's mutable fields (#474). Pass only the fields you want to change. `targets_add` / `targets_remove` mutate the recipient list in the same transaction; re-adding a previously-cancelled target drops the tombstone + history (operator intent: "fresh start"). Refuses cancelled rows. Same authorization as `cancel_schedule`. Note: clearing scalar fields (e.g. flipping recurring→one-shot) is operator-only via the dashboard PATCH — the agent surface only supports positive sets on `description` / `interval_seconds`. +- `mcp__hyperhive__edit_schedule(id, body?, description?, interval_seconds?, next_fire_at_unix?, targets_add?, targets_remove?)` — 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; re-adding a previously-cancelled target drops the tombstone + history (operator intent: "fresh start"). Refuses cancelled rows. Same authorization as `cancel_schedule`. Note: clearing scalar fields (e.g. flipping recurring→one-shot) is operator-only via the dashboard PATCH — the agent surface only supports positive sets on `description` / `interval_seconds`. - `mcp__hyperhive__list_schedules()` — snapshot every schedule in the queue (active + cancelled-but-not-reaped). Returns id, owner, body, target set with per-target `last_fired_at` + `last_result`, `next_fire_at_unix`, recurring `interval_seconds`. Use to look up an id before cancelling, or to audit upcoming wake-ups across the swarm. - `mcp__hyperhive__get_logs(agent, lines?)` — fetch recent journal lines for a sub-agent container. Use to diagnose MCP-server registration failures, startup crashes, or harness issues you can't see from inside. Pass the plain logical agent name; `lines` defaults to 50 (capped at 500). diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 5770c2df..3b806fcd 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -295,9 +295,10 @@ fn loose_end_kind_label(kind: hive_sh4re::CancelLooseEndKind) -> &'static str { /// `hyperhive_rev`, and `running` are always shown; `status` only /// appears when one is set, otherwise the line reads `status: `. /// When `running` is false the host has already cleared `status_text` -/// (it would be stale from before the stop, #432) so the status line -/// is implicitly `` in that case — but the explicit `running: -/// no` line tells the caller WHY. +/// (it would be a stale snapshot from before the stop) so the status +/// line is implicitly `` in that case — but the explicit +/// `running: no` line tells the caller WHY. See +/// `docs/turn-loop.md::Sub-agent tools` (`get_agent_meta`). #[must_use] pub fn format_agent_meta(resp: Result) -> String { match resp { @@ -315,8 +316,8 @@ pub fn format_agent_meta(resp: Result) -> String { let run = if running { "yes" } else { "no" }; let mut out = format!("name: {name}\nrole: {role}\nhyperhive_rev: {rev}\nrunning: {run}"); - // #710: surface hive + swarm display names only when set, - // so single-hive deployments don't see noisy `` lines. + // Surface hive + swarm display names only when set, so + // single-hive deployments don't see noisy `` lines. if let Some(hn) = hive_name.as_deref() { use std::fmt::Write as _; let _ = write!(out, "\nhive_name: {hn}"); @@ -507,8 +508,7 @@ impl AgentServer { // its name to `allowed_mcp_tools(Flavor::Agent)` below. Claude // Code's permission gate refuses uninlisted MCP tools in // non-interactive `--print` mode with "permissions not granted yet" -// — same failure mode #511 cleaned up on the manager side. Keep the -// two lists in lockstep. +// — keep the two lists in lockstep. #[tool_router] impl AgentServer { #[tool( @@ -1102,8 +1102,7 @@ impl ManagerServer { // its name to `allowed_mcp_tools(Flavor::Manager)` below. Claude // Code's permission gate refuses uninlisted MCP tools in // non-interactive `--print` mode with "permissions not granted yet" -// — exactly the failure mode PR #511 cleaned up. Keep the two lists -// in lockstep. +// — keep the two lists in lockstep. #[tool_router] impl ManagerServer { #[tool( @@ -1387,11 +1386,11 @@ impl ManagerServer { } #[tool( - description = "Edit an existing scheduled prompt's mutable fields (#474). Pass only \ + description = "Edit an existing scheduled prompt's mutable fields. Pass only \ the fields you want to change — anything omitted keeps its current value. Editable: \ `body`, `description`, `interval_seconds` (positive only via this tool; flipping \ recurring→one-shot is operator-only via the dashboard), `next_fire_at_unix`, and \ - the target set via `targets_add` / `targets_remove` (#478). Both target lists are \ + the target set via `targets_add` / `targets_remove`. Both target lists are \ applied in the same transaction with removes-before-adds, so a single edit can \ swap a target atomically. Re-adding a previously-removed target starts a fresh \ per-target history (drops the tombstone). Draining all targets auto-cancels the \ @@ -1659,7 +1658,7 @@ impl ManagerServer { with the operator-override sentinel so the asker unblocks), a `reminder` \ (hard-deleted before fire), or an `approval` (withdraws a pending approval \ you submitted; the dashboard pulls the card from pending and the row resolves \ - as `cancelled` instead of approved/denied/failed — closes #250). `kind` is \ + as `cancelled` instead of approved/denied/failed). `kind` is \ `\"question\"`, `\"reminder\"`, or `\"approval\"`; `id` is the row id from \ `get_loose_ends` or the original submission reply. Manager surface bypasses \ the owner check on the sub-agent flavour — use for hive-wide cleanup of \ @@ -1795,13 +1794,12 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec { "restart", "update", "request_apply_commit", - // Tools added post-#444 / #235 / #467 / #472 / #474 / #478 that - // got missed in the allow-list when their `#[tool]` impls - // landed. Claude Code's permission gate refuses uninlisted - // tools in non-interactive `--print` mode with a "permissions - // not granted yet" error (hm1nd hit this trying to run the - // dedup pass for #509). Keep this block in lockstep with the - // `#[tool]` fns in the `ManagerServer` impl. + // The remaining manager tools below were added incrementally + // and have historically been missed in the allow-list when + // their `#[tool]` impls landed. Claude Code's permission gate + // refuses uninlisted tools in non-interactive `--print` mode + // with a "permissions not granted yet" error — keep this + // block in lockstep with the `#[tool]` fns in `ManagerServer`. "request_update_meta_inputs", "request_schedule_prompt", "fire_schedule_now", @@ -1912,7 +1910,8 @@ fn check_send_allowed(to: &str) -> Result<(), String> { // can rewire who the parent IS via `set_parent` without // having to remember to update the per-agent allow-list. // The broker resolves the sentinel to the real parent label - // on the host side per topology.json (#692). + // on the host side per topology.json (falls back to `operator` + // for root agents). return Ok(()); } let Ok(raw) = std::fs::read_to_string(SEND_ALLOW_PATH) else {