From b88a5b2430cfa3116997bc44ab374cb94b9db525 Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 20 Sep 2026 19:36:09 +0200 Subject: [PATCH] remove the list_containers and request_update_meta_inputs MCP tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both agent-facing tools go away end to end, with no replacement. This is an intentional capability removal: agents can no longer enumerate their own subtree, and can no longer queue a meta-flake input bump. The system prompt and docs/tools/lifecycle.md land in this same commit on purpose. A tool named in the prompt but absent from the server makes agents confidently call something that doesn't exist, and the failure then surfaces far from its cause. Removed: - MCP registrations and bodies (hive-agent-mcp), plus the now-unused UpdateMetaInputsArgs. - Wire variants Request::ListDescendants, Request::RequestUpdateMetaInputs and Response::Containers, plus ContainerInfo, whose only consumer was that response. - hive-c0re's handle_list_descendants (its whole module) and handle_request_update_meta_inputs, the two dispatch arms, and the require_group(agent, "approvals", ...) gate on the meta-inputs verb. - The stream_enrich emoji entry and argument formatter. - docs/tools/lifecycle.md (both tools it documented are gone), its two referrers, the tool-group tables and the agent-hierarchy prose. Tool groups are kept, deliberately. ToolGroup::Lifecycle listed exactly one tool and now lists none — it is vestigial, but the variant stays so existing meta/capabilities.json grants still parse; retiring it is a separate decision. ToolGroup::Approvals also listed exactly one tool, but the group is NOT dead: check_can_cancel_approval still gates cancel_loose_end's approval-cancel arm on it server-side. ApprovalKind::UpdateMetaInputs stays too. Nothing in production code produces it any more, but pre-existing approval rows may still carry it, and the operator's own path to a meta update is unaffected — the dashboard's POST /api/meta-update inserts the meta_update job directly, bypassing approvals entirely. The two format_ack tests in hive-agent-mcp that named request_update_meta_inputs were only using it as a label string while exercising the generic OkWarn/Ok renderer, so they are retargeted to a surviving tool rather than deleted. Note hive-c0re's priv_client::list_containers is a different thing (the host-side privileged container listing behind hive-priv) and is untouched. Closes #4591 --- docs/agent-lifecycle/agent-hierarchy.md | 21 +++-- docs/process/conventions.md | 6 +- docs/tools/README.md | 2 - docs/tools/lifecycle.md | 58 -------------- docs/turn-loop/mcp.md | 10 ++- hive-agent-mcp/src/mcp/args.rs | 11 --- hive-agent-mcp/src/mcp/mod.rs | 77 +------------------ hive-agent-mcp/src/mcp/render.rs | 8 +- hive-agent/prompts/system.md | 4 +- hive-agent/src/stream_enrich.rs | 17 +--- hive-c0re/src/job_queue/exec.rs | 4 +- .../src/socket_server/config_approvals.rs | 52 +------------ .../src/socket_server/lifecycle_handlers.rs | 48 ------------ hive-c0re/src/socket_server/mod.rs | 31 +++----- hive-c0re/src/stores/approvals.rs | 8 +- hive-core-agent-sock/src/lib.rs | 19 +---- hive-sh4re/src/container.rs | 15 +--- hive-sh4re/src/permissions.rs | 39 ++++++---- 18 files changed, 74 insertions(+), 356 deletions(-) delete mode 100644 docs/tools/lifecycle.md delete mode 100644 hive-c0re/src/socket_server/lifecycle_handlers.rs diff --git a/docs/agent-lifecycle/agent-hierarchy.md b/docs/agent-lifecycle/agent-hierarchy.md index e4b75733..a84b235c 100644 --- a/docs/agent-lifecycle/agent-hierarchy.md +++ b/docs/agent-lifecycle/agent-hierarchy.md @@ -96,7 +96,6 @@ umount-old / mount-new / restart-cascade step. | config change via forge PR (any descendant's config) | any ancestor | | moderate reminders (cancel any open thread of a descendant) | any ancestor | | `send` / `recv` routing | parent ↔ same-parent siblings ↔ self ↔ descendants; explicit allow-list for anyone else | -| `request_update_meta_inputs` (bump meta lock) | root agents only (today: just `manager`) | "Ancestor" walks `ContainerView.parent` chains; a visited-set guards against cycles at dispatch time (a malformed `topology.json` can't lock @@ -116,14 +115,11 @@ other agents don't: approval step — every other agent goes through a `Spawn` approval. Topology-wise, `ruth` is still just another root agent. - **Wire-protocol** — the privileged `Request` variants - (`Kill` / `Start` / `Restart` / `Update`; - `GetLogs`; `RequestUpdateMetaInputs`) — marked `*(privileged)*` in - `hive-core-agent-sock`'s unified `Request` enum — are reachable only - from the manager's socket flavour today. Planned rule for each is in the - table above ("any ancestor" for lifecycle/logs); - `RequestUpdateMetaInputs` stays - a root-only capability even post-milestone, not a topology rule. - One exception: `Wake` (inject a `from: ` message into the + (`Kill` / `Start` / `Restart` / `Update`; `GetLogs`) — marked + `*(privileged)*` in `hive-core-agent-sock`'s unified `Request` enum — + are reachable only from the manager's socket flavour today. Planned + rule for each is in the table above ("any ancestor" for + lifecycle/logs). One exception: `Wake` (inject a `from: ` message into the caller's own inbox) isn't really privileged — every per-agent daemon (for example `hive-forge-notify`) needs it, and sub-agents already have the equivalent on their own socket. @@ -135,9 +131,10 @@ other agents don't: deploy log). Planned: each agent gets RW to `/agents//` for just its own subtree — the manager's full-forest RW becomes the "root's subtree is everything" case of that same rule. hive-c0re will - gate RO `/meta` access on a "meta read" capability; only - `request_update_meta_inputs` writes `flake.lock`, gated by its own - capability. + gate RO `/meta` access on a "meta read" capability; no agent-facing + path writes `flake.lock` any more — `request_update_meta_inputs` was + removed, leaving the operator dashboard's `POST +/api/meta-update` as the only entry point. - **Prompt/tools** — the system prompt uses `` / `` marker blocks, and a `Flavor::{Agent, Manager}` switch picks the MCP tool allow-list claude sees. Both are diff --git a/docs/process/conventions.md b/docs/process/conventions.md index 8bbffe1a..d21bc6a0 100644 --- a/docs/process/conventions.md +++ b/docs/process/conventions.md @@ -310,8 +310,8 @@ binary flavor. | `meta` | `get_agent_meta` (`set_status` is always-on, see below) | | `inbox` | `get_loose_ends`, `cancel_loose_end`, `remind` | | `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` | `list_containers` *(privileged)* | -| `approvals` | `request_update_meta_inputs` *(privileged)* | +| `lifecycle` | none — `list_containers` no longer exists, with no replacement; the variant survives only so existing grants parse. | +| `approvals` | none — `request_update_meta_inputs` no longer exists, with no replacement. Still a live server-side gate: `cancel_loose_end`'s approval-cancel arm requires it. | | `scheduling` | `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, `edit_schedule`, `list_schedules` *(privileged)* | | `forge` | `create_repo` — create git repos through hive-c0re (operator-gated merge) | | `web_tools` | none (gates the Claude built-ins `WebFetch`/`WebSearch`, not an MCP tool) | @@ -391,7 +391,7 @@ bypass the operator approval gate. `hive-sh4re/src/permissions.rs` + an arm to `as_str`. Add it to `Capability::ALL` (the source of truth for the permissions UI columns). Implement the access check in the relevant handler (`hive-c0re/src/socket_server/mod.rs`, -`hive-c0re/src/socket_server/lifecycle_handlers.rs`, `coordinator.rs`, or a +`hive-c0re/src/socket_server/schedules.rs`, `coordinator.rs`, or a handler under `hive-c0re/src/dashboard/`). ## Async forms diff --git a/docs/tools/README.md b/docs/tools/README.md index 8fea9e1d..f88794d8 100644 --- a/docs/tools/README.md +++ b/docs/tools/README.md @@ -40,8 +40,6 @@ debug agent behavior. - **[forge-cli](forge-cli.md)** — the exhaustive, autogenerated flag-by-flag reference for `hive-forge`, kept in lockstep with the binary by CI the same way `hivectl-cli.md` is. -- **[lifecycle](lifecycle.md)** — listing the agents in a caller's own - subtree, plus the approval-gated config-change tools. - **[matrix](matrix.md)** — the matrix MCP tool surface (`mcp__matrix__*`) for agents with a matrix account, multiple accounts per agent, and declaring extra MCP servers generally. diff --git a/docs/tools/lifecycle.md b/docs/tools/lifecycle.md deleted file mode 100644 index 88b6f90f..00000000 --- a/docs/tools/lifecycle.md +++ /dev/null @@ -1,58 +0,0 @@ -# Lifecycle and approvals tools - -Two tool groups govern agent lifecycle management and config changes. -The server scopes both to the caller's **own subtree** (topology-enforced -per `topology.json`: a child, a child's child, every agent below them — -plus the caller itself). No privileged class exists to belong to; the -root agent reaches every agent purely because the check is transitive and -everything sits under it. - -## `lifecycle` tool group - -No operator approval required. The caller's own subtree. - -### `list_containers()` - -List the caller's whole **subtree** with running status — children, -their children, every agent below them. The calling agent is part of its -own subtree, so it appears in its own listing; a leaf agent gets a -one-row answer naming itself. - -## `approvals` tool group - -Meta-flake input bumps route through the operator approval queue. - -Creating a new agent is **not** in this group — agents have no tool for -it. The swarm controller's `InitAgentConfigRepo` job scaffolds a new -agent's config repo (`POST /api/agents`, see -`swarm-controller/`), and the operator spawns the container from the -dashboard (`◆ R3QU3ST SP4WN` / `Spawn` approval, routed via -`HostRequest::RequestSpawn`). - -Config changes on an existing agent go through a **forge PR** on the -agent's `agent-configs/` repo (queues a `MergeConfigPr` approval -on open/update — no MCP tool involved), not a tool call. See -`docs/agent-lifecycle/approvals.md`. - -### `request_update_meta_inputs(inputs?, description?)` - -Queue an approval to run `nix flake update [inputs...]` on the meta -flake. Pass specific input names (for example `["bitburner-agent"]`) or omit -/ pass `[]` for all inputs. Returns immediately; the lock update runs -on operator approval. - -**Doesn't** trigger container rebuilds — the operator rebuilds affected -agents after the approval resolves. - -## Boundary summary - -| Operation | Requires approval? | Scope | -| ---------------------------- | ------------------ | ---------------------------- | -| `list_containers` | No | Own subtree, caller included | -| `request_update_meta_inputs` | Yes (MetaUpdate) | Meta flake (global) | - -## See also - -- [`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md) — full approval flow, kinds, - helper events (`approval_resolved`), flake.lock - validation. diff --git a/docs/turn-loop/mcp.md b/docs/turn-loop/mcp.md index 79784309..bab51e46 100644 --- a/docs/turn-loop/mcp.md +++ b/docs/turn-loop/mcp.md @@ -130,9 +130,13 @@ hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a - **Subagent spawning** — headless claude sub-instances as background tasks, shipped default-on like bash execution (no tool group gates it yet). See [`docs/tools/subagent.md`](../tools/subagent.md). -- **Lifecycle + config** (`lifecycle`, `approvals`) — list the child - agents in your own subtree, apply config commits. See - [`docs/tools/lifecycle.md`](../tools/lifecycle.md). +- **Lifecycle + config** (`lifecycle`, `approvals`) — neither group + carries an MCP tool any more: `list_containers` and + `request_update_meta_inputs` no longer exist, with no + replacement. `approvals` survives as a server-side gate on + `cancel_loose_end`'s approval-cancel arm; `lifecycle` gates nothing. + Config changes go through a forge PR on `agent-configs/` — see + [`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md). - **Scheduling** (`scheduling`) — scheduled prompts. See [`docs/tools/scheduling.md`](../tools/scheduling.md). - **Forge repos** (`forge`) — `create_repo` — the only agent path to diff --git a/hive-agent-mcp/src/mcp/args.rs b/hive-agent-mcp/src/mcp/args.rs index 2c3b4ac7..4051294e 100644 --- a/hive-agent-mcp/src/mcp/args.rs +++ b/hive-agent-mcp/src/mcp/args.rs @@ -131,17 +131,6 @@ pub struct CancelLooseEndArgs { pub id: i64, } -#[derive(Debug, serde::Deserialize, schemars::JsonSchema)] -pub struct UpdateMetaInputsArgs { - /// Flake input names to update (e.g. `["bitburner-agent", "nixpkgs"]`). - /// Pass an empty list to update ALL inputs. - #[serde(default)] - pub inputs: Vec, - /// Optional description shown on the dashboard approval card. - #[serde(default)] - pub description: Option, -} - #[derive(Debug, serde::Deserialize, schemars::JsonSchema)] pub struct RequestSchedulePromptArgs { /// Recipient agents — one schedule fires to many inboxes at the diff --git a/hive-agent-mcp/src/mcp/mod.rs b/hive-agent-mcp/src/mcp/mod.rs index c4ba2e45..bdc99a4c 100644 --- a/hive-agent-mcp/src/mcp/mod.rs +++ b/hive-agent-mcp/src/mcp/mod.rs @@ -26,7 +26,7 @@ mod render; pub use args::{ AckUntilArgs, CancelLooseEndArgs, CancelScheduleArgs, CompactArgs, CreateRepoArgs, EditScheduleArgs, FireScheduleNowArgs, GetAgentMetaArgs, GetHostJournalArgs, MarkTodosDoneArgs, - RecvArgs, RemindArgs, RequestSchedulePromptArgs, SendArgs, SetStatusArgs, UpdateMetaInputsArgs, + RecvArgs, RemindArgs, RequestSchedulePromptArgs, SendArgs, SetStatusArgs, }; pub use render::{annotate_retries, format_ack, format_agent_meta, format_recv}; @@ -547,44 +547,6 @@ impl AgentServer { .await } - // IMPORTANT: this tool is only available when the `lifecycle` tool group - // is granted to this agent. Returns the calling agent's whole subtree, - // itself included, with running status. - #[tool( - description = "List this agent's whole subtree — children, their children, and so on \ - down — with running status. The calling agent is part of its own subtree, so it \ - appears in the listing too. Requires the `lifecycle` tool group. \ - Returns every known descendant regardless of running state — check the `running` \ - field to distinguish live from stopped containers. Ordered by topology depth \ - (parents before children), then alphabetically within each tier." - )] - async fn list_containers(&self) -> String { - run_tool_envelope("list_containers", String::new(), async move { - let (resp, retries) = self - .dispatch(hive_core_agent_sock::Request::ListDescendants) - .await; - let body = match resp { - Ok(hive_core_agent_sock::Response::Containers { containers }) => { - if containers.is_empty() { - "no descendant containers".to_owned() - } else { - containers - .iter() - .map(|c| { - let status = if c.running { "running" } else { "stopped" }; - format!("{} ({})", c.name, status) - }) - .collect::>() - .join("\n") - } - } - other => reply_err(other, "list_containers"), - }; - annotate_retries(body, retries) - }) - .await - } - // IMPORTANT: this tool is capability-gated (`read_host_journal`). // It is added to `--allowedTools` by `allowed_capability_tools` only // when `HIVE_CAPABILITIES` contains `read_host_journal`. hive-c0re @@ -627,43 +589,6 @@ impl AgentServer { .await } - #[tool( - description = "Queue an approval for the operator to run `nix flake update` on the \ - meta flake and commit the resulting lock changes. Pass specific input names to update \ - only those inputs (e.g. `[\"bitburner-agent\"]`), or pass an empty list to update ALL \ - inputs. Returns immediately — the lock update runs when the operator approves. \ - Does NOT trigger container rebuilds — the operator rebuilds affected agents \ - after the approval resolves." - )] - async fn request_update_meta_inputs( - &self, - Parameters(args): Parameters, - ) -> String { - let log = format!("{args:?}"); - run_tool_envelope("request_update_meta_inputs", log, async move { - let label = if args.inputs.is_empty() { - "all inputs".to_string() - } else { - args.inputs.join(", ") - }; - let (resp, retries) = self - .dispatch(hive_core_agent_sock::Request::RequestUpdateMetaInputs { - inputs: args.inputs, - description: args.description, - }) - .await; - annotate_retries( - format_ack( - resp, - "request_update_meta_inputs", - format!("approval queued: {label}"), - ), - retries, - ) - }) - .await - } - #[tool( description = "Queue an approval to add a scheduled prompt — one body delivered to \ N agent inboxes at a target time, optionally recurring every `interval_seconds`. \ diff --git a/hive-agent-mcp/src/mcp/render.rs b/hive-agent-mcp/src/mcp/render.rs index ef086450..3a920f1a 100644 --- a/hive-agent-mcp/src/mcp/render.rs +++ b/hive-agent-mcp/src/mcp/render.rs @@ -643,12 +643,12 @@ mod tests { Ok(hive_core_agent_sock::Response::OkWarn { warnings: vec!["name is reserved".to_owned(), "second thing".to_owned()], }), - "request_update_meta_inputs", - "update_meta_inputs approval queued".to_owned(), + "request_schedule_prompt", + "schedule approval queued".to_owned(), ); // The operation HAPPENED — dropping the success line would read as a // failure and invite a retry that queues a second approval. - assert!(out.starts_with("update_meta_inputs approval queued")); + assert!(out.starts_with("schedule approval queued")); assert!(out.contains("⚠️ name is reserved")); assert!(out.contains("⚠️ second thing")); } @@ -659,7 +659,7 @@ mod tests { // warning marker would pass the test above. let out = format_ack( Ok(hive_core_agent_sock::Response::Ok), - "request_update_meta_inputs", + "request_schedule_prompt", "queued".to_owned(), ); assert_eq!(out, "queued"); diff --git a/hive-agent/prompts/system.md b/hive-agent/prompts/system.md index c4b85380..53d7d18b 100644 --- a/hive-agent/prompts/system.md +++ b/hive-agent/prompts/system.md @@ -4,8 +4,6 @@ Tools (hyperhive surface). Full signature + behavior for each comes from the too - **Inbox / messaging** (always available): `mcp__hyperhive__recv`, `mcp__hyperhive__ack_until`, `mcp__hyperhive__send`, `mcp__hyperhive__get_loose_ends`, `mcp__hyperhive__cancel_loose_end`, `mcp__hyperhive__mark_todos_done`, `mcp__hyperhive__remind`, `mcp__hyperhive__set_status`, `mcp__hyperhive__get_agent_meta`. One habit worth internalizing beyond the tool descriptions themselves: prefer ending the turn over repeatedly polling `recv` when idle (only turn-boundaries observe in-container todo wakes — bash-task completions, matrix unread, forge activity — and ending the turn is also your checkpoint). For a large todo backlog (`get_loose_ends` caps at 40 rows), clear reviewed ids in bulk with `mark_todos_done` rather than cancelling one at a time — there's no blind range-clear, only ids you've actually looked at. - **Extra MCP tools** (some agents only): `mcp____` — agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. First-class tools, already operator-approved at deploy time. -- **Lifecycle** (_requires `lifecycle` tool group_, your own subtree — children, their children, and so on down, no approval needed): `list_containers`. -- **Approvals** (_requires `approvals` tool group_, queues an operator approval): `request_update_meta_inputs`. - **Scheduling** (_requires `scheduling` tool group_): `request_schedule_prompt` (queues an approval), `cancel_schedule`, `fire_schedule_now`, `edit_schedule`, `list_schedules` (these four don't need approval — you can manage schedules you own or that a sub-agent in your subtree owns). - **Diagnostics**: `get_host_journal` (_requires `read_host_journal` capability_). @@ -19,7 +17,7 @@ Messages from sender `system` are hyperhive helper events (JSON body, `event` fi - `needs_update` — agent's flake rev is stale. Ask the operator to rebuild it. - `container_crash` — ask the operator to start it again; if it keeps crashing, say so with what you saw. -- `approval_resolved` — one of your own submitted approvals (`request_update_meta_inputs`, a scheduled prompt, a config PR, …) was approved, denied, or failed; the body carries the resolution. +- `approval_resolved` — one of your own submitted approvals (a scheduled prompt, a config PR, …) was approved, denied, or failed; the body carries the resolution. Lifecycle notices that don't need an immediate turn — a new agent spawned, a container rebuilt/killed/destroyed, or its login state changing — surface as todos instead of messages now. Call `get_loose_ends` to see them. diff --git a/hive-agent/src/stream_enrich.rs b/hive-agent/src/stream_enrich.rs index 33a3848d..2a5056b1 100644 --- a/hive-agent/src/stream_enrich.rs +++ b/hive-agent/src/stream_enrich.rs @@ -498,8 +498,7 @@ fn tool_icon(name: &str) -> &'static str { "mcp__hyperhive__cancel_loose_end" => "✂️", "mcp__hyperhive__ack_until" => "✅", "mcp__hyperhive__get_agent_meta" => "ℹ️", - "mcp__hyperhive__list_containers" - | "mcp__matrix__list_rooms" + "mcp__matrix__list_rooms" | "mcp__matrix__list_room_members" | "mcp__matrix__list_invites" => "📋", "mcp__hyperhive__get_host_journal" => "📜", @@ -666,8 +665,7 @@ fn fmt_hyperhive_tool(name: &str, short: &str, input: &Value) -> String { } } "mcp__hyperhive__remind" => fmt_hyperhive_remind(short, input), - "mcp__hyperhive__request_update_meta_inputs" - | "mcp__hyperhive__list_schedules" + "mcp__hyperhive__list_schedules" | "mcp__hyperhive__cancel_schedule" | "mcp__hyperhive__fire_schedule_now" | "mcp__hyperhive__edit_schedule" @@ -792,17 +790,6 @@ fn fmt_hyperhive_edit_schedule(short: &str, input: &Value) -> String { /// Schedule-management hyperhive tools (list/cancel/fire/edit/request). fn fmt_hyperhive_schedule_tool(name: &str, short: &str, input: &Value) -> String { match name { - "mcp__hyperhive__request_update_meta_inputs" => { - let ins = match input.get("inputs").and_then(Value::as_array) { - Some(arr) if !arr.is_empty() => { - let names: Vec<&str> = arr.iter().filter_map(Value::as_str).take(4).collect(); - let tail = if arr.len() > 4 { ", …" } else { "" }; - format!("[{}{}]", names.join(", "), tail) - } - _ => "all".to_owned(), - }; - format!("{short} {ins}") - } "mcp__hyperhive__list_schedules" => format!("{short}()"), "mcp__hyperhive__cancel_schedule" => { let id = input diff --git a/hive-c0re/src/job_queue/exec.rs b/hive-c0re/src/job_queue/exec.rs index 92bd448b..27309eba 100644 --- a/hive-c0re/src/job_queue/exec.rs +++ b/hive-c0re/src/job_queue/exec.rs @@ -892,8 +892,8 @@ async fn run_deploy_tail( /// otherwise just the agents named by `agent-` inputs. /// Topology-sorted so parents rebuild before their children. /// -/// `inputs` is the caller-supplied flake-input-name list -/// (`RequestUpdateMetaInputs`'s `inputs` field, operator-approved but not +/// `inputs` is the caller-supplied flake-input-name list (the dashboard's +/// `POST /api/meta-update` form field, operator-supplied but not /// otherwise validated) — the `agent-` branch parses agent names /// straight out of it, so each is validated through [`hive_types::Ident`] /// before it ever reaches a filesystem path or a forge URL built from a diff --git a/hive-c0re/src/socket_server/config_approvals.rs b/hive-c0re/src/socket_server/config_approvals.rs index 147c135e..c1aeeb14 100644 --- a/hive-c0re/src/socket_server/config_approvals.rs +++ b/hive-c0re/src/socket_server/config_approvals.rs @@ -1,5 +1,4 @@ -//! Config-approval request handlers: `RequestUpdateMetaInputs`, plus the -//! shared submit helper `submit_merge_config_pr`. +//! Config-approval submit helper `submit_merge_config_pr`. //! //! `submit_merge_config_pr` is called from the dashboard webhook handler //! (`dashboard::webhook`) — agents no longer need an MCP tool for config @@ -8,57 +7,8 @@ use std::sync::Arc; -use hive_core_agent_sock::Response; - use crate::coordinator::Coordinator; -/// `RequestUpdateMetaInputs` — queue an `UpdateMetaInputs` approval -/// carrying the JSON-encoded input list in `commit_ref` (no git commit -/// is involved; the field is the payload the approval handler decodes). -pub(super) fn handle_request_update_meta_inputs( - coord: &Arc, - requester: &str, - inputs: &[String], - description: Option<&str>, -) -> Response { - let label = if inputs.is_empty() { - "all inputs".to_string() - } else { - inputs.join(", ") - }; - tracing::info!(%requester, %label, "request_update_meta_inputs"); - let commit_ref = serde_json::to_string(inputs).unwrap_or_default(); - let id = match coord - .approvals - .submit_kind( - requester, - hive_sh4re::approvals::ApprovalKind::UpdateMetaInputs, - &commit_ref, - description, - requester, - None, - ) - .map_err(|e| anyhow::anyhow!("{e:#}")) - { - Ok(id) => id, - Err(e) => { - return Response::Err { - message: format!("queue update_meta_inputs approval: {e:#}"), - }; - } - }; - tracing::info!(%id, %label, "update_meta_inputs approval queued"); - coord.emit_approval_added(crate::coordinator::ApprovalAdded { - id, - agent: requester, - approval_kind: "update_meta_inputs", - sha_short: None, - description: description.map(str::to_owned), - pr_number: None, - }); - Response::Ok -} - /// Submit-time half of the PR-merge flow: fetch the PR head sha from the /// forge, queue the approval row, and emit the `approval_added` event so the /// dashboard shows the pending card immediately. diff --git a/hive-c0re/src/socket_server/lifecycle_handlers.rs b/hive-c0re/src/socket_server/lifecycle_handlers.rs deleted file mode 100644 index fe19b735..00000000 --- a/hive-c0re/src/socket_server/lifecycle_handlers.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! `ListDescendants` request handler — the `lifecycle` tool group's -//! remaining verb, a read of the caller's own subtree. - -use std::sync::Arc; - -use hive_core_agent_sock::Response; - -use crate::coordinator::Coordinator; - -/// `ListDescendants` — every topological descendant of `agent` with -/// its running/stopped state, parents before children. -pub(super) async fn handle_list_descendants(coord: &Arc, agent: &str) -> Response { - tracing::debug!(%agent, "agent: list descendants"); - // Walk the full topology and collect every descendant. - let topo = crate::topology::read(); - let mut names: Vec = topo - .keys() - .filter(|name| crate::topology::is_descendant_of(name, agent)) - .cloned() - .collect(); - // Parents before children, then alpha within each tier. - crate::auto_update::topology_sort(&mut names, &topo); - // Read from the coordinator's cached container snapshot instead of - // live-querying each container's systemd unit state — the same - // `containers_snapshot()` the dashboard's `/api/state` cold-load path - // already uses, kept fresh by `rescan_containers_and_emit()` on every - // mutation plus the crash-watcher's periodic poll. Avoids N - // `systemctl is-active` subprocess spawns per `list_containers` call; - // per mara, daemons should do the expensive work themselves and serve - // clients a cheap cached read. - let snapshot = coord.containers_snapshot().await; - let running_by_name: std::collections::HashMap<&str, bool> = snapshot - .iter() - .map(|v| (v.name.as_str(), v.running)) - .collect(); - let containers = names - .into_iter() - .map(|name| { - // A descendant absent from the snapshot (not yet scanned since - // its own registration, e.g. mid-spawn) reads as not running - // rather than erroring — matches the old membership-check's - // default-false behavior for an unknown name. - let running = running_by_name.get(name.as_str()).copied().unwrap_or(false); - hive_sh4re::container::ContainerInfo { name, running } - }) - .collect(); - Response::Containers { containers } -} diff --git a/hive-c0re/src/socket_server/mod.rs b/hive-c0re/src/socket_server/mod.rs index 5084cd30..2b808f8c 100644 --- a/hive-c0re/src/socket_server/mod.rs +++ b/hive-c0re/src/socket_server/mod.rs @@ -23,15 +23,12 @@ use tokio::task::JoinHandle; use crate::coordinator::Coordinator; mod config_approvals; -mod lifecycle_handlers; mod schedules; pub(crate) use config_approvals::submit_merge_config_pr; pub(crate) use schedules::filter_ghost_schedule_targets; pub use schedules::schedule_to_wire_public; -use config_approvals::handle_request_update_meta_inputs; -use lifecycle_handlers::handle_list_descendants; use schedules::{ EditSchedulePatch, handle_cancel_schedule, handle_edit_schedule, handle_fire_schedule_now, handle_list_schedules, handle_request_schedule_prompt, @@ -181,9 +178,9 @@ pub(crate) fn recv_timeout(wait_seconds: Option) -> std::time::Duration { /// Handle the subset of `Request` variants that are identical on both /// the agent socket and the manager socket. Returns `Some(response)` for /// every variant it handles; returns `None` for the remaining variants — -/// `ListDescendants` and the orchestration verbs (schedules / meta-inputs), -/// which need per-verb tool-group gating — or for host-admin / unknown -/// requests invalid on either socket. +/// `GetLooseEnds` and the orchestration verbs (schedules), which need +/// per-verb tool-group gating — or for host-admin / unknown requests +/// invalid on either socket. /// /// The unified `dispatch` calls this first; the remaining arms (which gate /// on topology / capabilities / tool-groups) are handled there. @@ -542,14 +539,13 @@ fn handle_requeue_inflight( /// Unified dispatch for every socket connection — per-agent sockets and the /// (now pure-transport) manager socket alike. There is no privilege bit; /// authority derives uniformly from the caller's identity: hive-wide -/// orchestration verbs (schedules / meta-inputs) require the matching -/// tool-group (the grantable capability). +/// orchestration verbs (schedules) require the matching tool-group (the +/// grantable capability). async fn dispatch(req: &Request, agent: &str, coord: &Arc) -> Response { if let Some(resp) = dispatch_shared(req, agent, coord).await { return resp; } match req { - Request::ListDescendants => handle_list_descendants(coord, agent).await, Request::GetLooseEnds => handle_get_loose_ends(coord, agent), // Orchestration verbs — gated per-verb on tool-group membership // (see `dispatch_orchestration`). @@ -557,21 +553,12 @@ async fn dispatch(req: &Request, agent: &str, coord: &Arc) -> Respo } } -/// Handle the hive-wide orchestration verbs (scheduling, meta-input updates). -/// No blanket socket gate: each verb gates on the grantable capability that -/// authorises it — the matching tool-group (`scheduling` / `approvals`). Any -/// other variant is a host-admin / unknown request invalid on either socket. +/// Handle the hive-wide orchestration verbs (scheduling). No blanket socket +/// gate: each verb gates on the grantable capability that authorises it — +/// the matching tool-group (`scheduling`). Any other variant is a +/// host-admin / unknown request invalid on either socket. async fn dispatch_orchestration(req: &Request, agent: &str, coord: &Arc) -> Response { match req { - Request::RequestUpdateMetaInputs { - inputs, - description, - } => { - if let Some(err) = require_group(agent, "approvals", "request update_meta_inputs") { - return err; - } - handle_request_update_meta_inputs(coord, agent, inputs, description.as_deref()) - } Request::RequestSchedulePrompt(payload) => { if let Some(err) = require_group(agent, "scheduling", "schedule a prompt") { return err; diff --git a/hive-c0re/src/stores/approvals.rs b/hive-c0re/src/stores/approvals.rs index 12376da0..cc201f12 100644 --- a/hive-c0re/src/stores/approvals.rs +++ b/hive-c0re/src/stores/approvals.rs @@ -1,7 +1,11 @@ -//! Approval queue. Requests are submitted by the manager -//! (`RequestUpdateMetaInputs`), the config-PR webhook (`MergeConfigPr`), or +//! Approval queue. Requests are submitted by an agent +//! (`RequestSchedulePrompt`), the config-PR webhook (`MergeConfigPr`), or //! the operator (`Spawn`); the user approves/denies via the host admin CLI; //! on approval the host runs the corresponding action. +//! +//! `UpdateMetaInputs` rows are legacy: the MCP tool that queued them was +//! removed and nothing produces the kind any more. The variant and +//! its (de)serialization stay so pre-existing rows still read back. use std::path::Path; use std::sync::Mutex; diff --git a/hive-core-agent-sock/src/lib.rs b/hive-core-agent-sock/src/lib.rs index 83634056..f7c37212 100644 --- a/hive-core-agent-sock/src/lib.rs +++ b/hive-core-agent-sock/src/lib.rs @@ -7,7 +7,7 @@ //! shared payload types it references (`Message`, `LooseEnd`, `Approval`, …) //! stay in `hive-sh4re`, which this crate depends on. -use hive_sh4re::container::{ContainerInfo, MatrixIdentity}; +use hive_sh4re::container::MatrixIdentity; use hive_sh4re::inbox::{CancelLooseEndKind, DeliveredMessage, InboxRow, LooseEnd}; use hive_sh4re::journal::JournalPriority; use hive_sh4re::manager::SchedulePromptPayload; @@ -149,13 +149,6 @@ pub enum Request { }, // ---- privileged (manager socket only for now) --------------------------- - /// *(privileged)* Queue an approval to run `nix flake update [inputs...]`. - RequestUpdateMetaInputs { - #[serde(default)] - inputs: Vec, - #[serde(default, skip_serializing_if = "Option::is_none")] - description: Option, - }, /// *(privileged)* Queue an approval to add a scheduled prompt. RequestSchedulePrompt(SchedulePromptPayload), /// *(privileged)* Cancel a scheduled prompt. @@ -168,12 +161,6 @@ pub enum Request { /// plus any owned by an agent in its subtree (everything, for the /// operator). ListSchedules, - /// List the calling agent's subtree — children, their children, and so - /// on down, plus the caller itself, which is part of its own subtree. - /// Gated by the `lifecycle` tool group. The result includes every known - /// member regardless of whether the container is currently - /// running — use `running` to distinguish. - ListDescendants, /// *(privileged)* Fire a scheduled prompt out of band immediately. FireScheduleNow { id: i64 }, /// *(privileged)* Edit an existing schedule's mutable fields. @@ -276,10 +263,6 @@ pub enum Response { full_name: String, clone_url: String, }, - /// `ListDescendants` result: all descendant containers, with running - /// status. Ordered by topology depth (parents before children), then - /// alphabetically within each depth tier. - Containers { containers: Vec }, /// `Recv` result when a graceful stop is pending for this agent /// (set by hive-c0re's `GracefulStop` orchestration). Returned in /// place of `Messages` — it doubles as the inbound fence: the harness diff --git a/hive-sh4re/src/container.rs b/hive-sh4re/src/container.rs index 3af61f95..33ce770d 100644 --- a/hive-sh4re/src/container.rs +++ b/hive-sh4re/src/container.rs @@ -1,19 +1,10 @@ -//! Container/agent-roster wire shapes: what `ListDescendants` and -//! `HostRequest::AgentStatus` return, plus the per-account matrix -//! identity shape surfaced by `GetAgentMeta`. +//! Container/agent-roster wire shapes: what `HostRequest::AgentStatus` +//! returns, plus the per-account matrix identity shape surfaced by +//! `GetAgentMeta`. use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -/// One entry in a `ListDescendants` result. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ContainerInfo { - /// Logical agent name (no `h-` prefix). - pub name: String, - /// Whether the container is currently running. - pub running: bool, -} - /// One row in a `HostRequest::AgentStatus` result — the operator-CLI /// projection of the dashboard's per-agent `ContainerView`. Carries the /// agent's running/health flags plus the technical state an operator diff --git a/hive-sh4re/src/permissions.rs b/hive-sh4re/src/permissions.rs index 8c8b4276..d03515ad 100644 --- a/hive-sh4re/src/permissions.rs +++ b/hive-sh4re/src/permissions.rs @@ -152,9 +152,15 @@ pub enum ToolGroup { Meta, /// `get_loose_ends`, `cancel_loose_end`, `remind` Inbox, - /// `list_containers` - *(privileged)* + /// Gates no tool today — `list_containers`, its only member, was + /// removed with no replacement. Kept so existing + /// `meta/capabilities.json` grants still parse; `tools()` returns + /// `&[]`. Lifecycle, - /// `request_update_meta_inputs` - *(privileged)* + /// Grants no MCP tool today — `request_update_meta_inputs`, its only + /// member, was removed with no replacement. Still a live + /// server-side gate: `cancel_loose_end`'s approval-cancel arm + /// requires it (`socket_server::check_can_cancel_approval`). Approvals, /// `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, /// `edit_schedule`, `list_schedules` - *(privileged)* @@ -215,16 +221,15 @@ impl ToolGroup { } /// The MCP tool names (without the `mcp__hyperhive__` prefix) in this group. - /// Returns `&[]` for `WebTools` — it enables Claude built-in tools, - /// not MCP tools; see `builtin_tools()`. + /// Returns `&[]` for `WebTools` (it enables Claude built-in tools, not MCP + /// tools — see `builtin_tools()`) and for `Lifecycle` / `Approvals` + /// (their tools were removed); see each variant's doc comment. #[must_use] pub fn tools(self) -> &'static [&'static str] { match self { Self::Messaging => &["send", "recv", "ack_until"], Self::Meta => &["get_agent_meta"], Self::Inbox => &["get_loose_ends", "cancel_loose_end", "remind"], - Self::Lifecycle => &["list_containers"], - Self::Approvals => &["request_update_meta_inputs"], Self::Scheduling => &[ "request_schedule_prompt", "fire_schedule_now", @@ -233,16 +238,22 @@ impl ToolGroup { "list_schedules", ], Self::Forge => &["create_repo"], - // Both empty, for different reasons — see each variant's own - // doc comment above. `Execution` grants the out-of-process - // `bash` MCP server (`mcp__bash__run`/`status`/`kill`), gated - // at config-render time by `extra_server_required_group` in + // All four empty, for four different reasons — see each + // variant's own doc comment above. `Execution` grants the + // out-of-process `bash` MCP server + // (`mcp__bash__run`/`status`/`kill`), gated at config-render + // time by `extra_server_required_group` in // `hive-agent/src/mcp_config.rs`, not by this list — an // out-of-process server has no later enforcement point, so // that gate is the actual security boundary. `WebTools` // grants Claude built-in tools, not MCP ones; see - // `builtin_tools()`. - Self::Execution | Self::WebTools => &[], + // `builtin_tools()`. `Lifecycle` and `Approvals` each listed + // exactly one tool — `list_containers` and + // `request_update_meta_inputs` respectively — and both tools + // were removed outright; the variants stay so existing + // grants parse, and `Approvals` still gates + // `cancel_loose_end`'s approval-cancel arm server-side. + Self::Lifecycle | Self::Approvals | Self::Execution | Self::WebTools => &[], } } @@ -324,9 +335,9 @@ impl ToolGroup { "get_agent_meta — identity introspection (set_status is always available)" } Self::Inbox => "get_loose_ends, cancel_loose_end, remind — self-scheduling", - Self::Lifecycle => "list_containers — own-subtree container listing (privileged)", + Self::Lifecycle => "no tools — vestigial since list_containers was removed", Self::Approvals => { - "request_update_meta_inputs — operator-approved meta-flake input bumps (privileged)" + "no tools — grants cancel_loose_end's approval-cancel arm (privileged)" } Self::Scheduling => { "request_schedule_prompt and related — operator-visible scheduled prompts (privileged)"