#[non_exhaustive]pub enum AgentEvent {
Show 16 variants
TurnStarted,
UserPromptCommitted {
content: Vec<ContentBlock>,
},
TurnEnded {
reason: StopReason,
usage: Usage,
},
TurnAborted,
AssistantText {
content: ContentBlock,
},
AssistantThought {
content: ContentBlock,
},
ToolCallStarted {
id: ToolCallId,
name: String,
fields: ToolCallUpdateFields,
},
ToolCallProgress {
id: ToolCallId,
fields: ToolCallUpdateFields,
},
ToolCallFinished {
id: ToolCallId,
fields: ToolCallUpdateFields,
},
PolicyDecision {
id: ToolCallId,
decision: PolicyDecision,
},
PermissionResolved {
id: ToolCallId,
outcome: PermissionResolution,
},
LlmCallStarted {
model: String,
attempt: u32,
request: Arc<LlmRequestSnapshot>,
},
LlmCallFinished {
model: String,
attempt: u32,
usage: Usage,
error: Option<String>,
},
ContextCompressed {
tokens_before: u64,
tokens_after: u64,
},
ContextMicrocompacted {
tokens_before: u64,
tokens_after: u64,
cleared: usize,
},
Subagent {
ancestor_path: Vec<ToolCallId>,
agent_type: String,
inner: Box<AgentEvent>,
},
}Expand description
Events published by the agent main loop.
Final-state semantics: the event stream for a turn starts with
AgentEvent::TurnStarted and ends with AgentEvent::TurnEnded. After
TurnEnded, no more events for that turn are produced — defect-acp stops pushing
session/update and responds with PromptResponse upon seeing it.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
TurnStarted
A prompt turn has started.
UserPromptCommitted
The user prompt has been committed to history by the main loop.
Fields
content: Vec<ContentBlock>TurnEnded
A prompt turn ended. reason directly borrows the semantic category from ACP.
Fields
reason: StopReasonusage: UsageCumulative token usage for this turn (field-wise sum from
crate::llm::ProviderChunk::Usage).
TurnAborted
A prompt turn failed permanently (e.g. a provider error after retries) and was rolled back. Everything the turn appended to history — starting with the user prompt — has been discarded in memory; consumers that persist or mirror history (storage) must drop the same tail so a failed turn leaves no orphan to be replayed on reload or re-sent on the next request. Audit-only on the wire (the ACP bridge reports failure via the JSON-RPC error, not this event).
AssistantText
Incremental assistant text. Maps to ACP SessionUpdate::AgentMessageChunk.
Fields
content: ContentBlockAssistantThought
Assistant thought chain delta. Maps to ACP SessionUpdate::AgentThoughtChunk.
Fields
content: ContentBlockToolCallStarted
A tool call start declaration.
Maps to ACP SessionUpdate::ToolCall (status = Pending).
ToolCallProgress
Tool call progress delta.
Maps to ACP SessionUpdate::ToolCallUpdate.
ToolCallFinished
Tool call finished (success/failure is indicated by fields.status).
Maps to ACP SessionUpdate::ToolCallUpdate (with a terminal status).
PolicyDecision
The sandbox policy makes a decision about a tool call. Ask triggers the ACP
session/request_permission; Allow / Deny are only audited and not sent over
the wire.
PermissionResolved
User’s response to a PolicyDecision::Ask. Audit-only, not sent on the wire.
LlmCallStarted
A single LLM provider call has started.
Fields
request: Arc<LlmRequestSnapshot>A snapshot of the request sent to the provider (system message + full message history).
Used by observability to reconstruct the generation’s input as a standard
chat message array (including the system message). Not sent over the wire;
storage currently ignores this field.
Wrapped in Arc: when events are fanned out to subscribers via
crate::session::EventEmitter, each subscriber clones the event. With long
contexts, deep-copying the entire message history repeatedly is expensive. The
snapshot is read-only once inside the event, so Arc reduces clone to a
reference-count increment.
#[serde(skip)]: the serde derive on AgentEvent is not currently used, and
we prefer not to enable serde’s rc feature for it—on deserialization this
field takes the default empty snapshot.
LlmCallFinished
A single LLM provider call has finished. error being Some indicates failure
(the retry hint determines whether to proceed to the next attempt).
Fields
ContextCompressed
The main loop compressed / truncated the history.
ContextMicrocompacted
The main loop performed a micro-compaction: it cleaned up oversized
tool_result bodies from older turns (without calling the LLM or deleting
messages). cleared is the number of tool_result entries actually cleaned. This
is distinguished from Self::ContextCompressed so that observability and the UI
can display them separately.
Subagent
A leaf event produced inside a spawn_agent sub-agent turn, bridged from the
sub-turn’s isolated event stream into the parent session’s event stream.
Design intent: the sub-agent runs in a fresh, isolated context (its own
crate::session::EventEmitter), and the parent agent cannot see its
intermediate steps — this is the isolation contract of spawn_agent. However,
observability (langfuse) wants to display the sub-turn’s LLM calls / tool calls
nested under the parent’s spawn_agent tool call span. So spawn_agent attaches
a bridging subscriber to the sub-emitter, wrapping each sub-event as this variant
and forwarding it to the parent emitter.
§Flattening (supports recursive subagents)
inner is always a leaf event (never another Subagent). Nesting depth is
expressed by the ancestor chain Self::Subagent::ancestor_path, not by
nested Box wrappers: the chain lists ids from the top-level spawn_agent tool
call down to the current layer. Each bridging layer prepends its own
parent_tool_call_id to the chain head, leaving the leaf inner unchanged (see
the bridge closure in spawn_agent.rs). The projector uses the full chain to
locate the parent mount point — the chain is globally unique, naturally avoids
ToolCallId collisions across sub-sessions, and the projector does not need to
recursively unwrap.
Consumption contract: only the langfuse projector processes this (emitting
nested generations/spans under the parent tool span). All other consumers
(defect-storage persistence, defect-acp wire projection, REPL rendering)
ignore it — the isolation contract remains unchanged for them.
Fields
ancestor_path: Vec<ToolCallId>Ancestor chain of ToolCallIds from the top-level spawn_agent tool call
down to the current subagent layer.
The head is the top-level spawn_agent (directly attached to the parent turn
trace), and the tail is the spawn_agent that initiated this leaf event.
Depth equals ancestor_path.len().
agent_type: StringThe profile name of the subagent that initiated this leaf event (e.g.
weebs-in), used for naming / metadata of nested spans.
inner: Box<AgentEvent>The bridged child turn leaf event (never another Subagent). Box
prevents the enum from growing unbounded due to self-reference.
Trait Implementations§
Source§impl Clone for AgentEvent
impl Clone for AgentEvent
Source§fn clone(&self) -> AgentEvent
fn clone(&self) -> AgentEvent
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 AgentEvent
impl Debug for AgentEvent
Source§impl<'de> Deserialize<'de> for AgentEvent
impl<'de> Deserialize<'de> for AgentEvent
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for AgentEvent
impl PartialEq for AgentEvent
Source§fn eq(&self, other: &AgentEvent) -> bool
fn eq(&self, other: &AgentEvent) -> bool
self and other values to be equal, and is used by ==.