Skip to main content

vv_agent/events/
payload.rs

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        microcompact_target: u64,
128        candidate_count: usize,
129        estimated_reclaimable_tokens: u64,
130        model_context_window: u64,
131        model_max_output_tokens: Option<u64>,
132        reserved_output_tokens: u64,
133        reserved_output_source: ReservedOutputSource,
134        autocompact_buffer_tokens: u64,
135    },
136    MemoryCompactCompleted {
137        before_count: usize,
138        after_count: usize,
139        #[serde(default, skip_serializing_if = "Option::is_none")]
140        summary_tokens: Option<u64>,
141        mode: MemoryCompactMode,
142        changed: bool,
143        archived_count: usize,
144        reclaimed_tokens: u64,
145        artifact_failure_count: usize,
146    },
147    SubRunStarted {
148        parent_tool_call_id: String,
149        #[serde(default, skip_serializing_if = "Option::is_none")]
150        child_session_id: Option<String>,
151        #[serde(default, skip_serializing_if = "Option::is_none")]
152        task_id: Option<String>,
153    },
154    SubRunCompleted {
155        parent_tool_call_id: String,
156        status: AgentStatus,
157        #[serde(default, skip_serializing_if = "Option::is_none")]
158        final_output: Option<String>,
159    },
160    HandoffStarted {
161        source_agent: String,
162        target_agent: String,
163        tool_call_id: String,
164    },
165    HandoffCompleted {
166        source_agent: String,
167        target_agent: String,
168        tool_call_id: String,
169    },
170    SessionPersisted,
171    BudgetSnapshot {
172        enforcement_boundary: BudgetEnforcementBoundary,
173        budget_usage: BudgetUsageSnapshot,
174    },
175    BudgetExhausted {
176        enforcement_boundary: BudgetEnforcementBoundary,
177        budget_usage: BudgetUsageSnapshot,
178        budget_exhaustion: BudgetExhaustion,
179    },
180    CheckpointCreated {
181        checkpoint_key: String,
182        resume_attempt: u64,
183    },
184    CheckpointResumed {
185        checkpoint_key: String,
186        resume_attempt: u64,
187    },
188    OperationReplayed {
189        checkpoint_key: String,
190        operation_id: String,
191        operation_kind: OperationKind,
192        receipt_state: OperationState,
193    },
194    OperationAmbiguous {
195        checkpoint_key: String,
196        operation_id: String,
197        operation_kind: OperationKind,
198        risk: String,
199        idempotency_support: Option<ToolIdempotency>,
200    },
201    ReconciliationRequired {
202        checkpoint_key: String,
203        operation_id: String,
204        operation_kind: OperationKind,
205        interruption_reason: String,
206        resume_observation: ResumeObservation,
207    },
208    ModelRetryDuplicateRisk {
209        checkpoint_key: String,
210        operation_id: String,
211        operation_kind: OperationKind,
212        risk: String,
213    },
214    ReconciliationResolved {
215        checkpoint_key: String,
216        operation_id: String,
217        operation_kind: OperationKind,
218        decision: ReconciliationDecisionKind,
219    },
220    RunCompleted {
221        status: AgentStatus,
222    },
223    RunFailed {
224        error: String,
225    },
226    RunCancelled {
227        reason: String,
228    },
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
232#[serde(rename_all = "snake_case")]
233pub enum MemoryCompactTrigger {
234    MicroThreshold,
235    FullThreshold,
236    PromptTooLong,
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
240#[serde(rename_all = "snake_case")]
241pub enum MemoryCompactMode {
242    None,
243    Micro,
244    Structural,
245    Summary,
246    Emergency,
247}
248
249#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
250#[serde(rename_all = "snake_case")]
251pub enum ReservedOutputSource {
252    ModelSettings,
253    TaskMetadata,
254    FrameworkFallback,
255    FrameworkFallbackCappedByModelCapability,
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
259#[serde(rename_all = "snake_case")]
260pub enum ModelCallFailureOutcome {
261    Definitive,
262    Ambiguous,
263}
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
266#[serde(rename_all = "snake_case")]
267pub enum DiagnosticLevel {
268    Debug,
269    Info,
270    Warning,
271    Error,
272}
273
274impl DiagnosticLevel {
275    pub fn as_str(self) -> &'static str {
276        match self {
277            Self::Debug => "debug",
278            Self::Info => "info",
279            Self::Warning => "warning",
280            Self::Error => "error",
281        }
282    }
283}
284
285#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
286#[serde(rename_all = "snake_case")]
287pub enum ApprovalAction {
288    Allow,
289    AllowSession,
290    Deny,
291    Timeout,
292}
293
294impl ApprovalAction {
295    pub fn parse(value: &str) -> Option<Self> {
296        match value {
297            "allow" => Some(Self::Allow),
298            "allow_session" => Some(Self::AllowSession),
299            "deny" => Some(Self::Deny),
300            "timeout" => Some(Self::Timeout),
301            _ => None,
302        }
303    }
304
305    pub fn as_str(self) -> &'static str {
306        match self {
307            Self::Allow => "allow",
308            Self::AllowSession => "allow_session",
309            Self::Deny => "deny",
310            Self::Timeout => "timeout",
311        }
312    }
313
314    pub fn is_approved(self) -> bool {
315        matches!(self, Self::Allow | Self::AllowSession)
316    }
317}
318
319#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
320#[serde(rename_all = "snake_case")]
321pub enum ToolStatus {
322    Started,
323    Success,
324    Error,
325    WaitResponse,
326    Running,
327    PendingCompress,
328}
329
330#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
331pub struct AgentErrorPayload {
332    pub message: String,
333    pub code: Option<String>,
334}
335
336impl AgentErrorPayload {
337    pub fn new(message: impl Into<String>) -> Self {
338        Self {
339            message: message.into(),
340            code: None,
341        }
342    }
343}