1use serde::{Deserialize, Serialize};
2
3
4use crate::types::message::{Message, ToolCall, ToolResult};
5
6#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
15pub struct ProviderReplay {
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub native_blocks: Option<Vec<serde_json::Value>>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub reasoning_content: Option<String>,
20 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
21 pub extra: serde_json::Map<String, serde_json::Value>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
25#[serde(tag = "kind", rename_all = "snake_case")]
26pub enum RollbackReason {
27 FatalToolError { tool_name: String, error: String },
28 GovernanceDenied { tool_name: String, reason: String },
29 ProviderFailure { error: String },
30 Timeout,
31 UserInterrupt,
32 MalformedReplay { reason: String },
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(tag = "kind", rename_all = "snake_case")]
41pub enum SessionEvent {
42 RunStarted {
44 run_id: String,
45 goal: String,
46 #[serde(default)]
47 criteria: Vec<String>,
48 agent_id: Option<String>,
49 system_prompt: Option<String>,
50 },
51 LlmCompleted {
52 turn: u32,
53 message: Message,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
55 provider_replay: Option<ProviderReplay>,
56 },
57 ToolRequested {
58 turn: u32,
59 calls: Vec<ToolCall>,
60 },
61 ToolCompleted {
62 turn: u32,
63 results: Vec<ToolResult>,
64 },
65 Compressed {
66 turn: u32,
67 archived_seq_range: (u64, u64),
68 #[serde(default, skip_serializing_if = "Option::is_none")]
69 action: Option<String>,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
71 summary: Option<String>,
72 #[serde(default, skip_serializing_if = "Option::is_none")]
73 summary_tokens: Option<u32>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
75 archive_ref: Option<String>,
76 #[serde(default, skip_serializing_if = "Vec::is_empty")]
77 preserved_refs: Vec<String>,
78 },
79 PageOut {
81 turn: u32,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
83 action: Option<String>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 summary: Option<String>,
86 #[serde(default, skip_serializing_if = "Option::is_none")]
87 tier_hint: Option<String>,
88 #[serde(default)]
89 message_count: u32,
90 },
91 PageIn {
93 turn: u32,
94 entry_count: u32,
95 },
96 LargeResultSpooled {
98 turn: u32,
99 call_id: String,
100 tool: String,
101 original_size: u32,
102 preview_size: u32,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
104 spool_ref: Option<String>,
105 },
106 RunTerminal {
107 reason: String,
108 turns_used: u32,
109 total_tokens: u64,
110 },
111
112 ToolArgumentRepaired {
115 turn: u32,
116 tool: String,
117 original_arguments: String,
118 repaired_arguments: String,
119 },
120 PermissionRequested {
122 turn: u32,
123 tool: String,
124 arguments: String,
125 reason: Option<String>,
126 },
127 PermissionResolved {
129 turn: u32,
130 approved: bool,
131 responder: String, },
133 ToolDenied {
135 turn: u32,
136 call_id: String,
137 tool_name: String,
138 reason: String,
139 },
140
141 CapabilityChanged {
144 turn: u32,
145 #[serde(default, skip_serializing_if = "Vec::is_empty")]
146 added: Vec<String>,
147 #[serde(default, skip_serializing_if = "Vec::is_empty")]
148 removed: Vec<String>,
149 #[serde(default, skip_serializing_if = "Option::is_none")]
150 change_kind: Option<String>,
151 #[serde(default, skip_serializing_if = "Option::is_none")]
152 capability_id: Option<String>,
153 #[serde(default, skip_serializing_if = "Option::is_none")]
154 version: Option<String>,
155 #[serde(default, skip_serializing_if = "Option::is_none")]
156 mounted_by: Option<String>,
157 #[serde(default, skip_serializing_if = "Option::is_none")]
158 mount_reason: Option<String>,
159 },
160 ContextRenewed {
162 turn: u32,
163 sprint: u32,
164 handoff_ref: String,
165 },
166
167 Suspended {
169 turn: u32,
170 reason: String,
171 #[serde(default, skip_serializing_if = "Vec::is_empty")]
172 pending_calls: Vec<String>,
173 },
174 Resumed {
176 turn: u32,
177 #[serde(default, skip_serializing_if = "Vec::is_empty")]
178 approved: Vec<String>,
179 #[serde(default, skip_serializing_if = "Vec::is_empty")]
180 denied: Vec<String>,
181 },
182 ToolGated {
184 turn: u32,
185 call_id: String,
186 tool: String,
187 reason: String,
188 },
189 SignalDisposed {
191 turn: u32,
192 signal_id: String,
193 disposition: String,
194 queue_depth: u32,
195 },
196 BudgetExceeded {
198 turn: u32,
199 budget: String,
200 },
201 CheckpointTaken {
203 turn: u32,
204 history_len: u32,
205 },
206 EntropySample {
208 turn: u32,
209 score: f64,
210 score_version: u32,
211 rho: f64,
212 repeat_pressure: f64,
213 failure_rate: f64,
214 rollbacks_in_window: u32,
215 window_turns: u32,
216 },
217 EntropyAlert {
219 turn: u32,
220 score: f64,
221 threshold: f64,
222 },
223 Rollbacked {
225 turn: u32,
226 checkpoint_history_len: u32,
227 #[serde(default, skip_serializing_if = "Option::is_none")]
228 reason: Option<RollbackReason>,
229 },
230
231 AgentProcessChanged {
234 turn: u32,
235 agent_id: String,
236 parent_session_id: String,
237 role: String,
238 isolation: String,
239 context_inheritance: String,
240 state: String,
241 #[serde(default, skip_serializing_if = "Vec::is_empty")]
242 permitted_capability_ids: Vec<String>,
243 #[serde(default, skip_serializing_if = "Option::is_none")]
244 result_termination: Option<String>,
245 },
246
247 MilestoneAdvanced {
250 turn: u32,
251 phase_id: String,
252 #[serde(default)]
253 capabilities_unlocked: Vec<String>,
254 },
255 MilestoneBlocked {
257 turn: u32,
258 phase_id: String,
259 reason: String,
260 },
261
262 MemoryWritten {
265 turn: u32,
266 memory_id: String,
267 memory_kind: String,
268 size_bytes: u32,
269 },
270 MemoryQueried {
272 turn: u32,
273 query_context: String,
274 requested_k: usize,
275 requires_async_response: bool,
276 },
277 MemoryValidationFailed {
279 turn: u32,
280 memory_id: String,
281 error: String,
282 },
283 MemoryRetrievalResult {
285 retrieval: crate::mm::memory::MemoryRetrieval,
286 },
287}
288
289impl SessionEvent {
290 pub fn kind_str(&self) -> &'static str {
292 match self {
293 Self::RunStarted { .. } => "run_started",
294 Self::LlmCompleted { .. } => "llm_completed",
295 Self::ToolRequested { .. } => "tool_requested",
296 Self::ToolCompleted { .. } => "tool_completed",
297 Self::Compressed { .. } => "compressed",
298 Self::PageOut { .. } => "page_out",
299 Self::PageIn { .. } => "page_in",
300 Self::LargeResultSpooled { .. } => "large_result_spooled",
301 Self::RunTerminal { .. } => "run_terminal",
302 Self::ToolArgumentRepaired { .. } => "tool_argument_repaired",
303 Self::PermissionRequested { .. } => "permission_requested",
304 Self::PermissionResolved { .. } => "permission_resolved",
305 Self::ToolDenied { .. } => "tool_denied",
306 Self::CapabilityChanged { .. } => "capability_changed",
307 Self::ContextRenewed { .. } => "context_renewed",
308 Self::Suspended { .. } => "suspended",
309 Self::Resumed { .. } => "resumed",
310 Self::ToolGated { .. } => "tool_gated",
311 Self::SignalDisposed { .. } => "signal_disposed",
312 Self::BudgetExceeded { .. } => "budget_exceeded",
313 Self::CheckpointTaken { .. } => "checkpoint_taken",
314 Self::EntropySample { .. } => "entropy_sample",
315 Self::EntropyAlert { .. } => "entropy_alert",
316 Self::Rollbacked { .. } => "rollbacked",
317 Self::AgentProcessChanged { .. } => "agent_process_changed",
318 Self::MilestoneAdvanced { .. } => "milestone_advanced",
319 Self::MilestoneBlocked { .. } => "milestone_blocked",
320 Self::MemoryWritten { .. } => "memory_written",
321 Self::MemoryQueried { .. } => "memory_queried",
322 Self::MemoryValidationFailed { .. } => "memory_validation_failed",
323 Self::MemoryRetrievalResult { .. } => "memory_retrieval_result",
324 }
325 }
326
327 pub fn is_kernel_os_event(&self) -> bool {
329 matches!(
330 self,
331 Self::Compressed { .. }
332 | Self::PageOut { .. }
333 | Self::PageIn { .. }
334 | Self::LargeResultSpooled { .. }
335 | Self::CapabilityChanged { .. }
336 | Self::ContextRenewed { .. }
337 | Self::Suspended { .. }
338 | Self::Resumed { .. }
339 | Self::ToolGated { .. }
340 | Self::SignalDisposed { .. }
341 | Self::BudgetExceeded { .. }
342 | Self::CheckpointTaken { .. }
343 | Self::EntropySample { .. }
344 | Self::EntropyAlert { .. }
345 | Self::Rollbacked { .. }
346 | Self::AgentProcessChanged { .. }
347 | Self::MilestoneAdvanced { .. }
348 | Self::MilestoneBlocked { .. }
349 | Self::MemoryWritten { .. }
350 | Self::MemoryQueried { .. }
351 | Self::MemoryValidationFailed { .. }
352 )
353 }
354}