feat(#2302): thread &Ident through agent path builders

This commit is contained in:
damocles 2026-07-20 00:22:06 +02:00 committed by mara
commit bf644cc126
23 changed files with 168 additions and 85 deletions

View file

@ -64,20 +64,27 @@ pub async fn build_all(coord: &Coordinator) -> Vec<ContainerView> {
let topology = crate::topology::read();
let mut out = Vec::new();
for c in &raw {
let Some(logical) = c.strip_prefix(AGENT_PREFIX).map(str::to_owned) else {
let Some(logical) = c.strip_prefix(AGENT_PREFIX) else {
continue;
};
// Parse the nspawn machine suffix into an Ident once at this
// enumeration origin; a suffix that isn't a valid ident isn't one
// of our agents, so skip it.
let Ok(logical) = hive_host_sock::Ident::parse(logical) else {
continue;
};
let deployed_full = locked
.get(&format!("agent-{logical}"))
.map(std::string::String::as_str);
let needs_update = crate::auto_update::agent_config_pending(&logical, deployed_full).await;
let needs_update =
crate::auto_update::agent_config_pending(logical.as_str(), deployed_full).await;
let deployed_sha = deployed_full.map(|s| s[..s.len().min(12)].to_owned());
let pending_reminders = coord
.broker
.count_pending_reminders_for(logical.as_str())
.unwrap_or(0);
let parent = topology.get(&logical).cloned().flatten();
let running = lifecycle::is_running(&logical).await;
let parent = topology.get(logical.as_str()).cloned().flatten();
let running = lifecycle::is_running(logical.as_str()).await;
// needs_login fires when EITHER the claude session dir is missing
// (boot-time / fresh container) OR the harness wrote the auth-failed
// sentinel because a turn hit 401. Cleared for stopped containers —
@ -94,10 +101,10 @@ pub async fn build_all(coord: &Coordinator) -> Vec<ContainerView> {
None
};
out.push(ContainerView {
port: lifecycle::agent_web_port(&logical),
port: lifecycle::agent_web_port(logical.as_str()),
running,
container: c.clone(),
name: logical,
name: logical.into_string(),
needs_update,
needs_login,
deployed_sha,
@ -126,7 +133,7 @@ pub fn claude_has_session(dir: &Path) -> bool {
/// the consolidated `hyperhive-harness.json`. Falls back to the legacy
/// individual sentinel files written by older harness builds so in-place
/// upgrades don't lose state during the transition window.
fn read_harness_flags(name: &str) -> (bool, bool) {
fn read_harness_flags(name: &hive_host_sock::Ident) -> (bool, bool) {
let dir = Coordinator::agent_notes_dir(name);
if let Ok(raw) = std::fs::read_to_string(dir.join("hyperhive-harness.json"))
&& let Ok(v) = serde_json::from_str::<serde_json::Value>(&raw)
@ -147,7 +154,7 @@ fn read_harness_flags(name: &str) -> (bool, bool) {
(rate_limited, needs_login)
}
fn auth_failed_sentinel(name: &str) -> bool {
fn auth_failed_sentinel(name: &hive_host_sock::Ident) -> bool {
read_harness_flags(name).1
}
@ -158,7 +165,7 @@ fn auth_failed_sentinel(name: &str) -> bool {
/// NB: callers building `AgentMeta` for a *stopped* container should
/// clear the result — the on-disk status is a stale snapshot from
/// before the stop. Use `read_agent_status_live` for that.
pub fn read_agent_status(name: &str) -> (Option<String>, Option<i64>) {
pub fn read_agent_status(name: &hive_host_sock::Ident) -> (Option<String>, Option<i64>) {
let path = Coordinator::agent_notes_dir(name).join("hyperhive-status");
let meta = std::fs::metadata(&path).ok();
// Read at most STATUS_MAX_CHARS * 4 + 2 bytes: 4 is the max UTF-8 byte
@ -198,8 +205,10 @@ pub fn read_agent_status(name: &str) -> (Option<String>, Option<i64>) {
///
/// Returned tuple is `(status_text, status_set_at, running)`.
/// `name` is the logical agent name (same as the broker recipient).
pub async fn read_agent_status_live(name: &str) -> (Option<String>, Option<i64>, bool) {
if !lifecycle::is_running(name).await {
pub async fn read_agent_status_live(
name: &hive_host_sock::Ident,
) -> (Option<String>, Option<i64>, bool) {
if !lifecycle::is_running(name.as_str()).await {
return (None, None, false);
}
let (text, set_at) = read_agent_status(name);
@ -212,7 +221,7 @@ pub async fn read_agent_status_live(name: &str) -> (Option<String>, Option<i64>,
/// so it always reflects the resolved priority (nix config > runtime
/// override > default). Returns `None` when the field is absent or the
/// harness has not yet started a turn.
fn read_active_model(name: &str) -> Option<String> {
fn read_active_model(name: &hive_host_sock::Ident) -> Option<String> {
let path = Coordinator::agent_notes_dir(name).join("hyperhive-harness.json");
let raw = std::fs::read_to_string(path).ok()?;
let v: serde_json::Value = serde_json::from_str(&raw).ok()?;