1use crate::{AgentServerCommand, AgentServerSettings};
2use acp_thread::AgentConnection;
3use anyhow::Result;
4use gpui::{App, Entity, SharedString, Task};
5use language_models::provider::anthropic::AnthropicLanguageModelProvider;
6use project::Project;
7use std::{path::Path, rc::Rc};
8use ui::IconName;
9
10/// A generic agent server implementation for custom user-defined agents
11pub struct CustomAgentServer {
12 name: SharedString,
13 command: AgentServerCommand,
14}
15
16impl CustomAgentServer {
17 pub fn new(name: SharedString, settings: &AgentServerSettings) -> Self {
18 Self {
19 name,
20 command: settings.command.clone(),
21 }
22 }
23}
24
25impl crate::AgentServer for CustomAgentServer {
26 fn telemetry_id(&self) -> &'static str {
27 "custom"
28 }
29
30 fn name(&self) -> SharedString {
31 self.name.clone()
32 }
33
34 fn logo(&self) -> IconName {
35 IconName::Terminal
36 }
37
38 fn empty_state_headline(&self) -> SharedString {
39 "No conversations yet".into()
40 }
41
42 fn empty_state_message(&self) -> SharedString {
43 format!("Start a conversation with {}", self.name).into()
44 }
45
46 fn connect(
47 &self,
48 root_dir: &Path,
49 _project: &Entity<Project>,
50 cx: &mut App,
51 ) -> Task<Result<Rc<dyn AgentConnection>>> {
52 let server_name = self.name();
53 let mut command = self.command.clone();
54 let root_dir = root_dir.to_path_buf();
55
56 // TODO: Remove this once we have Claude properly
57 cx.spawn(async move |mut cx| {
58 if let Some(api_key) = cx
59 .update(AnthropicLanguageModelProvider::api_key)?
60 .await
61 .ok()
62 {
63 command
64 .env
65 .get_or_insert_default()
66 .insert("ANTHROPIC_API_KEY".to_owned(), api_key.key);
67 }
68
69 crate::acp::connect(server_name, command, &root_dir, &mut cx).await
70 })
71 }
72
73 fn install_command(&self) -> Option<&'static str> {
74 None
75 }
76
77 fn into_any(self: Rc<Self>) -> Rc<dyn std::any::Any> {
78 self
79 }
80}