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