manager: needs_login / logged_in / needs_update events + update tool

crash_watch grows two more state-axes alongside running/stopped:

- logged-in (claude session dir populated for the agent)
- up-to-date (recorded flake rev matches current)

per-tick transitions emit HelperEvent::NeedsLogin / LoggedIn /
NeedsUpdate. seed-on-first-tick semantics retained — nothing fires
on harness boot for agents that were already in their state. only
needs_update fires the 'stale appeared' direction; the resolved
direction is already covered by Rebuilt.

new mcp__hyperhive__update(name) on the manager surface: idempotent
rebuild via auto_update::rebuild_agent. transient-aware (Rebuilding)
so the dashboard shows the spinner. login intentionally has NO tool
— it's interactive OAuth, only the operator can complete it.

prompts + approvals doc + turn-loop doc updated. todo grows a
'show per-agent applied config in dashboard' entry (separate
follow-up).
This commit is contained in:
müde 2026-05-15 21:42:13 +02:00
parent b374f39b0d
commit 80229c6af9
8 changed files with 230 additions and 34 deletions

View file

@ -240,6 +240,12 @@ pub struct RestartArgs {
pub name: String,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct UpdateArgs {
/// Sub-agent name (without the `h-` container prefix).
pub name: String,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct AskOperatorArgs {
/// The question to surface on the dashboard.
@ -400,6 +406,23 @@ impl ManagerServer {
.await
}
#[tool(
description = "Rebuild a sub-agent: re-applies the current hyperhive flake + agent.nix \
and restarts the container. No approval required idempotent. Use when you receive a \
`needs_update` system event for an agent."
)]
async fn update(&self, Parameters(args): Parameters<UpdateArgs>) -> String {
let log = format!("{args:?}");
let name = args.name.clone();
run_tool_envelope("update", log, async move {
let resp = self
.dispatch(hive_sh4re::ManagerRequest::Update { name: args.name })
.await;
format_ack(resp, "update", format!("updated {name}"))
})
.await
}
#[tool(
description = "Surface a question to the operator on the dashboard. Returns immediately \
with a question id do NOT wait inline. When the operator answers, a system message \
@ -512,6 +535,7 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
"kill",
"start",
"restart",
"update",
"request_apply_commit",
"ask_operator",
],