Extract an agent_ui crate from agent (#33284)

This PR moves the UI-dependent logic in the `agent` crate into its own
crate, `agent_ui`. The remaining `agent` crate no longer depends on
`editor`, `picker`, `ui`, `workspace`, etc.

This has compile time benefits, but the main motivation is to isolate
our core agentic logic, so that we can make agents more
pluggable/configurable.

Release Notes:

- N/A
This commit is contained in:
Max Brunsfeld 2025-06-23 18:00:28 -07:00 committed by GitHub
parent 371b7355d3
commit 2283ec5de2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 865 additions and 752 deletions

View file

@ -21,6 +21,7 @@ path = "src/main.rs"
[dependencies]
activity_indicator.workspace = true
agent.workspace = true
agent_ui.workspace = true
agent_settings.workspace = true
anyhow.workspace = true
askpass.workspace = true

View file

@ -531,7 +531,7 @@ pub fn main() {
cx,
);
let prompt_builder = PromptBuilder::load(app_state.fs.clone(), stdout_is_a_pty(), cx);
agent::init(
agent_ui::init(
app_state.fs.clone(),
app_state.client.clone(),
prompt_builder.clone(),

View file

@ -9,7 +9,7 @@ mod quick_action_bar;
#[cfg(target_os = "windows")]
pub(crate) mod windows_only_instance;
use agent::AgentDiffToolbar;
use agent_ui::AgentDiffToolbar;
use anyhow::Context as _;
pub use app_menus::*;
use assets::Assets;
@ -515,7 +515,7 @@ fn initialize_panels(
let is_assistant2_enabled = !cfg!(test);
let agent_panel = if is_assistant2_enabled {
let agent_panel =
agent::AgentPanel::load(workspace_handle.clone(), prompt_builder, cx.clone())
agent_ui::AgentPanel::load(workspace_handle.clone(), prompt_builder, cx.clone())
.await?;
Some(agent_panel)
@ -536,13 +536,13 @@ fn initialize_panels(
// Once we ship `assistant2` we can push this back down into `agent::agent_panel::init`.
if is_assistant2_enabled {
<dyn AgentPanelDelegate>::set_global(
Arc::new(agent::ConcreteAssistantPanelDelegate),
Arc::new(agent_ui::ConcreteAssistantPanelDelegate),
cx,
);
workspace
.register_action(agent::AgentPanel::toggle_focus)
.register_action(agent::InlineAssistant::inline_assist);
.register_action(agent_ui::AgentPanel::toggle_focus)
.register_action(agent_ui::InlineAssistant::inline_assist);
}
})?;
@ -4320,7 +4320,7 @@ mod tests {
web_search::init(cx);
web_search_providers::init(app_state.client.clone(), cx);
let prompt_builder = PromptBuilder::load(app_state.fs.clone(), false, cx);
agent::init(
agent_ui::init(
app_state.fs.clone(),
app_state.client.clone(),
prompt_builder.clone(),

View file

@ -5,20 +5,14 @@
mod persistence;
mod preview_support;
use std::ops::Range;
use std::sync::Arc;
use std::iter::Iterator;
use agent::{ActiveThread, TextThreadStore, ThreadStore};
use agent::{TextThreadStore, ThreadStore};
use agent_ui::ActiveThread;
use client::UserStore;
use collections::HashMap;
use component::{ComponentId, ComponentMetadata, ComponentStatus, components};
use gpui::{
App, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity, Window, list, prelude::*,
};
use collections::HashMap;
use gpui::{ListState, ScrollHandle, ScrollStrategy, UniformListScrollHandle};
use languages::LanguageRegistry;
use notifications::status_toast::{StatusToast, ToastIcon};
@ -27,11 +21,14 @@ use preview_support::active_thread::{
load_preview_text_thread_store, load_preview_thread_store, static_active_thread,
};
use project::Project;
use std::{iter::Iterator, ops::Range, sync::Arc};
use ui::{ButtonLike, Divider, HighlightedLabel, ListItem, ListSubHeader, Tooltip, prelude::*};
use ui_input::SingleLineInput;
use util::ResultExt as _;
use workspace::{AppState, ItemId, SerializableItem, delete_unloaded_items};
use workspace::{Item, Workspace, WorkspaceId, item::ItemEvent};
use workspace::{
AppState, Item, ItemId, SerializableItem, Workspace, WorkspaceId, delete_unloaded_items,
item::ItemEvent,
};
pub fn init(app_state: Arc<AppState>, cx: &mut App) {
workspace::register_serializable_item::<ComponentPreview>(cx);
@ -642,7 +639,7 @@ impl ComponentPreview {
// Check if the component's scope is Agent
if scope == ComponentScope::Agent {
if let Some(active_thread) = self.active_thread.clone() {
if let Some(element) = agent::get_agent_preview(
if let Some(element) = agent_ui::get_agent_preview(
&component.id(),
self.workspace.clone(),
active_thread,
@ -1140,7 +1137,7 @@ impl ComponentPreviewPage {
fn render_preview(&self, window: &mut Window, cx: &mut App) -> impl IntoElement {
// Try to get agent preview first if we have an active thread
let maybe_agent_preview = if let Some(active_thread) = self.active_thread.as_ref() {
agent::get_agent_preview(
agent_ui::get_agent_preview(
&self.component.id(),
self.workspace.clone(),
active_thread.clone(),

View file

@ -1,4 +1,5 @@
use agent::{ActiveThread, ContextStore, MessageSegment, TextThreadStore, ThreadStore};
use agent::{ContextStore, MessageSegment, TextThreadStore, ThreadStore};
use agent_ui::ActiveThread;
use anyhow::{Result, anyhow};
use assistant_tool::ToolWorkingSet;
use gpui::{AppContext, AsyncApp, Entity, Task, WeakEntity};