Detailed changes
@@ -11952,6 +11952,8 @@ version = "0.1.0"
dependencies = [
"anyhow",
"askpass",
+ "assistant_tool",
+ "assistant_tools",
"async-watch",
"backtrace",
"cargo_toml",
@@ -11974,6 +11976,7 @@ dependencies = [
"http_client",
"language",
"language_extension",
+ "language_model",
"languages",
"libc",
"log",
@@ -40,13 +40,12 @@ use crate::find_path_tool::FindPathTool;
use crate::grep_tool::GrepTool;
use crate::list_directory_tool::ListDirectoryTool;
use crate::now_tool::NowTool;
-use crate::read_file_tool::ReadFileTool;
use crate::thinking_tool::ThinkingTool;
pub use edit_file_tool::EditFileToolInput;
pub use find_path_tool::FindPathToolInput;
pub use open_tool::OpenTool;
-pub use read_file_tool::ReadFileToolInput;
+pub use read_file_tool::{ReadFileTool, ReadFileToolInput};
pub use terminal_tool::TerminalTool;
pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut App) {
@@ -581,7 +581,11 @@ async fn test_ssh_collaboration_formatting_with_prettier(
}
#[gpui::test]
-async fn test_remote_server_debugger(cx_a: &mut TestAppContext, server_cx: &mut TestAppContext) {
+async fn test_remote_server_debugger(
+ cx_a: &mut TestAppContext,
+ server_cx: &mut TestAppContext,
+ executor: BackgroundExecutor,
+) {
cx_a.update(|cx| {
release_channel::init(SemanticVersion::default(), cx);
command_palette_hooks::init(cx);
@@ -679,7 +683,7 @@ async fn test_remote_server_debugger(cx_a: &mut TestAppContext, server_cx: &mut
});
client_ssh.update(cx_a, |a, _| {
- a.shutdown_processes(Some(proto::ShutdownRemoteServer {}))
+ a.shutdown_processes(Some(proto::ShutdownRemoteServer {}), executor)
});
shutdown_session.await.unwrap();
@@ -281,6 +281,9 @@ impl BackgroundExecutor {
}
if !dispatcher.parking_allowed() {
+ if dispatcher.advance_clock_to_next_delayed() {
+ continue;
+ }
let mut backtrace_message = String::new();
let mut waiting_message = String::new();
if let Some(backtrace) = dispatcher.waiting_backtrace() {
@@ -89,6 +89,15 @@ impl TestDispatcher {
self.state.lock().time = new_now;
}
+ pub fn advance_clock_to_next_delayed(&self) -> bool {
+ let next_due_time = self.state.lock().delayed.first().map(|(time, _)| *time);
+ if let Some(next_due_time) = next_due_time {
+ self.state.lock().time = next_due_time;
+ return true;
+ }
+ false
+ }
+
pub fn simulate_random_delay(&self) -> impl 'static + Send + Future<Output = ()> + use<> {
struct YieldNow {
pub(crate) count: usize,
@@ -1130,9 +1130,10 @@ impl Project {
cx.on_release(Self::release),
cx.on_app_quit(|this, cx| {
let shutdown = this.ssh_client.take().and_then(|client| {
- client
- .read(cx)
- .shutdown_processes(Some(proto::ShutdownRemoteServer {}))
+ client.read(cx).shutdown_processes(
+ Some(proto::ShutdownRemoteServer {}),
+ cx.background_executor().clone(),
+ )
});
cx.background_executor().spawn(async move {
@@ -1472,9 +1473,10 @@ impl Project {
fn release(&mut self, cx: &mut App) {
if let Some(client) = self.ssh_client.take() {
- let shutdown = client
- .read(cx)
- .shutdown_processes(Some(proto::ShutdownRemoteServer {}));
+ let shutdown = client.read(cx).shutdown_processes(
+ Some(proto::ShutdownRemoteServer {}),
+ cx.background_executor().clone(),
+ );
cx.background_spawn(async move {
if let Some(shutdown) = shutdown {
@@ -18,8 +18,8 @@ use futures::{
select, select_biased,
};
use gpui::{
- App, AppContext as _, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Global,
- SemanticVersion, Task, WeakEntity,
+ App, AppContext as _, AsyncApp, BackgroundExecutor, BorrowAppContext, Context, Entity,
+ EventEmitter, Global, SemanticVersion, Task, WeakEntity,
};
use itertools::Itertools;
use parking_lot::Mutex;
@@ -683,6 +683,7 @@ impl SshRemoteClient {
pub fn shutdown_processes<T: RequestMessage>(
&self,
shutdown_request: Option<T>,
+ executor: BackgroundExecutor,
) -> Option<impl Future<Output = ()> + use<T>> {
let state = self.state.lock().take()?;
log::info!("shutting down ssh processes");
@@ -705,7 +706,7 @@ impl SshRemoteClient {
// We wait 50ms instead of waiting for a response, because
// waiting for a response would require us to wait on the main thread
// which we want to avoid in an `on_app_quit` callback.
- smol::Timer::after(Duration::from_millis(50)).await;
+ executor.timer(Duration::from_millis(50)).await;
}
// Drop `multiplex_task` because it owns our ssh_proxy_process, which is a
@@ -69,6 +69,8 @@ fork.workspace = true
libc.workspace = true
[dev-dependencies]
+assistant_tool.workspace = true
+assistant_tools.workspace = true
client = { workspace = true, features = ["test-support"] }
clock = { workspace = true, features = ["test-support"] }
dap = { workspace = true, features = ["test-support"] }
@@ -79,6 +81,7 @@ language = { workspace = true, features = ["test-support"] }
node_runtime = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
remote = { workspace = true, features = ["test-support"] }
+language_model = { workspace = true, features = ["test-support"] }
lsp = { workspace = true, features=["test-support"] }
unindent.workspace = true
serde_json.workspace = true
@@ -2,8 +2,11 @@
/// The tests in this file assume that server_cx is running on Windows too.
/// We neead to find a way to test Windows-Non-Windows interactions.
use crate::headless_project::HeadlessProject;
+use assistant_tool::Tool as _;
+use assistant_tools::{ReadFileTool, ReadFileToolInput};
use client::{Client, UserStore};
use clock::FakeSystemClock;
+use language_model::{LanguageModelRequest, fake_provider::FakeLanguageModel};
use extension::ExtensionHostProxy;
use fs::{FakeFs, Fs};
@@ -1548,6 +1551,70 @@ async fn test_remote_git_branches(cx: &mut TestAppContext, server_cx: &mut TestA
assert_eq!(server_branch.name(), "totally-new-branch");
}
+#[gpui::test]
+async fn test_remote_agent_fs_tool_calls(cx: &mut TestAppContext, server_cx: &mut TestAppContext) {
+ let fs = FakeFs::new(server_cx.executor());
+ fs.insert_tree(
+ path!("/project"),
+ json!({
+ "a.txt": "A",
+ "b.txt": "B",
+ }),
+ )
+ .await;
+
+ let (project, _headless_project) = init_test(&fs, cx, server_cx).await;
+ project
+ .update(cx, |project, cx| {
+ project.find_or_create_worktree(path!("/project"), true, cx)
+ })
+ .await
+ .unwrap();
+
+ let action_log = cx.new(|_| assistant_tool::ActionLog::new(project.clone()));
+ let model = Arc::new(FakeLanguageModel::default());
+ let request = Arc::new(LanguageModelRequest::default());
+
+ let input = ReadFileToolInput {
+ path: "project/b.txt".into(),
+ start_line: None,
+ end_line: None,
+ };
+ let exists_result = cx.update(|cx| {
+ ReadFileTool::run(
+ Arc::new(ReadFileTool),
+ serde_json::to_value(input).unwrap(),
+ request.clone(),
+ project.clone(),
+ action_log.clone(),
+ model.clone(),
+ None,
+ cx,
+ )
+ });
+ let output = exists_result.output.await.unwrap().content;
+ assert_eq!(output, "B");
+
+ let input = ReadFileToolInput {
+ path: "project/c.txt".into(),
+ start_line: None,
+ end_line: None,
+ };
+ let does_not_exist_result = cx.update(|cx| {
+ ReadFileTool::run(
+ Arc::new(ReadFileTool),
+ serde_json::to_value(input).unwrap(),
+ request.clone(),
+ project.clone(),
+ action_log.clone(),
+ model.clone(),
+ None,
+ cx,
+ )
+ });
+ does_not_exist_result.output.await.unwrap_err();
+}
+
pub async fn init_test(
server_fs: &Arc<FakeFs>,
cx: &mut TestAppContext,