1use crate::tools::{ToolCall, ToolResult};
7use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11pub struct NullOutput;
15
16#[async_trait]
17impl AgentOutput for NullOutput {
18 async fn emit_event(
19 &self,
20 _event: AgentEvent,
21 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
22 Ok(())
23 }
24}
25
26pub mod events {
28 pub use super::NullOutput;
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
33pub enum ToolExecutionStatus {
34 Executing,
36 Success,
38 Error,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44pub enum ConfirmationKind {
45 ToolExecution,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct ConfirmationRequest {
52 pub id: String,
54 pub kind: ConfirmationKind,
56 pub title: String,
58 pub message: String,
60 pub metadata: HashMap<String, serde_json::Value>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct ConfirmationDecision {
67 pub approved: bool,
69 pub note: Option<String>,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct ToolExecutionInfo {
76 pub execution_id: String,
78 pub tool_name: String,
80 pub parameters: HashMap<String, serde_json::Value>,
82 pub status: ToolExecutionStatus,
84 pub result: Option<ToolResult>,
86 pub timestamp: chrono::DateTime<chrono::Utc>,
88 pub metadata: HashMap<String, serde_json::Value>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct AgentStepInfo {
95 pub step_number: usize,
97 pub task: String,
99 pub thinking: Option<String>,
101 pub tool_executions: Vec<ToolExecutionInfo>,
103 pub completed: bool,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize, Default)]
109pub struct TokenUsage {
110 pub input_tokens: u32,
112 pub output_tokens: u32,
114 pub total_tokens: u32,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct AgentExecutionContext {
121 pub agent_id: String,
123 pub original_goal: String,
125 pub current_task: String,
127 pub project_path: String,
129 pub max_steps: usize,
131 pub current_step: usize,
133 pub execution_time: std::time::Duration,
135 pub token_usage: TokenUsage,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub enum AgentEvent {
142 ExecutionStarted { context: AgentExecutionContext },
144 ExecutionCompleted {
146 context: AgentExecutionContext,
147 success: bool,
148 summary: String,
149 },
150 ExecutionInterrupted {
152 context: AgentExecutionContext,
153 reason: String,
154 },
155 StepStarted { step_info: AgentStepInfo },
157 StepCompleted { step_info: AgentStepInfo },
159 ToolExecutionStarted { tool_info: ToolExecutionInfo },
161 ToolExecutionUpdated { tool_info: ToolExecutionInfo },
163 ToolExecutionCompleted { tool_info: ToolExecutionInfo },
165 AgentThinking {
167 step_number: usize,
168 thinking: String,
169 },
170 TokenUsageUpdated { token_usage: TokenUsage },
172 StatusUpdate {
174 status: String,
175 metadata: HashMap<String, serde_json::Value>,
176 },
177 Message {
179 level: MessageLevel,
180 content: String,
181 metadata: HashMap<String, serde_json::Value>,
182 },
183 CompressionStarted {
185 level: String,
186 current_tokens: u32,
187 target_tokens: u32,
188 reason: String,
189 },
190 CompressionCompleted {
192 summary: String,
193 tokens_saved: u32,
194 messages_before: u32,
195 messages_after: u32,
196 },
197 CompressionFailed {
199 error: String,
200 fallback_action: String,
201 },
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
206pub enum MessageLevel {
207 Debug,
208 Info,
209 Normal,
210 Warning,
211 Error,
212}
213
214#[async_trait]
216pub trait AgentOutput: Send + Sync {
217 async fn emit_event(
219 &self,
220 event: AgentEvent,
221 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
222
223 async fn emit_message(
225 &self,
226 level: MessageLevel,
227 content: &str,
228 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
229 self.emit_event(AgentEvent::Message {
230 level,
231 content: content.to_string(),
232 metadata: HashMap::new(),
233 })
234 .await
235 }
236
237 async fn debug(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
239 self.emit_message(MessageLevel::Debug, content).await
240 }
241
242 async fn emit_token_update(
244 &self,
245 token_usage: TokenUsage,
246 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
247 self.emit_event(AgentEvent::TokenUsageUpdated { token_usage })
248 .await
249 }
250
251 async fn info(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
253 self.emit_message(MessageLevel::Info, content).await
254 }
255
256 async fn warning(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
258 self.emit_message(MessageLevel::Warning, content).await
259 }
260
261 async fn error(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
263 self.emit_message(MessageLevel::Error, content).await
264 }
265
266 async fn normal(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
268 self.emit_message(MessageLevel::Normal, content).await
269 }
270
271 async fn emit_status_update(
273 &self,
274 status: &str,
275 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
276 self.emit_event(AgentEvent::StatusUpdate {
277 status: status.to_string(),
278 metadata: HashMap::new(),
279 })
280 .await
281 }
282
283 async fn request_confirmation(
286 &self,
287 _request: &ConfirmationRequest,
288 ) -> Result<ConfirmationDecision, Box<dyn std::error::Error + Send + Sync>> {
289 Ok(ConfirmationDecision {
290 approved: false,
291 note: Some("No confirmation handler available; default deny".to_string()),
292 })
293 }
294
295 fn supports_realtime_updates(&self) -> bool {
297 false
298 }
299
300 async fn flush(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
302 Ok(())
303 }
304}
305
306pub trait ToolExecutionInfoBuilder {
308 fn create_tool_execution_info(
309 tool_call: &ToolCall,
310 status: ToolExecutionStatus,
311 result: Option<&ToolResult>,
312 ) -> ToolExecutionInfo;
313}
314
315impl ToolExecutionInfoBuilder for ToolExecutionInfo {
316 fn create_tool_execution_info(
317 tool_call: &ToolCall,
318 status: ToolExecutionStatus,
319 result: Option<&ToolResult>,
320 ) -> ToolExecutionInfo {
321 let parameters = if let serde_json::Value::Object(map) = &tool_call.parameters {
322 map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
323 } else {
324 let mut map = HashMap::new();
325 map.insert("raw_parameters".to_string(), tool_call.parameters.clone());
326 map
327 };
328
329 ToolExecutionInfo {
330 execution_id: tool_call.id.clone(),
331 tool_name: tool_call.name.clone(),
332 parameters,
333 status,
334 result: result.cloned(),
335 timestamp: chrono::Utc::now(),
336 metadata: HashMap::new(),
337 }
338 }
339}