diff --git a/crates/goose/src/agents/recipe_tools/sub_recipe_tools.rs b/crates/goose/src/agents/recipe_tools/sub_recipe_tools.rs index a3a15b05e6..b3f02ff9b4 100644 --- a/crates/goose/src/agents/recipe_tools/sub_recipe_tools.rs +++ b/crates/goose/src/agents/recipe_tools/sub_recipe_tools.rs @@ -57,15 +57,13 @@ fn create_tasks_from_params( sub_recipe: &SubRecipe, command_params: &[std::collections::HashMap], ) -> Result> { + let recipe_file = load_local_recipe_file(&sub_recipe.path) + .map_err(|e| anyhow::anyhow!("Failed to load recipe {}: {}", sub_recipe.path, e))?; + let mut tasks = Vec::new(); - for task_command_param in command_params { - // Load the recipe file for each task (can't clone RecipeFile) - let recipe_file = load_local_recipe_file(&sub_recipe.path) - .map_err(|e| anyhow::anyhow!("Failed to load recipe {}: {}", sub_recipe.path, e))?; - let recipe = build_recipe_from_template( - recipe_file.content, + recipe_file.content.clone(), &recipe_file.parent_dir, task_command_param .iter() diff --git a/crates/goose/src/agents/subagent_execution_tool/task_execution_tracker.rs b/crates/goose/src/agents/subagent_execution_tool/task_execution_tracker.rs index f3a5e4a764..67731cc781 100644 --- a/crates/goose/src/agents/subagent_execution_tool/task_execution_tracker.rs +++ b/crates/goose/src/agents/subagent_execution_tool/task_execution_tracker.rs @@ -17,6 +17,8 @@ use crate::agents::subagent_execution_tool::utils::{count_by_status, get_task_na use crate::utils::is_token_cancelled; use tokio::sync::mpsc::Sender; +const RECIPE_TASK_TYPE: &str = "recipe"; + #[derive(Debug, Clone, PartialEq)] pub enum DisplayMode { MultipleTasksOutput, @@ -148,13 +150,15 @@ impl TaskExecutionTracker { async fn format_line(&self, task_info: Option<&TaskInfo>, line: &str) -> String { if let Some(task_info) = task_info { let task_name = get_task_name(task_info); - let task_type = "recipe"; let metadata = format_task_metadata(task_info); if metadata.is_empty() { - format!("[{} ({})] {}", task_name, task_type, line) + format!("[{} ({})] {}", task_name, RECIPE_TASK_TYPE, line) } else { - format!("[{} ({}) {}] {}", task_name, task_type, metadata, line) + format!( + "[{} ({}) {}] {}", + task_name, RECIPE_TASK_TYPE, metadata, line + ) } } else { line.to_string() @@ -229,7 +233,7 @@ impl TaskExecutionTracker { } }), current_output: task_info.current_output.clone(), - task_type: "recipe".to_string(), + task_type: RECIPE_TASK_TYPE.to_string(), task_name: get_task_name(task_info).to_string(), task_metadata: format_task_metadata(task_info), error: task_info.error().cloned(), diff --git a/crates/goose/src/agents/subagent_execution_tool/task_types.rs b/crates/goose/src/agents/subagent_execution_tool/task_types.rs index 2ed232306a..91d1ec6d3a 100644 --- a/crates/goose/src/agents/subagent_execution_tool/task_types.rs +++ b/crates/goose/src/agents/subagent_execution_tool/task_types.rs @@ -20,9 +20,7 @@ pub enum ExecutionMode { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TaskPayload { pub recipe: Recipe, - #[serde(default)] pub return_last_only: bool, - #[serde(default)] pub sequential_when_repeated: bool, #[serde(skip_serializing_if = "Option::is_none")] pub parameter_values: Option>, diff --git a/crates/goose/src/agents/subagent_execution_tool/tasks.rs b/crates/goose/src/agents/subagent_execution_tool/tasks.rs index 59982bf0a6..9583b281ec 100644 --- a/crates/goose/src/agents/subagent_execution_tool/tasks.rs +++ b/crates/goose/src/agents/subagent_execution_tool/tasks.rs @@ -8,18 +8,11 @@ use crate::agents::subagent_task_config::TaskConfig; pub async fn process_task( task: &Task, - task_execution_tracker: Arc, + _task_execution_tracker: Arc, task_config: TaskConfig, cancellation_token: CancellationToken, ) -> TaskResult { - match get_task_result( - task.clone(), - task_execution_tracker, - task_config, - cancellation_token, - ) - .await - { + match handle_recipe_task(task.clone(), task_config, cancellation_token).await { Ok(data) => TaskResult { task_id: task.id.clone(), status: TaskStatus::Completed, @@ -35,15 +28,6 @@ pub async fn process_task( } } -async fn get_task_result( - task: Task, - _task_execution_tracker: Arc, - task_config: TaskConfig, - cancellation_token: CancellationToken, -) -> Result { - handle_recipe_task(task, task_config, cancellation_token).await -} - async fn handle_recipe_task( task: Task, mut task_config: TaskConfig, diff --git a/crates/goose/src/agents/subagent_execution_tool/utils/mod.rs b/crates/goose/src/agents/subagent_execution_tool/utils/mod.rs index 8afa29dd57..58de4c27d2 100644 --- a/crates/goose/src/agents/subagent_execution_tool/utils/mod.rs +++ b/crates/goose/src/agents/subagent_execution_tool/utils/mod.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use crate::agents::subagent_execution_tool::task_types::{TaskInfo, TaskStatus}; pub fn get_task_name(task_info: &TaskInfo) -> &str { - // Return the recipe title (always present as it's a required field) &task_info.task.payload.recipe.title } diff --git a/crates/goose/src/agents/subagent_handler.rs b/crates/goose/src/agents/subagent_handler.rs index 06d1a58ea3..a67f4fa55a 100644 --- a/crates/goose/src/agents/subagent_handler.rs +++ b/crates/goose/src/agents/subagent_handler.rs @@ -12,7 +12,6 @@ use std::future::Future; use std::pin::Pin; use tracing::{debug, info}; -// Type alias for the complex return type type AgentMessagesFuture = Pin)>> + Send>>; diff --git a/crates/goose/src/recipe/read_recipe_file_content.rs b/crates/goose/src/recipe/read_recipe_file_content.rs index 61b740304a..740e61a2a9 100644 --- a/crates/goose/src/recipe/read_recipe_file_content.rs +++ b/crates/goose/src/recipe/read_recipe_file_content.rs @@ -1,6 +1,8 @@ use anyhow::{anyhow, Result}; use std::fs; use std::path::{Path, PathBuf}; + +#[derive(Clone)] pub struct RecipeFile { pub content: String, pub parent_dir: PathBuf, diff --git a/crates/goose/src/session/session_manager.rs b/crates/goose/src/session/session_manager.rs index a5ef66dc92..283c52f434 100644 --- a/crates/goose/src/session/session_manager.rs +++ b/crates/goose/src/session/session_manager.rs @@ -14,11 +14,11 @@ use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; use std::sync::Arc; -use tokio::sync::{Mutex, OnceCell}; +use tokio::sync::OnceCell; use tracing::{info, warn}; use utoipa::ToSchema; -const CURRENT_SCHEMA_VERSION: i32 = 3; +const CURRENT_SCHEMA_VERSION: i32 = 2; static SESSION_STORAGE: OnceCell> = OnceCell::const_new(); @@ -245,7 +245,6 @@ impl SessionManager { pub struct SessionStorage { pool: Pool, - create_session_mutex: Arc>, } pub fn ensure_session_dir() -> Result { @@ -354,7 +353,8 @@ impl SessionStorage { let options = SqliteConnectOptions::new() .filename(db_path) .create_if_missing(create_if_missing) - .busy_timeout(std::time::Duration::from_secs(5)); + .busy_timeout(std::time::Duration::from_secs(5)) + .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal); sqlx::SqlitePool::connect_with(options).await.map_err(|e| { anyhow::anyhow!( @@ -368,10 +368,7 @@ impl SessionStorage { async fn open(db_path: &Path) -> Result { let pool = Self::get_pool(db_path, false).await?; - let storage = Self { - pool, - create_session_mutex: Arc::new(Mutex::new(())), - }; + let storage = Self { pool }; storage.run_migrations().await?; Ok(storage) } @@ -428,8 +425,7 @@ impl SessionStorage { content_json TEXT NOT NULL, created_timestamp INTEGER NOT NULL, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - tokens INTEGER, - metadata_json TEXT + tokens INTEGER ) "#, ) @@ -446,10 +442,7 @@ impl SessionStorage { .execute(&pool) .await?; - Ok(Self { - pool, - create_session_mutex: Arc::new(Mutex::new(())), - }) + Ok(Self { pool }) } async fn import_legacy(&self, session_dir: &PathBuf) -> Result<()> { @@ -617,15 +610,6 @@ impl SessionStorage { .execute(&self.pool) .await?; } - 3 => { - sqlx::query( - r#" - ALTER TABLE messages ADD COLUMN metadata_json TEXT - "#, - ) - .execute(&self.pool) - .await?; - } _ => { anyhow::bail!("Unknown migration version: {}", version); } @@ -635,9 +619,6 @@ impl SessionStorage { } async fn create_session(&self, working_dir: PathBuf, description: String) -> Result { - // Mutex to prevent concurrent session creation race conditions - let _guard = self.create_session_mutex.lock().await; - let today = chrono::Utc::now().format("%Y%m%d").to_string(); Ok(sqlx::query_as( r#" @@ -787,15 +768,15 @@ impl SessionStorage { } async fn get_conversation(&self, session_id: &str) -> Result { - let rows = sqlx::query_as::<_, (String, String, i64, Option)>( - "SELECT role, content_json, created_timestamp, metadata_json FROM messages WHERE session_id = ? ORDER BY timestamp", + let rows = sqlx::query_as::<_, (String, String, i64)>( + "SELECT role, content_json, created_timestamp FROM messages WHERE session_id = ? ORDER BY timestamp", ) .bind(session_id) .fetch_all(&self.pool) .await?; let mut messages = Vec::new(); - for (role_str, content_json, created_timestamp, metadata_json) in rows { + for (role_str, content_json, created_timestamp) in rows { let role = match role_str.as_str() { "user" => Role::User, "assistant" => Role::Assistant, @@ -803,12 +784,7 @@ impl SessionStorage { }; let content = serde_json::from_str(&content_json)?; - let metadata = metadata_json - .and_then(|json| serde_json::from_str(&json).ok()) - .unwrap_or_default(); - - let mut message = Message::new(role, created_timestamp, content); - message.metadata = metadata; + let message = Message::new(role, created_timestamp, content); messages.push(message); } @@ -816,19 +792,16 @@ impl SessionStorage { } async fn add_message(&self, session_id: &str, message: &Message) -> Result<()> { - let metadata_json = serde_json::to_string(&message.metadata)?; - sqlx::query( r#" - INSERT INTO messages (session_id, role, content_json, created_timestamp, metadata_json) - VALUES (?, ?, ?, ?, ?) + INSERT INTO messages (session_id, role, content_json, created_timestamp) + VALUES (?, ?, ?, ?) "#, ) .bind(session_id) .bind(role_to_string(&message.role)) .bind(serde_json::to_string(&message.content)?) .bind(message.created) - .bind(metadata_json) .execute(&self.pool) .await?; @@ -853,19 +826,16 @@ impl SessionStorage { .await?; for message in conversation.messages() { - let metadata_json = serde_json::to_string(&message.metadata)?; - sqlx::query( r#" - INSERT INTO messages (session_id, role, content_json, created_timestamp, metadata_json) - VALUES (?, ?, ?, ?, ?) + INSERT INTO messages (session_id, role, content_json, created_timestamp) + VALUES (?, ?, ?, ?) "#, ) .bind(session_id) .bind(role_to_string(&message.role)) .bind(serde_json::to_string(&message.content)?) .bind(message.created) - .bind(metadata_json) .execute(&mut *tx) .await?; }