Skip to main content

chimera_core/
event.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{Action, PlanItem, TurnResult, Usage};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(tag = "type", rename_all = "snake_case")]
7#[non_exhaustive]
8pub enum AgentEvent {
9    // Session lifecycle
10    SessionStarted {
11        session_id: String,
12    },
13    TurnStarted,
14    TurnCompleted {
15        #[serde(default)]
16        usage: Option<Usage>,
17        #[serde(default)]
18        result: Option<TurnResult>,
19    },
20    TurnFailed {
21        message: String,
22    },
23
24    // Content
25    Message {
26        id: String,
27        text: String,
28    },
29    TextDelta {
30        id: String,
31        delta: String,
32    },
33    Reasoning {
34        id: String,
35        text: String,
36    },
37
38    // Actions
39    ActionStarted {
40        id: String,
41        action: Action,
42    },
43    ActionUpdated {
44        id: String,
45        action: Action,
46    },
47    ActionCompleted {
48        id: String,
49        action: Action,
50    },
51
52    // Progress
53    PlanUpdated {
54        items: Vec<PlanItem>,
55    },
56
57    // Tool / function calls
58    ToolCall {
59        /// Call ID assigned by the model (used for multi-call correlation).
60        id: String,
61        /// Name of the function the model chose to call.
62        name: String,
63        /// Raw JSON string of the function arguments.
64        arguments: String,
65    },
66
67    // Errors
68    Error {
69        message: String,
70    },
71
72    // Pass-through
73    Raw {
74        backend: String,
75        data: serde_json::Value,
76    },
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::ActionStatus;
83
84    #[test]
85    fn session_started_serde() {
86        let event = AgentEvent::SessionStarted {
87            session_id: "abc-123".into(),
88        };
89        let json = serde_json::to_string(&event).unwrap();
90        assert!(json.contains("\"type\":\"session_started\""));
91
92        let parsed: AgentEvent = serde_json::from_str(&json).unwrap();
93        match parsed {
94            AgentEvent::SessionStarted { session_id } => assert_eq!(session_id, "abc-123"),
95            _ => panic!("wrong variant"),
96        }
97    }
98
99    #[test]
100    fn turn_completed_with_usage() {
101        let event = AgentEvent::TurnCompleted {
102            usage: Some(Usage {
103                input_tokens: Some(100),
104                output_tokens: Some(50),
105                ..Default::default()
106            }),
107            result: None,
108        };
109        let json = serde_json::to_string(&event).unwrap();
110        let parsed: AgentEvent = serde_json::from_str(&json).unwrap();
111        match parsed {
112            AgentEvent::TurnCompleted { usage, .. } => {
113                let u = usage.unwrap();
114                assert_eq!(u.input_tokens, Some(100));
115                assert_eq!(u.output_tokens, Some(50));
116            }
117            _ => panic!("wrong variant"),
118        }
119    }
120
121    #[test]
122    fn action_started_serde() {
123        let event = AgentEvent::ActionStarted {
124            id: "item-1".into(),
125            action: Action::Command {
126                command: "echo hi".into(),
127                output: String::new(),
128                exit_code: None,
129                status: ActionStatus::InProgress,
130            },
131        };
132        let json = serde_json::to_string(&event).unwrap();
133        assert!(json.contains("\"type\":\"action_started\""));
134
135        let parsed: AgentEvent = serde_json::from_str(&json).unwrap();
136        match parsed {
137            AgentEvent::ActionStarted { id, .. } => assert_eq!(id, "item-1"),
138            _ => panic!("wrong variant"),
139        }
140    }
141
142    #[test]
143    fn raw_event_serde() {
144        let event = AgentEvent::Raw {
145            backend: "codex".into(),
146            data: serde_json::json!({"custom": true}),
147        };
148        let json = serde_json::to_string(&event).unwrap();
149        let parsed: AgentEvent = serde_json::from_str(&json).unwrap();
150        match parsed {
151            AgentEvent::Raw { backend, data } => {
152                assert_eq!(backend, "codex");
153                assert_eq!(data["custom"], true);
154            }
155            _ => panic!("wrong variant"),
156        }
157    }
158}