Skip to main content

TurnEvent

Enum TurnEvent 

Source
pub enum TurnEvent {
    ContextCompacted {
        reason: CompactionReason,
        summarized_messages: u32,
        summary_preview: String,
    },
    TextDelta(String),
    ToolStarted {
        name: String,
    },
    ApprovalPending {
        turn_id: String,
        request_id: String,
        tool_name: String,
        title: String,
        args_json: String,
        reason: String,
        resolve_token: String,
        preview: Option<ApprovalPreview>,
    },
    QuestionPending {
        turn_id: String,
        call_id: String,
        index: u32,
        header: String,
        question: String,
        options: Vec<QuestionOptionPrompt>,
        args_json: String,
        answer_token: String,
        already_surfaced: bool,
    },
    HandoffStarted {
        child_agent_id: String,
        reason: String,
    },
    InviteDelivery {
        target_user_id: String,
        code: String,
        inviter_display: String,
    },
    WalletLinkPrompt {
        link_url: Option<String>,
        renewal: bool,
        requested: bool,
    },
    WalletUpdatePrompt {
        update_url: String,
        new_limit: String,
    },
    WalletRevokePrompt {
        revoke_url: String,
    },
    TurnFailed {
        kind: TurnFailureKind,
        message: String,
    },
    Done,
}
Expand description

Incremental event emitted while a turn streams from the control plane.

Unlike AgentDialer::run_turn, which folds the whole turn into one string, the streaming API surfaces each meaningful step as it arrives so a live surface (e.g. Slack chat.appendStream) can update in place.

Variants§

§

ContextCompacted

The turn’s context was auto-compacted before it ran. Emitted ONCE, as the first event of the turn (before any TurnEvent::TextDelta), so a live surface can render a brief notice rather than letting earlier work silently vanish from the model’s view. The full transcript is retained server-side; only this turn’s prompt was compacted.

Fields

§reason: CompactionReason

Whether the context was summarized or truncated.

§summarized_messages: u32

Earlier messages folded into the summary (0 when truncated).

§summary_preview: String

Short preview of the surviving summary; empty when truncated.

§

TextDelta(String)

Incremental assistant answer text (model/assistant role).

§

ToolStarted

A tool call has started, named for a user-visible “thinking step”.

Fields

§name: String

The tool/function name, or the call id when the name is absent.

§

ApprovalPending

The turn paused before executing a tool that requires human approval. Emitted (one per pending call) from the terminal AgentEnd just before TurnEvent::Done. The caller submits a decision via ApprovalService.Respond (the THIN path) and re-drives the turn.

Fields

§turn_id: String

Turn that emitted this occurrence of request_id.

§request_id: String

Tool-call id == the approval request_id to answer.

§tool_name: String

The tool/function name awaiting approval. Raw machine identifier; the field of record for trust/audit.

§title: String

Human display label (MCP-style title) for the tool, for rendering in the approval prompt. May be empty; the surface then derives one from tool_name.

§args_json: String

Arguments JSON for the call.

§reason: String

Why this call is gated, when the pause is an OVERRIDE of a call that would not otherwise need approval. Empty for an ordinary gated call; non-empty only for the lethal-trifecta / Rule-of-Two containment override — rendered on the approval card so the approver sees that untrusted content is in context and this is an outbound call.

§resolve_token: String

Short-lived signed capability (#787), freshly minted for THIS card and scoped to turn_id + request_id + the conversation it belongs to. Opaque to the caller: carry it back unmodified to ApprovalDialer::respond. ApprovalService.Respond rejects a decision whose token is missing, expired, or bound to a different request or conversation.

§preview: Option<ApprovalPreview>

Computed-preview enrichment (#1496), set only for a routine_create call whose arguments parsed against the compiled spec shape — None for every other tool. Built by the control plane from the compiled spec, never from the model’s narration. Render it with approval_preview_text, never a hand-written per-surface rendering.

§

QuestionPending

A question the model asked via ask_question is awaiting an answer (#1660). Emitted (one per pending question) from the terminal AgentEnd just before TurnEvent::Done — the question-pause SIBLING of TurnEvent::ApprovalPending above, not a reuse of it: ask_question is a decision the user makes, not a danger/permission gate, so it has its own event-log pair and its own signed-answer canonical (polyc_crypto::question). The caller submits a decision via QuestionService.Respond (the THIN path, mirroring ApprovalService.Respond) and re-drives the turn.

Fields

§turn_id: String

Turn that emitted this question occurrence. A provider re-mints a tool-call id across turns, so the turn is what separates two questions that share a (call_id, index) (#2523).

§call_id: String

The ask_question call id this question came from. Several pending questions can share the same call_id (one call can ask up to three questions at once) — identity is (turn_id, call_id, index).

§index: u32

This question’s position within its call’s questions array (0-based).

§header: String

The short label (fits a chat-surface button-row heading).

§question: String

The one-sentence question to ask.

§options: Vec<QuestionOptionPrompt>

2-4 mutually exclusive options to offer.

§args_json: String

The raw ask_question call’s full arguments JSON (every question in the call, not just this one) — the audit binding a signed answer must match against.

§answer_token: String

Short-lived signed capability, freshly minted for THIS card and scoped to turn_id + call_id + index + the conversation it belongs to. Opaque to the caller: carry it back unmodified to QuestionDialer::respond. QuestionService.Respond rejects a decision whose token is missing, expired, or bound to a different occurrence or conversation.

§already_surfaced: bool

Whether this still-unanswered question was already surfaced once (a later turn’s boundary marker sits after its question_request in the event log while it stayed unanswered). false: render the full interactive card; true: render a compact reminder instead (#1970). Derived by the control plane at replay time — never edge-cached. Unlike TurnEvent::ApprovalPending (which never carries this bit — approvals only ride it through PendingApproval’s recovery path), THIS streamed event is itself the vehicle for re-surfacing a question on every redrive, so it copies the control plane’s already-computed value straight through rather than always reporting false.

§

HandoffStarted

The turn suspended to delegate to a sub-agent: the model invoked the reserved __handoff_to primitive. Surfaced (once) from the terminal AgentEnd.handoff just before TurnEvent::Done. The child runs in an independent conversation. The parent receives no child result. An edge can render “delegating…” instead of going silent.

Fields

§child_agent_id: String

The child agent / planner chosen; empty selects the parent’s default planner.

§reason: String

Free-form reason captured for operator visibility.

§

InviteDelivery

The control plane minted an admin invite this turn for the edge to deliver privately (agent-evaluable admin invite, #698). Surfaced from the terminal AgentEnd just before TurnEvent::Done. The edge opens the target’s direct message and delivers the code there — and ONLY there. The code reached neither the agent nor a channel; the edge fails closed (telling the admin, delivering nothing) if it can’t reach the target privately.

Fields

§target_user_id: String

The target’s provider-native user id (from the mention markup). The edge opens THIS person’s direct message.

§code: String

The single-use invite code — the one secret on this event. Deliver it only to the target’s direct message; never log it or post it to a channel.

§inviter_display: String

The inviting admin’s display name, for the target-facing copy. May be empty; the edge then uses a neutral phrasing.

§

WalletLinkPrompt

A paid tool call this turn needed a USABLE linked wallet (#519; renewal branch #2122). Surfaced from the terminal AgentEnd just before TurnEvent::Done. Carries only the URL and the renewal bit — no copy — so every edge renders the identical “Link a wallet”/“Renew access” card text through the shared polyc_proto helpers instead of hand-writing its own wording.

Fields

§link_url: Option<String>

The deployment’s wallet-link URL, when known. None = point the reader at an admin instead of rendering a dangling button.

§renewal: bool

True when the caller HAD a linked wallet whose delegation is no longer usable (expired, revoked, or otherwise broken) — false when they never linked one at all. Selects the “renew” card copy over the first-time “set one up” copy.

§requested: bool

True when the caller directly asked to link (or replace) a wallet — the wallet_link tool called on its own, never the paid_fetch/web_fetch payment-interrupt path renewal covers. Selects the “here’s a secure link” card copy over both the first-time and renewal payment-interrupt wording.

§

WalletUpdatePrompt

wallet_update_limit minted a fresh TIP-1011 in-place spending-limit-update ceremony this turn (issue #1041/#1159). Surfaced from the terminal AgentEnd just before TurnEvent::Done. Carries only the URL and the requested cap — no copy — so every edge renders the identical “Update spending cap” card text through the shared polyc_proto helpers instead of hand-writing its own wording.

Fields

§update_url: String

The one-time spending-limit-update ceremony URL. Always non-empty when this variant is emitted — the tool that produces this either mints a URL or refuses outright.

§new_limit: String

The requested new daily spending cap, in the settlement currency’s human units (e.g. "20"), echoed on the card.

§

WalletRevokePrompt

This turn’s unlink_self (wallet target) call minted a TIP-1011 hard-revoke ceremony for the just-unlinked delegated key (issue #1042/#1156). Surfaced from the terminal AgentEnd just before TurnEvent::Done. Carries only the URL — no copy — so every edge renders the identical “Revoke access” card text through the shared polyc_proto helpers instead of hand-writing its own wording.

Fields

§revoke_url: String

The one-time hard-revoke ceremony URL. Always non-empty when this variant is emitted — only set for a delegated key that was provisioned onchain.

§

TurnFailed

The turn failed durably instead of completing (#756). Surfaced from the terminal AgentEnd.failure just before TurnEvent::Done — a structured, durable fact the control plane persisted, not merely the Connect RPC status a dial error would carry. AgentEnd.failure exists specifically so “external surfaces (Slack, the cockpit) can react” (see its wire doc comment); before this variant existed, no edge ever read it, and a durably-failed turn with no other content (no messages, no pending approvals, no handoff, no invites) surfaced as silence — every edge saw Done with nothing to show and posted nothing.

Fields

§kind: TurnFailureKind

Provider-agnostic failure classification (mirrors the wire TurnFailureKind 1:1; TURN_FAILURE_KIND_UNSPECIFIED maps to TurnFailureKind::Other — a definite failure with an unknown reason is still a definite failure).

§message: String

Human-readable diagnostic text (the underlying provider/tool error). Log-only — an edge’s user-facing wording comes from its own shared failure-notice helper keyed on TurnFailureKind::is_retryable, not this string.

§

Done

Terminal event: the turn has ended and no further events follow.

Trait Implementations§

Source§

impl Clone for TurnEvent

Source§

fn clone(&self) -> TurnEvent

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TurnEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for TurnEvent

Source§

impl From<PendingApprovalPrompt> for TurnEvent

TurnEvent::ApprovalPending carries the exact same fields as PendingApprovalPrompt — reuse that conversion rather than a second hand-written field list that could drift from it.

Source§

fn from(p: PendingApprovalPrompt) -> Self

Converts to this type from the input type.
Source§

impl From<PendingQuestionPrompt> for TurnEvent

TurnEvent::QuestionPending carries the exact same fields as PendingQuestionPrompt — reuse that conversion rather than a second hand-written field list that could drift from it.

Source§

fn from(p: PendingQuestionPrompt) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for TurnEvent

Source§

fn eq(&self, other: &TurnEvent) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for TurnEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more