subagent: give each run its own signal URL, and drop the name argument
`goal_reached`/`need_help` took the session name as a tool argument, so identity was an assertion by the caller and the only guard on it was `occupancy()` — "does that name have a turn in flight", which two concurrently running siblings both satisfy for each other. A subagent could stop its sibling's run by naming it. Identity moves into the URL. Each spawned run is minted an unguessable token (`Uuid::new_v4`, the OS CSPRNG), the URL carrying it goes into that one subagent's own `--mcp-config`, and the route resolves it back to a session before dispatching to a handler bound to that session. Neither tool takes a `name` any more: a subagent has no field in which to name a sibling, and a sibling's name — which a brief may well mention — is not a token. One route with a path parameter, not a route per session: the `Router` is built once at startup and subagents come and go for the daemon's whole life. An unminted or revoked token gets a bare 404, the same answer either way, so nothing enumerates. A run's token is revoked when the run ends (`finish_turn`) or when a call never reached a spawn. Two things fall out of that: - the config file becomes one per session. A single shared path was already a race between two `start`s; with a per-session URL in it, the loser would read the winner's identity. - `occupancy()` stops being the identity guard and is gone from the signal path entirely rather than kept "just in case" — a revoked token can't reach it, and it never answered the question it was standing in for. It still backs `status`, which is what it was always actually for. Refs #4403 Refs #4413
This commit is contained in:
parent
b5fc17aa59
commit
34129d776c
11 changed files with 575 additions and 180 deletions
|
|
@ -44,13 +44,16 @@ async fn main() -> Result<()> {
|
|||
"hive-subagent-daemon starting"
|
||||
);
|
||||
|
||||
// The one place the subagent-facing signal URL can come from: the
|
||||
// The one place the subagent-facing signal URLs can come from: the
|
||||
// address this process was told to listen on. Anything else would be a
|
||||
// guess at the deployment's own port assignment.
|
||||
let signal_url = format!("http://{}{}", cli.http, hive_subagent_mcp::mcp::SIGNAL_PATH);
|
||||
// guess at the deployment's own port assignment. This is only their
|
||||
// common prefix — each session's actual URL is this plus a token minted
|
||||
// for that session alone, which is what makes a signal's identity a
|
||||
// property of the endpoint rather than of the payload.
|
||||
let signal_base = format!("http://{}{}", cli.http, hive_subagent_mcp::mcp::SIGNAL_PATH);
|
||||
let state = Arc::new(hive_subagent_mcp::session::State::new(
|
||||
todo_socket,
|
||||
signal_url,
|
||||
signal_base,
|
||||
));
|
||||
|
||||
// Serve the MCP tools over streamable-http forever. No background poll
|
||||
|
|
|
|||
Loading…
Reference in a new issue