Up to here, your code has orchestrated the model one call at a time. This lesson hands the wheel to the model. delegate gives a goal to the autonomous loop, which decides for itself which tools to call and when it is done. That is the part that feels like magic and the part that needs a fence around it, so this lesson is equally about policy: the safety envelope that bounds what the loop can do.
delegate
delegate goal hands the goal to the perceive-think-act loop. The model perceives the goal and the available tools, thinks about the next step, acts by calling a tool, reads the result, and repeats, looping until it produces a final answer. It runs over the agent's tools and exposed skills, the same set advertised with JSON-schema params that you saw in the tools and skills lesson.
on message(text: str) -> str {
return delegate text
}You can scope a delegation with a with clause, restricting the tools it may use, capping its budget, or limiting its steps. This is how you give one span exactly the capabilities it needs and nothing more.
let report = delegate "Research this angle and cite sources: {angle}"
with { tools: [web_search, fetch_page], max_steps: 8 }The policy block
Where with bounds a single delegation, policy is the agent-wide safety envelope. Every delegation runs inside it, and a delegation can only ever tighten these limits, never loosen them.
policy {
max_steps: 20
max_tool_calls: 60
max_spend: $1.00
allow_shell: ask
allowed_domains: ["api.example.com"]
}max_stepscaps how many perceive-think-act iterations the loop may run. The default is25.max_tool_callscaps the total number of tool invocations. The default is100.max_spendcaps how much money the loop may spend on model calls, as amoneyliteral.allow_shellgates shell execution. It is"never"by default, which refuses all shell; set it toaskfor an interactive y/n per command, oralwaysonly for inputs you fully trust.allowed_domainsis the egress allowlist for HTTP. An empty list permits any domain (the egress guard still blocks loopback and private addresses); pin it to specific hosts to narrow the prompt-injection surface.
Two more defaults are worth knowing: max_delegate_depth is 4, which bounds how deeply a delegation may nest further delegations, and an unattended serve run carries a spend cap of $5.00 so a long-running agent cannot quietly run up a bill. The gate is enforced by the runtime, not the model. A confused or prompt-injected model cannot talk its way past allow_shell.
budget
budget(spend, steps, tool_calls) { ... } opens a tighter, nested policy scope around a block. It can only tighten an outer limit, never raise it, so wrapping work in a budget is always safe. Use it to cap an open-ended request so it cannot run away.
on message(text: str) -> str {
return budget(spend: $0.10, steps: 6) {
delegate text
}
}As you saw in the concurrency lesson, a budget around concurrent work tightens the shared cap for the whole scope, and every branch draws from that one pool.
Reliability
Models are not deterministic and tools fail, so Orchard gives you a few constructs to make a flow reliable.
retry(n) { ... } until (v) => ... re-runs a block until a predicate over its value holds, up to n attempts. It is the right tool when the model might produce something you can check but not on the first try.
let label = retry(3) {
gen as Severity "Be decisive: {issue}"
} until (r) => r != Severity.low or attempts == 3try/catch handles a Throw the ordinary way. This is where you catch the error a delegate or an await re-raised, and decide whether to recover, retry, or report.
try {
return delegate text
} catch (e) {
return "I could not complete that: {e.message}"
}halt "reason" stops the agent deliberately with a stated reason. Reach for it when the agent has hit a condition it should not proceed past, rather than throwing an error you would only catch and re-raise.
policy is the agent's outer wall, budget tightens it for a block, and a with clause tightens it for a single delegation. None of them can loosen what encloses it, so the tightest one always wins.Next. Your agent is now bounded and reliable. The last step is getting it out into the world: continue to Running and deploying for the CLI in depth, the compiled IR, and serving an agent.
