diff --git a/crates/goose/src/agents/truncate.rs b/crates/goose/src/agents/truncate.rs index 70fe57f0beb..8cedfc11fae 100644 --- a/crates/goose/src/agents/truncate.rs +++ b/crates/goose/src/agents/truncate.rs @@ -235,6 +235,21 @@ impl Agent for TruncateAgent { tools.push(list_resources_tool); } + let (tools_with_readonly_annotation, tools_without_annotation): (Vec, Vec) = + tools.iter().fold((vec![], vec![]), |mut acc, tool| { + match &tool.annotations { + Some(annotations) => { + if annotations.read_only_hint { + acc.0.push(tool.name.clone()); + } + } + None => { + acc.1.push(tool.name.clone()); + } + } + acc + }); + let config = capabilities.provider().get_model_config(); let mut system_prompt = capabilities.get_system_prompt().await; let mut toolshim_tools = vec![]; @@ -310,30 +325,40 @@ impl Agent for TruncateAgent { let mode = goose_mode.clone(); match mode.as_str() { "approve" | "smart_approve" => { - let mut read_only_tools = Vec::new(); let mut needs_confirmation = Vec::<&ToolRequest>::new(); let mut approved_tools = Vec::new(); + let mut llm_detect_candidates = Vec::<&ToolRequest>::new(); + let mut detected_read_only_tools = Vec::new(); // First check permissions for all tools let store = ToolPermissionStore::load()?; for request in tool_requests.iter() { if let Ok(tool_call) = request.tool_call.clone() { - if let Some(allowed) = store.check_permission(request) { + if tools_with_readonly_annotation.contains(&tool_call.name) { + approved_tools.push((request.id.clone(), tool_call)); + } else if let Some(allowed) = store.check_permission(request) { if allowed { // Instead of executing immediately, collect approved tools approved_tools.push((request.id.clone(), tool_call)); } else { + // If the tool doesn't have any annotation, we can use llm-as-a-judge to check permission. + if tools_without_annotation.contains(&tool_call.name) { + llm_detect_candidates.push(request); + } needs_confirmation.push(request); } } else { + if tools_without_annotation.contains(&tool_call.name) { + llm_detect_candidates.push(request); + } needs_confirmation.push(request); } } } - // Only check read-only status for tools needing confirmation - if !needs_confirmation.is_empty() && mode == "smart_approve" { - read_only_tools = detect_read_only_tools(&capabilities, needs_confirmation.clone()).await; + // Only check read-only status for tools without annotation + if !llm_detect_candidates.is_empty() && mode == "smart_approve" { + detected_read_only_tools = detect_read_only_tools(&capabilities, llm_detect_candidates.clone()).await; } // Handle pre-approved and read-only tools in parallel @@ -349,7 +374,7 @@ impl Agent for TruncateAgent { for request in &needs_confirmation { if let Ok(tool_call) = request.tool_call.clone() { // Skip confirmation if the tool_call.name is in the read_only_tools list - if read_only_tools.contains(&tool_call.name) { + if detected_read_only_tools.contains(&tool_call.name) { let tool_future = Self::create_tool_future(&capabilities, tool_call, request.id.clone()); tool_futures.push(tool_future); } else {