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)]
8#[serde(rename_all = "snake_case")]
9pub struct AgentEvent {
10 pub timestamp: chrono::DateTime<chrono::Utc>,
11 pub thread_id: String,
12 pub run_id: String,
13 pub event: AgentEventType,
14 pub task_id: String,
15 pub agent_id: String,
16}
17
18#[derive(Debug, Serialize, Deserialize, Clone)]
19#[serde(rename_all = "snake_case", tag = "type")]
20pub enum AgentEventType {
21 RunStarted {},
23 RunFinished {
24 success: bool,
25 total_steps: usize,
26 failed_steps: usize,
27 },
28 RunError {
29 message: String,
30 code: Option<String>,
31 },
32 PlanStarted {
33 initial_plan: bool,
34 },
35 PlanFinished {
36 total_steps: usize,
37 },
38 PlanPruned {
39 removed_steps: Vec<String>,
40 },
41 StepStarted {
43 step_id: String,
44 step_index: usize,
45 },
46 StepCompleted {
47 step_id: String,
48 success: bool,
49 },
50
51 ToolExecutionStart {
53 step_id: String,
54 tool_call_id: String,
55 tool_call_name: String,
56 input: Value,
57 },
58 ToolExecutionEnd {
59 step_id: String,
60 tool_call_id: String,
61 tool_call_name: String,
62 success: bool,
63 },
64
65 TextMessageStart {
67 message_id: String,
68 step_id: String,
69 role: MessageRole,
70 is_final: Option<bool>,
71 },
72 TextMessageContent {
73 message_id: String,
74 step_id: String,
75 delta: String,
76 stripped_content: Option<Vec<(usize, String)>>,
77 },
78 TextMessageEnd {
79 message_id: String,
80 step_id: String,
81 },
82
83 ToolCalls {
85 step_id: String,
86 parent_message_id: Option<String>,
87 tool_calls: Vec<ToolCall>,
88 },
89 ToolResults {
90 step_id: String,
91 parent_message_id: Option<String>,
92 results: Vec<ToolResponse>,
93 },
94
95 AgentHandover {
97 from_agent: String,
98 to_agent: String,
99 reason: Option<String>,
100 },
101
102 WorkflowStarted {
104 workflow_name: String,
105 total_steps: usize,
106 },
107 NodeStarted {
108 node_id: String,
109 node_name: String,
110 step_type: String,
111 },
112 NodeCompleted {
113 node_id: String,
114 node_name: String,
115 success: bool,
116 error: Option<String>,
117 },
118 RunCompleted {
119 workflow_name: String,
120 success: bool,
121 total_steps: usize,
122 },
123 RunFailed {
124 workflow_name: String,
125 error: String,
126 failed_at_step: Option<String>,
127 },
128
129 BrowserScreenshot {
130 image: String,
131 format: Option<String>,
132 filename: Option<String>,
133 size: Option<u64>,
134 timestamp_ms: Option<i64>,
135 },
136
137 InlineHookRequested {
138 request: InlineHookRequest,
139 },
140
141 TodosUpdated {
143 formatted_todos: String,
144 action: String,
145 todo_count: usize,
146 },
147}
148
149impl AgentEvent {
150 pub fn new(event: AgentEventType) -> Self {
151 Self {
152 timestamp: chrono::Utc::now(),
153 thread_id: uuid::Uuid::new_v4().to_string(),
154 run_id: uuid::Uuid::new_v4().to_string(),
155 event,
156 task_id: uuid::Uuid::new_v4().to_string(),
157 agent_id: "default".to_string(),
158 }
159 }
160
161 pub fn with_context(
162 event: AgentEventType,
163 thread_id: String,
164 run_id: String,
165 task_id: String,
166 agent_id: String,
167 ) -> Self {
168 Self {
169 timestamp: chrono::Utc::now(),
170 thread_id,
171 run_id,
172 task_id,
173 event,
174 agent_id,
175 }
176 }
177}