distri_types/
events.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::core::{MessageRole, ToolCall, ToolResponse};
5use crate::hooks::InlineHookRequest;
6
7/// Token usage information for a run
8#[derive(Debug, Serialize, Deserialize, Clone, Default)]
9pub struct RunUsage {
10    /// Actual tokens used (from LLM response)
11    pub total_tokens: u32,
12    pub input_tokens: u32,
13    pub output_tokens: u32,
14    /// Estimated tokens (pre-call estimate)
15    pub estimated_tokens: u32,
16}
17
18#[derive(Debug, Serialize, Deserialize, Clone)]
19#[serde(rename_all = "snake_case")]
20pub struct AgentEvent {
21    pub timestamp: chrono::DateTime<chrono::Utc>,
22    pub thread_id: String,
23    pub run_id: String,
24    pub event: AgentEventType,
25    pub task_id: String,
26    pub agent_id: String,
27    /// User ID for usage tracking
28    #[serde(default)]
29    pub user_id: Option<String>,
30    /// Identifier ID for tenant/project-level usage tracking
31    #[serde(default)]
32    pub identifier_id: Option<String>,
33    /// Workspace ID for workspace-scoped usage tracking
34    #[serde(default)]
35    pub workspace_id: Option<String>,
36}
37
38#[derive(Debug, Serialize, Deserialize, Clone)]
39#[serde(rename_all = "snake_case", tag = "type")]
40pub enum AgentEventType {
41    // Main run events
42    RunStarted {},
43    RunFinished {
44        success: bool,
45        total_steps: usize,
46        failed_steps: usize,
47        /// Token usage for this run
48        usage: Option<RunUsage>,
49    },
50    RunError {
51        message: String,
52        code: Option<String>,
53    },
54    PlanStarted {
55        initial_plan: bool,
56    },
57    PlanFinished {
58        total_steps: usize,
59    },
60    PlanPruned {
61        removed_steps: Vec<String>,
62    },
63    // Step execution events
64    StepStarted {
65        step_id: String,
66        step_index: usize,
67    },
68    StepCompleted {
69        step_id: String,
70        success: bool,
71    },
72
73    // Tool execution events
74    ToolExecutionStart {
75        step_id: String,
76        tool_call_id: String,
77        tool_call_name: String,
78        input: Value,
79    },
80    ToolExecutionEnd {
81        step_id: String,
82        tool_call_id: String,
83        tool_call_name: String,
84        success: bool,
85    },
86
87    // Message events for streaming
88    TextMessageStart {
89        message_id: String,
90        step_id: String,
91        role: MessageRole,
92        is_final: Option<bool>,
93    },
94    TextMessageContent {
95        message_id: String,
96        step_id: String,
97        delta: String,
98        stripped_content: Option<Vec<(usize, String)>>,
99    },
100    TextMessageEnd {
101        message_id: String,
102        step_id: String,
103    },
104
105    // Tool call events with parent/child relationships
106    ToolCalls {
107        step_id: String,
108        parent_message_id: Option<String>,
109        tool_calls: Vec<ToolCall>,
110    },
111    ToolResults {
112        step_id: String,
113        parent_message_id: Option<String>,
114        results: Vec<ToolResponse>,
115    },
116
117    // Agent transfer events
118    AgentHandover {
119        from_agent: String,
120        to_agent: String,
121        reason: Option<String>,
122    },
123
124    // Workflow events
125    WorkflowStarted {
126        workflow_name: String,
127        total_steps: usize,
128    },
129    NodeStarted {
130        node_id: String,
131        node_name: String,
132        step_type: String,
133    },
134    NodeCompleted {
135        node_id: String,
136        node_name: String,
137        success: bool,
138        error: Option<String>,
139    },
140    RunCompleted {
141        workflow_name: String,
142        success: bool,
143        total_steps: usize,
144    },
145    RunFailed {
146        workflow_name: String,
147        error: String,
148        failed_at_step: Option<String>,
149    },
150
151    BrowserScreenshot {
152        image: String,
153        format: Option<String>,
154        filename: Option<String>,
155        size: Option<u64>,
156        timestamp_ms: Option<i64>,
157    },
158
159    BrowserSessionStarted {
160        session_id: String,
161        viewer_url: Option<String>,
162        stream_url: Option<String>,
163    },
164
165    InlineHookRequested {
166        request: InlineHookRequest,
167    },
168
169    // TODO events
170    TodosUpdated {
171        formatted_todos: String,
172        action: String,
173        todo_count: usize,
174    },
175}
176
177impl AgentEvent {
178    pub fn new(event: AgentEventType) -> Self {
179        Self {
180            timestamp: chrono::Utc::now(),
181            thread_id: uuid::Uuid::new_v4().to_string(),
182            run_id: uuid::Uuid::new_v4().to_string(),
183            event,
184            task_id: uuid::Uuid::new_v4().to_string(),
185            agent_id: "default".to_string(),
186            user_id: None,
187            identifier_id: None,
188            workspace_id: None,
189        }
190    }
191
192    pub fn with_context(
193        event: AgentEventType,
194        thread_id: String,
195        run_id: String,
196        task_id: String,
197        agent_id: String,
198    ) -> Self {
199        Self {
200            timestamp: chrono::Utc::now(),
201            thread_id,
202            run_id,
203            task_id,
204            event,
205            agent_id,
206            user_id: None,
207            identifier_id: None,
208            workspace_id: None,
209        }
210    }
211}