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}
146
147impl std::fmt::Display for EventType {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 match self {
150 EventType::MemoryWrite => write!(f, "memory_write"),
151 EventType::MemoryRead => write!(f, "memory_read"),
152 EventType::MemoryDelete => write!(f, "memory_delete"),
153 EventType::MemoryShare => write!(f, "memory_share"),
154 EventType::MemoryExpired => write!(f, "memory_expired"),
155 EventType::MemoryRedact => write!(f, "memory_redact"),
156 EventType::Checkpoint => write!(f, "checkpoint"),
157 EventType::Branch => write!(f, "branch"),
158 EventType::Merge => write!(f, "merge"),
159 EventType::UserMessage => write!(f, "user_message"),
160 EventType::AssistantMessage => write!(f, "assistant_message"),
161 EventType::ToolCall => write!(f, "tool_call"),
162 EventType::ToolResult => write!(f, "tool_result"),
163 EventType::Error => write!(f, "error"),
164 EventType::RetrievalQuery => write!(f, "retrieval_query"),
165 EventType::RetrievalResult => write!(f, "retrieval_result"),
166 EventType::Decision => write!(f, "decision"),
167 EventType::ReflectionCompleted => write!(f, "reflection_completed"),
168 EventType::DreamReportIngested => write!(f, "dream_report_ingested"),
169 EventType::McpToolCatalogDrift => write!(f, "mcp_tool_catalog_drift"),
170 }
171 }
172}
173
174impl std::str::FromStr for EventType {
175 type Err = crate::error::Error;
176 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
177 match s {
178 "memory_write" => Ok(EventType::MemoryWrite),
179 "memory_read" => Ok(EventType::MemoryRead),
180 "memory_delete" => Ok(EventType::MemoryDelete),
181 "memory_share" => Ok(EventType::MemoryShare),
182 "memory_expired" => Ok(EventType::MemoryExpired),
183 "memory_redact" => Ok(EventType::MemoryRedact),
184 "checkpoint" => Ok(EventType::Checkpoint),
185 "branch" => Ok(EventType::Branch),
186 "merge" => Ok(EventType::Merge),
187 "user_message" => Ok(EventType::UserMessage),
188 "assistant_message" => Ok(EventType::AssistantMessage),
189 "tool_call" => Ok(EventType::ToolCall),
190 "tool_result" => Ok(EventType::ToolResult),
191 "error" => Ok(EventType::Error),
192 "retrieval_query" => Ok(EventType::RetrievalQuery),
193 "retrieval_result" => Ok(EventType::RetrievalResult),
194 "decision" => Ok(EventType::Decision),
195 "reflection_completed" => Ok(EventType::ReflectionCompleted),
196 "dream_report_ingested" => Ok(EventType::DreamReportIngested),
197 "mcp_tool_catalog_drift" => Ok(EventType::McpToolCatalogDrift),
198 _ => Err(crate::error::Error::Validation(format!(
199 "invalid event type: {s}"
200 ))),
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn test_agent_event_serde() {
211 let event = AgentEvent {
212 id: Uuid::now_v7(),
213 agent_id: "agent-1".to_string(),
214 thread_id: Some("thread-1".to_string()),
215 run_id: None,
216 parent_event_id: None,
217 event_type: EventType::MemoryWrite,
218 payload: serde_json::json!({"memory_id": "abc"}),
219 trace_id: None,
220 span_id: None,
221 model: None,
222 tokens_input: None,
223 tokens_output: None,
224 latency_ms: None,
225 cost_usd: None,
226 timestamp: "2025-01-01T00:00:00Z".to_string(),
227 logical_clock: 1,
228 content_hash: vec![1, 2, 3],
229 prev_hash: None,
230 embedding: None,
231 };
232 let json = serde_json::to_string(&event).unwrap();
233 let deserialized: AgentEvent = serde_json::from_str(&json).unwrap();
234 assert_eq!(event, deserialized);
235 }
236
237 #[test]
238 fn test_event_type_display_fromstr() {
239 assert_eq!(EventType::MemoryWrite.to_string(), "memory_write");
240 assert_eq!(
241 "memory_read".parse::<EventType>().unwrap(),
242 EventType::MemoryRead
243 );
244 assert_eq!(
245 "checkpoint".parse::<EventType>().unwrap(),
246 EventType::Checkpoint
247 );
248 assert_eq!("error".parse::<EventType>().unwrap(), EventType::Error);
249 assert!("invalid".parse::<EventType>().is_err());
250 }
251}