1use crate::config::models::ModelId;
4use crate::config::types::*;
5use crate::core::agent::compaction::CompactionEngine;
6use crate::core::conversation_summarizer::ConversationSummarizer;
7use crate::core::decision_tracker::DecisionTracker;
8use crate::core::error_recovery::{ErrorRecoveryManager, ErrorType};
9use crate::llm::{AnyClient, make_client};
10use crate::tools::tree_sitter::{CodeAnalysis, TreeSitterAnalyzer};
11use crate::tools::{ToolRegistry, build_function_declarations};
12use anyhow::{Result, anyhow};
13use console::style;
14use std::sync::Arc;
15
16pub struct Agent {
18 config: AgentConfig,
19 client: AnyClient,
20 tool_registry: Arc<ToolRegistry>,
21 decision_tracker: DecisionTracker,
22 error_recovery: ErrorRecoveryManager,
23 summarizer: ConversationSummarizer,
24 tree_sitter_analyzer: TreeSitterAnalyzer,
25 compaction_engine: Arc<CompactionEngine>,
26 session_info: SessionInfo,
27 start_time: std::time::Instant,
28}
29
30impl Agent {
31 pub fn new(config: AgentConfig) -> Result<Self> {
33 let model_id = config
34 .model
35 .parse::<ModelId>()
36 .map_err(|_| anyhow!("Invalid model: {}", config.model))?;
37 let client = make_client(config.api_key.clone(), model_id);
38 let tool_registry = Arc::new(ToolRegistry::new(config.workspace.clone()));
39 let decision_tracker = DecisionTracker::new();
40 let error_recovery = ErrorRecoveryManager::new();
41 let summarizer = ConversationSummarizer::new();
42 let tree_sitter_analyzer = match TreeSitterAnalyzer::new() {
43 Ok(analyzer) => analyzer,
44 Err(e) => {
45 eprintln!("Warning: Failed to initialize tree-sitter analyzer: {}", e);
46 eprintln!("Continuing without tree-sitter analysis capabilities");
47 return Err(anyhow!("Tree-sitter analyzer initialization failed: {}", e));
50 }
51 };
52
53 let session_id = format!(
54 "session_{}",
55 std::time::SystemTime::now()
56 .duration_since(std::time::UNIX_EPOCH)
57 .unwrap()
58 .as_secs()
59 );
60
61 let session_info = SessionInfo {
62 session_id,
63 start_time: std::time::SystemTime::now()
64 .duration_since(std::time::UNIX_EPOCH)
65 .unwrap()
66 .as_secs(),
67 total_turns: 0,
68 total_decisions: 0,
69 error_count: 0,
70 };
71
72 Ok(Self {
73 config,
74 client,
75 tool_registry,
76 decision_tracker,
77 error_recovery,
78 summarizer,
79 tree_sitter_analyzer,
80 compaction_engine: Arc::new(CompactionEngine::new()),
81 session_info,
82 start_time: std::time::Instant::now(),
83 })
84 }
85
86 pub async fn initialize(&mut self) -> Result<()> {
88 let tool_names = build_function_declarations()
90 .iter()
91 .map(|fd| fd.name.clone())
92 .collect::<Vec<_>>();
93 self.decision_tracker.update_available_tools(tool_names);
94
95 self.session_info.start_time = std::time::SystemTime::now()
97 .duration_since(std::time::UNIX_EPOCH)
98 .unwrap()
99 .as_secs();
100
101 if self.config.verbose {
102 println!("{} {}", style("[INIT]").cyan().bold(), "Agent initialized");
103 println!(" {} Model: {}", style("").dim(), self.config.model);
104 println!(
105 " {} Workspace: {}",
106 style("").dim(),
107 self.config.workspace.display()
108 );
109 println!(
110 " {} Tools loaded: {}",
111 style("").dim(),
112 build_function_declarations().len()
113 );
114 println!(
115 " {} Session ID: {}",
116 style("(ID)").dim(),
117 self.session_info.session_id
118 );
119 println!();
120 }
121
122 Ok(())
123 }
124
125 pub fn config(&self) -> &AgentConfig {
127 &self.config
128 }
129
130 pub fn session_info(&self) -> &SessionInfo {
132 &self.session_info
133 }
134
135 pub fn performance_metrics(&self) -> PerformanceMetrics {
137 let duration = self.start_time.elapsed();
138
139 PerformanceMetrics {
140 session_duration_seconds: duration.as_secs(),
141 total_api_calls: self.session_info.total_turns,
142 total_tokens_used: None, average_response_time_ms: if self.session_info.total_turns > 0 {
144 duration.as_millis() as f64 / self.session_info.total_turns as f64
145 } else {
146 0.0
147 },
148 tool_execution_count: self.session_info.total_decisions,
149 error_count: self.session_info.error_count,
150 recovery_success_rate: self.calculate_recovery_rate(),
151 }
152 }
153
154 pub fn decision_tracker(&self) -> &DecisionTracker {
156 &self.decision_tracker
157 }
158
159 pub fn decision_tracker_mut(&mut self) -> &mut DecisionTracker {
161 &mut self.decision_tracker
162 }
163
164 pub fn error_recovery(&self) -> &ErrorRecoveryManager {
166 &self.error_recovery
167 }
168
169 pub fn error_recovery_mut(&mut self) -> &mut ErrorRecoveryManager {
171 &mut self.error_recovery
172 }
173
174 pub fn summarizer(&self) -> &ConversationSummarizer {
176 &self.summarizer
177 }
178
179 pub fn tool_registry(&self) -> Arc<ToolRegistry> {
181 Arc::clone(&self.tool_registry)
182 }
183
184 pub fn tool_registry_mut(&mut self) -> &mut ToolRegistry {
186 Arc::get_mut(&mut self.tool_registry)
187 .expect("ToolRegistry should not have other references")
188 }
189
190 pub fn llm(&self) -> &AnyClient {
192 &self.client
193 }
194
195 pub fn tree_sitter_analyzer(&self) -> &TreeSitterAnalyzer {
197 &self.tree_sitter_analyzer
198 }
199
200 pub fn tree_sitter_analyzer_mut(&mut self) -> &mut TreeSitterAnalyzer {
202 &mut self.tree_sitter_analyzer
203 }
204
205 pub fn compaction_engine(&self) -> Arc<CompactionEngine> {
207 Arc::clone(&self.compaction_engine)
208 }
209
210 pub async fn make_intelligent_compaction_decision(
212 &self,
213 ) -> Result<crate::core::agent::intelligence::CompactionDecision> {
214 let stats = self.compaction_engine.get_statistics().await?;
215 let should_compact = self.compaction_engine.should_compact().await?;
216 let strategy = if should_compact {
217 crate::core::agent::intelligence::CompactionStrategy::Aggressive
218 } else {
219 crate::core::agent::intelligence::CompactionStrategy::Conservative
220 };
221 let reasoning = if should_compact {
222 format!("{} messages exceed thresholds", stats.total_messages)
223 } else {
224 "within configured thresholds".to_string()
225 };
226
227 Ok(crate::core::agent::intelligence::CompactionDecision {
228 should_compact,
229 strategy,
230 reasoning,
231 estimated_benefit: stats.total_memory_usage,
232 })
233 }
234
235 pub async fn should_compact(&self) -> Result<bool> {
237 self.compaction_engine.should_compact().await
238 }
239
240 pub async fn compact_messages(&self) -> Result<crate::core::agent::types::CompactionResult> {
242 self.compaction_engine
243 .compact_messages_intelligently()
244 .await
245 }
246
247 pub async fn compact_context(
249 &self,
250 context_key: &str,
251 context_data: &mut std::collections::HashMap<String, serde_json::Value>,
252 ) -> Result<crate::core::agent::types::CompactionResult> {
253 self.compaction_engine
254 .compact_context(context_key, context_data)
255 .await
256 }
257
258 pub async fn get_compaction_stats(
260 &self,
261 ) -> Result<crate::core::agent::types::CompactionStatistics> {
262 self.compaction_engine.get_statistics().await
263 }
264
265 pub fn analyze_file_with_tree_sitter(
267 &mut self,
268 file_path: &std::path::Path,
269 source_code: &str,
270 ) -> Result<CodeAnalysis> {
271 let language = self
273 .tree_sitter_analyzer
274 .detect_language_from_path(file_path)
275 .map_err(|e| {
276 anyhow!(
277 "Failed to detect language for {}: {}",
278 file_path.display(),
279 e
280 )
281 })?;
282
283 let syntax_tree = self
285 .tree_sitter_analyzer
286 .parse(source_code, language.clone())?;
287
288 let symbols = self
290 .tree_sitter_analyzer
291 .extract_symbols(&syntax_tree, source_code, language.clone())
292 .unwrap_or_default();
293
294 let dependencies = self
296 .tree_sitter_analyzer
297 .extract_dependencies(&syntax_tree, language.clone())
298 .unwrap_or_default();
299
300 let metrics = self
302 .tree_sitter_analyzer
303 .calculate_metrics(&syntax_tree, source_code)
304 .unwrap_or_default();
305
306 Ok(CodeAnalysis {
307 file_path: file_path.to_string_lossy().to_string(),
308 language,
309 symbols,
310 dependencies,
311 metrics,
312 issues: Vec::new(),
313 complexity: crate::tools::tree_sitter::analysis::ComplexityMetrics::default(),
314 structure: crate::tools::tree_sitter::analysis::CodeStructure::default(),
315 })
316 }
317
318 pub fn update_session_stats(&mut self, turns: usize, decisions: usize, errors: usize) {
320 self.session_info.total_turns = turns;
321 self.session_info.total_decisions = decisions;
322 self.session_info.error_count = errors;
323 }
324
325 pub fn should_compress_context(&self, context_size: usize) -> bool {
327 self.error_recovery.should_compress_context(context_size)
328 }
329
330 pub fn generate_context_plan(
332 &self,
333 context_size: usize,
334 ) -> crate::core::error_recovery::ContextPreservationPlan {
335 self.error_recovery
336 .generate_context_preservation_plan(context_size, self.session_info.error_count)
337 }
338
339 pub fn detect_error_pattern(&self, error_type: &ErrorType, time_window_seconds: u64) -> bool {
341 self.error_recovery
342 .detect_error_pattern(error_type, time_window_seconds)
343 }
344
345 fn calculate_recovery_rate(&self) -> f64 {
347 let stats = self.error_recovery.get_error_statistics();
348 if stats.total_errors > 0 {
349 stats.resolved_errors as f64 / stats.total_errors as f64
350 } else {
351 1.0 }
353 }
354
355 pub fn show_transparency_report(&self, detailed: bool) {
357 let report = self.decision_tracker.generate_transparency_report();
358 let error_stats = self.error_recovery.get_error_statistics();
359
360 if detailed && self.config.verbose {
361 println!(
362 "{} {}",
363 style("[TRANSPARENCY]").magenta().bold(),
364 "Session Transparency Summary:"
365 );
366 println!(
367 " {} total decisions made",
368 style(report.total_decisions).cyan()
369 );
370 println!(
371 " {} successful ({}% success rate)",
372 style(report.successful_decisions).green(),
373 if report.total_decisions > 0 {
374 (report.successful_decisions * 100) / report.total_decisions
375 } else {
376 0
377 }
378 );
379 println!(
380 " {} failed decisions",
381 style(report.failed_decisions).red()
382 );
383 println!(" {} tool calls executed", style(report.tool_calls).blue());
384 println!(
385 " Session duration: {} seconds",
386 style(report.session_duration).yellow()
387 );
388 if let Some(avg_confidence) = report.avg_confidence {
389 println!(
390 " {:.1}% average decision confidence",
391 avg_confidence * 100.0
392 );
393 }
394
395 println!(
397 "\n{} {}",
398 style("[ERROR RECOVERY]").red().bold(),
399 "Error Statistics:"
400 );
401 println!(
402 " {} total errors occurred",
403 style(error_stats.total_errors).red()
404 );
405 println!(
406 " {} errors resolved ({}% recovery rate)",
407 style(error_stats.resolved_errors).green(),
408 if error_stats.total_errors > 0 {
409 (error_stats.resolved_errors * 100) / error_stats.total_errors
410 } else {
411 0
412 }
413 );
414 println!(
415 " {:.1} average recovery attempts per error",
416 style(error_stats.avg_recovery_attempts).yellow()
417 );
418
419 let summaries = self.summarizer.get_summaries();
421 if !summaries.is_empty() {
422 println!(
423 "\n{} {}",
424 style("[CONVERSATION SUMMARY]").green().bold(),
425 "Statistics:"
426 );
427 println!(" {} summaries generated", style(summaries.len()).cyan());
428 if let Some(latest) = self.summarizer.get_latest_summary() {
429 println!(
430 " {} Latest summary: {} turns, {:.1}% compression",
431 style("(SUMMARY)").dim(),
432 latest.total_turns,
433 latest.compression_ratio * 100.0
434 );
435 }
436 }
437 } else {
438 println!("{}", style(format!(" ↳ Session complete: {} decisions, {} successful ({}% success rate), {} errors",
440 report.total_decisions, report.successful_decisions,
441 if report.total_decisions > 0 { (report.successful_decisions * 100) / report.total_decisions } else { 0 },
442 error_stats.total_errors)).dim());
443 }
444 }
445
446 pub async fn shutdown(&mut self) -> Result<()> {
448 self.show_transparency_report(true);
450
451 if self.config.verbose {
452 println!(
453 "{} {}",
454 style("[SHUTDOWN]").cyan().bold(),
455 "Agent shutdown complete"
456 );
457 }
458
459 Ok(())
460 }
461}
462
463pub struct AgentBuilder {
465 config: AgentConfig,
466}
467
468impl AgentBuilder {
469 pub fn new() -> Self {
470 Self {
471 config: AgentConfig {
472 model: ModelId::default().as_str().to_string(),
473 api_key: String::new(),
474 workspace: std::env::current_dir()
475 .unwrap_or_else(|_| std::path::PathBuf::from(".")),
476 verbose: false,
477 theme: crate::config::constants::defaults::DEFAULT_THEME.to_string(),
478 },
479 }
480 }
481
482 pub fn with_model<S: Into<String>>(mut self, model: S) -> Self {
483 self.config.model = model.into();
484 self
485 }
486
487 pub fn with_api_key<S: Into<String>>(mut self, api_key: S) -> Self {
488 self.config.api_key = api_key.into();
489 self
490 }
491
492 pub fn with_workspace<P: Into<std::path::PathBuf>>(mut self, workspace: P) -> Self {
493 self.config.workspace = workspace.into();
494 self
495 }
496
497 pub fn with_verbose(mut self, verbose: bool) -> Self {
498 self.config.verbose = verbose;
499 self
500 }
501
502 pub fn build(self) -> Result<Agent> {
503 Agent::new(self.config)
504 }
505}
506
507impl Default for AgentBuilder {
508 fn default() -> Self {
509 Self::new()
510 }
511}