1mod acp;
2mod active_thread;
3mod agent_configuration;
4mod agent_diff;
5mod agent_model_selector;
6mod agent_panel;
7mod buffer_codegen;
8mod burn_mode_tooltip;
9mod context_picker;
10mod context_server_configuration;
11mod context_strip;
12mod debug;
13mod inline_assistant;
14mod inline_prompt_editor;
15mod language_model_selector;
16mod message_editor;
17mod profile_selector;
18mod slash_command;
19mod slash_command_picker;
20mod slash_command_settings;
21mod terminal_codegen;
22mod terminal_inline_assistant;
23mod text_thread_editor;
24mod thread_history;
25mod tool_compatibility;
26mod ui;
27
28use std::sync::Arc;
29
30use agent::{Thread, ThreadId};
31use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
32use assistant_slash_command::SlashCommandRegistry;
33use client::Client;
34use feature_flags::FeatureFlagAppExt as _;
35use fs::Fs;
36use gpui::{Action, App, Entity, actions};
37use language::LanguageRegistry;
38use language_model::{
39 ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry,
40};
41use prompt_store::PromptBuilder;
42use schemars::JsonSchema;
43use serde::Deserialize;
44use settings::{Settings as _, SettingsStore};
45
46pub use crate::active_thread::ActiveThread;
47use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
48pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
49pub use crate::inline_assistant::InlineAssistant;
50use crate::slash_command_settings::SlashCommandSettings;
51pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
52pub use text_thread_editor::{AgentPanelDelegate, TextThreadEditor};
53pub use ui::preview::{all_agent_previews, get_agent_preview};
54
55actions!(
56 agent,
57 [
58 /// Creates a new text-based conversation thread.
59 NewTextThread,
60 /// Creates a new external agent conversation thread.
61 NewAcpThread,
62 /// Toggles the context picker interface for adding files, symbols, or other context.
63 ToggleContextPicker,
64 /// Toggles the navigation menu for switching between threads and views.
65 ToggleNavigationMenu,
66 /// Toggles the options menu for agent settings and preferences.
67 ToggleOptionsMenu,
68 /// Deletes the recently opened thread from history.
69 DeleteRecentlyOpenThread,
70 /// Toggles the profile selector for switching between agent profiles.
71 ToggleProfileSelector,
72 /// Removes all added context from the current conversation.
73 RemoveAllContext,
74 /// Expands the message editor to full size.
75 ExpandMessageEditor,
76 /// Opens the conversation history view.
77 OpenHistory,
78 /// Adds a context server to the configuration.
79 AddContextServer,
80 /// Removes the currently selected thread.
81 RemoveSelectedThread,
82 /// Starts a chat conversation with follow-up enabled.
83 ChatWithFollow,
84 /// Cycles to the next inline assist suggestion.
85 CycleNextInlineAssist,
86 /// Cycles to the previous inline assist suggestion.
87 CyclePreviousInlineAssist,
88 /// Moves focus up in the interface.
89 FocusUp,
90 /// Moves focus down in the interface.
91 FocusDown,
92 /// Moves focus left in the interface.
93 FocusLeft,
94 /// Moves focus right in the interface.
95 FocusRight,
96 /// Removes the currently focused context item.
97 RemoveFocusedContext,
98 /// Accepts the suggested context item.
99 AcceptSuggestedContext,
100 /// Opens the active thread as a markdown file.
101 OpenActiveThreadAsMarkdown,
102 /// Opens the agent diff view to review changes.
103 OpenAgentDiff,
104 /// Keeps the current suggestion or change.
105 Keep,
106 /// Rejects the current suggestion or change.
107 Reject,
108 /// Rejects all suggestions or changes.
109 RejectAll,
110 /// Keeps all suggestions or changes.
111 KeepAll,
112 /// Follows the agent's suggestions.
113 Follow,
114 /// Resets the trial upsell notification.
115 ResetTrialUpsell,
116 /// Resets the trial end upsell notification.
117 ResetTrialEndUpsell,
118 /// Continues the current thread.
119 ContinueThread,
120 /// Continues the thread with burn mode enabled.
121 ContinueWithBurnMode,
122 /// Toggles burn mode for faster responses.
123 ToggleBurnMode,
124 ]
125);
126
127/// Creates a new conversation thread, optionally based on an existing thread.
128#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema, Action)]
129#[action(namespace = agent)]
130#[serde(deny_unknown_fields)]
131pub struct NewThread {
132 #[serde(default)]
133 from_thread_id: Option<ThreadId>,
134}
135
136/// Opens the profile management interface for configuring agent tools and settings.
137#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema, Action)]
138#[action(namespace = agent)]
139#[serde(deny_unknown_fields)]
140pub struct ManageProfiles {
141 #[serde(default)]
142 pub customize_tools: Option<AgentProfileId>,
143}
144
145impl ManageProfiles {
146 pub fn customize_tools(profile_id: AgentProfileId) -> Self {
147 Self {
148 customize_tools: Some(profile_id),
149 }
150 }
151}
152
153#[derive(Clone)]
154pub(crate) enum ModelUsageContext {
155 Thread(Entity<Thread>),
156 InlineAssistant,
157}
158
159impl ModelUsageContext {
160 pub fn configured_model(&self, cx: &App) -> Option<ConfiguredModel> {
161 match self {
162 Self::Thread(thread) => thread.read(cx).configured_model(),
163 Self::InlineAssistant => {
164 LanguageModelRegistry::read_global(cx).inline_assistant_model()
165 }
166 }
167 }
168
169 pub fn language_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> {
170 self.configured_model(cx)
171 .map(|configured_model| configured_model.model)
172 }
173}
174
175/// Initializes the `agent` crate.
176pub fn init(
177 fs: Arc<dyn Fs>,
178 client: Arc<Client>,
179 prompt_builder: Arc<PromptBuilder>,
180 language_registry: Arc<LanguageRegistry>,
181 is_eval: bool,
182 cx: &mut App,
183) {
184 AgentSettings::register(cx);
185 SlashCommandSettings::register(cx);
186
187 assistant_context::init(client.clone(), cx);
188 rules_library::init(cx);
189 if !is_eval {
190 // Initializing the language model from the user settings messes with the eval, so we only initialize them when
191 // we're not running inside of the eval.
192 init_language_model_settings(cx);
193 }
194 assistant_slash_command::init(cx);
195 agent::init(cx);
196 agent_panel::init(cx);
197 context_server_configuration::init(language_registry.clone(), fs.clone(), cx);
198 TextThreadEditor::init(cx);
199
200 register_slash_commands(cx);
201 inline_assistant::init(
202 fs.clone(),
203 prompt_builder.clone(),
204 client.telemetry().clone(),
205 cx,
206 );
207 terminal_inline_assistant::init(
208 fs.clone(),
209 prompt_builder.clone(),
210 client.telemetry().clone(),
211 cx,
212 );
213 indexed_docs::init(cx);
214 cx.observe_new(move |workspace, window, cx| {
215 ConfigureContextServerModal::register(workspace, language_registry.clone(), window, cx)
216 })
217 .detach();
218 cx.observe_new(ManageProfilesModal::register).detach();
219}
220
221fn init_language_model_settings(cx: &mut App) {
222 update_active_language_model_from_settings(cx);
223
224 cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
225 .detach();
226 cx.subscribe(
227 &LanguageModelRegistry::global(cx),
228 |_, event: &language_model::Event, cx| match event {
229 language_model::Event::ProviderStateChanged
230 | language_model::Event::AddedProvider(_)
231 | language_model::Event::RemovedProvider(_) => {
232 update_active_language_model_from_settings(cx);
233 }
234 _ => {}
235 },
236 )
237 .detach();
238}
239
240fn update_active_language_model_from_settings(cx: &mut App) {
241 let settings = AgentSettings::get_global(cx);
242
243 fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel {
244 language_model::SelectedModel {
245 provider: LanguageModelProviderId::from(selection.provider.0.clone()),
246 model: LanguageModelId::from(selection.model.clone()),
247 }
248 }
249
250 let default = settings.default_model.as_ref().map(to_selected_model);
251 let inline_assistant = settings
252 .inline_assistant_model
253 .as_ref()
254 .map(to_selected_model);
255 let commit_message = settings
256 .commit_message_model
257 .as_ref()
258 .map(to_selected_model);
259 let thread_summary = settings
260 .thread_summary_model
261 .as_ref()
262 .map(to_selected_model);
263 let inline_alternatives = settings
264 .inline_alternatives
265 .iter()
266 .map(to_selected_model)
267 .collect::<Vec<_>>();
268
269 LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
270 registry.select_default_model(default.as_ref(), cx);
271 registry.select_inline_assistant_model(inline_assistant.as_ref(), cx);
272 registry.select_commit_message_model(commit_message.as_ref(), cx);
273 registry.select_thread_summary_model(thread_summary.as_ref(), cx);
274 registry.select_inline_alternative_models(inline_alternatives, cx);
275 });
276}
277
278fn register_slash_commands(cx: &mut App) {
279 let slash_command_registry = SlashCommandRegistry::global(cx);
280
281 slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
282 slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
283 slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
284 slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
285 slash_command_registry
286 .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
287 slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
288 slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
289 slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
290 slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
291 slash_command_registry
292 .register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
293 slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
294
295 cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
296 let slash_command_registry = slash_command_registry.clone();
297 move |is_enabled, _cx| {
298 if is_enabled {
299 slash_command_registry.register_command(
300 assistant_slash_commands::StreamingExampleSlashCommand,
301 false,
302 );
303 }
304 }
305 })
306 .detach();
307
308 update_slash_commands_from_settings(cx);
309 cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
310 .detach();
311}
312
313fn update_slash_commands_from_settings(cx: &mut App) {
314 let slash_command_registry = SlashCommandRegistry::global(cx);
315 let settings = SlashCommandSettings::get_global(cx);
316
317 if settings.docs.enabled {
318 slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
319 } else {
320 slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
321 }
322
323 if settings.cargo_workspace.enabled {
324 slash_command_registry
325 .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
326 } else {
327 slash_command_registry
328 .unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
329 }
330}