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:
1 parent
a261164ec8
commit
050d760ec8
11 files changed
+542
-672
No files matched your search
+34
-32
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user