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}
31
32// ---------------------------------------------------------------------------
33// RuntimeEvent — unified event stream for all consumers
34// ---------------------------------------------------------------------------
35
36/// Unified runtime event — the single event type for both internal and external
37/// consumers (frontends, CLIs, tests).
38#[derive(Clone, Debug, Serialize, Deserialize)]
39#[serde(tag = "runtimeEventType", rename_all = "camelCase")]
40pub enum RuntimeEvent {
41    // --- Framework system events ---
42    TextDelta {
43        session_id: SessionId,
44        text: String,
45        /// Identifies the agent that produced this event (root / sub-agent path).
46        #[serde(default, skip_serializing_if = "Option::is_none")]
47        agent_id: Option<String>,
48        /// Distributed tracing context carried across systems (e.g. MCP caller → phi-agent).
49        #[serde(default, skip_serializing_if = "Option::is_none")]
50        trace_id: Option<String>,
51    },
52    ThoughtDelta {
53        session_id: SessionId,
54        text: String,
55        #[serde(default, skip_serializing_if = "Option::is_none")]
56        agent_id: Option<String>,
57        #[serde(default, skip_serializing_if = "Option::is_none")]
58        trace_id: Option<String>,
59    },
60    ToolCallStarted {
61        session_id: SessionId,
62        tool_name: String,
63        args_json: String,
64        #[serde(default, skip_serializing_if = "Option::is_none")]
65        agent_id: Option<String>,
66        #[serde(default, skip_serializing_if = "Option::is_none")]
67        trace_id: Option<String>,
68    },
69    ToolCallFinished {
70        session_id: SessionId,
71        tool_name: String,
72        summary: String,
73        #[serde(default, skip_serializing_if = "Option::is_none")]
74        agent_id: Option<String>,
75        #[serde(default, skip_serializing_if = "Option::is_none")]
76        trace_id: Option<String>,
77    },
78    AwaitingApproval {
79        session_id: SessionId,
80        request: ApprovalRequest,
81        #[serde(default, skip_serializing_if = "Option::is_none")]
82        agent_id: Option<String>,
83        #[serde(default, skip_serializing_if = "Option::is_none")]
84        trace_id: Option<String>,
85    },
86    Checkpoint {
87        session_id: SessionId,
88        checkpoint: CheckpointData,
89        #[serde(default, skip_serializing_if = "Option::is_none")]
90        agent_id: Option<String>,
91        #[serde(default, skip_serializing_if = "Option::is_none")]
92        trace_id: Option<String>,
93    },
94    RunFinished {
95        session_id: SessionId,
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    RunCancelled {
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    // --- Lightweight plan update (display-only, no execution semantics) ---
109    PlanUpdated {
110        session_id: SessionId,
111        objective: String,
112        explanation: Option<String>,
113        plan: Vec<PlanItem>,
114        #[serde(default, skip_serializing_if = "Option::is_none")]
115        agent_id: Option<String>,
116        #[serde(default, skip_serializing_if = "Option::is_none")]
117        trace_id: Option<String>,
118    },
119    // --- User-space events ---
120    /// A user-space event produced by a tool.
121    UserEvent {
122        session_id: SessionId,
123        event: UserEvent,
124        #[serde(default, skip_serializing_if = "Option::is_none")]
125        agent_id: Option<String>,
126        #[serde(default, skip_serializing_if = "Option::is_none")]
127        trace_id: Option<String>,
128    },
129}
130
131impl RuntimeEvent {
132    /// Get the session ID associated with this event.
133    pub fn session_id(&self) -> &SessionId {
134        match self {
135            RuntimeEvent::TextDelta { session_id, .. } => session_id,
136            RuntimeEvent::ThoughtDelta { session_id, .. } => session_id,
137            RuntimeEvent::ToolCallStarted { session_id, .. } => session_id,
138            RuntimeEvent::ToolCallFinished { session_id, .. } => session_id,
139            RuntimeEvent::AwaitingApproval { session_id, .. } => session_id,
140            RuntimeEvent::Checkpoint { session_id, .. } => session_id,
141            RuntimeEvent::RunFinished { session_id, .. } => session_id,
142            RuntimeEvent::RunCancelled { session_id, .. } => session_id,
143            RuntimeEvent::PlanUpdated { session_id, .. } => session_id,
144            RuntimeEvent::UserEvent { session_id, .. } => session_id,
145        }
146    }
147
148    /// Get the agent ID associated with this event, if any.
149    pub fn agent_id(&self) -> Option<&str> {
150        match self {
151            RuntimeEvent::TextDelta { agent_id, .. } => agent_id.as_deref(),
152            RuntimeEvent::ThoughtDelta { agent_id, .. } => agent_id.as_deref(),
153            RuntimeEvent::ToolCallStarted { agent_id, .. } => agent_id.as_deref(),
154            RuntimeEvent::ToolCallFinished { agent_id, .. } => agent_id.as_deref(),
155            RuntimeEvent::AwaitingApproval { agent_id, .. } => agent_id.as_deref(),
156            RuntimeEvent::Checkpoint { agent_id, .. } => agent_id.as_deref(),
157            RuntimeEvent::RunFinished { agent_id, .. } => agent_id.as_deref(),
158            RuntimeEvent::RunCancelled { agent_id, .. } => agent_id.as_deref(),
159            RuntimeEvent::PlanUpdated { agent_id, .. } => agent_id.as_deref(),
160            RuntimeEvent::UserEvent { agent_id, .. } => agent_id.as_deref(),
161        }
162    }
163
164    /// Get the trace ID associated with this event, if any.
165    pub fn trace_id(&self) -> Option<&str> {
166        match self {
167            RuntimeEvent::TextDelta { trace_id, .. } => trace_id.as_deref(),
168            RuntimeEvent::ThoughtDelta { trace_id, .. } => trace_id.as_deref(),
169            RuntimeEvent::ToolCallStarted { trace_id, .. } => trace_id.as_deref(),
170            RuntimeEvent::ToolCallFinished { trace_id, .. } => trace_id.as_deref(),
171            RuntimeEvent::AwaitingApproval { trace_id, .. } => trace_id.as_deref(),
172            RuntimeEvent::Checkpoint { trace_id, .. } => trace_id.as_deref(),
173            RuntimeEvent::RunFinished { trace_id, .. } => trace_id.as_deref(),
174            RuntimeEvent::RunCancelled { trace_id, .. } => trace_id.as_deref(),
175            RuntimeEvent::PlanUpdated { trace_id, .. } => trace_id.as_deref(),
176            RuntimeEvent::UserEvent { trace_id, .. } => trace_id.as_deref(),
177        }
178    }
179}