Context
AgentContext is the ctx passed to every agent. It extends the
workflow context, so all the durable primitives
are there, plus a few things specific to agents.
Inherited durable primitives
Section titled “Inherited durable primitives”These behave exactly as they do in a workflow. Use them for anything non-deterministic or side-effecting:
ctx.step, ctx.now, ctx.uuid, ctx.random, ctx.emit, ctx.emit_status,
ctx.sleep, ctx.sleep_until, ctx.wait_for_signal, ctx.interrupt,
ctx.checkpoint.
See Timers, Signals, and Human-in-the-Loop.
Long-running turns: continue_as_new
Section titled “Long-running turns: continue_as_new”A tool-loop turn that runs for a very long time accumulates an event log like any
other run. ctx.continue_as_new(params=...) ends the current run and starts a fresh
successor run in the same thread, carrying forward whatever you pack into params.
It’s the same primitive workflows use to bound history growth. Downstream consumers
(run(), thread turn history, projections) follow the chain transparently, so the
handoff presents as a single logical turn even though it is several physical runs
under the hood.
ctx.continue_suggested turns True once the run’s event log has consumed 80% or
more of max_event_count_per_run, so you hand off at a clean tool-loop boundary
instead of hitting the hard cap:
if ctx.continue_suggested: # roll the conversation into a compact summary and start a fresh run await ctx.continue_as_new(params={"summary": running_summary})Kafka-driven and branch/stream (non-durable) executions reject continue_as_new
with a typed ContinueAsNewNotSupportedError. A consumed record or an in-memory
branch replay is its own unit of work, so there’s no “successor run” to hand off to.
Sub-agents
Section titled “Sub-agents”call_agent runs another agent and returns its result. It records like any other
step, so the child isn’t re-run on replay.
summary = await ctx.call_agent(summarizer, text=document, id="summarize")gather runs several calls concurrently and waits for all of them:
results = await ctx.gather( (classifier, {"text": a}), (classifier, {"text": b}), id="classify-batch",)AgentLimits on the parent caps depth and parallelism so a fan-out can’t run away.
Workflows
Section titled “Workflows”An agent can hand off to the durable engine directly. spawn_workflow starts a
workflow run; wait_for_workflow blocks on its result.
run_id = await ctx.spawn_workflow("nightly_report", params={"date": today})result = await ctx.wait_for_workflow("nightly_report", run_id)Memory and threads
Section titled “Memory and threads”ctx.memory is cross-turn state for an agent, keyed by a namespace tuple so you can
scope it per user or per thread:
# remember a preference this turn, recall it on a later oneawait ctx.memory.put(("user", user_id), "preferences", {"tone": "concise"})prefs = await ctx.memory.get(("user", user_id), "preferences") # {"tone": "concise"} or Nonectx.thread is the conversation thread the turn belongs to, when there is one. Both let
an agent carry context across separate turns without you wiring up storage by hand.
ctx.turn_id is the id of the current turn, handy for logging and idempotency keys.