mints a fresh provider per delivery, for the same reason the dispatcher does: deliveries run outside any request and concurrently with task execution, so sharing one provider would share one transaction scope across unrelated work.
OptionalpubSub: PubSubEnginePosts the roll-up as an AI-role message in the graph's conversation, on the fork of the message that started the graph (Main when that message is in Main).
Never throws. The dispatcher calls this inside the compare-and-swap that marks a completion delivered; an error escaping would either abort that guard or leave the graph looking undelivered and re-notifying on every later sweep.
Starts the submitting agent a fresh turn carrying the graph's outcome.
Never throws, for the same reason PostMessage does not: the dispatcher calls this inside the
compare-and-swap that marks a completion delivered.
The new run records ReinvokeDepth + 1 so the chain is bounded. The dispatcher has already
refused to call this when the cap is reached — it degrades to PostMessage — so arriving here
means there is budget left, and stamping the depth is what keeps that true for the next hop.
Delivers a finished graph's outcome — as a conversation message, or by starting the submitting agent a fresh turn.
The chain is bounded, and that is what took a schema change to make true. A graph may declare
continuation: 'reinvoke', and the agent it restarts can emit another graph, which can reinvoke again. The guard for that (MAX_REINVOKE_DEPTH) shipped in Phase 3, but the number it compared was permanently zero:TaskGraphService.SubmitreadsReinvokeDepthfrom its caller, and a reinvoked agent had no way to know it was a continuation.AIAgentRun.ContinuationDepthcloses that loop — this stamps depth + 1 on the run it starts,BaseAgentpasses it into any graph that run submits, and the cap finally compares against something real.