diff --git a/crates/goose/src/agents/subagent.rs b/crates/goose/src/agents/subagent.rs index d4150f2ff975..40b7d9e4f23d 100644 --- a/crates/goose/src/agents/subagent.rs +++ b/crates/goose/src/agents/subagent.rs @@ -1,3 +1,4 @@ +use crate::agents::subagent_task_config::DEFAULT_SUBAGENT_MAX_TURNS; use crate::{ agents::extension::ExtensionConfig, agents::{extension_manager::ExtensionManager, Agent, TaskConfig}, @@ -203,20 +204,6 @@ impl SubAgent { .as_ref() .ok_or_else(|| anyhow!("No provider configured for subagent"))?; - // Check if we've exceeded max turns - { - let turn_count = *self.turn_count.lock().await; - if let Some(max_turns) = self.config.max_turns { - if turn_count >= max_turns { - self.set_status(SubAgentStatus::Completed( - "Maximum turns exceeded".to_string(), - )) - .await; - return Err(anyhow!("Maximum turns ({}) exceeded", max_turns)); - } - } - } - // Set status to processing self.set_status(SubAgentStatus::Processing).await; @@ -227,17 +214,6 @@ impl SubAgent { conversation.push(user_message.clone()); } - // Increment turn count - { - let mut turn_count = self.turn_count.lock().await; - *turn_count += 1; - self.send_mcp_notification( - "turn_progress", - &format!("Turn {}/{}", turn_count, self.config.max_turns.unwrap_or(0)), - ) - .await; - } - // Get the current conversation for context let mut messages = self.get_conversation().await; @@ -255,8 +231,14 @@ impl SubAgent { // Build system prompt using the template let system_prompt = self.build_system_prompt(&tools).await?; + // Generate response from provider with loop for tool processing (max_turns iterations) + let mut loop_count = 0; + let max_turns = self.config.max_turns.unwrap_or(DEFAULT_SUBAGENT_MAX_TURNS); + // Generate response from provider loop { + loop_count += 1; + match Agent::generate_response_from_provider( Arc::clone(provider), &system_prompt, @@ -281,7 +263,7 @@ impl SubAgent { .collect(); // If there are no tool requests, we're done - if tool_requests.is_empty() { + if tool_requests.is_empty() || loop_count >= max_turns { self.add_message(response.clone()).await; // Send notification about response @@ -291,9 +273,6 @@ impl SubAgent { ) .await; - // Add delay before completion to ensure all processing finishes - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - // Set status back to ready and return the final response self.set_status(SubAgentStatus::Completed("Completed!".to_string())) .await; diff --git a/crates/goose/src/agents/subagent_task_config.rs b/crates/goose/src/agents/subagent_task_config.rs index 81bcd999b8e8..cbbc7821676f 100644 --- a/crates/goose/src/agents/subagent_task_config.rs +++ b/crates/goose/src/agents/subagent_task_config.rs @@ -1,10 +1,17 @@ use crate::providers::base::Provider; use rmcp::model::JsonRpcMessage; +use std::env; use std::fmt; use std::sync::Arc; use tokio::sync::mpsc; use uuid::Uuid; +/// Default maximum number of turns for task execution +pub const DEFAULT_SUBAGENT_MAX_TURNS: usize = 5; + +/// Environment variable name for configuring max turns +pub const GOOSE_SUBAGENT_MAX_TURNS_ENV_VAR: &str = "GOOSE_SUBAGENT_MAX_TURNS"; + /// Configuration for task execution with all necessary dependencies #[derive(Clone)] pub struct TaskConfig { @@ -31,7 +38,12 @@ impl TaskConfig { id: Uuid::new_v4().to_string(), provider, mcp_tx, - max_turns: Some(10), + max_turns: Some( + env::var(GOOSE_SUBAGENT_MAX_TURNS_ENV_VAR) + .ok() + .and_then(|val| val.parse::().ok()) + .unwrap_or(DEFAULT_SUBAGENT_MAX_TURNS), + ), } }