pub enum Outcome {
#[non_exhaustive] Done {
text: Option<String>,
iters: u32,
tools_called: u32,
usage: Usage,
verified: Option<Verdict>,
contract: SealSet,
seal_breach: Option<String>,
},
#[non_exhaustive] BudgetExhausted {
iters: u32,
last_text: Option<String>,
tools_called: u32,
usage: Usage,
deadline_reached: bool,
},
#[non_exhaustive] Stuck {
reason: String,
repeated: u32,
iters: u32,
last_text: Option<String>,
tools_called: u32,
usage: Usage,
},
#[non_exhaustive] Cancelled {
iters: u32,
last_text: Option<String>,
tools_called: u32,
usage: Usage,
},
}Expand description
Where a run finished. Each variant is #[non_exhaustive] so new fields
don’t break downstream matches — always include .. when destructuring.
Variants§
#[non_exhaustive]Done
Model returned text with no tool calls (natural end).
Fields
This variant is marked as non-exhaustive
verified: Option<Verdict>What the acceptance checks said. None means nothing was asked —
so “the model stopped” is all this outcome claims.
contract: SealSetThe sealed contract as it stood before the model’s first turn.
Carried out of the run because a receipt has to say what was measured, not only that the measurement held. Empty when nothing was sealed.
seal_breach: Option<String>Set when a sealed acceptance contract changed during the run.
Distinct from a plain failed verified because the two mean
different things to a caller: a failed check is work not finished,
this is the measuring instrument having been moved. A host may want
to fail a build on the first and page someone on the second.
#[non_exhaustive]BudgetExhausted
Policy budget exhausted before the model stopped requesting tools. Carries everything we know so the caller can recover partial work (saved notes, files written by tools, the last assistant text, etc.) instead of seeing a single bare “budget out” string.
Fields
This variant is marked as non-exhaustive
#[non_exhaustive]Stuck
The agent got stuck and the loop terminated early to save the rest of
the budget. Carries partial work (last text, files already written by
tools) like BudgetExhausted.
Two independent detectors end here, and reason says which: it repeated
the same tool call for StuckPolicy::abort_after rounds without
progress, or it worked a single tool for MonotonyPolicy::abort_after
rounds without touching anything else. Only the second forces a final
synthesis before returning — see the abort site for why.
Fields
This variant is marked as non-exhaustive
#[non_exhaustive]Cancelled
The caller stopped the run through AgentLoop::with_cancellation
before the model reached a natural end. Carries partial work like
Stuck does, because the user who pressed Ctrl-C still wants what was
done. The loop makes no further model call after this — not even
the forced final synthesis the other early exits perform.
A tool that was mid-flight when the token fired may still finish its
side effects — a file write on a blocking thread completes, a child
process keeps running unless its runner kills it on drop — but its
result is discarded before it reaches history. The dispatch is still
counted in tools_called — the telemetry run.end line reconciles
against that number.