Skip to main content

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    /// Channel ID for channel-scoped usage tracking
37    #[serde(default)]
38    pub channel_id: Option<String>,
39}
40
41#[derive(Debug, Serialize, Deserialize, Clone)]
42#[serde(rename_all = "snake_case", tag = "type")]
43pub enum AgentEventType {
44    // Main run events
45    RunStarted {},
46    RunFinished {
47        success: bool,
48        total_steps: usize,
49        failed_steps: usize,
50        /// Token usage for this run
51        usage: Option<RunUsage>,
52    },
53    RunError {
54        message: String,
55        code: Option<String>,
56    },
57    PlanStarted {
58        initial_plan: bool,
59    },
60    PlanFinished {
61        total_steps: usize,
62    },
63    PlanPruned {
64        removed_steps: Vec<String>,
65    },
66    // Step execution events
67    StepStarted {
68        step_id: String,
69        step_index: usize,
70    },
71    StepCompleted {
72        step_id: String,
73        success: bool,
74    },
75
76    // Tool execution events
77    ToolExecutionStart {
78        step_id: String,
79        tool_call_id: String,
80        tool_call_name: String,
81        input: Value,
82    },
83    ToolExecutionEnd {
84        step_id: String,
85        tool_call_id: String,
86        tool_call_name: String,
87        success: bool,
88    },
89
90    // Message events for streaming
91    TextMessageStart {
92        message_id: String,
93        step_id: String,
94        role: MessageRole,
95        is_final: Option<bool>,
96    },
97    TextMessageContent {
98        message_id: String,
99        step_id: String,
100        delta: String,
101        stripped_content: Option<Vec<(usize, String)>>,
102    },
103    TextMessageEnd {
104        message_id: String,
105        step_id: String,
106    },
107
108    // Tool call events with parent/child relationships
109    ToolCalls {
110        step_id: String,
111        parent_message_id: Option<String>,
112        tool_calls: Vec<ToolCall>,
113    },
114    ToolResults {
115        step_id: String,
116        parent_message_id: Option<String>,
117        results: Vec<ToolResponse>,
118    },
119
120    // Agent transfer events
121    AgentHandover {
122        from_agent: String,
123        to_agent: String,
124        reason: Option<String>,
125    },
126
127    BrowserScreenshot {
128        image: String,
129        format: Option<String>,
130        filename: Option<String>,
131        size: Option<u64>,
132        timestamp_ms: Option<i64>,
133    },
134
135    BrowserSessionStarted {
136        session_id: String,
137        viewer_url: Option<String>,
138        stream_url: Option<String>,
139    },
140
141    InlineHookRequested {
142        request: InlineHookRequest,
143    },
144
145    // TODO events
146    TodosUpdated {
147        formatted_todos: String,
148        action: String,
149        todo_count: usize,
150    },
151
152    // Context management events
153    ContextCompaction {
154        /// Which tier of compaction was applied
155        tier: CompactionTier,
156        /// Token count before compaction
157        tokens_before: usize,
158        /// Token count after compaction
159        tokens_after: usize,
160        /// Number of entries removed or summarized
161        entries_affected: usize,
162        /// Context budget limit that triggered compaction
163        context_limit: usize,
164        /// Usage ratio that triggered compaction (0.0 - 1.0)
165        usage_ratio: f64,
166        /// Optional summary text (for Tier 2 summarization)
167        summary: Option<String>,
168    },
169}
170
171/// Tier of context compaction applied
172#[derive(Debug, Clone, Serialize, Deserialize)]
173#[serde(rename_all = "snake_case")]
174pub enum CompactionTier {
175    /// Mechanical: drop old entries, truncate payloads
176    Trim,
177    /// Semantic: LLM-powered summarization of history
178    Summarize,
179    /// Emergency: preserve only essentials
180    Reset,
181}
182
183impl AgentEvent {
184    pub fn new(event: AgentEventType) -> Self {
185        Self {
186            timestamp: chrono::Utc::now(),
187            thread_id: uuid::Uuid::new_v4().to_string(),
188            run_id: uuid::Uuid::new_v4().to_string(),
189            event,
190            task_id: uuid::Uuid::new_v4().to_string(),
191            agent_id: "default".to_string(),
192            user_id: None,
193            identifier_id: None,
194            workspace_id: None,
195            channel_id: None,
196        }
197    }
198
199    pub fn with_context(
200        event: AgentEventType,
201        thread_id: String,
202        run_id: String,
203        task_id: String,
204        agent_id: String,
205    ) -> Self {
206        Self {
207            timestamp: chrono::Utc::now(),
208            thread_id,
209            run_id,
210            task_id,
211            event,
212            agent_id,
213            user_id: None,
214            identifier_id: None,
215            workspace_id: None,
216            channel_id: None,
217        }
218    }
219}