Separate parent runs from detached work
The SDK has several forms of concurrency with different owners and lifetimes. Cancelling a parent turn does not imply that session-owned work should be discarded.clear, reset_in_place, shutdown, and session drop cancel detached workers, workflows, and background processes. An in-flight host foreground Subagents::run follows its caller’s cancellation token; shutdown neither cancels nor awaits it. A parent RunHandle controls only its active turn.
Drain events while the run executes
Session::start returns immediately. A session allows one active parent run, so another run, run_outcome, or start returns SdkError::BusySession until the handle settles.
Events are observational: The event channel is bounded and lossy: a full channel drops lifecycle events rather than blocking the run. Drain concurrently to preserve observability and use
RunOutcome as the authoritative terminal record. Dropping the entire RunHandle requests cancellation.Cancel cooperatively
Keep the handle’s cancellation token for UI signals or caller deadlines. Tools receive a child token in their execution context, but the kernel may drop a tool future as it selects cancellation, so the run outcome—not a tool-produced “cancelled” payload—is authoritative.CancellationToken can govern several requests through RunRequest::cancellation. Each run receives a child token: cancelling the parent cancels all children, while cancelling one run does not cancel its siblings.
Treat notifications as wakeups
Subscribe before work can spawn. Background notifications are broadcast observations: reading them does not consume the durable result owed to the parent, and a slow receiver can miss old entries.pending_completions()for results awaiting parent delivery;subagents().active()for detached workers;workflows().runs()for graph state and terminal outcomes;processes().list()for process status;- the transcript for completion batches already delivered.
SubagentFinished can arrive before inbox admission, and CompletionsReady can be coalesced or stale by the time it is read. At run end, the SDK re-arms the wakeup and announces pending completions that arrived after the last model boundary. Reconcile all state before declaring quiescence.
Continue without inventing a user prompt
RunRequest::continuation() adds pending detached results as one generated user turn and starts the next model step without appending a user-authored prompt.
Determine quiescence from all services
A robust host waits until workers, workflows, and processes are terminal, then delivers pending completions and verifies that every successfully acknowledged job reached the transcript. Derive expected job IDs from successful tool results—not from model prose or proposed calls.Shut down with a bound
Finish or cancel and await any active parentRunHandle; shutdown returns BusySession while a parent run is active. Then stop session-owned work with a finite grace period. If a driver times out, dropping its future may only request cancellation: retain, cancel, and await its active RunHandle before attempting shutdown.
shutdown closes the session to new work, cancels detached workers and workflows, kills session-owned processes, drops undelivered completion results, and waits up to the grace period. Inspect required workflow/process outcomes and deliver required completions before calling it. If the grace period expires, SdkError::ShutdownTimeout reports workers and processes still winding down; the session remains closed to new work, and exit notifications may still arrive. Dropping a session requests best-effort cancellation but does not wait.