1use std::{any::Any, path::Path, rc::Rc, sync::Arc};
2
3use agent_servers::AgentServer;
4use anyhow::Result;
5use fs::Fs;
6use gpui::{App, Entity, SharedString, Task};
7use project::Project;
8use prompt_store::PromptStore;
9
10use crate::{HistoryStore, NativeAgent, NativeAgentConnection, templates::Templates};
11
12#[derive(Clone)]
13pub struct NativeAgentServer {
14 fs: Arc<dyn Fs>,
15 history: Entity<HistoryStore>,
16}
17
18impl NativeAgentServer {
19 pub fn new(fs: Arc<dyn Fs>, history: Entity<HistoryStore>) -> Self {
20 Self { fs, history }
21 }
22}
23
24impl AgentServer for NativeAgentServer {
25 fn telemetry_id(&self) -> &'static str {
26 "zed"
27 }
28
29 fn name(&self) -> SharedString {
30 "Zed Agent".into()
31 }
32
33 fn empty_state_headline(&self) -> SharedString {
34 self.name()
35 }
36
37 fn empty_state_message(&self) -> SharedString {
38 "".into()
39 }
40
41 fn logo(&self) -> ui::IconName {
42 ui::IconName::ZedAgent
43 }
44
45 fn install_command(&self) -> Option<&'static str> {
46 None
47 }
48
49 fn connect(
50 &self,
51 _root_dir: &Path,
52 project: &Entity<Project>,
53 cx: &mut App,
54 ) -> Task<Result<Rc<dyn acp_thread::AgentConnection>>> {
55 log::debug!(
56 "NativeAgentServer::connect called for path: {:?}",
57 _root_dir
58 );
59 let project = project.clone();
60 let fs = self.fs.clone();
61 let history = self.history.clone();
62 let prompt_store = PromptStore::global(cx);
63 cx.spawn(async move |cx| {
64 log::debug!("Creating templates for native agent");
65 let templates = Templates::new();
66 let prompt_store = prompt_store.await?;
67
68 log::debug!("Creating native agent entity");
69 let agent =
70 NativeAgent::new(project, history, templates, Some(prompt_store), fs, cx).await?;
71
72 // Create the connection wrapper
73 let connection = NativeAgentConnection(agent);
74 log::debug!("NativeAgentServer connection established successfully");
75
76 Ok(Rc::new(connection) as Rc<dyn acp_thread::AgentConnection>)
77 })
78 }
79
80 fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
81 self
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 use assistant_context::ContextStore;
90 use gpui::AppContext;
91
92 agent_servers::e2e_tests::common_e2e_tests!(
93 async |fs, project, cx| {
94 let auth = cx.update(|cx| {
95 prompt_store::init(cx);
96 terminal::init(cx);
97
98 let registry = language_model::LanguageModelRegistry::read_global(cx);
99 let auth = registry
100 .provider(&language_model::ANTHROPIC_PROVIDER_ID)
101 .unwrap()
102 .authenticate(cx);
103
104 cx.spawn(async move |_| auth.await)
105 });
106
107 auth.await.unwrap();
108
109 cx.update(|cx| {
110 let registry = language_model::LanguageModelRegistry::global(cx);
111
112 registry.update(cx, |registry, cx| {
113 registry.select_default_model(
114 Some(&language_model::SelectedModel {
115 provider: language_model::ANTHROPIC_PROVIDER_ID,
116 model: language_model::LanguageModelId("claude-sonnet-4-latest".into()),
117 }),
118 cx,
119 );
120 });
121 });
122
123 let history = cx.update(|cx| {
124 let context_store = cx.new(move |cx| ContextStore::fake(project.clone(), cx));
125 cx.new(move |cx| HistoryStore::new(context_store, cx))
126 });
127
128 NativeAgentServer::new(fs.clone(), history)
129 },
130 allow_option_id = "allow"
131 );
132}