From 597e4ba03afe0433b05669e44a80d6508f6482fd Mon Sep 17 00:00:00 2001 From: damocles Date: Fri, 22 May 2026 01:31:26 +0200 Subject: [PATCH] manager: add update_meta_inputs tool to update flake.lock on demand (closes #235) --- hive-ag3nt/prompts/manager.md | 1 + hive-ag3nt/src/mcp.rs | 40 +++++++++++++++++++++++++++++++++ hive-c0re/src/manager_server.rs | 16 +++++++++++++ hive-c0re/src/meta.rs | 16 ++++++------- hive-sh4re/src/lib.rs | 11 +++++++++ 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/hive-ag3nt/prompts/manager.md b/hive-ag3nt/prompts/manager.md index a50e10a..64f44c2 100644 --- a/hive-ag3nt/prompts/manager.md +++ b/hive-ag3nt/prompts/manager.md @@ -10,6 +10,7 @@ Tools (hyperhive surface): - `mcp__hyperhive__start(name)` — start a stopped sub-agent. No approval required. - `mcp__hyperhive__restart(name)` — stop + start a sub-agent. No approval required. - `mcp__hyperhive__update(name)` — rebuild a sub-agent (re-applies the current hyperhive flake + agent.nix, restarts the container). No approval required — idempotent. Use when you receive a `needs_update` system event. +- `mcp__hyperhive__update_meta_inputs(inputs?)` — run `nix flake update [inputs...]` on the meta flake and commit the lock changes. Pass specific input names (e.g. `["bitburner-agent"]`) to update just those, or omit / pass `[]` to update everything. Blocks until complete. Does NOT trigger rebuilds — call `update(name)` on affected agents afterward. - `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). - `mcp__hyperhive__request_apply_commit(agent, commit_ref, description?)` — submit a config change for any agent (`hm1nd` for self) for operator approval. Pass an optional `description` and it appears on the dashboard approval card so the operator knows what changed without opening the diff. At submit time hive-c0re fetches your commit into the agent's applied repo and pins it as `proposal/`; from that moment your proposed-side commit can be amended or force-pushed freely without changing what the operator will build. - `mcp__hyperhive__ask(question, options?, multi?, ttl_seconds?, to?)` — surface a structured question to the operator (default, or `to: "operator"`) OR a sub-agent (`to: ""`). Returns immediately with a question id; the answer arrives later as a system `question_answered { id, question, answer, answerer }` event in your inbox. Options are advisory: the dashboard always lets the operator type a free-text answer in addition. Set `multi: true` to render options as checkboxes (operator can pick multiple); the answer comes back as `, `-separated. Set `ttl_seconds` to auto-cancel after a deadline (capped at 6h server-side) — on expiry the answer is `[expired]` and `answerer` is `"ttl-watchdog"`. Do not poll inside the same turn — finish the current work and react when the event lands. diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index fd3ad1a..3ba1430 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -807,6 +807,14 @@ pub struct RequestApplyCommitArgs { pub description: Option, } +#[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, +} + #[derive(Debug, serde::Deserialize, schemars::JsonSchema)] pub struct GetLogsArgs { /// Logical agent name to fetch logs for (e.g. `gui`, `hm1nd`). @@ -1022,6 +1030,38 @@ impl ManagerServer { .await } + #[tool( + description = "Run `nix flake update` on the meta flake and commit the resulting \ + `flake.lock` changes. Pass specific input names to update only those inputs \ + (e.g. `[\"bitburner-agent\"]`), or pass an empty list to update ALL inputs. \ + Blocks until the lock step completes (~30-120s depending on download size). \ + Does NOT trigger container rebuilds — call `update` on each affected agent \ + separately after the lock settles." + )] + async fn update_meta_inputs( + &self, + Parameters(args): Parameters, + ) -> String { + let log = format!("{args:?}"); + run_tool_envelope("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_sh4re::ManagerRequest::UpdateMetaInputs { + inputs: args.inputs, + }) + .await; + annotate_retries( + format_ack(resp, "update_meta_inputs", format!("lock updated: {label}")), + retries, + ) + }) + .await + } + #[tool( description = "Surface a structured question to either the operator OR a sub-agent. \ Returns immediately with a question id — do NOT wait inline. When the recipient \ diff --git a/hive-c0re/src/manager_server.rs b/hive-c0re/src/manager_server.rs index 75b6075..3c432ac 100644 --- a/hive-c0re/src/manager_server.rs +++ b/hive-c0re/src/manager_server.rs @@ -311,6 +311,22 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc) -> ManagerResp }, } } + ManagerRequest::UpdateMetaInputs { inputs } => { + let label = if inputs.is_empty() { + "all inputs".to_string() + } else { + inputs.join(", ") + }; + tracing::info!(%label, "manager: update_meta_inputs"); + // Treat empty list as "update all" by passing the full list + // to lock_update; it calls bare `nix flake update` when empty. + match crate::meta::lock_update(inputs).await { + Ok(()) => ManagerResponse::Ok, + Err(e) => ManagerResponse::Err { + message: format!("update_meta_inputs({label}): {e:#}"), + }, + } + } ManagerRequest::Ask { question, options, diff --git a/hive-c0re/src/meta.rs b/hive-c0re/src/meta.rs index d7aa44c..c5899be 100644 --- a/hive-c0re/src/meta.rs +++ b/hive-c0re/src/meta.rs @@ -190,14 +190,12 @@ pub async fn lock_update_for_rebuild(name: &str) -> Result<()> { /// Used by the dashboard's "update meta inputs" form so the /// operator can bulk-bump `hyperhive` + selected agents in one /// shot. Each input name is passed verbatim to -/// `nix flake update`; the caller is responsible for picking -/// real input keys (e.g. via `inputs_view()` snapshotted from -/// the lock file). -#[allow(dead_code)] // wired up by dashboard handler in the same commit +/// Run `nix flake update [inputs...]` on the meta flake and commit the +/// resulting lock changes. When `inputs` is empty, updates ALL inputs +/// (bare `nix flake update`). The caller is responsible for picking +/// real input keys (e.g. via `inputs_view()` snapshotted from the lock +/// file) when targeting specific inputs. pub async fn lock_update(inputs: &[String]) -> Result<()> { - if inputs.is_empty() { - return Ok(()); - } let _guard = META_LOCK.lock().await; let dir = meta_dir(); let mut args: Vec<&str> = vec!["flake", "update"]; @@ -209,7 +207,9 @@ pub async fn lock_update(inputs: &[String]) -> Result<()> { return Ok(()); } git(&dir, &["add", "flake.lock"]).await?; - let msg = if inputs.len() == 1 { + let msg = if inputs.is_empty() { + "lock update: all inputs".to_string() + } else if inputs.len() == 1 { format!("lock update: {}", inputs[0]) } else { format!("lock update: {}", inputs.join(", ")) diff --git a/hive-sh4re/src/lib.rs b/hive-sh4re/src/lib.rs index 6b59aa3..944cc97 100644 --- a/hive-sh4re/src/lib.rs +++ b/hive-sh4re/src/lib.rs @@ -842,6 +842,17 @@ pub enum ManagerRequest { /// message into the manager's own broker inbox. `from` is caller- /// chosen; `body` becomes the wake prompt body. Wake { from: String, body: String }, + /// Run `nix flake update [inputs...]` on the meta flake and commit + /// the resulting `flake.lock` changes. `inputs` is the list of + /// named inputs to update (e.g. `["bitburner-agent", "nixpkgs"]`). + /// Pass an empty list to update ALL inputs (equivalent to bare + /// `nix flake update`). Blocks until the lock step completes; + /// does NOT trigger a rebuild — call `update` on each affected + /// agent separately after the lock settles. + UpdateMetaInputs { + #[serde(default)] + inputs: Vec, + }, } #[derive(Debug, Clone, Serialize, Deserialize)]