1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5pub struct AgentEvent {
6 pub id: Uuid,
7 pub agent_id: String,
8 pub thread_id: Option<String>,
9 pub run_id: Option<String>,
10 pub parent_event_id: Option<Uuid>,
11 pub event_type: EventType,
12 pub payload: serde_json::Value,
13 pub trace_id: Option<String>,
15 pub span_id: Option<String>,
16 pub model: Option<String>,
17 pub tokens_input: Option<i64>,
18 pub tokens_output: Option<i64>,
19 pub latency_ms: Option<i64>,
20 pub cost_usd: Option<f64>,
21 pub timestamp: String,
23 pub logical_clock: i64,
24 pub content_hash: Vec<u8>,
26 pub prev_hash: Option<Vec<u8>>,
27 pub embedding: Option<Vec<f32>>,
29}
30
31impl AgentEvent {
32 pub fn new(
34 agent_id: String,
35 event_type: EventType,
36 payload: serde_json::Value,
37 timestamp: String,
38 content_hash: Vec<u8>,
39 ) -> Self {
40 Self {
41 id: Uuid::now_v7(),
42 agent_id,
43 thread_id: None,
44 run_id: None,
45 parent_event_id: None,
46 event_type,
47 payload,
48 trace_id: None,
49 span_id: None,
50 model: None,
51 tokens_input: None,
52 tokens_output: None,
53 latency_ms: None,
54 cost_usd: None,
55 timestamp,
56 logical_clock: 0,
57 content_hash,
58 prev_hash: None,
59 embedding: None,
60 }
61 }
62
63 #[allow(clippy::too_many_arguments)]
66 pub fn from_parts(
67 id: Uuid,
68 agent_id: String,
69 thread_id: Option<String>,
70 run_id: Option<String>,
71 parent_event_id: Option<Uuid>,
72 event_type: EventType,
73 payload: serde_json::Value,
74 trace_id: Option<String>,
75 span_id: Option<String>,
76 model: Option<String>,
77 tokens_input: Option<i64>,
78 tokens_output: Option<i64>,
79 latency_ms: Option<i64>,
80 cost_usd: Option<f64>,
81 timestamp: String,
82 logical_clock: i64,
83 content_hash: Vec<u8>,
84 prev_hash: Option<Vec<u8>>,
85 embedding: Option<Vec<f32>>,
86 ) -> Self {
87 Self {
88 id,
89 agent_id,
90 thread_id,
91 run_id,
92 parent_event_id,
93 event_type,
94 payload,
95 trace_id,
96 span_id,
97 model,
98 tokens_input,
99 tokens_output,
100 latency_ms,
101 cost_usd,
102 timestamp,
103 logical_clock,
104 content_hash,
105 prev_hash,
106 embedding,
107 }
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112#[serde(rename_all = "snake_case")]
113pub enum EventType {
114 MemoryWrite,
115 MemoryRead,
116 MemoryDelete,
117 MemoryShare,
118 MemoryExpired,
119 MemoryRedact,
120 Checkpoint,
121 Branch,
122 Merge,
123 UserMessage,
124 AssistantMessage,
125 ToolCall,
126 ToolResult,
127 Error,
128 RetrievalQuery,
129 RetrievalResult,
130 Decision,
131 ReflectionCompleted,
135 DreamReportIngested,
138 McpToolCatalogDrift,
145 MemoryConsolidated,
151 MemoryRevised,
157}
158
159impl std::fmt::Display for EventType {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 match self {
162 EventType::MemoryWrite => write!(f, "memory_write"),
163 EventType::MemoryRead => write!(f, "memory_read"),
164 EventType::MemoryDelete => write!(f, "memory_delete"),
165 EventType::MemoryShare => write!(f, "memory_share"),
166 EventType::MemoryExpired => write!(f, "memory_expired"),
167 EventType::MemoryRedact => write!(f, "memory_redact"),
168 EventType::Checkpoint => write!(f, "checkpoint"),
169 EventType::Branch => write!(f, "branch"),
170 EventType::Merge => write!(f, "merge"),
171 EventType::UserMessage => write!(f, "user_message"),
172 EventType::AssistantMessage => write!(f, "assistant_message"),
173 EventType::ToolCall => write!(f, "tool_call"),
174 EventType::ToolResult => write!(f, "tool_result"),
175 EventType::Error => write!(f, "error"),
176 EventType::RetrievalQuery => write!(f, "retrieval_query"),
177 EventType::RetrievalResult => write!(f, "retrieval_result"),
178 EventType::Decision => write!(f, "decision"),
179 EventType::ReflectionCompleted => write!(f, "reflection_completed"),
180 EventType::DreamReportIngested => write!(f, "dream_report_ingested"),
181 EventType::McpToolCatalogDrift => write!(f, "mcp_tool_catalog_drift"),
182 EventType::MemoryConsolidated => write!(f, "memory_consolidated"),
183 EventType::MemoryRevised => write!(f, "memory_revised"),
184 }
185 }
186}
187
188impl std::str::FromStr for EventType {
189 type Err = crate::error::Error;
190 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
191 match s {
192 "memory_write" => Ok(EventType::MemoryWrite),
193 "memory_read" => Ok(EventType::MemoryRead),
194 "memory_delete" => Ok(EventType::MemoryDelete),
195 "memory_share" => Ok(EventType::MemoryShare),
196 "memory_expired" => Ok(EventType::MemoryExpired),
197 "memory_redact" => Ok(EventType::MemoryRedact),
198 "checkpoint" => Ok(EventType::Checkpoint),
199 "branch" => Ok(EventType::Branch),
200 "merge" => Ok(EventType::Merge),
201 "user_message" => Ok(EventType::UserMessage),
202 "assistant_message" => Ok(EventType::AssistantMessage),
203 "tool_call" => Ok(EventType::ToolCall),
204 "tool_result" => Ok(EventType::ToolResult),
205 "error" => Ok(EventType::Error),
206 "retrieval_query" => Ok(EventType::RetrievalQuery),
207 "retrieval_result" => Ok(EventType::RetrievalResult),
208 "decision" => Ok(EventType::Decision),
209 "reflection_completed" => Ok(EventType::ReflectionCompleted),
210 "dream_report_ingested" => Ok(EventType::DreamReportIngested),
211 "mcp_tool_catalog_drift" => Ok(EventType::McpToolCatalogDrift),
212 "memory_consolidated" => Ok(EventType::MemoryConsolidated),
213 "memory_revised" => Ok(EventType::MemoryRevised),
214 _ => Err(crate::error::Error::Validation(format!(
215 "invalid event type: {s}"
216 ))),
217 }
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn test_agent_event_serde() {
227 let event = AgentEvent {
228 id: Uuid::now_v7(),
229 agent_id: "agent-1".to_string(),
230 thread_id: Some("thread-1".to_string()),
231 run_id: None,
232 parent_event_id: None,
233 event_type: EventType::MemoryWrite,
234 payload: serde_json::json!({"memory_id": "abc"}),
235 trace_id: None,
236 span_id: None,
237 model: None,
238 tokens_input: None,
239 tokens_output: None,
240 latency_ms: None,
241 cost_usd: None,
242 timestamp: "2025-01-01T00:00:00Z".to_string(),
243 logical_clock: 1,
244 content_hash: vec![1, 2, 3],
245 prev_hash: None,
246 embedding: None,
247 };
248 let json = serde_json::to_string(&event).unwrap();
249 let deserialized: AgentEvent = serde_json::from_str(&json).unwrap();
250 assert_eq!(event, deserialized);
251 }
252
253 #[test]
254 fn test_event_type_display_fromstr() {
255 assert_eq!(EventType::MemoryWrite.to_string(), "memory_write");
256 assert_eq!(
257 "memory_read".parse::<EventType>().unwrap(),
258 EventType::MemoryRead
259 );
260 assert_eq!(
261 "checkpoint".parse::<EventType>().unwrap(),
262 EventType::Checkpoint
263 );
264 assert_eq!("error".parse::<EventType>().unwrap(), EventType::Error);
265 assert!("invalid".parse::<EventType>().is_err());
266 }
267}