hive-c0re: let request_init_config spawn a brand-new sub-agent under its requester
This commit is contained in:
parent
63a79d76ba
commit
b3d002e4a7
8 changed files with 254 additions and 23 deletions
|
|
@ -456,6 +456,40 @@ fn require_child(agent: &str, target: &str, action: &str) -> Option<AgentRespons
|
|||
}
|
||||
}
|
||||
|
||||
/// Topology guard for `request_init_config` / `request_apply_commit`,
|
||||
/// which may legitimately target a child that does not exist *yet*
|
||||
/// (spawning a brand-new sub-agent). The caller may act on a
|
||||
/// `target` that is EITHER already its direct child (re-init / config
|
||||
/// update of an existing child) OR brand-new (absent from the topology
|
||||
/// tree — the requester becomes its parent). A name that already
|
||||
/// belongs to a *different* parent (or is a root agent) is refused so
|
||||
/// one agent can't hijack another's sub-tree.
|
||||
///
|
||||
/// Also re-runs the agent-name format check that `require_child`
|
||||
/// implicitly provided (a traversal / malformed name could never be a
|
||||
/// child): a brand-new name now flows straight to `submit_init_config`,
|
||||
/// which builds filesystem paths from it, so validate before that.
|
||||
fn require_new_child(agent: &str, target: &str, action: &str) -> Option<AgentResponse> {
|
||||
if let Some(reason) = crate::dashboard::validate_agent_name(target) {
|
||||
return Some(AgentResponse::Err {
|
||||
message: format!("agent `{agent}` cannot {action} `{target}`: {reason}"),
|
||||
});
|
||||
}
|
||||
match crate::topology::read().get(target) {
|
||||
// brand-new name — requester becomes the parent on approval.
|
||||
None => None,
|
||||
// already our direct child — re-init / config update path.
|
||||
Some(Some(p)) if p == agent => None,
|
||||
// owned by someone else, or a root agent — refuse.
|
||||
Some(_) => Some(AgentResponse::Err {
|
||||
message: format!(
|
||||
"agent `{agent}` cannot {action} `{target}`: it already exists \
|
||||
under a different parent in the topology tree"
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// `GetLooseEnds` — resolve the (optionally cross-agent) target then
|
||||
/// read its loose ends.
|
||||
fn handle_get_loose_ends(
|
||||
|
|
@ -696,11 +730,11 @@ fn handle_request_init_config(
|
|||
name: &str,
|
||||
description: Option<String>,
|
||||
) -> AgentResponse {
|
||||
if let Some(err) = require_child(agent, name, "request_init_config for") {
|
||||
if let Some(err) = require_new_child(agent, name, "request_init_config for") {
|
||||
return err;
|
||||
}
|
||||
tracing::info!(%agent, %name, "agent: request_init_config for child");
|
||||
match crate::manager_server::submit_init_config(coord, name, description) {
|
||||
match crate::manager_server::submit_init_config(coord, name, Some(agent), description) {
|
||||
Ok(_id) => AgentResponse::Ok,
|
||||
Err(e) => AgentResponse::Err {
|
||||
message: format!("{e:#}"),
|
||||
|
|
@ -717,7 +751,7 @@ async fn handle_request_apply_commit(
|
|||
commit_ref: &str,
|
||||
description: Option<&str>,
|
||||
) -> AgentResponse {
|
||||
if let Some(err) = require_child(agent, target_agent, "request_apply_commit for") {
|
||||
if let Some(err) = require_new_child(agent, target_agent, "request_apply_commit for") {
|
||||
return err;
|
||||
}
|
||||
tracing::info!(%agent, %target_agent, %commit_ref, "agent: request_apply_commit for child");
|
||||
|
|
|
|||
Loading…
Reference in a new issue