remove the list_containers and request_update_meta_inputs MCP tools
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
This commit is contained in:
parent
8614cb2613
commit
b88a5b2430
18 changed files with 74 additions and 356 deletions
|
|
@ -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<Coordinator>,
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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<Coordinator>, 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<String> = 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 }
|
||||
}
|
||||
|
|
@ -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<u64>) -> 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<Coordinator>) -> 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<Coordinator>) -> 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<Coordinator>) -> 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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue