subagent: close the start/continue TOCTOU race with an atomic reservation

This commit is contained in:
damocles 2026-09-09 19:49:19 +02:00
commit 561bd09618
3 changed files with 181 additions and 35 deletions

View file

@ -1,5 +1,6 @@
//! The MCP tool surface: `start` / `continue` / `interrupt`, served
//! directly over streamable-http — no stdio bridge, no round-trip socket.
//! The MCP tool surface: `start` / `continue` / `status` / `interrupt`,
//! served directly over streamable-http — no stdio bridge, no round-trip
//! socket.
use std::sync::Arc;
@ -116,9 +117,11 @@ impl SubagentMcp {
}
#[tool(
description = "Signal a currently-running subagent session to stop. Only works while \
it's actually running there's no queued/pending state to cancel pre-emptively, \
only running or not tracked at all. `force: true` for SIGKILL, otherwise SIGINT."
description = "Signal a currently-running subagent session to stop. Only works once \
it's actually running a `start`/`continue` still in its brief window before the \
process is confirmed spawned refuses interrupt too (nothing to signal yet; retry \
shortly), same as a name with nothing tracked at all. `force: true` for SIGKILL, \
otherwise SIGINT."
)]
fn interrupt(&self, Parameters(args): Parameters<InterruptArgs>) -> String {
match session::interrupt(&self.state, &args.name, args.force) {
@ -129,9 +132,10 @@ impl SubagentMcp {
#[tool(
description = "Report whether a subagent is currently running — a zero-cost check that \
never launches a process, unlike `continue`. Distinguishes running, idle (a session \
exists but nothing is in flight `continue` to give it another turn), and no such \
session at all."
never launches a process, unlike `continue`. Distinguishes running, starting (a \
`start`/`continue` is in flight but not yet a confirmed spawn this is normally \
over in well under a second), idle (a session exists but nothing is in flight \
`continue` to give it another turn), and no such session at all."
)]
fn status(&self, Parameters(args): Parameters<StatusArgs>) -> String {
match session::status(&self.state, &args.name) {