Interrupts
An interrupt is a durable pause. A worker stops the session to wait for a human, and a later resume hands it back the human's answer. A paused session holds no compute and survives a restart.
Example
The worker pauses before a sensitive action, then acts on the answer.
function decide({ trigger, proposed }) {
if (trigger.type === "tool.execute" && trigger.name === "send_email") {
return { actions: [{ type: "interrupt", reason: "confirm", payload: { message: "Send the email?" } }] };
}
if (trigger.type === "interrupt.resumed") {
return trigger.payload?.approved
? { actions: [{ type: "tool.result", result: "sent" }] }
: { actions: [{ type: "tool.error", error: "declined" }] };
}
return proposed;
}A human resumes it, addressing the interrupt by id:
{ "type": "interrupt.resume", "interrupt_id": "int-1", "payload": { "approved": true } }Pausing
Return an interrupt action. reason is required; payload carries whatever
the human needs; the engine mints an interrupt_id when you omit one.
An interrupt is anchored to the conversation head where it was raised: it parks that branch, not the whole session. Several interrupts can be open at once, on the same or different branches. An interrupt raised before any message exists has no anchor and parks every branch (the global case — how system-level pauses like budget stops behave).
LLM calls in flight on the parked branch are voided; calls on other branches, and tools and sub-agents already running, keep going.
Resuming
An interrupt.resume input clears the interrupt by id. If the interrupt was
parking the active branch, the worker gets an interrupt.resumed trigger with
the resume payload; clearing an interrupt left behind on an abandoned branch
delivers nothing. A stale or duplicate id is a no-op.
While parked
New messages extending the parked branch are refused until it resumes. Work that settles an in-flight call is still recorded, but its follow-on decision is held and delivered only after the resume.
The rest of the tree stays live. A client view that edits an earlier message — branching off below the interrupt's anchor — dispatches normally: the user walks away from the parked question and the interrupt stays open on the abandoned branch. Switching back to that branch re-enters the parked state, and the interrupt is still there to answer or clear.
Spec
// action
{ type: "interrupt"; interrupt_id?: string; reason: string; payload?: unknown }
// trigger
{ type: "interrupt.resumed"; interrupt_id: string; payload?: unknown }
// client input
{ type: "interrupt.resume"; interrupt_id: string; payload?: unknown }Next
- Durability: the pause is persisted state.
- Deferred tools: waiting on one call instead of the session.
- Protocol: the interrupt action, trigger, and input.