Tools a human has to approve
Mark the tools an agent should not use unattended. The run parks on a reviewer before the tool runs, holding no worker while it waits:requiresApproval: true raises the gate and says nothing else about it. Say more with approval,
and the same annotations a hand-written ctx.step.approval takes apply to the
tool’s gate:
Each gate is its own durable step, so resuming the run replays the agent without asking anyone
twice. Both the approval and the tool call show up under their turn in the console, so a parked
agent reads as parked rather than as a tool that never finished.
Gating some calls and not others
requiresApproval is one bit for the whole tool, so a refund tool marked true wakes someone for
a 50,000 one. approval.if and
approval.minRisk are the third answer: a condition on this call, written as data and evaluated
by Duraton.
tool, args, risk and iteration - and every
JSON number in args reaches it as a double. A rule that cannot be evaluated raises the gate rather
than skipping it, and one that declines completes the step as decidedBy: "system:rule" with no
approval for anyone to answer. The full environment, the failure modes and when this beats a
hand-written if are on Approvals.
Which rule applies to a tool
A rule can be declared in two places: on the tool, and on the agent as the default for every tool that has not answered for itself.requiresApproval is tri-state on purpose - absent,
true and false are three different answers rather than a boolean with a default:
Two rows are worth reading twice.
requiresApproval: true on a tool the agent has a default for
takes that default’s annotations rather than erasing them: true asks for a gate, it does not claim
there is nothing to say about one. And a tool’s own approval replaces the default rather than
merging with it - a rule is one policy, so a tool that states risk and no timeout has no timeout,
whatever the default said.
The default is also what covers an MCP server’s tools that the
server’s own predicate says nothing about. Those tools are attached here and discovered at runtime,
so there is no declaration above to annotate and no list to enumerate ahead of time.
Capping the decisions an agent asks for
maxApprovals is a ceiling on how many human decisions one agent may ask for. When a turn’s tool
calls would take it past the ceiling, the agent stops instead of running them, and returns
stopReason: "approval-budget":
maxIterations bounds how long it runs, and this bounds how much of a person’s attention it can
spend doing so. Handle the stop like any other terminal reason - the agent returns rather than
throwing, so the workflow decides what a run that ran out of sign-off does next.
Showing the model less than you record
A tool that returns a thousand rows costs a thousand rows of context on every turn after it. Give it atoModelOutput and the model reads the summary while the durable step keeps the whole thing:
Checking the model’s tool arguments
The arguments a tool is called with are written by the model. Pass a guardrail and they are checked against the tool’s owninputSchema before the handler ever sees them; a refusal comes back to the
model as that tool’s result, so it can correct itself.