agent
agent(ctx, id, options) takes the workflow context, a stable step id, and:
string
required
The model each turn calls.
string
required
The task. Every turn after the first sees it again together with the tool results so far.
number
required
Hard cap on turns. Required, not defaulted: how long an agent may keep calling tools is your decision.
string
The system prompt - who the agent is and how it should behave.
ToolDef[]
The tools the model may call, each from tool().
McpServerDef[]
External MCP servers whose tools join the ones above. Each server is discovered once as a durable step.
ApprovalRule
The gate for every tool that has not answered for itself. See Which rule applies to a tool below.
number
Ceiling on the human decisions this agent may ask for. Omitted, there is none. See Capping the decisions an agent asks for below.
Record<string, unknown>
A JSON Schema the final answer must satisfy. Set, the answer comes back parsed as the object rather than as text. See Answering in a shape below.
"function-calling"
How a turn is composed. Defaults to function-calling.
ProviderName | AIProvider
A provider name resolves through the built-in registry; pass an AIProvider to run against your own adapter.
string
Passed per call and never stored. Omit it to use the provider SDK’s conventional env var.
number
Per-turn output cap, passed to the provider.
number
Passed to the provider when set.
boolean
Stream each turn’s tokens onto the run timeline as they arrive. Opt-in; the turn’s durable result is unchanged either way. See Streaming agent turns.
PromptCacheScope
Ask the provider to bill this agent’s repeated prefix at its own cached rate. “conversation” is the scope an agent wants: every turn re-sends the instructions, the tool declarations and every exchange so far. Opt-in, and refused rather than silently downgraded when the provider cannot place a cache breakpoint. See Prompt caching.
StopCondition[]
Named ceilings checked after each turn, in order; the first met ends the run and names itself. See Stopping an agent below.
(ctx) => boolean
Optional early stop once a turn’s tools have run; must be pure for replay. Prefer stopWhen.
final (the model’s answer), iterations, stopReason
("final", "max-iterations", "stopped", "approval-budget", "bail" or "guardrail"),
stoppedBy - which named condition ended it, when one did - and haltedBy, which names the
guardrail on "guardrail". See Guardrails.
Stopping an agent
maxIterations bounds turns. It does not bound time, and it does not bound the work one
turn generates: a single turn calling twenty tools in parallel is one iteration and
twenty-one steps. stopWhen covers the rest.
wallClock
read a real clock and still replay to the same decision - a replayed run re-walks its
committed turns in milliseconds, and a re-read clock would end it somewhere else.
Two other things follow from being data rather than a closure: a condition registers into
the workflow’s agent manifest, so the console can show what bounds an agent before it has
ever run; and stoppedBy names the one that fired, on the result and on the run’s trace.
The stop closure still works and is still the right tool for a predicate nobody needs to
read back - it reports stopReason: "stopped" with no stoppedBy, because a closure has
no name.
Ending early on success
A tool that has already produced the answer says so withbail(), and the loop returns
that value instead of handing the result back for a turn it does not need.
stopReason: "bail" and the value as final. Every other tool the same
turn started still runs to completion first - a side effect that already happened is not
dropped from the record because a sibling finished sooner.
bail() is returned, never thrown. A tool handler runs inside a durable step, so a
throw is committed as that step’s failure - an early success would be indistinguishable
from a broken tool, and would replay as a failure forever.tool
A tool is one definition. Give it a name the model calls back with, a description it reads to
decide, and a JSON Schema for the input it must produce:
string
required
The tool’s identity: what the model calls back with, and the tail of the durable step name its call writes. Must be unique within an agent.
string
What the tool does. This is what the model reads to decide whether to call it.
Record<string, unknown>
JSON Schema for the input the model must produce. A tool with none is declared as an object schema with no properties.
Record<string, unknown>
JSON Schema for what the tool returns. Nothing validates against it - it is published, so a surface can render a result before it has seen one.
(output: unknown) => unknown
Project the result for the model only. The durable step still records what the tool returned. See Showing the model less than you record below.
ToolAnnotations
Behaviour hints - title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint. Hints, never gates. See Behaviour hints below.
(input: unknown) => unknown | Promise<unknown>
Your own code. Runs as a durable step: the model’s input in, the return value back to the model.
string
A workflow to run instead of a handler. The tool call becomes a linked child run.
string
The workflow tool’s app; addressed like step.runWorkflow.
string
Pin the workflow tool to a specific runner.
boolean
Park the run on a human before this tool runs. See Tools a human has to approve below.
ApprovalRule
Annotates the gate (risk, summary, policy, context, escalatesTo, allow, timeout, onTimeout) and decides whether it is raised at all (if, minRisk). Declaring one gates the tool; requiresApproval: false opts out of it.
string
The integration this tool reaches, e.g. “google-calendar” - a free-text label, not a Credential id. Feeds the tools manifest below.
handler or workflow - a tool is backed by your code or by a workflow:
childRunId - the id of the run it started - and the run it started carries parentRunId,
parentStep (the step it was spawned on) and parentAttempt (which attempt of that step spawned it).
So GET /runs?parentRunId=<id> returns every child one agent run started, and each child links back
to the exact call that made it. A handler-backed tool has neither end of the edge: it runs inside the
agent’s own run. See the runs API.
A tool’s
input is raw model output. The schema tells the model what to produce; it does not
validate what arrives, so parse it in the handler before you trust it.Behaviour hints
annotations describes how a tool behaves, in the same field names MCP uses, so one declaration
means the same thing to Duraton and to any MCP client reading your tools:
What the model sees
Every turn sees the task, the exchanges so far, and your tools declared as function-calling schemas. How the exchanges reach the model depends on the adapter:
The fallback is not a lesser answer, just a lesser shape: a model reading its own tool calls as
prose is working in a form it was not trained on. Either way the turns are read from the loop’s
recorded history and nothing else, so a replayed turn is identical to the original one.
Bounding context
Nothing trims history by default, so an agent that runs long enough eventually hits the model’s context window and fails. Passcontext and every turn - both a native transcript and the
rendered-text fallback above - sees a bounded view instead of the full, ever-growing history:
count and token-budget are free: recomputed fresh every turn, they never change what the run
itself remembers. summarize is the one that costs something, so it is the one that persists - a
later turn starts from the already-reduced history instead of paying to re-summarize the same
prefix every time. summarize’s model call rides its own durable step, so a replay reads the
recorded summary rather than generating a new one - full detail on that step and its replay
guarantee lives on the Context management page, the SDK-level page for the
loop-level context option this wraps.
Without
context, an agent behaves exactly as it did before the option existed - the whole
history reaches every turn. The option changes nothing for an agent that does not use it.