claude_code_acp/agent/
core.rs

1//! Core ACP Agent structure
2//!
3//! The ClaudeAcpAgent holds shared state and configuration for handling
4//! ACP protocol requests.
5
6use std::sync::Arc;
7
8use crate::session::{PromptManager, SessionManager};
9use crate::types::AgentConfig;
10
11/// Claude ACP Agent
12///
13/// The main agent struct that holds configuration and session state.
14/// This is shared across all request handlers.
15///
16/// Configuration is loaded from (in priority order):
17/// 1. Environment variables (e.g., ANTHROPIC_MODEL, ANTHROPIC_BASE_URL)
18/// 2. Settings files (~/.claude/settings.json, .claude/settings.json, etc.)
19/// 3. Defaults
20#[derive(Debug)]
21pub struct ClaudeAcpAgent {
22    /// Agent configuration from environment
23    config: AgentConfig,
24    /// Session manager for tracking active sessions
25    sessions: Arc<SessionManager>,
26    /// Prompt manager for tracking and cancelling active prompts
27    prompt_manager: Arc<PromptManager>,
28}
29
30impl ClaudeAcpAgent {
31    /// Create a new agent with configuration from environment and settings files
32    ///
33    /// Configuration is loaded from:
34    /// 1. Environment variables (highest priority)
35    /// 2. Settings files (~/.claude/settings.json, .claude/settings.json, etc.)
36    /// 3. Defaults
37    pub fn new() -> Self {
38        let project_dir = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
39
40        let config = AgentConfig::from_settings_or_env(&project_dir);
41
42        tracing::info!(
43            model = ?config.model,
44            base_url = ?config.base_url,
45            api_key = ?config.masked_api_key(),
46            "Agent initialized with configuration"
47        );
48
49        Self {
50            config,
51            sessions: Arc::new(SessionManager::new()),
52            prompt_manager: Arc::new(PromptManager::new()),
53        }
54    }
55
56    /// Create with custom configuration
57    pub fn with_config(config: AgentConfig) -> Self {
58        Self {
59            config,
60            sessions: Arc::new(SessionManager::new()),
61            prompt_manager: Arc::new(PromptManager::new()),
62        }
63    }
64
65    /// Get the agent configuration
66    pub fn config(&self) -> &AgentConfig {
67        &self.config
68    }
69
70    /// Get the session manager
71    pub fn sessions(&self) -> &Arc<SessionManager> {
72        &self.sessions
73    }
74
75    /// Get the prompt manager
76    pub fn prompt_manager(&self) -> &Arc<PromptManager> {
77        &self.prompt_manager
78    }
79
80    /// Get agent name for logging
81    pub fn name(&self) -> &'static str {
82        "claude-code-acp-rs"
83    }
84
85    /// Get agent version
86    pub fn version(&self) -> &'static str {
87        env!("CARGO_PKG_VERSION")
88    }
89}
90
91impl Default for ClaudeAcpAgent {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_agent_new() {
103        let agent = ClaudeAcpAgent::new();
104        assert_eq!(agent.name(), "claude-code-acp-rs");
105        assert_eq!(agent.sessions().session_count(), 0);
106    }
107
108    #[test]
109    fn test_agent_with_config() {
110        let config = AgentConfig {
111            base_url: Some("https://api.example.com".to_string()),
112            api_key: Some("test-key".to_string()),
113            model: Some("claude-3-opus".to_string()),
114            small_fast_model: None,
115            max_thinking_tokens: Some(4096),
116        };
117
118        let agent = ClaudeAcpAgent::with_config(config);
119        assert_eq!(
120            agent.config().base_url,
121            Some("https://api.example.com".to_string())
122        );
123        assert_eq!(agent.config().max_thinking_tokens, Some(4096));
124    }
125}