More progress

This commit is contained in:
Conrad Irwin 2025-08-15 23:43:16 -06:00
parent eebe425c1d
commit fa6c0a1a49
4 changed files with 158 additions and 22 deletions

View file

@ -31,6 +31,16 @@ pub trait AgentConnection {
return None; return None;
} }
fn load_thread(
self: Rc<Self>,
_project: Entity<Project>,
_cwd: &Path,
_session_id: acp::SessionId,
_cx: &mut App,
) -> Task<Result<Entity<AcpThread>>> {
Task::ready(Err(anyhow::anyhow!("load thread not implemented")))
}
fn auth_methods(&self) -> &[acp::AuthMethod]; fn auth_methods(&self) -> &[acp::AuthMethod];
fn authenticate(&self, method: acp::AuthMethodId, cx: &mut App) -> Task<Result<()>>; fn authenticate(&self, method: acp::AuthMethodId, cx: &mut App) -> Task<Result<()>>;

View file

@ -1,4 +1,3 @@
use crate::ThreadsDatabase;
use crate::native_agent_server::NATIVE_AGENT_SERVER_NAME; use crate::native_agent_server::NATIVE_AGENT_SERVER_NAME;
use crate::{ use crate::{
AgentResponseEvent, ContextServerRegistry, CopyPathTool, CreateDirectoryTool, DeletePathTool, AgentResponseEvent, ContextServerRegistry, CopyPathTool, CreateDirectoryTool, DeletePathTool,
@ -6,7 +5,8 @@ use crate::{
MovePathTool, NowTool, OpenTool, ReadFileTool, TerminalTool, ThinkingTool, Thread, MovePathTool, NowTool, OpenTool, ReadFileTool, TerminalTool, ThinkingTool, Thread,
ToolCallAuthorization, UserMessageContent, WebSearchTool, templates::Templates, ToolCallAuthorization, UserMessageContent, WebSearchTool, templates::Templates,
}; };
use acp_thread::{AcpThreadMetadata, AgentModelSelector}; use crate::{DbThread, ThreadsDatabase};
use acp_thread::{AcpThread, AcpThreadMetadata, AgentModelSelector};
use agent_client_protocol as acp; use agent_client_protocol as acp;
use agent_settings::AgentSettings; use agent_settings::AgentSettings;
use anyhow::{Context as _, Result, anyhow}; use anyhow::{Context as _, Result, anyhow};
@ -18,7 +18,7 @@ use futures::{SinkExt, StreamExt, future};
use gpui::{ use gpui::{
App, AppContext, AsyncApp, Context, Entity, SharedString, Subscription, Task, WeakEntity, App, AppContext, AsyncApp, Context, Entity, SharedString, Subscription, Task, WeakEntity,
}; };
use language_model::{LanguageModel, LanguageModelProvider, LanguageModelRegistry}; use language_model::{LanguageModel, LanguageModelProvider, LanguageModelRegistry, SelectedModel};
use project::{Project, ProjectItem, ProjectPath, Worktree}; use project::{Project, ProjectItem, ProjectPath, Worktree};
use prompt_store::{ use prompt_store::{
ProjectContext, PromptId, PromptStore, RulesFileContext, UserRulesContext, WorktreeContext, ProjectContext, PromptId, PromptStore, RulesFileContext, UserRulesContext, WorktreeContext,
@ -759,7 +759,6 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
} }
fn list_threads(&self, cx: &mut App) -> Option<UnboundedReceiver<Vec<AcpThreadMetadata>>> { fn list_threads(&self, cx: &mut App) -> Option<UnboundedReceiver<Vec<AcpThreadMetadata>>> {
dbg!("listing!");
let (mut tx, rx) = futures::channel::mpsc::unbounded(); let (mut tx, rx) = futures::channel::mpsc::unbounded();
let database = self.0.update(cx, |this, _| { let database = self.0.update(cx, |this, _| {
this.history_listeners.push(tx.clone()); this.history_listeners.push(tx.clone());
@ -790,6 +789,114 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
Some(rx) Some(rx)
} }
fn load_thread(
self: Rc<Self>,
project: Entity<Project>,
cwd: &Path,
session_id: acp::SessionId,
cx: &mut App,
) -> Task<Result<Entity<acp_thread::AcpThread>>> {
let database = self.0.update(cx, |this, _| this.thread_database.clone());
cx.spawn(async move |cx| {
let database = database.await.map_err(|e| anyhow!(e))?;
let db_thread = database
.load_thread(session_id.clone())
.await?
.context("no such thread found")?;
let acp_thread = cx.update(|cx| {
cx.new(|cx| {
acp_thread::AcpThread::new(
db_thread.title,
self.clone(),
project.clone(),
session_id.clone(),
cx,
)
})
})?;
let action_log = cx.update(|cx| acp_thread.read(cx).action_log().clone())?;
let agent = self.0.clone();
// Create Thread
let thread = agent.update(
cx,
|agent, cx: &mut gpui::Context<NativeAgent>| -> Result<_> {
let configured_model = LanguageModelRegistry::global(cx)
.update(cx, |registry, cx| {
db_thread
.model
.and_then(|model| {
let model = SelectedModel {
provider: model.provider.clone().into(),
model: model.model.clone().into(),
};
registry.select_model(&model, cx)
})
.or_else(|| registry.default_model())
})
.context("no default model configured")?;
let model = agent
.models
.model_from_id(&LanguageModels::model_id(&configured_model.model))
.context("no model by id")?;
let thread = cx.new(|cx| {
let mut thread = Thread::new(
project.clone(),
agent.project_context.clone(),
agent.context_server_registry.clone(),
action_log.clone(),
agent.templates.clone(),
model,
cx,
);
// todo!() factor this out
thread.add_tool(CopyPathTool::new(project.clone()));
thread.add_tool(CreateDirectoryTool::new(project.clone()));
thread.add_tool(DeletePathTool::new(project.clone(), action_log.clone()));
thread.add_tool(DiagnosticsTool::new(project.clone()));
thread.add_tool(EditFileTool::new(cx.entity()));
thread.add_tool(FetchTool::new(project.read(cx).client().http_client()));
thread.add_tool(FindPathTool::new(project.clone()));
thread.add_tool(GrepTool::new(project.clone()));
thread.add_tool(ListDirectoryTool::new(project.clone()));
thread.add_tool(MovePathTool::new(project.clone()));
thread.add_tool(NowTool);
thread.add_tool(OpenTool::new(project.clone()));
thread.add_tool(ReadFileTool::new(project.clone(), action_log));
thread.add_tool(TerminalTool::new(project.clone(), cx));
thread.add_tool(ThinkingTool);
thread.add_tool(WebSearchTool); // TODO: Enable this only if it's a zed model.
thread
});
Ok(thread)
},
)??;
// Store the session
agent.update(cx, |agent, cx| {
agent.sessions.insert(
session_id,
Session {
thread,
acp_thread: acp_thread.downgrade(),
_subscription: cx.observe_release(&acp_thread, |this, acp_thread, _cx| {
this.sessions.remove(acp_thread.session_id());
}),
},
);
})?;
// we need to actually deserialize the DbThread.
todo!()
Ok(acp_thread)
})
}
fn model_selector(&self) -> Option<Rc<dyn AgentModelSelector>> { fn model_selector(&self) -> Option<Rc<dyn AgentModelSelector>> {
Some(Rc::new(self.clone()) as Rc<dyn AgentModelSelector>) Some(Rc::new(self.clone()) as Rc<dyn AgentModelSelector>)
} }

View file

@ -21,7 +21,6 @@ use ui::{
use util::ResultExt; use util::ResultExt;
pub struct AcpThreadHistory { pub struct AcpThreadHistory {
agent_panel: WeakEntity<AgentPanel>,
history_store: Entity<HistoryStore>, history_store: Entity<HistoryStore>,
scroll_handle: UniformListScrollHandle, scroll_handle: UniformListScrollHandle,
selected_index: usize, selected_index: usize,
@ -473,16 +472,17 @@ impl AcpThreadHistory {
let Some(entry) = self.get_match(ix) else { let Some(entry) = self.get_match(ix) else {
return; return;
}; };
let task_result = match entry { todo!();
HistoryEntry::Thread(thread) => todo!(), // let task_result = match entry {
HistoryEntry::Context(context) => self // HistoryEntry::Thread(thread) => todo!(),
.agent_panel // HistoryEntry::Context(context) => self
.update(cx, |this, cx| this.delete_context(context.path.clone(), cx)), // .agent_panel
}; // .update(cx, |this, cx| this.delete_context(context.path.clone(), cx)),
// };
if let Some(task) = task_result.log_err() { // if let Some(task) = task_result.log_err() {
task.detach_and_log_err(cx); // task.detach_and_log_err(cx);
}; // };
cx.notify(); cx.notify();
} }

View file

@ -169,7 +169,14 @@ impl AcpThreadView {
project: project.clone(), project: project.clone(),
thread_store, thread_store,
text_thread_store, text_thread_store,
thread_state: Self::initial_state(agent, workspace, project, window, cx), thread_state: Self::initial_state(
agent,
restore_thread,
workspace,
project,
window,
cx,
),
message_editor, message_editor,
model_selector: None, model_selector: None,
notifications: Vec::new(), notifications: Vec::new(),
@ -193,6 +200,7 @@ impl AcpThreadView {
fn initial_state( fn initial_state(
agent: Rc<dyn AgentServer>, agent: Rc<dyn AgentServer>,
restore_thread: Option<AcpThreadMetadata>,
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
project: Entity<Project>, project: Entity<Project>,
window: &mut Window, window: &mut Window,
@ -232,19 +240,27 @@ impl AcpThreadView {
// .detach(); // .detach();
// }) // })
// .ok(); // .ok();
//
let Some(result) = cx let task = cx.update(|_, cx| {
.update(|_, cx| { if let Some(restore_thread) = restore_thread {
connection.clone().load_thread(
project.clone(),
&root_dir,
restore_thread.id,
cx,
)
} else {
connection connection
.clone() .clone()
.new_thread(project.clone(), &root_dir, cx) .new_thread(project.clone(), &root_dir, cx)
}) }
.log_err() });
else {
let Ok(task) = task else {
return; return;
}; };
let result = match result.await { let result = match task.await {
Err(e) => { Err(e) => {
let mut cx = cx.clone(); let mut cx = cx.clone();
if e.is::<acp_thread::AuthRequired>() { if e.is::<acp_thread::AuthRequired>() {
@ -618,6 +634,7 @@ impl AcpThreadView {
} else { } else {
this.thread_state = Self::initial_state( this.thread_state = Self::initial_state(
agent, agent,
None, // todo!()
this.workspace.clone(), this.workspace.clone(),
project.clone(), project.clone(),
window, window,
@ -3733,6 +3750,7 @@ pub(crate) mod tests {
project, project,
thread_store.clone(), thread_store.clone(),
text_thread_store.clone(), text_thread_store.clone(),
None,
window, window,
cx, cx,
) )
@ -3884,6 +3902,7 @@ pub(crate) mod tests {
project.clone(), project.clone(),
thread_store.clone(), thread_store.clone(),
text_thread_store.clone(), text_thread_store.clone(),
None,
window, window,
cx, cx,
) )