pub struct TurnResult {
pub messages: Vec<Message>,
pub usage: Usage,
pub stop: Option<StopReason>,
pub pending_approvals: Vec<PendingApproval>,
pub handoff: Option<HandoffRequest>,
pub unattended_denials: Vec<UnattendedDenial>,
pub mid_stream_failure: Option<MidStreamFailure>,
pub delegate_records: Vec<DelegateRecord>,
pub grounded: bool,
pub pending_questions: Vec<PendingQuestion>,
}Expand description
Output of one run_turn call.
Carries the wire messages produced (assistant text and tool results), the aggregated usage across every provider call in the loop, and the stop reason from the final step.
When Self::pending_approvals is non-empty the turn is paused:
the model asked for one or more sensitive tools, run_turn short-
circuited before executing them, and the caller must capture a
human-in-the-loop decision (idiomatic Temporal “signal” pattern) before
re-driving. The choice to surface this as a result field rather than an
out-of-band callback keeps run_turn pure (no side-effect handle), keeps
the durability boundary at the caller (the event log already gives us
replay), and lets the per-conversation Mutex / Lease release while we
wait — matching the durable-workflow pattern.
Fields§
§messages: Vec<Message>Wire messages — assistant text + tool result messages, in order.
usage: UsageSum of input_tokens / output_tokens across every provider call
this turn made (the function-calling loop may iterate multiple times).
stop: Option<StopReason>Stop reason of the final provider step.
pending_approvals: Vec<PendingApproval>Tool calls awaiting human approval. Empty in the common case; when non-empty, the turn paused before executing any tool in this batch.
handoff: Option<HandoffRequest>Populated when the model emits the reserved __handoff_to tool call.
The loop suspends before it executes more tools. The control plane emits
a signed
polyc_proto::proto::polychrome::handoff::v1::Handoff event into the
parent event log. The transfer is one-way.
If multiple __handoff_to calls appear in the same tool batch (the
model emitted two at once), only the first is honored — fan-out is a
V2 concern and the wire shape doesn’t model parallel children today.
unattended_denials: Vec<UnattendedDenial>Gated calls an unattended turn denied fail-closed (#623): one entry
per tool call the capability gate would have escalated on a turn with
RunTurnOptions::unattended set.
Each never ran and never paused; the model saw a legible denial result.
Empty for every attended turn and for an unattended turn whose calls all
cleared the gate. The control plane appends one durable, signed audit event
per entry so the forensics trail records what was attempted and why it did
not run — a tracing line cannot satisfy PRD §12.
mid_stream_failure: Option<MidStreamFailure>Set when the provider stream failed mid-turn — after complete_with_retry
exhausted the connect/initial-response retry boundary, or during
collect_turn’s fold of an already-open stream (#798).
The loop returns Ok with this populated rather than propagating the
error via ?, so Self::messages / Self::usage still carry
whatever earlier iterations already executed (tool calls, produced
text) instead of discarding it. None on an ordinary turn. The caller
(the harness loop / control plane) is expected to persist the partial
result AND fail the turn with a typed error — never treat a Some
here as a successful completion.
delegate_records: Vec<DelegateRecord>One entry per __delegate_to call this turn dispatched (#872): the
forensic record of a worker sub-agent invocation, surfaced so the
control plane can append a signed subagent_spawn/subagent_result
pair plus a subagent_model_call determinism record — the
delegation’s own forensic trail, attributed per sub-agent rather than
folded into Self::usage. Empty for every turn that never called
__delegate_to.
grounded: boolWhether native search grounding (CompletionRequest::web_search) was
allowed for ANY step this turn made, conservatively treated as having
ingested untrusted web content — grounding never produces a
tool_result for worker_ingested_untrusted_content (private) to see, so a
worker (or top-level turn) that grounded would otherwise come back
laundered as fully first-party. The turn’s provider decides mid-
generation whether it actually grounded; this flag doesn’t know
either way, so it fails safe by tainting whenever grounding was
merely allowed, not only when it was demonstrably used. false for
a turn that never had the primitive granted or ran entirely under
taint (which denies it outright).
pending_questions: Vec<PendingQuestion>Questions from an ask_question call awaiting an answer (#1660).
Empty in the common case; when non-empty, the turn paused before
executing any tool in this batch — mirroring
Self::pending_approvals, but as an independent pause path (a
clarifying question is not a danger/permission decision, so it never
enters the HITL approval gate).
Trait Implementations§
Source§impl Clone for TurnResult
impl Clone for TurnResult
Source§fn clone(&self) -> TurnResult
fn clone(&self) -> TurnResult
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for TurnResult
impl Debug for TurnResult
Source§impl Default for TurnResult
impl Default for TurnResult
Source§fn default() -> TurnResult
fn default() -> TurnResult
Auto Trait Implementations§
impl Freeze for TurnResult
impl RefUnwindSafe for TurnResult
impl Send for TurnResult
impl Sync for TurnResult
impl Unpin for TurnResult
impl UnsafeUnpin for TurnResult
impl UnwindSafe for TurnResult
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more