Cargo.lock 🔗
@@ -8,6 +8,7 @@ version = "0.1.0"
dependencies = [
"action_log",
"agent-client-protocol",
+ "agent_settings",
"anyhow",
"buffer_diff",
"collections",
Agus Zubiaga created
Release Notes:
- Gemini integration: Fixed a bug with permission requests when
`always_allow_tool_calls` is enabled
Cargo.lock | 1
crates/acp_thread/Cargo.toml | 1
crates/acp_thread/src/acp_thread.rs | 35 ++++++++++++++++++++++++++++-
crates/acp_thread/src/connection.rs | 17 +++++++------
crates/agent2/src/agent.rs | 13 ++++------
crates/agent_servers/src/acp.rs | 36 +++---------------------------
6 files changed, 53 insertions(+), 50 deletions(-)
@@ -8,6 +8,7 @@ version = "0.1.0"
dependencies = [
"action_log",
"agent-client-protocol",
+ "agent_settings",
"anyhow",
"buffer_diff",
"collections",
@@ -19,6 +19,7 @@ test-support = ["gpui/test-support", "project/test-support", "dep:parking_lot"]
action_log.workspace = true
agent-client-protocol.workspace = true
anyhow.workspace = true
+agent_settings.workspace = true
buffer_diff.workspace = true
collections.workspace = true
editor.workspace = true
@@ -3,6 +3,7 @@ mod diff;
mod mention;
mod terminal;
+use agent_settings::AgentSettings;
use collections::HashSet;
pub use connection::*;
pub use diff::*;
@@ -10,6 +11,7 @@ use language::language_settings::FormatOnSave;
pub use mention::*;
use project::lsp_store::{FormatTrigger, LspFormatTarget};
use serde::{Deserialize, Serialize};
+use settings::Settings as _;
pub use terminal::*;
use action_log::ActionLog;
@@ -1230,9 +1232,29 @@ impl AcpThread {
tool_call: acp::ToolCallUpdate,
options: Vec<acp::PermissionOption>,
cx: &mut Context<Self>,
- ) -> Result<oneshot::Receiver<acp::PermissionOptionId>, acp::Error> {
+ ) -> Result<BoxFuture<'static, acp::RequestPermissionOutcome>> {
let (tx, rx) = oneshot::channel();
+ if AgentSettings::get_global(cx).always_allow_tool_actions {
+ // Don't use AllowAlways, because then if you were to turn off always_allow_tool_actions,
+ // some tools would (incorrectly) continue to auto-accept.
+ if let Some(allow_once_option) = options.iter().find_map(|option| {
+ if matches!(option.kind, acp::PermissionOptionKind::AllowOnce) {
+ Some(option.id.clone())
+ } else {
+ None
+ }
+ }) {
+ self.upsert_tool_call_inner(tool_call, ToolCallStatus::Pending, cx)?;
+ return Ok(async {
+ acp::RequestPermissionOutcome::Selected {
+ option_id: allow_once_option,
+ }
+ }
+ .boxed());
+ }
+ }
+
let status = ToolCallStatus::WaitingForConfirmation {
options,
respond_tx: tx,
@@ -1240,7 +1262,16 @@ impl AcpThread {
self.upsert_tool_call_inner(tool_call, status, cx)?;
cx.emit(AcpThreadEvent::ToolAuthorizationRequired);
- Ok(rx)
+
+ let fut = async {
+ match rx.await {
+ Ok(option) => acp::RequestPermissionOutcome::Selected { option_id: option },
+ Err(oneshot::Canceled) => acp::RequestPermissionOutcome::Cancelled,
+ }
+ }
+ .boxed();
+
+ Ok(fut)
}
pub fn authorize_tool_call(
@@ -393,14 +393,15 @@ mod test_support {
};
let task = cx.spawn(async move |cx| {
if let Some((tool_call, options)) = permission_request {
- let permission = thread.update(cx, |thread, cx| {
- thread.request_tool_call_authorization(
- tool_call.clone().into(),
- options.clone(),
- cx,
- )
- })?;
- permission?.await?;
+ thread
+ .update(cx, |thread, cx| {
+ thread.request_tool_call_authorization(
+ tool_call.clone().into(),
+ options.clone(),
+ cx,
+ )
+ })??
+ .await;
}
thread.update(cx, |thread, cx| {
thread.handle_session_update(update.clone(), cx).unwrap();
@@ -762,18 +762,15 @@ impl NativeAgentConnection {
options,
response,
}) => {
- let recv = acp_thread.update(cx, |thread, cx| {
+ let outcome_task = acp_thread.update(cx, |thread, cx| {
thread.request_tool_call_authorization(tool_call, options, cx)
- })?;
+ })??;
cx.background_spawn(async move {
- if let Some(recv) = recv.log_err()
- && let Some(option) = recv
- .await
- .context("authorization sender was dropped")
- .log_err()
+ if let acp::RequestPermissionOutcome::Selected { option_id } =
+ outcome_task.await
{
response
- .send(option)
+ .send(option_id)
.map(|_| anyhow!("authorization receiver was dropped"))
.log_err();
}
@@ -3,15 +3,13 @@ use acp_thread::AgentConnection;
use acp_tools::AcpConnectionRegistry;
use action_log::ActionLog;
use agent_client_protocol::{self as acp, Agent as _, ErrorCode};
-use agent_settings::AgentSettings;
use anyhow::anyhow;
use collections::HashMap;
use futures::AsyncBufReadExt as _;
-use futures::channel::oneshot;
use futures::io::BufReader;
use project::Project;
use serde::Deserialize;
-use settings::Settings as _;
+
use std::{any::Any, cell::RefCell};
use std::{path::Path, rc::Rc};
use thiserror::Error;
@@ -345,28 +343,7 @@ impl acp::Client for ClientDelegate {
) -> Result<acp::RequestPermissionResponse, acp::Error> {
let cx = &mut self.cx.clone();
- // If always_allow_tool_actions is enabled, then auto-choose the first "Allow" button
- if AgentSettings::try_read_global(cx, |settings| settings.always_allow_tool_actions)
- .unwrap_or(false)
- {
- // Don't use AllowAlways, because then if you were to turn off always_allow_tool_actions,
- // some tools would (incorrectly) continue to auto-accept.
- if let Some(allow_once_option) = arguments.options.iter().find_map(|option| {
- if matches!(option.kind, acp::PermissionOptionKind::AllowOnce) {
- Some(option.id.clone())
- } else {
- None
- }
- }) {
- return Ok(acp::RequestPermissionResponse {
- outcome: acp::RequestPermissionOutcome::Selected {
- option_id: allow_once_option,
- },
- });
- }
- }
-
- let rx = self
+ let task = self
.sessions
.borrow()
.get(&arguments.session_id)
@@ -374,14 +351,9 @@ impl acp::Client for ClientDelegate {
.thread
.update(cx, |thread, cx| {
thread.request_tool_call_authorization(arguments.tool_call, arguments.options, cx)
- })?;
+ })??;
- let result = rx?.await;
-
- let outcome = match result {
- Ok(option) => acp::RequestPermissionOutcome::Selected { option_id: option },
- Err(oneshot::Canceled) => acp::RequestPermissionOutcome::Cancelled,
- };
+ let outcome = task.await;
Ok(acp::RequestPermissionResponse { outcome })
}