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

@ -204,6 +204,27 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc<Coordinator>) -> ManagerResp
},
}
}
ManagerRequest::Update { name } => {
tracing::info!(%name, "manager: update");
let Some(current_rev) = crate::auto_update::current_flake_rev(&coord.hyperhive_flake)
else {
return ManagerResponse::Err {
message: "update: hyperhive_flake has no canonical path".into(),
};
};
coord.set_transient(name, crate::coordinator::TransientKind::Rebuilding);
let result = crate::auto_update::rebuild_agent(coord, name, &current_rev).await;
coord.clear_transient(name);
match result {
Ok(()) => {
coord.kick_agent(name, "container rebuilt");
ManagerResponse::Ok
}
Err(e) => ManagerResponse::Err {
message: format!("{e:#}"),
},
}
}
ManagerRequest::AskOperator {
question,
options,