1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::budget::{BudgetEnforcementBoundary, BudgetExhaustion, BudgetUsageSnapshot};
5use crate::checkpoint::{
6 OperationKind, OperationState, ReconciliationDecisionKind, ResumeObservation, ToolIdempotency,
7};
8use crate::types::{AgentStatus, ModelCallOperation, TokenUsage, ToolDirective};
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11#[serde(tag = "type", rename_all = "snake_case")]
12pub enum RunEventPayload {
13 RunStarted {
14 input: String,
15 },
16 RunStateChanged {
17 state: String,
18 },
19 AgentStarted,
20 CycleStarted,
21 ModelCallStarted {
22 call_id: String,
23 operation_id: String,
24 attempt: u32,
25 operation: ModelCallOperation,
26 backend: String,
27 model: String,
28 },
29 ModelCallCompleted {
30 call_id: String,
31 operation_id: String,
32 attempt: u32,
33 operation: ModelCallOperation,
34 backend: String,
35 model: String,
36 usage: TokenUsage,
37 },
38 ModelCallFailed {
39 call_id: String,
40 operation_id: String,
41 attempt: u32,
42 operation: ModelCallOperation,
43 backend: String,
44 model: String,
45 outcome: ModelCallFailureOutcome,
46 usage: TokenUsage,
47 error_code: String,
48 },
49 Diagnostic {
50 level: DiagnosticLevel,
51 code: String,
52 details: serde_json::Map<String, Value>,
53 },
54 AssistantDelta {
55 delta: String,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
57 content_chars: Option<u64>,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 estimated_tokens: Option<u64>,
60 },
61 ReasoningDelta {
62 delta: String,
63 #[serde(default, skip_serializing_if = "Option::is_none")]
64 reasoning_chars: Option<u64>,
65 #[serde(default, skip_serializing_if = "Option::is_none")]
66 estimated_tokens: Option<u64>,
67 },
68 ModelToolCallStarted {
69 tool_call_id: String,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
71 tool_call_index: Option<u64>,
72 tool_name: String,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
74 arguments_chars: Option<u64>,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
76 estimated_tokens: Option<u64>,
77 },
78 ModelToolCallProgress {
79 tool_call_id: String,
80 #[serde(default, skip_serializing_if = "Option::is_none")]
81 tool_call_index: Option<u64>,
82 tool_name: String,
83 #[serde(default, skip_serializing_if = "Option::is_none")]
84 arguments_chars: Option<u64>,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
86 estimated_tokens: Option<u64>,
87 },
88 ToolCallPlanned {
89 tool_call_id: String,
90 tool_name: String,
91 arguments: Value,
92 },
93 ToolCallStarted {
94 tool_call_id: String,
95 tool_name: String,
96 arguments: Value,
97 },
98 ApprovalRequested {
99 request_id: String,
100 tool_call_id: String,
101 tool_name: String,
102 message: String,
103 },
104 ApprovalResolved {
105 request_id: String,
106 tool_name: String,
107 tool_call_id: String,
108 action: ApprovalAction,
109 },
110 ToolCallCompleted {
111 tool_call_id: String,
112 tool_name: String,
113 status: ToolStatus,
114 directive: ToolDirective,
115 error_code: Option<String>,
116 execution_started: bool,
117 duration_ms: Option<u64>,
118 },
119 MemoryCompactStarted {
120 message_count: usize,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
122 estimated_tokens: Option<u64>,
123 trigger: MemoryCompactTrigger,
124 configured_threshold: u64,
125 effective_threshold: u64,
126 microcompact_threshold: u64,
127 model_context_window: u64,
128 model_max_output_tokens: Option<u64>,
129 reserved_output_tokens: u64,
130 reserved_output_source: ReservedOutputSource,
131 autocompact_buffer_tokens: u64,
132 },
133 MemoryCompactCompleted {
134 before_count: usize,
135 after_count: usize,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 summary_tokens: Option<u64>,
138 mode: MemoryCompactMode,
139 changed: bool,
140 },
141 SubRunStarted {
142 parent_tool_call_id: String,
143 #[serde(default, skip_serializing_if = "Option::is_none")]
144 child_session_id: Option<String>,
145 #[serde(default, skip_serializing_if = "Option::is_none")]
146 task_id: Option<String>,
147 },
148 SubRunCompleted {
149 parent_tool_call_id: String,
150 status: AgentStatus,
151 #[serde(default, skip_serializing_if = "Option::is_none")]
152 final_output: Option<String>,
153 },
154 HandoffStarted {
155 source_agent: String,
156 target_agent: String,
157 tool_call_id: String,
158 },
159 HandoffCompleted {
160 source_agent: String,
161 target_agent: String,
162 tool_call_id: String,
163 },
164 SessionPersisted,
165 BudgetSnapshot {
166 enforcement_boundary: BudgetEnforcementBoundary,
167 budget_usage: BudgetUsageSnapshot,
168 },
169 BudgetExhausted {
170 enforcement_boundary: BudgetEnforcementBoundary,
171 budget_usage: BudgetUsageSnapshot,
172 budget_exhaustion: BudgetExhaustion,
173 },
174 CheckpointCreated {
175 checkpoint_key: String,
176 resume_attempt: u64,
177 },
178 CheckpointResumed {
179 checkpoint_key: String,
180 resume_attempt: u64,
181 },
182 OperationReplayed {
183 checkpoint_key: String,
184 operation_id: String,
185 operation_kind: OperationKind,
186 receipt_state: OperationState,
187 },
188 OperationAmbiguous {
189 checkpoint_key: String,
190 operation_id: String,
191 operation_kind: OperationKind,
192 risk: String,
193 idempotency_support: Option<ToolIdempotency>,
194 },
195 ReconciliationRequired {
196 checkpoint_key: String,
197 operation_id: String,
198 operation_kind: OperationKind,
199 interruption_reason: String,
200 resume_observation: ResumeObservation,
201 },
202 ModelRetryDuplicateRisk {
203 checkpoint_key: String,
204 operation_id: String,
205 operation_kind: OperationKind,
206 risk: String,
207 },
208 ReconciliationResolved {
209 checkpoint_key: String,
210 operation_id: String,
211 operation_kind: OperationKind,
212 decision: ReconciliationDecisionKind,
213 },
214 RunCompleted {
215 status: AgentStatus,
216 },
217 RunFailed {
218 error: String,
219 },
220 RunCancelled {
221 reason: String,
222 },
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
226#[serde(rename_all = "snake_case")]
227pub enum MemoryCompactTrigger {
228 MicroThreshold,
229 FullThreshold,
230 PromptTooLong,
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
234#[serde(rename_all = "snake_case")]
235pub enum MemoryCompactMode {
236 None,
237 Micro,
238 Structural,
239 Summary,
240 Emergency,
241}
242
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
244#[serde(rename_all = "snake_case")]
245pub enum ReservedOutputSource {
246 ModelSettings,
247 TaskMetadata,
248 FrameworkFallback,
249 FrameworkFallbackCappedByModelCapability,
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
253#[serde(rename_all = "snake_case")]
254pub enum ModelCallFailureOutcome {
255 Definitive,
256 Ambiguous,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
260#[serde(rename_all = "snake_case")]
261pub enum DiagnosticLevel {
262 Debug,
263 Info,
264 Warning,
265 Error,
266}
267
268impl DiagnosticLevel {
269 pub fn as_str(self) -> &'static str {
270 match self {
271 Self::Debug => "debug",
272 Self::Info => "info",
273 Self::Warning => "warning",
274 Self::Error => "error",
275 }
276 }
277}
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
280#[serde(rename_all = "snake_case")]
281pub enum ApprovalAction {
282 Allow,
283 AllowSession,
284 Deny,
285 Timeout,
286}
287
288impl ApprovalAction {
289 pub fn parse(value: &str) -> Option<Self> {
290 match value {
291 "allow" => Some(Self::Allow),
292 "allow_session" => Some(Self::AllowSession),
293 "deny" => Some(Self::Deny),
294 "timeout" => Some(Self::Timeout),
295 _ => None,
296 }
297 }
298
299 pub fn as_str(self) -> &'static str {
300 match self {
301 Self::Allow => "allow",
302 Self::AllowSession => "allow_session",
303 Self::Deny => "deny",
304 Self::Timeout => "timeout",
305 }
306 }
307
308 pub fn is_approved(self) -> bool {
309 matches!(self, Self::Allow | Self::AllowSession)
310 }
311}
312
313#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
314#[serde(rename_all = "snake_case")]
315pub enum ToolStatus {
316 Started,
317 Success,
318 Error,
319 WaitResponse,
320 Running,
321 PendingCompress,
322}
323
324#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
325pub struct AgentErrorPayload {
326 pub message: String,
327 pub code: Option<String>,
328}
329
330impl AgentErrorPayload {
331 pub fn new(message: impl Into<String>) -> Self {
332 Self {
333 message: message.into(),
334 code: None,
335 }
336 }
337}