Bound work with budgets and cancellation
Apply hard limits across an entire run tree and stop descendants with structured cancellation.
Budget dimensions
Budget can bound tokens, cost in micro-USD, wall-clock duration, model turns,
tool calls, and delegations. Limits apply to the shared run tree, not just one
Agent loop.
Choose limits from product behavior and failure cost. A token cap alone does not protect a tool-heavy or highly parallel run.
Create a bounded context
use runifold::{Budget, BudgetTracker, CapabilitySet, RunContext};
let run = RunContext::root(
BudgetTracker::new(Budget {
tokens: Some(20_000),
turns: Some(10),
tool_calls: Some(6),
delegations: Some(2),
..Budget::default()
}),
CapabilitySet::new(),
);Budget exhaustion is a typed terminal condition. Surface it separately from a provider outage or malformed model output.
Deadlines
Add a deadline at the ingress boundary so provider calls, tools, and child runs observe the same remaining lifetime.
use std::time::{Duration, Instant};
let run = run.with_deadline(Instant::now() + Duration::from_secs(20));A deadline limits total useful time; an individual network timeout limits one operation. Production systems normally need both.
Hierarchical cancellation
Cancellation propagates from parent to descendants. Long-running tools should
observe their ToolContext and stop cooperatively. Cancellation is not a
rollback: already-completed external effects still exist.
Record the cancellation reason and return a stable application status rather than an arbitrary transport error.
Parallel reservations
Parallel work must reserve shared budget before it starts. This avoids every branch independently observing the same remaining allowance and collectively overspending it.
Keep a small reserve for cleanup, persistence, and the final response. Treat reservation mismatch as an accounting defect, not a retry hint.