Skip to main content

agent_base/types/
events.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use super::approval::ApprovalRequest;
5use super::checkpoint::CheckpointData;
6use super::plan_update::PlanItem;
7use super::session::SessionId;
8
9// ---------------------------------------------------------------------------
10// UserEvent — user-space events produced by tools
11// ---------------------------------------------------------------------------
12
13/// User-space events produced by tools during execution.
14///
15/// Tools send these through `ToolContext::emit_user_event()`. The framework
16/// wraps them in [`RuntimeEvent::UserEvent`] before delivering to external
17/// consumers.
18#[derive(Clone, Debug, Serialize, Deserialize)]
19#[serde(tag = "userEventType", rename_all = "camelCase")]
20pub enum UserEvent {
21    /// Tool progress notification.
22    Progress { text: String },
23    /// Sub-agent event forwarding (used by `SubAgentTool`).
24    SubAgentEvent {
25        subagent: String,
26        event: Box<RuntimeEvent>,
27    },
28    /// User-defined structured event for custom business semantics.
29    Structured { event_type: String, data: Value },
30    /// Tool partial result — emitted during long-running tool execution.
31    /// `is_partial: true` means more output is coming; `false` means this is the final chunk.
32    ToolPartialResult {
33        tool_call_id: String,
34        content: String,
35        is_partial: bool,
36    },
37}
38
39// ---------------------------------------------------------------------------
40// RuntimeEvent — unified event stream for all consumers
41// ---------------------------------------------------------------------------
42
43/// Unified runtime event — the single event type for both internal and external
44/// consumers (frontends, CLIs, tests).
45#[derive(Clone, Debug, Serialize, Deserialize)]
46#[serde(tag = "runtimeEventType", rename_all = "camelCase")]
47pub enum RuntimeEvent {
48    // --- Framework system events ---
49    TextDelta {
50        session_id: SessionId,
51        text: String,
52        /// Identifies the agent that produced this event (root / sub-agent path).
53        #[serde(default, skip_serializing_if = "Option::is_none")]
54        agent_id: Option<String>,
55        /// Distributed tracing context carried across systems (e.g. MCP caller → phi-agent).
56        #[serde(default, skip_serializing_if = "Option::is_none")]
57        trace_id: Option<String>,
58    },
59    ThoughtDelta {
60        session_id: SessionId,
61        text: String,
62        #[serde(default, skip_serializing_if = "Option::is_none")]
63        agent_id: Option<String>,
64        #[serde(default, skip_serializing_if = "Option::is_none")]
65        trace_id: Option<String>,
66    },
67    ToolCallStarted {
68        session_id: SessionId,
69        tool_name: String,
70        args_json: String,
71        #[serde(default, skip_serializing_if = "Option::is_none")]
72        agent_id: Option<String>,
73        #[serde(default, skip_serializing_if = "Option::is_none")]
74        trace_id: Option<String>,
75    },
76    ToolCallFinished {
77        session_id: SessionId,
78        tool_name: String,
79        summary: String,
80        #[serde(default, skip_serializing_if = "Option::is_none")]
81        agent_id: Option<String>,
82        #[serde(default, skip_serializing_if = "Option::is_none")]
83        trace_id: Option<String>,
84    },
85    AwaitingApproval {
86        session_id: SessionId,
87        request: ApprovalRequest,
88        #[serde(default, skip_serializing_if = "Option::is_none")]
89        agent_id: Option<String>,
90        #[serde(default, skip_serializing_if = "Option::is_none")]
91        trace_id: Option<String>,
92    },
93    Checkpoint {
94        session_id: SessionId,
95        checkpoint: CheckpointData,
96        #[serde(default, skip_serializing_if = "Option::is_none")]
97        agent_id: Option<String>,
98        #[serde(default, skip_serializing_if = "Option::is_none")]
99        trace_id: Option<String>,
100    },
101    RunFinished {
102        session_id: SessionId,
103        #[serde(default, skip_serializing_if = "Option::is_none")]
104        agent_id: Option<String>,
105        #[serde(default, skip_serializing_if = "Option::is_none")]
106        trace_id: Option<String>,
107    },
108    RunCancelled {
109        session_id: SessionId,
110        #[serde(default, skip_serializing_if = "Option::is_none")]
111        agent_id: Option<String>,
112        #[serde(default, skip_serializing_if = "Option::is_none")]
113        trace_id: Option<String>,
114    },
115    // --- Lightweight plan update (display-only, no execution semantics) ---
116    PlanUpdated {
117        session_id: SessionId,
118        objective: String,
119        explanation: Option<String>,
120        plan: Vec<PlanItem>,
121        #[serde(default, skip_serializing_if = "Option::is_none")]
122        agent_id: Option<String>,
123        #[serde(default, skip_serializing_if = "Option::is_none")]
124        trace_id: Option<String>,
125    },
126    // --- User-space events ---
127    /// A user-space event produced by a tool.
128    UserEvent {
129        session_id: SessionId,
130        event: UserEvent,
131        #[serde(default, skip_serializing_if = "Option::is_none")]
132        agent_id: Option<String>,
133        #[serde(default, skip_serializing_if = "Option::is_none")]
134        trace_id: Option<String>,
135    },
136}
137
138impl RuntimeEvent {
139    /// Get the session ID associated with this event.
140    pub fn session_id(&self) -> &SessionId {
141        match self {
142            RuntimeEvent::TextDelta { session_id, .. } => session_id,
143            RuntimeEvent::ThoughtDelta { session_id, .. } => session_id,
144            RuntimeEvent::ToolCallStarted { session_id, .. } => session_id,
145            RuntimeEvent::ToolCallFinished { session_id, .. } => session_id,
146            RuntimeEvent::AwaitingApproval { session_id, .. } => session_id,
147            RuntimeEvent::Checkpoint { session_id, .. } => session_id,
148            RuntimeEvent::RunFinished { session_id, .. } => session_id,
149            RuntimeEvent::RunCancelled { session_id, .. } => session_id,
150            RuntimeEvent::PlanUpdated { session_id, .. } => session_id,
151            RuntimeEvent::UserEvent { session_id, .. } => session_id,
152        }
153    }
154
155    /// Get the agent ID associated with this event, if any.
156    pub fn agent_id(&self) -> Option<&str> {
157        match self {
158            RuntimeEvent::TextDelta { agent_id, .. } => agent_id.as_deref(),
159            RuntimeEvent::ThoughtDelta { agent_id, .. } => agent_id.as_deref(),
160            RuntimeEvent::ToolCallStarted { agent_id, .. } => agent_id.as_deref(),
161            RuntimeEvent::ToolCallFinished { agent_id, .. } => agent_id.as_deref(),
162            RuntimeEvent::AwaitingApproval { agent_id, .. } => agent_id.as_deref(),
163            RuntimeEvent::Checkpoint { agent_id, .. } => agent_id.as_deref(),
164            RuntimeEvent::RunFinished { agent_id, .. } => agent_id.as_deref(),
165            RuntimeEvent::RunCancelled { agent_id, .. } => agent_id.as_deref(),
166            RuntimeEvent::PlanUpdated { agent_id, .. } => agent_id.as_deref(),
167            RuntimeEvent::UserEvent { agent_id, .. } => agent_id.as_deref(),
168        }
169    }
170
171    /// Get the trace ID associated with this event, if any.
172    pub fn trace_id(&self) -> Option<&str> {
173        match self {
174            RuntimeEvent::TextDelta { trace_id, .. } => trace_id.as_deref(),
175            RuntimeEvent::ThoughtDelta { trace_id, .. } => trace_id.as_deref(),
176            RuntimeEvent::ToolCallStarted { trace_id, .. } => trace_id.as_deref(),
177            RuntimeEvent::ToolCallFinished { trace_id, .. } => trace_id.as_deref(),
178            RuntimeEvent::AwaitingApproval { trace_id, .. } => trace_id.as_deref(),
179            RuntimeEvent::Checkpoint { trace_id, .. } => trace_id.as_deref(),
180            RuntimeEvent::RunFinished { trace_id, .. } => trace_id.as_deref(),
181            RuntimeEvent::RunCancelled { trace_id, .. } => trace_id.as_deref(),
182            RuntimeEvent::PlanUpdated { trace_id, .. } => trace_id.as_deref(),
183            RuntimeEvent::UserEvent { trace_id, .. } => trace_id.as_deref(),
184        }
185    }
186}