diff --git a/crates/colibri-daemon/src/socket.rs b/crates/colibri-daemon/src/socket.rs index 7ad3d79..b888536 100644 --- a/crates/colibri-daemon/src/socket.rs +++ b/crates/colibri-daemon/src/socket.rs @@ -630,6 +630,43 @@ pub async fn autospawn_agent_if_configured(state: &SharedState) { if resp.ok { info!("autospawn: agent spawned"); + // Register the autospawned agent in the local store so the scheduler + // can route queued tasks to it. Capabilities come from hw-probe + // (dynamic — not hardcoded per platform). + if let Some(ref aid) = resp + .data + .as_ref() + .and_then(|d| d.get("agent_id")) + .and_then(|v| v.as_str()) + { + let hostname = + std::env::var("HOSTNAME").unwrap_or_else(|_| "unknown".to_string()); + let caps = probe_capabilities(); + match state.store.try_lock() { + Ok(store) => { + if let Err(e) = + store.register_agent(aid, serde_json::to_value(&caps).unwrap_or_default(), Some(&hostname)) + { + warn!(agent_id = %aid, error = %e, "autospawn: failed to register agent in store"); + } else { + info!(agent_id = %aid, hostname = %hostname, capabilities = ?caps, "autospawn: agent registered for task work"); + } + } + Err(std::sync::TryLockError::WouldBlock) => { + warn!("autospawn: store locked; agent registration deferred"); + } + Err(std::sync::TryLockError::Poisoned(e)) => { + if let Err(e) = e.into_inner().register_agent( + aid, + serde_json::to_value(&caps).unwrap_or_default(), + Some(&hostname), + ) { + warn!(agent_id = %aid, error = %e, "autospawn: failed to register agent (poisoned store)"); + } + } + } + } + // An RPC agent (zot rpc) idles until it gets a prompt on stdin. Send a // one-time bootstrap so it comes online and emits events — opt-in via // COLIBRI_AUTOSPAWN_RPC_PROMPT so the default boot spends no tokens. @@ -853,6 +890,24 @@ async fn try_register_hw_with_mother(hostname: &str, hw_profile: &str) { } } +/// Run `clawdie-hw-probe --capabilities` to get a dynamic capability list. +/// Falls back to a platform-agnostic minimum if the probe is unavailable. +fn probe_capabilities() -> Vec { + match std::process::Command::new("clawdie-hw-probe") + .arg("--capabilities") + .output() + { + Ok(out) if out.status.success() => { + if let Ok(caps) = serde_json::from_slice::>(&out.stdout) { + return caps; + } + } + _ => {} + } + // Fallback: minimum capabilities that any zot node has. + vec!["shell".to_string(), "code".to_string()] +} + fn basename(path: &str) -> String { std::path::Path::new(path) .file_name()