claude_session_types/events/
system.rs1use chrono::{DateTime, Utc};
27use serde::{Deserialize, Serialize};
28use serde_json::Value as JsonValue;
29
30use super::metadata::EventMetadata;
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct SystemEvent {
50 pub uuid: Option<String>,
52
53 #[serde(rename = "parentUuid")]
55 pub parent_uuid: Option<String>,
56
57 #[serde(rename = "logicalParentUuid")]
65 pub logical_parent_uuid: Option<String>,
66
67 #[serde(rename = "sessionId")]
69 pub session_id: String,
70
71 pub timestamp: DateTime<Utc>,
73
74 #[serde(rename = "isSidechain")]
76 pub is_sidechain: bool,
77
78 #[serde(rename = "userType")]
80 pub user_type: Option<String>,
81
82 pub cwd: Option<String>,
84
85 pub version: Option<String>,
87
88 #[serde(rename = "gitBranch")]
90 pub git_branch: Option<String>,
91
92 pub slug: Option<String>,
94
95 pub subtype: Option<String>,
102
103 pub content: Option<String>,
105
106 #[serde(rename = "isMeta")]
108 pub is_meta: Option<bool>,
109
110 pub level: Option<String>,
112
113 #[serde(rename = "compactMetadata")]
115 pub compact_metadata: Option<CompactMetadata>,
116
117 pub error: Option<ErrorDetails>,
119}
120
121impl SystemEvent {
122 pub fn is_compact_boundary(&self) -> bool {
124 self.subtype.as_deref() == Some("compact_boundary")
125 }
126
127 pub fn is_microcompact_boundary(&self) -> bool {
129 self.subtype.as_deref() == Some("microcompact_boundary")
130 }
131
132 pub fn is_error(&self) -> bool {
134 self.subtype.as_deref() == Some("error")
135 }
136
137 pub fn compact_metadata(&self) -> Option<&CompactMetadata> {
139 self.compact_metadata.as_ref()
140 }
141
142 pub fn metadata(&self) -> EventMetadata {
144 EventMetadata {
145 uuid: self.uuid.clone().unwrap_or_default(),
146 parent_uuid: self.parent_uuid.clone(),
147 session_id: self.session_id.clone(),
148 timestamp: self.timestamp,
149 is_sidechain: self.is_sidechain,
150 user_type: self.user_type.clone(),
151 cwd: self.cwd.clone(),
152 version: self.version.clone(),
153 git_branch: self.git_branch.clone(),
154 slug: self.slug.clone(),
155 }
156 }
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct CompactMetadata {
186 pub trigger: String,
191
192 #[serde(rename = "preTokens")]
196 pub pre_tokens: u64,
197
198 #[serde(rename = "postTokens", skip_serializing_if = "Option::is_none")]
200 #[serde(default)]
201 pub post_tokens: Option<u64>,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct ErrorDetails {
207 #[serde(rename = "type")]
214 pub error_type: String,
215
216 pub message: String,
218
219 #[serde(flatten)]
221 pub extra: JsonValue,
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_parse_compact_boundary() {
230 let json = r#"{
231 "type": "system",
232 "subtype": "compact_boundary",
233 "uuid": "boundary-uuid",
234 "parentUuid": null,
235 "logicalParentUuid": "last-message-uuid",
236 "sessionId": "session-123",
237 "timestamp": "2024-01-01T00:00:00Z",
238 "isSidechain": false,
239 "cwd": "/test",
240 "version": "2.1.19",
241 "gitBranch": "main",
242 "content": "Conversation compacted",
243 "level": "info",
244 "compactMetadata": {
245 "trigger": "auto",
246 "preTokens": 156594,
247 "postTokens": 50000
248 }
249 }"#;
250
251 let event: SystemEvent = serde_json::from_str(json).unwrap();
252 assert!(event.is_compact_boundary());
253 assert!(!event.is_microcompact_boundary());
254 assert!(!event.is_error());
255
256 let metadata = event.compact_metadata().unwrap();
257 assert_eq!(metadata.trigger, "auto");
258 assert_eq!(metadata.pre_tokens, 156_594);
259 assert_eq!(metadata.post_tokens, Some(50_000));
260
261 assert_eq!(
262 event.logical_parent_uuid,
263 Some("last-message-uuid".to_string())
264 );
265 }
266
267 #[test]
268 fn test_parse_microcompact_boundary() {
269 let json = r#"{
270 "type": "system",
271 "subtype": "microcompact_boundary",
272 "uuid": "micro-uuid",
273 "parentUuid": null,
274 "sessionId": "session-123",
275 "timestamp": "2024-01-01T00:00:00Z",
276 "isSidechain": false,
277 "cwd": "/test"
278 }"#;
279
280 let event: SystemEvent = serde_json::from_str(json).unwrap();
281 assert!(event.is_microcompact_boundary());
282 assert!(!event.is_compact_boundary());
283 }
284
285 #[test]
286 fn test_parse_error_event() {
287 let json = r#"{
288 "type": "system",
289 "subtype": "error",
290 "uuid": "error-uuid",
291 "parentUuid": null,
292 "sessionId": "session-123",
293 "timestamp": "2024-01-01T00:00:00Z",
294 "isSidechain": false,
295 "cwd": "/test",
296 "level": "error",
297 "content": "API overloaded",
298 "error": {
299 "type": "overloaded_error",
300 "message": "API is currently overloaded, please try again"
301 }
302 }"#;
303
304 let event: SystemEvent = serde_json::from_str(json).unwrap();
305 assert!(event.is_error());
306 assert_eq!(event.level, Some("error".to_string()));
307 assert_eq!(event.content, Some("API overloaded".to_string()));
308
309 if let Some(error) = &event.error {
310 assert_eq!(error.error_type, "overloaded_error");
311 assert_eq!(
312 error.message,
313 "API is currently overloaded, please try again"
314 );
315 }
316 }
317
318 #[test]
319 fn test_system_event_metadata() {
320 let event = SystemEvent {
321 uuid: Some("test-uuid".to_string()),
322 parent_uuid: Some("parent-uuid".to_string()),
323 logical_parent_uuid: None,
324 session_id: "session-123".to_string(),
325 timestamp: Utc::now(),
326 is_sidechain: false,
327 user_type: None,
328 cwd: Some("/test".to_string()),
329 version: Some("2.1.19".to_string()),
330 git_branch: Some("main".to_string()),
331 slug: None,
332 subtype: Some("compact_boundary".to_string()),
333 content: None,
334 is_meta: None,
335 level: None,
336 compact_metadata: None,
337 error: None,
338 };
339
340 let metadata = event.metadata();
341 assert_eq!(metadata.uuid, "test-uuid");
342 assert_eq!(metadata.parent_uuid, Some("parent-uuid".to_string()));
343 assert_eq!(metadata.session_id, "session-123");
344 }
345
346 #[test]
347 fn test_system_event_without_uuid() {
348 let json = r#"{
349 "type": "system",
350 "subtype": "info",
351 "parentUuid": null,
352 "sessionId": "session-123",
353 "timestamp": "2024-01-01T00:00:00Z",
354 "isSidechain": false,
355 "cwd": "/test",
356 "content": "System notification"
357 }"#;
358
359 let event: SystemEvent = serde_json::from_str(json).unwrap();
360 assert!(event.uuid.is_none());
361
362 let metadata = event.metadata();
364 assert_eq!(metadata.uuid, "");
365 }
366}