1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::core::{MessageRole, ToolCall, ToolResponse};
5use crate::hooks::InlineHookRequest;
6
7#[derive(Debug, Serialize, Deserialize, Clone, Default)]
9pub struct RunUsage {
10 pub total_tokens: u32,
12 pub input_tokens: u32,
13 pub output_tokens: u32,
14 #[serde(default)]
16 pub cached_tokens: u32,
17 pub estimated_tokens: u32,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub model: Option<String>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub cost_usd: Option<f64>,
25}
26
27#[derive(Debug, Serialize, Deserialize, Clone)]
28#[serde(rename_all = "snake_case")]
29pub struct AgentEvent {
30 pub timestamp: chrono::DateTime<chrono::Utc>,
31 pub thread_id: String,
32 pub run_id: String,
33 pub event: AgentEventType,
34 pub task_id: String,
35 pub agent_id: String,
36 #[serde(default)]
38 pub user_id: Option<String>,
39 #[serde(default)]
41 pub identifier_id: Option<String>,
42 #[serde(default)]
44 pub workspace_id: Option<String>,
45 #[serde(default)]
47 pub channel_id: Option<String>,
48}
49
50impl AgentEvent {
51 pub fn from_task_event(task_event: &crate::TaskEvent, thread_id: &str) -> Self {
53 Self {
54 event: task_event.event.clone(),
55 agent_id: String::new(),
56 timestamp: chrono::DateTime::from_timestamp_millis(task_event.created_at)
57 .unwrap_or_default(),
58 thread_id: thread_id.to_string(),
59 run_id: String::new(),
60 task_id: String::new(),
61 user_id: None,
62 identifier_id: None,
63 workspace_id: None,
64 channel_id: None,
65 }
66 }
67}
68
69#[derive(Debug, Serialize, Deserialize, Clone)]
70#[serde(rename_all = "snake_case", tag = "type")]
71pub enum AgentEventType {
72 RunStarted {},
74 RunFinished {
75 success: bool,
76 total_steps: usize,
77 failed_steps: usize,
78 usage: Option<RunUsage>,
80 },
81 RunError {
82 message: String,
83 code: Option<String>,
84 },
85 PlanStarted {
86 initial_plan: bool,
87 },
88 PlanFinished {
89 total_steps: usize,
90 },
91 PlanPruned {
92 removed_steps: Vec<String>,
93 },
94 StepStarted {
96 step_id: String,
97 step_index: usize,
98 },
99 StepCompleted {
100 step_id: String,
101 success: bool,
102 },
103
104 ToolExecutionStart {
106 step_id: String,
107 tool_call_id: String,
108 tool_call_name: String,
109 input: Value,
110 },
111 ToolExecutionEnd {
112 step_id: String,
113 tool_call_id: String,
114 tool_call_name: String,
115 success: bool,
116 },
117
118 TextMessageStart {
120 message_id: String,
121 step_id: String,
122 role: MessageRole,
123 is_final: Option<bool>,
124 },
125 TextMessageContent {
126 message_id: String,
127 step_id: String,
128 delta: String,
129 stripped_content: Option<Vec<(usize, String)>>,
130 },
131 TextMessageEnd {
132 message_id: String,
133 step_id: String,
134 },
135
136 ToolCalls {
138 step_id: String,
139 parent_message_id: Option<String>,
140 tool_calls: Vec<ToolCall>,
141 },
142 ToolResults {
143 step_id: String,
144 parent_message_id: Option<String>,
145 results: Vec<ToolResponse>,
146 },
147
148 AgentHandover {
150 from_agent: String,
151 to_agent: String,
152 reason: Option<String>,
153 },
154
155 BrowserScreenshot {
156 image: String,
157 format: Option<String>,
158 filename: Option<String>,
159 size: Option<u64>,
160 timestamp_ms: Option<i64>,
161 },
162
163 BrowserSessionStarted {
164 session_id: String,
165 viewer_url: Option<String>,
166 stream_url: Option<String>,
167 },
168
169 InlineHookRequested {
170 request: InlineHookRequest,
171 },
172
173 TodosUpdated {
175 formatted_todos: String,
176 action: String,
177 todo_count: usize,
178 },
179
180 ContextCompaction {
182 tier: CompactionTier,
184 tokens_before: usize,
186 tokens_after: usize,
188 entries_affected: usize,
190 context_limit: usize,
192 usage_ratio: f64,
194 summary: Option<String>,
196 },
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
201#[serde(rename_all = "snake_case")]
202pub enum CompactionTier {
203 Trim,
205 Summarize,
207 Reset,
209}
210
211impl AgentEvent {
212 pub fn new(event: AgentEventType) -> Self {
213 Self {
214 timestamp: chrono::Utc::now(),
215 thread_id: uuid::Uuid::new_v4().to_string(),
216 run_id: uuid::Uuid::new_v4().to_string(),
217 event,
218 task_id: uuid::Uuid::new_v4().to_string(),
219 agent_id: "default".to_string(),
220 user_id: None,
221 identifier_id: None,
222 workspace_id: None,
223 channel_id: None,
224 }
225 }
226
227 pub fn with_context(
228 event: AgentEventType,
229 thread_id: String,
230 run_id: String,
231 task_id: String,
232 agent_id: String,
233 ) -> Self {
234 Self {
235 timestamp: chrono::Utc::now(),
236 thread_id,
237 run_id,
238 task_id,
239 event,
240 agent_id,
241 user_id: None,
242 identifier_id: None,
243 workspace_id: None,
244 channel_id: None,
245 }
246 }
247}