diff --git a/hive-c0re/src/agent_server.rs b/hive-c0re/src/agent_server.rs index 6a8197e..a4d3329 100644 --- a/hive-c0re/src/agent_server.rs +++ b/hive-c0re/src/agent_server.rs @@ -104,21 +104,7 @@ async fn dispatch(req: &AgentRequest, agent: &str, coord: &Arc) -> AgentRequest::Send { to, body } => { // Handle broadcast sends (recipient = "*") if to == "*" { - let agents = coord.list_agents(); - let broadcast_hint = "\n\n⚠️ _hint: this was a broadcast and may not need any action from you_"; - let broadcast_body = format!("{}{}", body, broadcast_hint); - let mut errors = Vec::new(); - - for agent_name in agents { - if let Err(e) = broker.send(&Message { - from: agent.to_owned(), - to: agent_name.clone(), - body: broadcast_body.clone(), - }) { - errors.push(format!("{}: {e}", agent_name)); - } - } - + let errors = coord.broadcast_send(agent, body); if errors.is_empty() { AgentResponse::Ok } else { diff --git a/hive-c0re/src/coordinator.rs b/hive-c0re/src/coordinator.rs index 2d347db..4d4cc8f 100644 --- a/hive-c0re/src/coordinator.rs +++ b/hive-c0re/src/coordinator.rs @@ -192,6 +192,27 @@ impl Coordinator { } } + /// Deliver `body` to every currently-registered agent, appending the + /// standard broadcast hint. Returns a list of per-agent error strings + /// for any that failed (empty = all ok). The sender's own inbox is + /// included — the hint text tells agents to ignore if no action needed. + pub fn broadcast_send(&self, from: &str, body: &str) -> Vec { + const HINT: &str = + "\n\n⚠️ _hint: this was a broadcast and may not need any action from you_"; + let broadcast_body = format!("{body}{HINT}"); + let mut errors = Vec::new(); + for agent_name in self.list_agents() { + if let Err(e) = self.broker.send(&hive_sh4re::Message { + from: from.to_owned(), + to: agent_name.clone(), + body: broadcast_body.clone(), + }) { + errors.push(format!("{agent_name}: {e}")); + } + } + errors + } + pub fn agent_dir(name: &str) -> PathBuf { PathBuf::from(format!("{AGENT_RUNTIME_ROOT}/{name}")) } diff --git a/hive-c0re/src/manager_server.rs b/hive-c0re/src/manager_server.rs index aa74a53..72d58d4 100644 --- a/hive-c0re/src/manager_server.rs +++ b/hive-c0re/src/manager_server.rs @@ -85,16 +85,29 @@ fn manager_recv_timeout(wait_seconds: Option) -> std::time::Duration { #[allow(clippy::too_many_lines)] async fn dispatch(req: &ManagerRequest, coord: &Arc) -> ManagerResponse { match req { - ManagerRequest::Send { to, body } => match coord.broker.send(&Message { - from: MANAGER_AGENT.to_owned(), - to: to.clone(), - body: body.clone(), - }) { - Ok(()) => ManagerResponse::Ok, - Err(e) => ManagerResponse::Err { - message: format!("{e:#}"), - }, - }, + ManagerRequest::Send { to, body } => { + if to == "*" { + let errors = coord.broadcast_send(MANAGER_AGENT, body); + if errors.is_empty() { + ManagerResponse::Ok + } else { + ManagerResponse::Err { + message: format!("broadcast failed for agents: {}", errors.join(", ")), + } + } + } else { + match coord.broker.send(&Message { + from: MANAGER_AGENT.to_owned(), + to: to.clone(), + body: body.clone(), + }) { + Ok(()) => ManagerResponse::Ok, + Err(e) => ManagerResponse::Err { + message: format!("{e:#}"), + }, + } + } + } ManagerRequest::OperatorMsg { body } => match coord.broker.send(&Message { from: hive_sh4re::OPERATOR_RECIPIENT.to_owned(), to: MANAGER_AGENT.to_owned(),