1use serde::{Deserialize, Serialize};
2
3use crate::runtime::kernel::wire::CancellationReason;
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 #[serde(default, skip_serializing_if = "Vec::is_empty")]
52 attachments: Vec<crate::types::message::ContentPart>,
53 },
54 LlmCompleted {
55 turn: u32,
56 message: Message,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 provider_replay: Option<ProviderReplay>,
59 },
60 ToolRequested {
61 turn: u32,
62 calls: Vec<ToolCall>,
63 },
64 ToolCompleted {
65 turn: u32,
66 results: Vec<ToolResult>,
67 },
68 Compressed {
69 turn: u32,
70 archived_seq_range: (u64, u64),
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 action: Option<String>,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
74 summary: Option<String>,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
76 summary_tokens: Option<u32>,
77 #[serde(default, skip_serializing_if = "Vec::is_empty")]
78 preserved_refs: Vec<String>,
79 },
80 PageOut {
82 turn: u32,
83 #[serde(default, skip_serializing_if = "Option::is_none")]
84 action: Option<String>,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
86 summary: Option<String>,
87 #[serde(default, skip_serializing_if = "Option::is_none")]
88 tier_hint: Option<String>,
89 #[serde(default)]
90 message_count: u32,
91 #[serde(default, skip_serializing_if = "Option::is_none")]
92 archive_ref: Option<String>,
93 },
94 PageIn {
96 turn: u32,
97 entry_count: u32,
98 },
99 RunTerminal {
100 reason: String,
101 turns_used: u32,
102 total_tokens: u64,
103 },
104
105 ToolArgumentRepaired {
108 turn: u32,
109 tool: String,
110 original_arguments: String,
111 repaired_arguments: String,
112 },
113 PermissionRequested {
115 turn: u32,
116 tool: String,
117 arguments: String,
118 reason: Option<String>,
119 },
120 PermissionResolved {
122 turn: u32,
123 approved: bool,
124 responder: String, },
126 ToolDenied {
128 turn: u32,
129 call_id: String,
130 tool_name: String,
131 reason: String,
132 },
133
134 CapabilityChanged {
137 turn: u32,
138 #[serde(default, skip_serializing_if = "Vec::is_empty")]
139 added: Vec<String>,
140 #[serde(default, skip_serializing_if = "Vec::is_empty")]
141 removed: Vec<String>,
142 #[serde(default, skip_serializing_if = "Option::is_none")]
143 change_kind: Option<String>,
144 #[serde(default, skip_serializing_if = "Option::is_none")]
145 capability_id: Option<String>,
146 #[serde(default, skip_serializing_if = "Option::is_none")]
147 version: Option<String>,
148 #[serde(default, skip_serializing_if = "Option::is_none")]
149 mounted_by: Option<String>,
150 #[serde(default, skip_serializing_if = "Option::is_none")]
151 mount_reason: Option<String>,
152 },
153 ContextRenewed {
155 turn: u32,
156 sprint: u32,
157 handoff_ref: String,
158 },
159
160 Suspended {
162 turn: u32,
163 reason: String,
164 #[serde(default, skip_serializing_if = "Vec::is_empty")]
165 pending_calls: Vec<String>,
166 },
167 Resumed {
169 turn: u32,
170 #[serde(default, skip_serializing_if = "Vec::is_empty")]
171 approved: Vec<String>,
172 #[serde(default, skip_serializing_if = "Vec::is_empty")]
173 denied: Vec<String>,
174 },
175 ToolGated {
177 turn: u32,
178 call_id: String,
179 tool: String,
180 reason: String,
181 },
182 SignalDeliveryDisposed {
184 turn: u32,
185 operation_id: String,
186 delivery_id: String,
187 attempt: u32,
188 signal_id: String,
189 disposition: String,
190 queue_depth: u32,
191 },
192 BudgetExceeded {
194 turn: u32,
195 operation_id: String,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
197 reservation_id: Option<String>,
198 budget: String,
199 },
200 BudgetUsageReported {
202 turn: u32,
203 operation_id: String,
204 reservation_id: String,
205 tokens: u64,
206 subagents: u32,
207 rounds: u32,
208 },
209 OperationCancelled {
211 turn: u32,
212 operation_id: String,
213 reason: CancellationReason,
214 #[serde(default)]
215 pending_call_ids: Vec<String>,
216 },
217 CheckpointTaken {
219 turn: u32,
220 history_len: u32,
221 },
222 EntropySample {
224 turn: u32,
225 score: f64,
226 score_version: u32,
227 rho: f64,
228 repeat_pressure: f64,
229 failure_rate: f64,
230 rollbacks_in_window: u32,
231 window_turns: u32,
232 },
233 EntropyAlert {
235 turn: u32,
236 score: f64,
237 threshold: f64,
238 },
239 Rollbacked {
241 turn: u32,
242 checkpoint_history_len: u32,
243 #[serde(default, skip_serializing_if = "Option::is_none")]
244 reason: Option<RollbackReason>,
245 },
246
247 AgentProcessChanged {
250 turn: u32,
251 agent_id: String,
252 parent_session_id: String,
253 role: String,
254 isolation: String,
255 context_inheritance: String,
256 state: String,
257 #[serde(default, skip_serializing_if = "Vec::is_empty")]
258 permitted_capability_ids: Vec<String>,
259 #[serde(default, skip_serializing_if = "Option::is_none")]
260 result_termination: Option<String>,
261 },
262
263 MilestoneAdvanced {
266 turn: u32,
267 phase_id: String,
268 #[serde(default)]
269 capabilities_unlocked: Vec<String>,
270 },
271 MilestoneBlocked {
273 turn: u32,
274 phase_id: String,
275 reason: String,
276 },
277
278 MemoryWritten {
281 turn: u32,
282 record_id: String,
283 scope: crate::mm::memory::MemoryScope,
284 memory_kind: crate::mm::memory::MemoryKind,
285 name: String,
286 size_bytes: u32,
287 },
288 MemoryQueried {
290 turn: u32,
291 scope: crate::mm::memory::MemoryScope,
292 query: String,
293 requested_k: usize,
294 requires_async_response: bool,
295 },
296 MemoryValidationFailed {
298 turn: u32,
299 record_id: String,
300 error: String,
301 },
302 MemoryRetrievalResult {
304 hits: Vec<crate::mm::memory::MemoryRecall>,
305 },
306}
307
308impl SessionEvent {
309 pub fn kind_str(&self) -> &'static str {
311 match self {
312 Self::RunStarted { .. } => "run_started",
313 Self::LlmCompleted { .. } => "llm_completed",
314 Self::ToolRequested { .. } => "tool_requested",
315 Self::ToolCompleted { .. } => "tool_completed",
316 Self::Compressed { .. } => "compressed",
317 Self::PageOut { .. } => "page_out",
318 Self::PageIn { .. } => "page_in",
319 Self::RunTerminal { .. } => "run_terminal",
320 Self::ToolArgumentRepaired { .. } => "tool_argument_repaired",
321 Self::PermissionRequested { .. } => "permission_requested",
322 Self::PermissionResolved { .. } => "permission_resolved",
323 Self::ToolDenied { .. } => "tool_denied",
324 Self::CapabilityChanged { .. } => "capability_changed",
325 Self::ContextRenewed { .. } => "context_renewed",
326 Self::Suspended { .. } => "suspended",
327 Self::Resumed { .. } => "resumed",
328 Self::ToolGated { .. } => "tool_gated",
329 Self::SignalDeliveryDisposed { .. } => "signal_delivery_disposed",
330 Self::BudgetExceeded { .. } => "budget_exceeded",
331 Self::BudgetUsageReported { .. } => "budget_usage_reported",
332 Self::OperationCancelled { .. } => "operation_cancelled",
333 Self::CheckpointTaken { .. } => "checkpoint_taken",
334 Self::EntropySample { .. } => "entropy_sample",
335 Self::EntropyAlert { .. } => "entropy_alert",
336 Self::Rollbacked { .. } => "rollbacked",
337 Self::AgentProcessChanged { .. } => "agent_process_changed",
338 Self::MilestoneAdvanced { .. } => "milestone_advanced",
339 Self::MilestoneBlocked { .. } => "milestone_blocked",
340 Self::MemoryWritten { .. } => "memory_written",
341 Self::MemoryQueried { .. } => "memory_queried",
342 Self::MemoryValidationFailed { .. } => "memory_validation_failed",
343 Self::MemoryRetrievalResult { .. } => "memory_retrieval_result",
344 }
345 }
346
347 pub fn is_kernel_os_event(&self) -> bool {
349 matches!(
350 self,
351 Self::Compressed { .. }
352 | Self::PageOut { .. }
353 | Self::PageIn { .. }
354 | Self::CapabilityChanged { .. }
355 | Self::ContextRenewed { .. }
356 | Self::Suspended { .. }
357 | Self::Resumed { .. }
358 | Self::ToolGated { .. }
359 | Self::SignalDeliveryDisposed { .. }
360 | Self::BudgetExceeded { .. }
361 | Self::BudgetUsageReported { .. }
362 | Self::OperationCancelled { .. }
363 | Self::CheckpointTaken { .. }
364 | Self::EntropySample { .. }
365 | Self::EntropyAlert { .. }
366 | Self::Rollbacked { .. }
367 | Self::AgentProcessChanged { .. }
368 | Self::MilestoneAdvanced { .. }
369 | Self::MilestoneBlocked { .. }
370 | Self::MemoryWritten { .. }
371 | Self::MemoryQueried { .. }
372 | Self::MemoryValidationFailed { .. }
373 )
374 }
375}