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