feat(subagent): add current-turn interrupt RPC

ctx.subagents.interrupt() stops one live continuable child's current turn
via Agent.cancel(cause, { keepInbox: true }) under either a human durable
parent address or an exact live ancestor Agent. Fire-and-return: admission
is synchronous, quiescence is not awaited. Pending inbox work, the
Activation, and published descendants are preserved; only a later waking
send resumes the parked FIFO queue. Absent, one-shot, and disposing
targets are accepted no-ops.

The new Host RPC subagent.interrupt calls only that primitive with user
authority — no catalog, history, persistence, or parent-registry lookup —
so a live child stays stoppable while its parent Agent is offline.

Refs #1535
This commit is contained in:
Hypatia May
2026-08-06 11:59:19 +08:00
committed by Tianyi Cui
parent 5f9456c24f
commit 66a21a38b1
27 changed files with 613 additions and 17 deletions

View File

@@ -58,6 +58,7 @@ import type {
ContinuableStart,
ContinuableStartSpec,
SubagentFollowupOptions,
SubagentInterruptAuthority,
SubagentReportOptions,
} from './continuation.ts'
import SubagentActivationSetupRegistry from './activation-setup-registry.ts'
@@ -111,6 +112,7 @@ export type {
ContinuableStartSpec,
CoordinatorMessageSource,
SubagentFollowupOptions,
SubagentInterruptAuthority,
SubagentReportDelivery,
SubagentReportMessageSource,
SubagentReportOptions,
@@ -231,6 +233,24 @@ export class SubagentService extends Service {
return this.requireContinuations().followup(parent, childId, content, options)
}
/**
* Interrupt one live continuable child's current turn under a human parent
* address or an exact live ancestor Agent. Fire-and-return: the cancel
* signal is issued before this returns, but the target may keep running
* until it observes the signal. Pending inbox work, the Activation, and
* published descendants are preserved; only a later waking send resumes the
* parked FIFO queue. An absent target — including a one-shot or unknown id —
* is an accepted no-op, as is a manager-less composition, which cannot own a
* live Activation.
* @param targetSessionId - the durable child session id to interrupt.
* @param authority - the human parent address or exact live ancestor Agent.
* @throws {SubagentError} `UNAUTHORIZED` when the authority does not own the
* live target.
*/
interrupt(targetSessionId: SessionId, authority: SubagentInterruptAuthority): void {
this.continuations?.interrupt(targetSessionId, authority)
}
/**
* Deliver selected content from one live continuable child to its durable
* direct parent. The child is the authority credential; callers cannot name a