WIP
This commit is contained in:
parent
fd8ea2acfc
commit
251baacdab
12 changed files with 75 additions and 33 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -11,6 +11,7 @@ dependencies = [
|
|||
"agent-client-protocol",
|
||||
"anyhow",
|
||||
"buffer_diff",
|
||||
"chrono",
|
||||
"collections",
|
||||
"editor",
|
||||
"env_logger 0.11.8",
|
||||
|
|
|
@ -21,6 +21,7 @@ agent-client-protocol.workspace = true
|
|||
agent.workspace = true
|
||||
anyhow.workspace = true
|
||||
buffer_diff.workspace = true
|
||||
chrono.workspace = true
|
||||
collections.workspace = true
|
||||
editor.workspace = true
|
||||
file_icons.workspace = true
|
||||
|
|
|
@ -6,11 +6,13 @@ mod terminal;
|
|||
pub use connection::*;
|
||||
pub use diff::*;
|
||||
pub use mention::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use terminal::*;
|
||||
|
||||
use action_log::ActionLog;
|
||||
use agent_client_protocol as acp;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use chrono::{DateTime, Utc};
|
||||
use editor::Bias;
|
||||
use futures::{FutureExt, channel::oneshot, future::BoxFuture};
|
||||
use gpui::{AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, WeakEntity};
|
||||
|
@ -632,6 +634,13 @@ impl PlanEntry {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AcpThreadMetadata {
|
||||
pub id: acp::SessionId,
|
||||
pub title: SharedString,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub struct AcpThread {
|
||||
title: SharedString,
|
||||
entries: Vec<AgentThreadEntry>,
|
||||
|
@ -1608,7 +1617,7 @@ mod tests {
|
|||
use super::*;
|
||||
use anyhow::anyhow;
|
||||
use futures::{channel::mpsc, future::LocalBoxFuture, select};
|
||||
use gpui::{AsyncApp, TestAppContext, WeakEntity};
|
||||
use gpui::{App, AsyncApp, TestAppContext, WeakEntity};
|
||||
use indoc::indoc;
|
||||
use project::{FakeFs, Fs};
|
||||
use rand::Rng as _;
|
||||
|
@ -2284,7 +2293,7 @@ mod tests {
|
|||
self: Rc<Self>,
|
||||
project: Entity<Project>,
|
||||
_cwd: &Path,
|
||||
cx: &mut gpui::App,
|
||||
cx: &mut App,
|
||||
) -> Task<gpui::Result<Entity<AcpThread>>> {
|
||||
let session_id = acp::SessionId(
|
||||
rand::thread_rng()
|
||||
|
@ -2300,6 +2309,10 @@ mod tests {
|
|||
Task::ready(Ok(thread))
|
||||
}
|
||||
|
||||
fn list_threads(&self, _: &mut App) -> Task<Result<Vec<AcpThreadMetadata>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn authenticate(&self, method: acp::AuthMethodId, _cx: &mut App) -> Task<gpui::Result<()>> {
|
||||
if self.auth_methods().iter().any(|m| m.id == method) {
|
||||
Task::ready(Ok(()))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::AcpThread;
|
||||
use crate::{AcpThread, AcpThreadMetadata};
|
||||
use agent_client_protocol::{self as acp};
|
||||
use anyhow::Result;
|
||||
use collections::IndexMap;
|
||||
|
@ -26,6 +26,8 @@ pub trait AgentConnection {
|
|||
cx: &mut App,
|
||||
) -> Task<Result<Entity<AcpThread>>>;
|
||||
|
||||
fn list_threads(&self, _cx: &mut App) -> Task<Result<Vec<AcpThreadMetadata>>>;
|
||||
|
||||
fn auth_methods(&self) -> &[acp::AuthMethod];
|
||||
|
||||
fn authenticate(&self, method: acp::AuthMethodId, cx: &mut App) -> Task<Result<()>>;
|
||||
|
@ -264,6 +266,10 @@ mod test_support {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn list_threads(&self, _: &mut App) -> Task<Result<Vec<AcpThreadMetadata>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn prompt(
|
||||
&self,
|
||||
_id: Option<UserMessageId>,
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
use crate::ThreadsDatabase;
|
||||
use crate::{
|
||||
AgentResponseEvent, ContextServerRegistry, CopyPathTool, CreateDirectoryTool, DeletePathTool,
|
||||
DiagnosticsTool, EditFileTool, FetchTool, FindPathTool, GrepTool, ListDirectoryTool,
|
||||
MovePathTool, NowTool, OpenTool, ReadFileTool, TerminalTool, ThinkingTool, Thread,
|
||||
ToolCallAuthorization, UserMessageContent, WebSearchTool, templates::Templates,
|
||||
};
|
||||
use acp_thread::AgentModelSelector;
|
||||
use acp_thread::{AcpThreadMetadata, AgentModelSelector};
|
||||
use agent_client_protocol as acp;
|
||||
use agent_settings::AgentSettings;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use collections::{HashSet, IndexMap};
|
||||
use fs::Fs;
|
||||
use futures::channel::mpsc;
|
||||
use futures::future::Shared;
|
||||
use futures::{StreamExt, future};
|
||||
use gpui::{
|
||||
App, AppContext, AsyncApp, Context, Entity, SharedString, Subscription, Task, WeakEntity,
|
||||
|
@ -166,6 +168,7 @@ pub struct NativeAgent {
|
|||
models: LanguageModels,
|
||||
project: Entity<Project>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
thread_database: Shared<Task<Result<Arc<ThreadsDatabase>, Arc<anyhow::Error>>>>,
|
||||
fs: Arc<dyn Fs>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
@ -208,6 +211,7 @@ impl NativeAgent {
|
|||
context_server_registry: cx.new(|cx| {
|
||||
ContextServerRegistry::new(project.read(cx).context_server_store(), cx)
|
||||
}),
|
||||
thread_database: ThreadsDatabase::connect(cx),
|
||||
templates,
|
||||
models: LanguageModels::new(cx),
|
||||
project,
|
||||
|
@ -751,6 +755,23 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
|
|||
Task::ready(Ok(()))
|
||||
}
|
||||
|
||||
fn list_threads(&self, cx: &mut App) -> Task<Result<Vec<AcpThreadMetadata>>> {
|
||||
let database = self.0.read(cx).thread_database.clone();
|
||||
cx.background_executor().spawn(async move {
|
||||
let database = database.await.map_err(|e| anyhow!(e))?;
|
||||
let results = database.list_threads().await?;
|
||||
|
||||
Ok(results
|
||||
.into_iter()
|
||||
.map(|thread| AcpThreadMetadata {
|
||||
id: thread.id,
|
||||
title: thread.title,
|
||||
updated_at: thread.updated_at,
|
||||
})
|
||||
.collect())
|
||||
})
|
||||
}
|
||||
|
||||
fn model_selector(&self) -> Option<Rc<dyn AgentModelSelector>> {
|
||||
Some(Rc::new(self.clone()) as Rc<dyn AgentModelSelector>)
|
||||
}
|
||||
|
|
|
@ -216,10 +216,6 @@ impl Column for DataType {
|
|||
}
|
||||
}
|
||||
|
||||
struct GlobalThreadsDatabase(Shared<Task<Result<Arc<ThreadsDatabase>, Arc<anyhow::Error>>>>);
|
||||
|
||||
impl Global for GlobalThreadsDatabase {}
|
||||
|
||||
pub(crate) struct ThreadsDatabase {
|
||||
executor: BackgroundExecutor,
|
||||
connection: Arc<Mutex<Connection>>,
|
||||
|
@ -234,34 +230,26 @@ impl ThreadsDatabase {
|
|||
}
|
||||
|
||||
impl ThreadsDatabase {
|
||||
fn global_future(
|
||||
cx: &mut App,
|
||||
) -> Shared<Task<Result<Arc<ThreadsDatabase>, Arc<anyhow::Error>>>> {
|
||||
GlobalThreadsDatabase::global(cx).0.clone()
|
||||
}
|
||||
|
||||
fn init(cx: &mut App) {
|
||||
pub fn connect(cx: &mut App) -> Shared<Task<Result<Arc<ThreadsDatabase>, Arc<anyhow::Error>>>> {
|
||||
let executor = cx.background_executor().clone();
|
||||
let database_future = executor
|
||||
executor
|
||||
.spawn({
|
||||
let executor = executor.clone();
|
||||
let threads_dir = paths::data_dir().join("threads");
|
||||
async move {
|
||||
match ThreadsDatabase::new(threads_dir, executor) {
|
||||
match ThreadsDatabase::new(executor) {
|
||||
Ok(db) => Ok(Arc::new(db)),
|
||||
Err(err) => Err(Arc::new(err)),
|
||||
}
|
||||
}
|
||||
})
|
||||
.shared();
|
||||
|
||||
cx.set_global(GlobalThreadsDatabase(database_future));
|
||||
.shared()
|
||||
}
|
||||
|
||||
pub fn new(threads_dir: PathBuf, executor: BackgroundExecutor) -> Result<Self> {
|
||||
pub fn new(executor: BackgroundExecutor) -> Result<Self> {
|
||||
let connection = if *ZED_STATELESS || cfg!(any(feature = "test-support", test)) {
|
||||
Connection::open_memory(Some("THREAD_FALLBACK_DB"))
|
||||
} else {
|
||||
let threads_dir = paths::data_dir().join("threads");
|
||||
std::fs::create_dir_all(&threads_dir)?;
|
||||
let sqlite_path = threads_dir.join("threads.db");
|
||||
Connection::open_file(&sqlite_path.to_string_lossy())
|
||||
|
@ -397,7 +385,6 @@ mod tests {
|
|||
use gpui::TestAppContext;
|
||||
use http_client::FakeHttpClient;
|
||||
use language_model::Role;
|
||||
use pretty_assertions::assert_matches;
|
||||
use project::Project;
|
||||
use settings::SettingsStore;
|
||||
|
||||
|
@ -408,7 +395,6 @@ mod tests {
|
|||
cx.set_global(settings_store);
|
||||
Project::init_settings(cx);
|
||||
language::init(cx);
|
||||
ThreadsDatabase::init(cx);
|
||||
|
||||
let http_client = FakeHttpClient::with_404_response();
|
||||
let clock = Arc::new(clock::FakeSystemClock::new());
|
||||
|
@ -453,10 +439,7 @@ mod tests {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
let db = cx
|
||||
.update(|cx| ThreadsDatabase::global_future(cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let db = cx.update(|cx| ThreadsDatabase::connect(cx)).await.unwrap();
|
||||
let threads = db.list_threads().await.unwrap();
|
||||
assert_eq!(threads.len(), 1);
|
||||
let thread = db
|
||||
|
|
|
@ -10,7 +10,7 @@ use ui::App;
|
|||
use util::ResultExt as _;
|
||||
|
||||
use crate::AgentServerCommand;
|
||||
use acp_thread::{AcpThread, AgentConnection, AuthRequired};
|
||||
use acp_thread::{AcpThread, AcpThreadMetadata, AgentConnection, AuthRequired};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct OldAcpClientDelegate {
|
||||
|
@ -451,6 +451,10 @@ impl AgentConnection for AcpConnection {
|
|||
})
|
||||
}
|
||||
|
||||
fn list_threads(&self, _cx: &mut App) -> Task<Result<Vec<AcpThreadMetadata>>> {
|
||||
Task::ready(Ok(Vec::default()))
|
||||
}
|
||||
|
||||
fn auth_methods(&self) -> &[acp::AuthMethod] {
|
||||
&[]
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use anyhow::{Context as _, Result};
|
|||
use gpui::{App, AppContext as _, AsyncApp, Entity, Task, WeakEntity};
|
||||
|
||||
use crate::{AgentServerCommand, acp::UnsupportedVersion};
|
||||
use acp_thread::{AcpThread, AgentConnection, AuthRequired};
|
||||
use acp_thread::{AcpThread, AcpThreadMetadata, AgentConnection, AuthRequired};
|
||||
|
||||
pub struct AcpConnection {
|
||||
server_name: &'static str,
|
||||
|
@ -169,6 +169,10 @@ impl AgentConnection for AcpConnection {
|
|||
})
|
||||
}
|
||||
|
||||
fn list_threads(&self, _cx: &mut App) -> Task<Result<Vec<AcpThreadMetadata>>> {
|
||||
Task::ready(Ok(Vec::default()))
|
||||
}
|
||||
|
||||
fn prompt(
|
||||
&self,
|
||||
_id: Option<acp_thread::UserMessageId>,
|
||||
|
|
|
@ -30,7 +30,7 @@ use util::{ResultExt, debug_panic};
|
|||
use crate::claude::mcp_server::{ClaudeZedMcpServer, McpConfig};
|
||||
use crate::claude::tools::ClaudeTool;
|
||||
use crate::{AgentServer, AgentServerCommand, AllAgentServersSettings};
|
||||
use acp_thread::{AcpThread, AgentConnection};
|
||||
use acp_thread::{AcpThread, AcpThreadMetadata, AgentConnection};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClaudeCode;
|
||||
|
@ -209,6 +209,10 @@ impl AgentConnection for ClaudeAgentConnection {
|
|||
Task::ready(Err(anyhow!("Authentication not supported")))
|
||||
}
|
||||
|
||||
fn list_threads(&self, _cx: &mut App) -> Task<Result<Vec<AcpThreadMetadata>>> {
|
||||
Task::ready(Ok(Vec::default()))
|
||||
}
|
||||
|
||||
fn prompt(
|
||||
&self,
|
||||
_id: Option<acp_thread::UserMessageId>,
|
||||
|
|
|
@ -3583,7 +3583,7 @@ fn terminal_command_markdown_style(window: &Window, cx: &App) -> MarkdownStyle {
|
|||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use acp_thread::StubAgentConnection;
|
||||
use acp_thread::{AcpThreadMetadata, StubAgentConnection};
|
||||
use agent::{TextThreadStore, ThreadStore};
|
||||
use agent_client_protocol::SessionId;
|
||||
use editor::EditorSettings;
|
||||
|
@ -3819,6 +3819,10 @@ pub(crate) mod tests {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn list_threads(&self, _cx: &mut App) -> Task<gpui::Result<Vec<AcpThreadMetadata>>> {
|
||||
Task::ready(Ok(vec![]))
|
||||
}
|
||||
|
||||
fn prompt(
|
||||
&self,
|
||||
_id: Option<acp_thread::UserMessageId>,
|
||||
|
|
|
@ -9,6 +9,7 @@ mod context_picker;
|
|||
mod context_server_configuration;
|
||||
mod context_strip;
|
||||
mod debug;
|
||||
mod history_store;
|
||||
mod inline_assistant;
|
||||
mod inline_prompt_editor;
|
||||
mod language_model_selector;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::history_store::{HistoryEntry, HistoryStore};
|
||||
use crate::{AgentPanel, RemoveSelectedThread};
|
||||
use agent::history_store::{HistoryEntry, HistoryStore};
|
||||
use chrono::{Datelike as _, Local, NaiveDate, TimeDelta};
|
||||
use editor::{Editor, EditorEvent};
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue