agent_ui: More refactors (#48186)

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
This commit is contained in:
authored and GitHub committed 2026-02-02 13:14:45 +00:00
1 parent a261164ec8
commit 050d760ec8
11 files changed
+542 -672

No files matched your search

+34 -32
View File
@@ -306,6 +306,36 @@ impl NativeAgent {
}))
}
fn new_session(
&mut self,
project: Entity<Project>,
cx: &mut Context<Self>,
) -> Entity<AcpThread> {
// Create Thread
// Fetch default model from registry settings
let registry = LanguageModelRegistry::read_global(cx);
// Log available models for debugging
let available_count = registry.available_models(cx).count();
log::debug!("Total available models: {}", available_count);
let default_model = registry.default_model().and_then(|default_model| {
self.models
.model_from_id(&LanguageModels::model_id(&default_model.model))
});
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
self.project_context.clone(),
self.context_server_registry.clone(),
self.templates.clone(),
default_model,
cx,
)
});
self.register_session(thread, cx)
}
fn register_session(
&mut self,
thread_handle: Entity<Thread>,
@@ -1187,38 +1217,10 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
cwd: &Path,
cx: &mut App,
) -> Task<Result<Entity<acp_thread::AcpThread>>> {
let agent = self.0.clone();
log::debug!("Creating new thread for project at: {:?}", cwd);
cx.spawn(async move |cx| {
log::debug!("Starting thread creation in async context");
// Create Thread
let thread = agent.update(cx, |agent, cx| {
// Fetch default model from registry settings
let registry = LanguageModelRegistry::read_global(cx);
// Log available models for debugging
let available_count = registry.available_models(cx).count();
log::debug!("Total available models: {}", available_count);
let default_model = registry.default_model().and_then(|default_model| {
agent
.models
.model_from_id(&LanguageModels::model_id(&default_model.model))
});
cx.new(|cx| {
Thread::new(
project.clone(),
agent.project_context.clone(),
agent.context_server_registry.clone(),
agent.templates.clone(),
default_model,
cx,
)
})
});
Ok(agent.update(cx, |agent, cx| agent.register_session(thread, cx)))
})
log::debug!("Creating new thread for project at: {cwd:?}");
Task::ready(Ok(self
.0
.update(cx, |agent, cx| agent.new_session(project, cx))))
}
fn supports_load_session(&self, _cx: &App) -> bool {
+6 -55
View File
@@ -4293,19 +4293,8 @@ async fn test_subagent_tool_cancellation(cx: &mut TestAppContext) {
)
});
let parent_tools: std::collections::BTreeMap<gpui::SharedString, Arc<dyn crate::AnyAgentTool>> =
std::collections::BTreeMap::new();
#[allow(clippy::arc_with_non_send_sync)]
let tool = Arc::new(SubagentTool::new(
parent.downgrade(),
project.clone(),
project_context,
context_server_registry,
Templates::new(),
0,
parent_tools,
));
let tool = Arc::new(SubagentTool::new(parent.downgrade(), 0));
let (event_stream, _rx, mut cancellation_tx) =
crate::ToolCallEventStream::test_with_cancellation();
@@ -4589,25 +4578,12 @@ async fn test_allowed_tools_rejects_unknown_tool(cx: &mut TestAppContext) {
thread
});
let mut parent_tools: std::collections::BTreeMap<
gpui::SharedString,
Arc<dyn crate::AnyAgentTool>,
> = std::collections::BTreeMap::new();
parent_tools.insert("echo".into(), EchoTool.erase());
#[allow(clippy::arc_with_non_send_sync)]
let tool = Arc::new(SubagentTool::new(
parent.downgrade(),
project,
project_context,
context_server_registry,
Templates::new(),
0,
parent_tools,
));
let tool = Arc::new(SubagentTool::new(parent.downgrade(), 0));
let allowed_tools = Some(vec!["nonexistent_tool".to_string()]);
let result = tool.validate_allowed_tools(&allowed_tools);
let result = cx.read(|cx| tool.validate_allowed_tools(&allowed_tools, cx));
assert!(result.is_err(), "should reject unknown tool");
let err_msg = result.unwrap_err().to_string();
assert!(
@@ -4910,19 +4886,8 @@ async fn test_max_parallel_subagents_enforced(cx: &mut TestAppContext) {
);
});
let parent_tools: std::collections::BTreeMap<gpui::SharedString, Arc<dyn crate::AnyAgentTool>> =
std::collections::BTreeMap::new();
#[allow(clippy::arc_with_non_send_sync)]
let tool = Arc::new(SubagentTool::new(
parent.downgrade(),
project.clone(),
project_context,
context_server_registry,
Templates::new(),
0,
parent_tools,
));
let tool = Arc::new(SubagentTool::new(parent.downgrade(), 0));
let (event_stream, _rx) = crate::ToolCallEventStream::test();
@@ -4983,22 +4948,8 @@ async fn test_subagent_tool_end_to_end(cx: &mut TestAppContext) {
thread
});
let mut parent_tools: std::collections::BTreeMap<
gpui::SharedString,
Arc<dyn crate::AnyAgentTool>,
> = std::collections::BTreeMap::new();
parent_tools.insert("echo".into(), EchoTool.erase());
#[allow(clippy::arc_with_non_send_sync)]
let tool = Arc::new(SubagentTool::new(
parent.downgrade(),
project.clone(),
project_context,
context_server_registry,
Templates::new(),
0,
parent_tools,
));
let tool = Arc::new(SubagentTool::new(parent.downgrade(), 0));
let (event_stream, _rx) = crate::ToolCallEventStream::test();
+4 -13
View File
@@ -784,16 +784,16 @@ pub struct Thread {
/// Used to signal that the turn should end at the next message boundary.
has_queued_message: bool,
pending_message: Option<AgentMessage>,
tools: BTreeMap<SharedString, Arc<dyn AnyAgentTool>>,
pub(crate) tools: BTreeMap<SharedString, Arc<dyn AnyAgentTool>>,
request_token_usage: HashMap<UserMessageId, language_model::TokenUsage>,
#[allow(unused)]
cumulative_token_usage: TokenUsage,
#[allow(unused)]
initial_project_snapshot: Shared<Task<Option<Arc<ProjectSnapshot>>>>,
context_server_registry: Entity<ContextServerRegistry>,
pub(crate) context_server_registry: Entity<ContextServerRegistry>,
profile_id: AgentProfileId,
project_context: Entity<ProjectContext>,
templates: Arc<Templates>,
pub(crate) templates: Arc<Templates>,
model: Option<Arc<dyn LanguageModel>>,
summarization_model: Option<Arc<dyn LanguageModel>>,
thinking_enabled: bool,
@@ -1280,16 +1280,7 @@ impl Thread {
self.add_tool(WebSearchTool);
if cx.has_flag::<SubagentsFeatureFlag>() && self.depth() < MAX_SUBAGENT_DEPTH {
let parent_tools = self.tools.clone();
self.add_tool(SubagentTool::new(
cx.weak_entity(),
self.project.clone(),
self.project_context.clone(),
self.context_server_registry.clone(),
self.templates.clone(),
self.depth(),
parent_tools,
));
self.add_tool(SubagentTool::new(cx.weak_entity(), self.depth()));
}
}
+28 -47
View File
@@ -7,7 +7,6 @@ use futures::{FutureExt, channel::mpsc};
use gpui::{App, AppContext, AsyncApp, Entity, SharedString, Task, WeakEntity};
use language_model::LanguageModelToolUseId;
use project::Project;
use prompt_store::ProjectContext;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use smol::stream::StreamExt;
@@ -20,8 +19,8 @@ use util::ResultExt;
use watch;
use crate::{
AgentTool, AnyAgentTool, ContextServerRegistry, MAX_PARALLEL_SUBAGENTS, MAX_SUBAGENT_DEPTH,
SubagentContext, Templates, Thread, ThreadEvent, ToolCallAuthorization, ToolCallEventStream,
AgentTool, AnyAgentTool, MAX_PARALLEL_SUBAGENTS, MAX_SUBAGENT_DEPTH, SubagentContext, Thread,
ThreadEvent, ToolCallAuthorization, ToolCallEventStream,
};
/// When a subagent's remaining context window falls below this fraction (25%),
@@ -87,57 +86,38 @@ pub struct SubagentToolInput {
/// Tool that spawns a subagent thread to work on a task.
pub struct SubagentTool {
parent_thread: WeakEntity<Thread>,
project: Entity<Project>,
project_context: Entity<ProjectContext>,
context_server_registry: Entity<ContextServerRegistry>,
templates: Arc<Templates>,
current_depth: u8,
/// The tools available to the parent thread, captured before SubagentTool was added.
/// Subagents inherit from this set (or a subset via `allowed_tools` in the config).
/// This is captured early so subagents don't get the subagent tool themselves.
parent_tools: BTreeMap<SharedString, Arc<dyn AnyAgentTool>>,
}
impl SubagentTool {
pub fn new(
parent_thread: WeakEntity<Thread>,
project: Entity<Project>,
project_context: Entity<ProjectContext>,
context_server_registry: Entity<ContextServerRegistry>,
templates: Arc<Templates>,
current_depth: u8,
parent_tools: BTreeMap<SharedString, Arc<dyn AnyAgentTool>>,
) -> Self {
pub fn new(parent_thread: WeakEntity<Thread>, current_depth: u8) -> Self {
Self {
parent_thread,
project,
project_context,
context_server_registry,
templates,
current_depth,
parent_tools,
}
}
pub fn validate_allowed_tools(&self, allowed_tools: &Option<Vec<String>>) -> Result<()> {
let Some(tools) = allowed_tools else {
pub fn validate_allowed_tools(
&self,
allowed_tools: &Option<Vec<String>>,
cx: &App,
) -> Result<()> {
let Some(allowed_tools) = allowed_tools else {
return Ok(());
};
let invalid_tools: Vec<&str> = tools
.iter()
.filter(|tool| !self.parent_tools.contains_key(tool.as_str()))
.map(|s| s.as_str())
.collect();
let invalid_tools: Vec<_> = self.parent_thread.read_with(cx, |thread, _cx| {
allowed_tools
.iter()
.filter(|tool| !thread.tools.contains_key(tool.as_str()))
.map(|s| format!("'{s}'"))
.collect()
})?;
if !invalid_tools.is_empty() {
return Err(anyhow!(
"The following tools do not exist: {}",
invalid_tools
.iter()
.map(|t| format!("'{}'", t))
.collect::<Vec<_>>()
.join(", ")
invalid_tools.join(", ")
));
}
@@ -180,18 +160,19 @@ impl AgentTool for SubagentTool {
)));
}
if let Err(e) = self.validate_allowed_tools(&input.allowed_tools) {
if let Err(e) = self.validate_allowed_tools(&input.allowed_tools, cx) {
return Task::ready(Err(e));
}
let Some(parent_thread) = self.parent_thread.upgrade() else {
let Some(parent_thread_entity) = self.parent_thread.upgrade() else {
return Task::ready(Err(anyhow!(
"Parent thread no longer exists (subagent depth={})",
self.current_depth + 1
)));
};
let parent_thread = parent_thread_entity.read(cx);
let running_count = parent_thread.read(cx).running_subagent_count();
let running_count = parent_thread.running_subagent_count();
if running_count >= MAX_PARALLEL_SUBAGENTS {
return Task::ready(Err(anyhow!(
"Maximum parallel subagents ({}) reached. Wait for existing subagents to complete.",
@@ -199,17 +180,17 @@ impl AgentTool for SubagentTool {
)));
}
let parent_model = parent_thread.read(cx).model().cloned();
let parent_model = parent_thread.model().cloned();
let Some(model) = parent_model else {
return Task::ready(Err(anyhow!("No model configured")));
};
let parent_thread_id = parent_thread.read(cx).id().clone();
let project = self.project.clone();
let project_context = self.project_context.clone();
let context_server_registry = self.context_server_registry.clone();
let templates = self.templates.clone();
let parent_tools = self.parent_tools.clone();
let parent_thread_id = parent_thread.id().clone();
let project = parent_thread.project.clone();
let project_context = parent_thread.project_context().clone();
let context_server_registry = parent_thread.context_server_registry.clone();
let templates = parent_thread.templates.clone();
let parent_tools = parent_thread.tools.clone();
let current_depth = self.current_depth;
let parent_thread_weak = self.parent_thread.clone();
+1 -1
View File
@@ -11,4 +11,4 @@ pub use mode_selector::ModeSelector;
pub use model_selector::AcpModelSelector;
pub use model_selector_popover::AcpModelSelectorPopover;
pub use thread_history::*;
pub use thread_view::AcpThreadView;
pub use thread_view::AcpServerView;
+3 -3
View File
@@ -1,4 +1,4 @@
use crate::acp::AcpThreadView;
use crate::acp::AcpServerView;
use crate::{AgentPanel, RemoveHistory, RemoveSelectedThread};
use acp_thread::{AgentSessionInfo, AgentSessionList, AgentSessionListRequest, SessionListUpdate};
use agent_client_protocol as acp;
@@ -843,7 +843,7 @@ impl Render for AcpThreadHistory {
#[derive(IntoElement)]
pub struct AcpHistoryEntryElement {
entry: AgentSessionInfo,
thread_view: WeakEntity<AcpThreadView>,
thread_view: WeakEntity<AcpServerView>,
selected: bool,
hovered: bool,
supports_delete: bool,
@@ -851,7 +851,7 @@ pub struct AcpHistoryEntryElement {
}
impl AcpHistoryEntryElement {
pub fn new(entry: AgentSessionInfo, thread_view: WeakEntity<AcpThreadView>) -> Self {
pub fn new(entry: AgentSessionInfo, thread_view: WeakEntity<AcpServerView>) -> Self {
Self {
entry,
thread_view,
File diff suppressed because it is too large. Load diff
@@ -1,6 +1,7 @@
use super::*;
pub struct ActiveThreadState {
pub struct AcpThreadView {
pub id: acp::SessionId,
pub thread: Entity<AcpThread>,
pub workspace: WeakEntity<Workspace>,
pub entry_view_state: Entity<EntryViewState>,
@@ -66,7 +67,7 @@ pub struct TurnFields {
pub turn_tokens: Option<u64>,
}
impl ActiveThreadState {
impl AcpThreadView {
pub fn new(
thread: Entity<AcpThread>,
workspace: WeakEntity<Workspace>,
@@ -84,8 +85,11 @@ impl ActiveThreadState {
resumed_without_history: bool,
resume_thread_metadata: Option<AgentSessionInfo>,
subscriptions: Vec<Subscription>,
cx: &App,
) -> Self {
let id = thread.read(cx).session_id().clone();
Self {
id,
thread,
workspace,
entry_view_state,
@@ -179,7 +183,7 @@ impl ActiveThreadState {
// turns
pub fn start_turn(&mut self, cx: &mut Context<AcpThreadView>) -> usize {
pub fn start_turn(&mut self, cx: &mut Context<AcpServerView>) -> usize {
self.turn_fields.turn_generation += 1;
let generation = self.turn_fields.turn_generation;
self.turn_fields.turn_started_at = Some(Instant::now());
@@ -223,10 +227,10 @@ impl ActiveThreadState {
pub fn send(
&mut self,
message_editor: Entity<MessageEditor>,
agent: Rc<dyn AgentServer>,
agent_name: SharedString,
login: Option<task::SpawnInTerminal>,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let thread = &self.thread;
@@ -269,13 +273,11 @@ impl ActiveThreadState {
message_editor.update(cx, |editor, cx| editor.clear(window, cx));
let this = cx.weak_entity();
let agent = agent.clone();
window.defer(cx, |window, cx| {
AcpThreadView::handle_auth_required(
AcpServerView::handle_auth_required(
this,
AuthRequired::new(),
agent,
connection,
agent_name,
window,
cx,
);
@@ -292,7 +294,7 @@ impl ActiveThreadState {
&mut self,
message_editor: Entity<MessageEditor>,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let full_mention_content = self.as_native_thread(cx).is_some_and(|thread| {
// Include full contents when using minimal profile
@@ -349,7 +351,7 @@ impl ActiveThreadState {
&mut self,
contents_task: Task<anyhow::Result<Option<(Vec<acp::ContentBlock>, Vec<Entity<Buffer>>)>>>,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let session_id = self.thread.read(cx).session_id().clone();
let agent_telemetry_id = self.thread.read(cx).connection().telemetry_id();
@@ -361,12 +363,8 @@ impl ActiveThreadState {
let mode_id = self.current_mode_id(cx);
let guard = cx.new(|_| ());
cx.observe_release(&guard, |this, _guard, cx| {
if let ThreadState::Active(ActiveThreadState {
is_loading_contents,
..
}) = &mut this.thread_state
{
*is_loading_contents = false;
if let Some(thread) = this.as_active_thread_mut() {
thread.is_loading_contents = false;
}
cx.notify();
})
@@ -444,17 +442,14 @@ impl ActiveThreadState {
.ok();
} else {
this.update(cx, |this, cx| {
if let ThreadState::Active(ActiveThreadState {
should_be_following,
..
}) = &mut this.thread_state
{
*should_be_following = this
.workspace
.update(cx, |workspace, _| {
workspace.is_being_followed(CollaboratorId::Agent)
})
.unwrap_or_default();
let should_be_following = this
.workspace
.update(cx, |workspace, _| {
workspace.is_being_followed(CollaboratorId::Agent)
})
.unwrap_or_default();
if let Some(thread) = this.as_active_thread_mut() {
thread.should_be_following = should_be_following;
}
})
.ok();
@@ -467,7 +462,7 @@ impl ActiveThreadState {
&mut self,
message_editor: Entity<MessageEditor>,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let thread = &self.thread;
@@ -486,7 +481,7 @@ impl ActiveThreadState {
pub fn stop_current_and_send_new_message(
&mut self,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let thread = self.thread.clone();
self.skip_queue_processing_count = 0;
@@ -507,14 +502,14 @@ impl ActiveThreadState {
// generation
pub fn cancel_generation(&mut self, cx: &mut Context<AcpThreadView>) {
pub fn cancel_generation(&mut self, cx: &mut Context<AcpServerView>) {
self.thread_retry_status.take();
self.thread_error.take();
self.user_interrupted_generation = true;
self._cancel_task = Some(self.thread.update(cx, |thread, cx| thread.cancel(cx)));
}
pub fn retry_generation(&mut self, cx: &mut Context<AcpThreadView>) {
pub fn retry_generation(&mut self, cx: &mut Context<AcpServerView>) {
self.thread_error.take();
let thread = &self.thread;
@@ -540,7 +535,7 @@ impl ActiveThreadState {
entry_ix: usize,
message_editor: Entity<MessageEditor>,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
if self.is_loading_contents {
return;
@@ -594,7 +589,7 @@ impl ActiveThreadState {
&mut self,
message_editor: Entity<MessageEditor>,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let is_idle = self.thread.read(cx).status() == acp_thread::ThreadStatus::Idle;
@@ -646,7 +641,7 @@ impl ActiveThreadState {
pub fn remove_from_queue(
&mut self,
index: usize,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) -> Option<QueuedMessage> {
if index < self.local_queued_messages.len() {
let removed = self.local_queued_messages.remove(index);
@@ -657,7 +652,7 @@ impl ActiveThreadState {
}
}
pub fn sync_queue_flag_to_native_thread(&self, cx: &mut Context<AcpThreadView>) {
pub fn sync_queue_flag_to_native_thread(&self, cx: &mut Context<AcpServerView>) {
if let Some(native_thread) = self.as_native_thread(cx) {
let has_queued = self.has_queued_messages();
native_thread.update(cx, |thread, _| {
@@ -671,7 +666,7 @@ impl ActiveThreadState {
index: usize,
is_send_now: bool,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let Some(queued) = self.remove_from_queue(index, cx) else {
return;
@@ -715,7 +710,7 @@ impl ActiveThreadState {
pub fn expand_message_editor(
&mut self,
message_editor: Entity<MessageEditor>,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
self.set_editor_is_expanded(!self.editor_expanded, message_editor, cx);
cx.stop_propagation();
@@ -726,7 +721,7 @@ impl ActiveThreadState {
&mut self,
is_expanded: bool,
message_editor: Entity<MessageEditor>,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
self.editor_expanded = is_expanded;
message_editor.update(cx, |editor, cx| {
@@ -758,7 +753,7 @@ impl ActiveThreadState {
title_editor: &Entity<Editor>,
event: &EditorEvent,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let thread = &self.thread;
@@ -786,7 +781,7 @@ impl ActiveThreadState {
&mut self,
focus_handle: FocusHandle,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
if let Some(index) = self.editing_message.take()
&& let Some(editor) = &self
@@ -820,7 +815,7 @@ impl ActiveThreadState {
option_id: acp::PermissionOptionId,
option_kind: acp::PermissionOptionKind,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let thread = &self.thread;
let agent_telemetry_id = thread.read(cx).connection().telemetry_id();
@@ -849,7 +844,7 @@ impl ActiveThreadState {
&mut self,
kind: acp::PermissionOptionKind,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) -> Option<()> {
let thread = self.thread.read(cx);
let tool_call = thread.first_tool_awaiting_confirmation()?;
@@ -872,7 +867,7 @@ impl ActiveThreadState {
pub fn handle_select_permission_granularity(
&mut self,
action: &SelectPermissionGranularity,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let tool_call_id = acp::ToolCallId::new(action.tool_call_id.clone());
self.selected_permission_granularity
@@ -883,7 +878,7 @@ impl ActiveThreadState {
// edits
pub fn keep_all(&mut self, cx: &mut Context<AcpThreadView>) {
pub fn keep_all(&mut self, cx: &mut Context<AcpServerView>) {
let thread = &self.thread;
let telemetry = ActionLogTelemetry::from(thread.read(cx));
let action_log = thread.read(cx).action_log().clone();
@@ -892,7 +887,7 @@ impl ActiveThreadState {
});
}
pub fn reject_all(&mut self, cx: &mut Context<AcpThreadView>) {
pub fn reject_all(&mut self, cx: &mut Context<AcpServerView>) {
let thread = &self.thread;
let telemetry = ActionLogTelemetry::from(thread.read(cx));
let action_log = thread.read(cx).action_log().clone();
@@ -907,7 +902,7 @@ impl ActiveThreadState {
&mut self,
buffer: &Entity<Buffer>,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
let thread = &self.thread;
@@ -928,7 +923,7 @@ impl ActiveThreadState {
&mut self,
project: Entity<Project>,
window: &mut Window,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
if !self.is_imported_thread(cx) {
return;
@@ -972,11 +967,8 @@ impl ActiveThreadState {
};
this.update_in(cx, |this, window, cx| {
if let ThreadState::Active(ActiveThreadState {
resume_thread_metadata,
..
}) = &mut this.thread_state
{
if let Some(thread) = this.as_active_thread_mut() {
let resume_thread_metadata = &mut thread.resume_thread_metadata;
*resume_thread_metadata = Some(thread_metadata);
}
this.reset(window, cx);
@@ -1006,7 +998,7 @@ impl ActiveThreadState {
pub fn restore_checkpoint(
&mut self,
message_id: &UserMessageId,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) {
self.thread
.update(cx, |thread, cx| {
@@ -1015,7 +1007,7 @@ impl ActiveThreadState {
.detach_and_log_err(cx);
}
pub fn clear_thread_error(&mut self, cx: &mut Context<AcpThreadView>) {
pub fn clear_thread_error(&mut self, cx: &mut Context<AcpServerView>) {
self.thread_error = None;
self.thread_error_markdown = None;
self.token_limit_callout_dismissed = true;
@@ -1051,7 +1043,7 @@ impl ActiveThreadState {
pub fn render_command_load_errors(
&self,
cx: &mut Context<AcpThreadView>,
cx: &mut Context<AcpServerView>,
) -> Option<impl IntoElement> {
let errors = self.cached_user_command_errors.borrow();
@@ -1164,7 +1156,7 @@ impl ActiveThreadState {
)
}
pub fn handle_open_rules(&mut self, window: &mut Window, cx: &mut Context<AcpThreadView>) {
pub fn handle_open_rules(&mut self, window: &mut Window, cx: &mut Context<AcpServerView>) {
let Some(thread) = self.as_native_thread(cx) else {
return;
};
+24 -31
View File
@@ -21,7 +21,7 @@ use crate::{
LoadThreadFromClipboard, NewTextThread, NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff,
OpenHistory, ResetTrialEndUpsell, ResetTrialUpsell, ToggleNavigationMenu, ToggleNewThreadMenu,
ToggleOptionsMenu,
acp::AcpThreadView,
acp::AcpServerView,
agent_configuration::{AgentConfiguration, AssistantConfigurationEvent},
slash_command::SlashCommandCompletionProvider,
text_thread_editor::{AgentPanelDelegate, TextThreadEditor, make_lsp_adapter_delegate},
@@ -243,8 +243,8 @@ enum HistoryKind {
enum ActiveView {
Uninitialized,
ExternalAgentThread {
thread_view: Entity<AcpThreadView>,
AgentThread {
thread_view: Entity<AcpServerView>,
},
TextThread {
text_thread_editor: Entity<TextThreadEditor>,
@@ -316,7 +316,7 @@ impl ActiveView {
pub fn which_font_size_used(&self) -> WhichFontSize {
match self {
ActiveView::Uninitialized
| ActiveView::ExternalAgentThread { .. }
| ActiveView::AgentThread { .. }
| ActiveView::History { .. } => WhichFontSize::AgentFont,
ActiveView::TextThread { .. } => WhichFontSize::BufferFont,
ActiveView::Configuration => WhichFontSize::None,
@@ -731,9 +731,9 @@ impl AgentPanel {
.unwrap_or(true)
}
pub(crate) fn active_thread_view(&self) -> Option<&Entity<AcpThreadView>> {
pub(crate) fn active_thread_view(&self) -> Option<&Entity<AcpServerView>> {
match &self.active_view {
ActiveView::ExternalAgentThread { thread_view, .. } => Some(thread_view),
ActiveView::AgentThread { thread_view, .. } => Some(thread_view),
ActiveView::Uninitialized
| ActiveView::TextThread { .. }
| ActiveView::History { .. }
@@ -1017,7 +1017,7 @@ impl AgentPanel {
self.active_view = previous_view;
match &self.active_view {
ActiveView::ExternalAgentThread { thread_view } => {
ActiveView::AgentThread { thread_view } => {
thread_view.focus_handle(cx).focus(window, cx);
}
ActiveView::TextThread {
@@ -1190,7 +1190,7 @@ impl AgentPanel {
};
match &self.active_view {
ActiveView::ExternalAgentThread { thread_view } => {
ActiveView::AgentThread { thread_view } => {
thread_view
.update(cx, |thread_view, cx| {
thread_view.open_thread_as_markdown(workspace, window, cx)
@@ -1418,7 +1418,7 @@ impl AgentPanel {
pub(crate) fn active_agent_thread(&self, cx: &App) -> Option<Entity<AcpThread>> {
match &self.active_view {
ActiveView::ExternalAgentThread { thread_view, .. } => thread_view
ActiveView::AgentThread { thread_view, .. } => thread_view
.read(cx)
.as_active_thread()
.map(|r| r.thread.clone()),
@@ -1428,7 +1428,7 @@ impl AgentPanel {
pub(crate) fn active_native_agent_thread(&self, cx: &App) -> Option<Entity<agent::Thread>> {
match &self.active_view {
ActiveView::ExternalAgentThread { thread_view, .. } => {
ActiveView::AgentThread { thread_view, .. } => {
thread_view.read(cx).as_native_thread(cx)
}
_ => None,
@@ -1702,7 +1702,7 @@ impl AgentPanel {
.then(|| self.thread_store.clone());
let thread_view = cx.new(|cx| {
crate::acp::AcpThreadView::new(
crate::acp::AcpServerView::new(
server,
resume_thread,
initial_content,
@@ -1716,12 +1716,7 @@ impl AgentPanel {
)
});
self.set_active_view(
ActiveView::ExternalAgentThread { thread_view },
true,
window,
cx,
);
self.set_active_view(ActiveView::AgentThread { thread_view }, true, window, cx);
}
}
@@ -1729,7 +1724,7 @@ impl Focusable for AgentPanel {
fn focus_handle(&self, cx: &App) -> FocusHandle {
match &self.active_view {
ActiveView::Uninitialized => self.focus_handle.clone(),
ActiveView::ExternalAgentThread { thread_view, .. } => thread_view.focus_handle(cx),
ActiveView::AgentThread { thread_view, .. } => thread_view.focus_handle(cx),
ActiveView::History { kind } => match kind {
HistoryKind::AgentThreads => self.acp_history.focus_handle(cx),
HistoryKind::TextThreads => self.text_thread_history.focus_handle(cx),
@@ -1845,7 +1840,7 @@ impl AgentPanel {
const LOADING_SUMMARY_PLACEHOLDER: &str = "Loading Summary…";
let content = match &self.active_view {
ActiveView::ExternalAgentThread { thread_view } => {
ActiveView::AgentThread { thread_view } => {
let is_generating_title = thread_view
.read(cx)
.as_native_thread(cx)
@@ -1975,7 +1970,7 @@ impl AgentPanel {
.into_any()
}
fn handle_regenerate_thread_title(thread_view: Entity<AcpThreadView>, cx: &mut App) {
fn handle_regenerate_thread_title(thread_view: Entity<AcpServerView>, cx: &mut App) {
thread_view.update(cx, |thread_view, cx| {
if let Some(thread) = thread_view.as_native_thread(cx) {
thread.update(cx, |thread, cx| {
@@ -2028,11 +2023,11 @@ impl AgentPanel {
};
let thread_view = match &self.active_view {
ActiveView::ExternalAgentThread { thread_view } => Some(thread_view.clone()),
ActiveView::AgentThread { thread_view } => Some(thread_view.clone()),
_ => None,
};
let thread_with_messages = match &self.active_view {
ActiveView::ExternalAgentThread { thread_view } => {
ActiveView::AgentThread { thread_view } => {
thread_view.read(cx).has_user_submitted_prompt(cx)
}
_ => false,
@@ -2196,9 +2191,7 @@ impl AgentPanel {
};
let active_thread = match &self.active_view {
ActiveView::ExternalAgentThread { thread_view } => {
thread_view.read(cx).as_native_thread(cx)
}
ActiveView::AgentThread { thread_view } => thread_view.read(cx).as_native_thread(cx),
ActiveView::Uninitialized
| ActiveView::TextThread { .. }
| ActiveView::History { .. }
@@ -2600,7 +2593,7 @@ impl AgentPanel {
}
}
ActiveView::Uninitialized
| ActiveView::ExternalAgentThread { .. }
| ActiveView::AgentThread { .. }
| ActiveView::History { .. }
| ActiveView::Configuration => return false,
}
@@ -2632,7 +2625,7 @@ impl AgentPanel {
ActiveView::Uninitialized | ActiveView::History { .. } | ActiveView::Configuration => {
false
}
ActiveView::ExternalAgentThread { thread_view, .. }
ActiveView::AgentThread { thread_view, .. }
if thread_view.read(cx).as_native_thread(cx).is_none() =>
{
false
@@ -2878,7 +2871,7 @@ impl AgentPanel {
cx: &mut Context<Self>,
) {
match &self.active_view {
ActiveView::ExternalAgentThread { thread_view } => {
ActiveView::AgentThread { thread_view } => {
thread_view.update(cx, |thread_view, cx| {
thread_view.insert_dragged_files(paths, added_worktrees, window, cx);
});
@@ -2936,7 +2929,7 @@ impl AgentPanel {
let mut key_context = KeyContext::new_with_defaults();
key_context.add("AgentPanel");
match &self.active_view {
ActiveView::ExternalAgentThread { .. } => key_context.add("acp_thread"),
ActiveView::AgentThread { .. } => key_context.add("acp_thread"),
ActiveView::TextThread { .. } => key_context.add("text_thread"),
ActiveView::Uninitialized | ActiveView::History { .. } | ActiveView::Configuration => {}
}
@@ -2988,7 +2981,7 @@ impl Render for AgentPanel {
.children(self.render_onboarding(window, cx))
.map(|parent| match &self.active_view {
ActiveView::Uninitialized => parent,
ActiveView::ExternalAgentThread { thread_view, .. } => parent
ActiveView::AgentThread { thread_view, .. } => parent
.child(thread_view.clone())
.child(self.render_drag_target(cx)),
ActiveView::History { kind } => match kind {
@@ -3246,7 +3239,7 @@ impl AgentPanel {
///
/// This is a test-only accessor that exposes the private `active_thread_view()`
/// method for test assertions. Not compiled into production builds.
pub fn active_thread_view_for_tests(&self) -> Option<&Entity<AcpThreadView>> {
pub fn active_thread_view_for_tests(&self) -> Option<&Entity<AcpServerView>> {
self.active_thread_view()
}
}
+3 -3
View File
@@ -3,7 +3,7 @@ use agent::{NativeAgentServer, ThreadStore};
use agent_client_protocol as acp;
use agent_servers::AgentServer;
use agent_settings::AgentSettings;
use agent_ui::acp::{AcpThreadHistory, AcpThreadView};
use agent_ui::acp::{AcpServerView, AcpThreadHistory};
use fs::Fs;
use gpui::{
Entity, EventEmitter, Focusable, Pixels, SharedString, Subscription, WeakEntity, prelude::*,
@@ -51,7 +51,7 @@ impl EventEmitter<MinimizePane> for AgentThreadPane {}
impl EventEmitter<ClosePane> for AgentThreadPane {}
struct ActiveThreadView {
view: Entity<AcpThreadView>,
view: Entity<AcpServerView>,
thread_id: acp::SessionId,
_notify: Subscription,
}
@@ -112,7 +112,7 @@ impl AgentThreadPane {
let history = self.history.clone();
let thread_view = cx.new(|cx| {
AcpThreadView::new(
AcpServerView::new(
agent,
resume_thread,
None,
+1 -1
View File
@@ -1951,7 +1951,7 @@ fn run_subagent_visual_tests(
)?;
// Expand the first subagent
thread_view.update(cx, |view: &mut agent_ui::acp::AcpThreadView, cx| {
thread_view.update(cx, |view: &mut agent_ui::acp::AcpServerView, cx| {
view.expand_subagent(acp::SessionId::new("subagent-1"), cx);
});