harness: remove deprecated manager-side request_spawn surface (closes #442)
This commit is contained in:
parent
03d39788d9
commit
dca62fda40
8 changed files with 56 additions and 124 deletions
|
|
@ -120,7 +120,6 @@ can't:
|
|||
|---|---|---|
|
||||
| `RequestInitConfig` | seed an agent's proposed config repo | **topology** — descendants only |
|
||||
| `RequestApplyCommit` | submit a commit sha for operator approval | **topology** — descendants only |
|
||||
| `RequestSpawn` (deprecated) | shortcut for spawn | **topology** — descendants only |
|
||||
| `Kill` / `Start` / `Restart` / `Update` | container lifecycle on an existing agent | **topology** — descendants only |
|
||||
| `RequestUpdateMetaInputs` | bump meta `flake.lock` | **per-agent cap** (root-only today; a future "let coder bump its own input" might grant it) |
|
||||
| `GetLogs` | journalctl scrape of a sub-agent | **topology** — descendants only |
|
||||
|
|
|
|||
|
|
@ -294,12 +294,13 @@ meta's.
|
|||
on operator approve, hive-c0re seeds the proposed config repo
|
||||
with a default `agent.nix` template and sends the manager a
|
||||
`HelperEvent::ConfigReady { agent }`. The manager then edits
|
||||
`agent.nix`, commits the changes, and calls `request_spawn`.
|
||||
Fails if a proposed repo for this name already exists.
|
||||
- `request_spawn(name)` — second step of a two-step spawn. Queues
|
||||
a Spawn approval; requires the proposed config repo to exist
|
||||
(from a prior approved `request_init_config`). Operator approves
|
||||
on the dashboard to create the container.
|
||||
`agent.nix`, commits the changes, and calls `request_apply_commit`
|
||||
with the commit sha — the first ApplyCommit on a freshly-init'd
|
||||
config creates the container. Fails if a proposed repo for this
|
||||
name already exists. (The pre-#442 path through a separate
|
||||
manager-side `request_spawn` was removed; operator can still
|
||||
direct-spawn an empty agent from the dashboard's `◆ R3QU3ST SP4WN`
|
||||
button which routes via `HostRequest::RequestSpawn`.)
|
||||
- `kill(name)` — graceful stop. No approval required.
|
||||
- `start(name)` — start a stopped sub-agent. No approval.
|
||||
- `restart(name)` — stop + start. No approval.
|
||||
|
|
@ -375,9 +376,9 @@ meta's.
|
|||
|
||||
The boundary: lifecycle ops on *existing* sub-agents
|
||||
(`kill`/`start`/`restart`) are at the manager's discretion — no
|
||||
operator approval. Creating a new agent (`request_spawn`) and
|
||||
changing any agent's config (`request_apply_commit`) still go
|
||||
through the approval queue.
|
||||
operator approval. Creating a new agent (`request_init_config` →
|
||||
`request_apply_commit` for the first sha) and changing any agent's
|
||||
config (`request_apply_commit`) still go through the approval queue.
|
||||
|
||||
### Authoritative state
|
||||
|
||||
|
|
|
|||
|
|
@ -1056,7 +1056,6 @@ window.marked = marked;
|
|||
if (input.max != null) parts.push('max ' + input.max);
|
||||
return short + (parts.length ? ' ' + parts.join(' · ') : '()');
|
||||
}
|
||||
case 'mcp__hyperhive__request_spawn': return short + ' ' + (input.name || '');
|
||||
case 'mcp__hyperhive__kill': return short + ' ' + (input.name || '');
|
||||
case 'mcp__hyperhive__request_apply_commit':
|
||||
return short + ' ' + (input.agent || '') + ' @ ' + (input.commit_ref || '').slice(0, 12);
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ enum Cmd {
|
|||
},
|
||||
/// Run the manager MCP server on stdio. Spawned by claude via
|
||||
/// `--mcp-config`; same shape as `hive-ag3nt mcp` but with the
|
||||
/// manager tool surface (`request_spawn`, `kill`, `start`, `restart`,
|
||||
/// `request_apply_commit`, `ask`, `answer`, `remind`).
|
||||
/// manager tool surface (`request_init_config`, `request_apply_commit`,
|
||||
/// `kill`, `start`, `restart`, `ask`, `answer`, `remind`, …).
|
||||
Mcp,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
//!
|
||||
//! Two server flavors:
|
||||
//! - `AgentServer` — sub-agent tools (`send`, `recv`).
|
||||
//! - `ManagerServer` — agent tools + lifecycle (`request_spawn`, `kill`,
|
||||
//! `request_apply_commit`).
|
||||
//! - `ManagerServer` — agent tools + lifecycle (`kill`,
|
||||
//! `request_init_config`, `request_apply_commit`).
|
||||
//!
|
||||
//! Both go through the same `run_tool_envelope` helper so logging + status
|
||||
//! line stay uniform.
|
||||
|
|
@ -792,26 +792,16 @@ pub async fn serve_manager_stdio(socket: PathBuf) -> Result<()> {
|
|||
pub struct RequestInitConfigArgs {
|
||||
/// New sub-agent name (≤9 chars). Queues an `InitConfig` approval; on
|
||||
/// approval hive-c0re seeds the proposed config repo at
|
||||
/// `/agents/<name>/config/agent.nix` with the default template.
|
||||
/// After the approval the manager can edit and commit the config before
|
||||
/// calling `request_spawn`.
|
||||
/// `/agents/<name>/config/agent.nix` with the default template. After
|
||||
/// the approval the manager edits + commits the config and calls
|
||||
/// `request_apply_commit` to pin the customised sha for the container's
|
||||
/// first build.
|
||||
pub name: String,
|
||||
/// Optional description shown on the dashboard approval card.
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct RequestSpawnArgs {
|
||||
/// New sub-agent name (≤9 chars). Queues a Spawn approval; the
|
||||
/// operator approves on the dashboard before the container is created.
|
||||
pub name: String,
|
||||
/// Optional description shown on the dashboard approval card so the
|
||||
/// operator knows what the new agent is for without a separate message.
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct KillArgs {
|
||||
/// Sub-agent name (without the `h-` container prefix).
|
||||
|
|
@ -1121,8 +1111,9 @@ impl ManagerServer {
|
|||
repo and queue an InitConfig approval. On operator approval hive-c0re seeds \
|
||||
`/agents/<name>/config/agent.nix` with the default template so the manager can \
|
||||
customise it before spawning. After the ConfigReady helper event arrives, edit \
|
||||
agent.nix, commit the changes, then call `request_spawn`. Fails if a config repo \
|
||||
for this name already exists (use `request_apply_commit` to update an existing agent)."
|
||||
agent.nix, commit the changes, then call `request_apply_commit` with the commit \
|
||||
sha — that's what creates the container. Fails if a config repo for this name \
|
||||
already exists (use `request_apply_commit` directly to update an existing agent)."
|
||||
)]
|
||||
async fn request_init_config(
|
||||
&self,
|
||||
|
|
@ -1149,34 +1140,6 @@ impl ManagerServer {
|
|||
.await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Step 2 of 2 for creating a new agent: queue a Spawn approval after \
|
||||
the config has been initialised and customised via `request_init_config`. Requires \
|
||||
a prior approved InitConfig so the manager can review and edit agent.nix first. \
|
||||
Fails if no proposed config repo exists for the given name."
|
||||
)]
|
||||
async fn request_spawn(&self, Parameters(args): Parameters<RequestSpawnArgs>) -> String {
|
||||
let log = format!("{args:?}");
|
||||
let name = args.name.clone();
|
||||
run_tool_envelope("request_spawn", log, async move {
|
||||
let (resp, retries) = self
|
||||
.dispatch(hive_sh4re::ManagerRequest::RequestSpawn {
|
||||
name: args.name,
|
||||
description: args.description,
|
||||
})
|
||||
.await;
|
||||
annotate_retries(
|
||||
format_ack(
|
||||
resp,
|
||||
"request_spawn",
|
||||
format!("spawn approval queued for {name}"),
|
||||
),
|
||||
retries,
|
||||
)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Stop a sub-agent container (graceful). The state dir is kept; \
|
||||
recreating reuses prior config + Claude credentials. No approval required."
|
||||
|
|
@ -1711,10 +1674,12 @@ impl ManagerServer {
|
|||
#[tool_handler(
|
||||
instructions = "You are the hyperhive manager (hm1nd). You coordinate sub-agents and \
|
||||
relay between them and the operator. Use `send` to talk to agents/operator, `recv` \
|
||||
to drain your inbox. Privileged: `request_init_config` + `request_spawn` (two-step \
|
||||
new agent creation - init config first, customise agent.nix, then spawn, both \
|
||||
gated on operator approval), `kill` (graceful stop), `request_apply_commit` (config change for \
|
||||
any agent including yourself), `ask` (structured question to the operator or a \
|
||||
to drain your inbox. Privileged: `request_init_config` (step 1 of new-agent \
|
||||
creation — seeds the proposed config repo so you can customise agent.nix; \
|
||||
operator-approved), `kill` (graceful stop), `request_apply_commit` (config \
|
||||
change for any agent including yourself — also doubles as step 2 of new-agent \
|
||||
creation: the first ApplyCommit on a freshly-init'd config creates the \
|
||||
container), `ask` (structured question to the operator or a \
|
||||
sub-agent — non-blocking, answer arrives later as a `question_answered` event), \
|
||||
`answer` (respond to a `question_asked` event directed at you), \
|
||||
`get_loose_ends` (hive-wide loose ends — pending approvals + unanswered \
|
||||
|
|
@ -1771,7 +1736,6 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
|
|||
"send",
|
||||
"recv",
|
||||
"request_init_config",
|
||||
"request_spawn",
|
||||
"kill",
|
||||
"start",
|
||||
"restart",
|
||||
|
|
|
|||
|
|
@ -203,34 +203,6 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc<Coordinator>) -> ManagerResp
|
|||
},
|
||||
}
|
||||
}
|
||||
ManagerRequest::RequestSpawn { name, description } => {
|
||||
tracing::info!(%name, "manager: request_spawn");
|
||||
let proposed_dir = crate::coordinator::Coordinator::agent_proposed_dir(name);
|
||||
if !proposed_dir.join(".git").exists() {
|
||||
return ManagerResponse::Err {
|
||||
message: format!(
|
||||
"no proposed config repo found for '{name}' - \
|
||||
call request_init_config first to initialise and customise \
|
||||
the config before spawning"
|
||||
),
|
||||
};
|
||||
}
|
||||
match coord.approvals.submit_kind(
|
||||
name,
|
||||
hive_sh4re::ApprovalKind::Spawn,
|
||||
"",
|
||||
description.as_deref(),
|
||||
) {
|
||||
Ok(id) => {
|
||||
tracing::info!(%id, %name, "spawn approval queued");
|
||||
coord.emit_approval_added(id, name, "spawn", None, None, description.clone());
|
||||
ManagerResponse::Ok
|
||||
}
|
||||
Err(e) => ManagerResponse::Err {
|
||||
message: format!("{e:#}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
ManagerRequest::Kill { name } => {
|
||||
tracing::info!(%name, "manager: kill");
|
||||
if name == crate::lifecycle::MANAGER_NAME {
|
||||
|
|
|
|||
|
|
@ -568,16 +568,19 @@ async fn dispatch(
|
|||
crate::actions::run_approval_spawn(coord, Some(entry.id), approval_id).await
|
||||
}
|
||||
(QueueKind::Spawn, None) => {
|
||||
// No non-approval Spawn caller today. The variant exists so
|
||||
// operator-triggered `RequestSpawn` (deprecated) and the
|
||||
// future direct-spawn admin path can route through here
|
||||
// without a wire change.
|
||||
tracing::debug!(
|
||||
id = entry.id,
|
||||
agent = %entry.agent,
|
||||
"rebuild_queue: Spawn entry without approval_id is a no-op"
|
||||
);
|
||||
Ok(())
|
||||
// Unreachable today: every Spawn entry is born from an
|
||||
// approval (HostRequest::RequestSpawn → submit_kind →
|
||||
// approve → enqueue with approval_id). The manager-side
|
||||
// `RequestSpawn` surface that used to bypass approvals
|
||||
// was removed in #442; if a future direct-spawn admin
|
||||
// path needs to skip the approval ride it should wire
|
||||
// its own action call rather than route through here.
|
||||
anyhow::bail!(
|
||||
"rebuild_queue: Spawn entry id={} agent={} arrived without an approval_id — \
|
||||
nothing should enqueue this shape today",
|
||||
entry.id,
|
||||
entry.agent,
|
||||
)
|
||||
}
|
||||
(QueueKind::Destroy, _) => {
|
||||
// Reserved for future `destroy --purge` integration.
|
||||
|
|
|
|||
|
|
@ -17,10 +17,14 @@ pub enum HostRequest {
|
|||
/// approval queue intentionally so test scripts and one-off recoveries
|
||||
/// don't need a separate approve step.
|
||||
Spawn { name: String },
|
||||
/// Submit a spawn request for the user to approve. On approval the host
|
||||
/// creates and starts the container. Mirrors the manager's
|
||||
/// `RequestSpawn` — exposed on the admin socket so the dashboard and CLI
|
||||
/// can also queue spawns through the approval flow.
|
||||
/// Submit a spawn request for the operator to approve. On approval the
|
||||
/// host creates and starts the container with the default `agent.nix`
|
||||
/// template. The dashboard's `◆ R3QU3ST SP4WN` button and the
|
||||
/// `hive-c0re request-spawn` CLI both go through this. The
|
||||
/// previously-mirrored manager-side `RequestSpawn` was removed
|
||||
/// (#442) — managers now go through the two-step `request_init_config`
|
||||
/// + `request_apply_commit` flow so the spawn captures the manager's
|
||||
/// customised config.
|
||||
RequestSpawn { name: String },
|
||||
/// Stop a managed container (graceful).
|
||||
Kill { name: String },
|
||||
|
|
@ -107,12 +111,16 @@ pub enum ApprovalKind {
|
|||
#[default]
|
||||
ApplyCommit,
|
||||
/// Create + start a new sub-agent container with the given name.
|
||||
/// Used today by the host admin socket / dashboard "request spawn"
|
||||
/// button; the manager-side `RequestSpawn` surface was removed in
|
||||
/// favour of the two-step `InitConfig` → `ApplyCommit` flow (#442).
|
||||
Spawn,
|
||||
/// Initialise a new agent's proposed config repo so the manager can
|
||||
/// customise it before submitting a `RequestSpawn`. On approval
|
||||
/// hive-c0re seeds `proposed/<name>/` with the default `agent.nix`
|
||||
/// template but does NOT create the container - that requires a
|
||||
/// subsequent `RequestSpawn` approval.
|
||||
/// customise it before submitting a `request_apply_commit`. On
|
||||
/// approval hive-c0re seeds `proposed/<name>/` with the default
|
||||
/// `agent.nix` template but does NOT create the container — that
|
||||
/// requires a subsequent `ApplyCommit` approval pinning the
|
||||
/// customised config sha.
|
||||
InitConfig,
|
||||
/// Run `nix flake update [inputs...]` on the meta flake and commit
|
||||
/// the resulting lock changes. The `commit_ref` field stores the
|
||||
|
|
@ -767,20 +775,6 @@ pub enum ManagerRequest {
|
|||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
description: Option<String>,
|
||||
},
|
||||
/// Submit a spawn request for the user to approve. On approval the host
|
||||
/// creates and starts the container. Requires a prior approved
|
||||
/// `request_init_config` so the manager can customise `agent.nix` first.
|
||||
/// Fails if the proposed config repo for this name does not exist yet.
|
||||
///
|
||||
/// Deprecated: prefer `request_apply_commit` after `config_ready` —
|
||||
/// it pins the exact commit sha, handles both first-time spawns and
|
||||
/// subsequent rebuilds, and always reflects the committed config.
|
||||
RequestSpawn {
|
||||
name: String,
|
||||
/// Optional description shown on the dashboard approval card.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
description: Option<String>,
|
||||
},
|
||||
/// Stop a sub-agent (graceful).
|
||||
Kill {
|
||||
name: String,
|
||||
|
|
|
|||
Loading…
Reference in a new issue