feat: add recipes, a custom goose agent configuration (#2115)

This commit is contained in:
Kalvin C
2025-04-09 18:57:24 -07:00
committed by GitHub
parent d84787f144
commit d1c124c28d
17 changed files with 884 additions and 145 deletions

View File

@@ -1,7 +1,7 @@
use console::style;
use goose::agents::extension::ExtensionError;
use goose::agents::Agent;
use goose::config::{Config, ExtensionConfigManager};
use goose::config::{Config, ExtensionConfig, ExtensionConfigManager};
use goose::session;
use goose::session::Identifier;
use mcp_client::transport::Error as McpClientError;
@@ -10,14 +10,31 @@ use std::process;
use super::output;
use super::Session;
pub async fn build_session(
identifier: Option<Identifier>,
resume: bool,
extensions: Vec<String>,
remote_extensions: Vec<String>,
builtins: Vec<String>,
debug: bool,
) -> Session {
/// Configuration for building a new Goose session
///
/// This struct contains all the parameters needed to create a new session,
/// including session identification, extension configuration, and debug settings.
#[derive(Default, Clone, Debug)]
pub struct SessionBuilderConfig {
/// Optional identifier for the session (name or path)
pub identifier: Option<Identifier>,
/// Whether to resume an existing session
pub resume: bool,
/// List of stdio extension commands to add
pub extensions: Vec<String>,
/// List of remote extension commands to add
pub remote_extensions: Vec<String>,
/// List of builtin extension commands to add
pub builtins: Vec<String>,
/// List of extensions to enable, enable only this set and ignore configured ones
pub extensions_override: Option<Vec<ExtensionConfig>>,
/// Any additional system prompt to append to the default
pub additional_system_prompt: Option<String>,
/// Enable debug printing
pub debug: bool,
}
pub async fn build_session(session_config: SessionBuilderConfig) -> Session {
// Load config and get provider/model
let config = Config::global();
@@ -36,8 +53,8 @@ pub async fn build_session(
let mut agent = Agent::new(provider);
// Handle session file resolution and resuming
let session_file = if resume {
if let Some(identifier) = identifier {
let session_file = if session_config.resume {
if let Some(identifier) = session_config.identifier {
let session_file = session::get_path(identifier);
if !session_file.exists() {
output::render_error(&format!(
@@ -60,7 +77,7 @@ pub async fn build_session(
}
} else {
// Create new session with provided name/path or generated name
let id = match identifier {
let id = match session_config.identifier {
Some(identifier) => identifier,
None => Identifier::Name(session::generate_session_id()),
};
@@ -69,7 +86,7 @@ pub async fn build_session(
session::get_path(id)
};
if resume {
if session_config.resume {
// Read the session metadata
let metadata = session::read_metadata(&session_file).unwrap_or_else(|e| {
output::render_error(&format!("Failed to read session metadata: {}", e));
@@ -92,34 +109,38 @@ pub async fn build_session(
// Setup extensions for the agent
// Extensions need to be added after the session is created because we change directory when resuming a session
for extension in ExtensionConfigManager::get_all().expect("should load extensions") {
if extension.enabled {
let config = extension.config.clone();
agent
.add_extension(config.clone())
.await
.unwrap_or_else(|e| {
let err = match e {
ExtensionError::Transport(McpClientError::StdioProcessError(inner)) => {
inner
}
_ => e.to_string(),
};
println!("Failed to start extension: {}, {:?}", config.name(), err);
println!(
"Please check extension configuration for {}.",
config.name()
);
process::exit(1);
});
// If we get extensions_override, only run those extensions and none other
let extensions_to_run: Vec<_> = if let Some(extensions) = session_config.extensions_override {
extensions.into_iter().collect()
} else {
ExtensionConfigManager::get_all()
.expect("should load extensions")
.into_iter()
.filter(|ext| ext.enabled)
.map(|ext| ext.config)
.collect()
};
for extension in extensions_to_run {
if let Err(e) = agent.add_extension(extension.clone()).await {
let err = match e {
ExtensionError::Transport(McpClientError::StdioProcessError(inner)) => inner,
_ => e.to_string(),
};
eprintln!("Failed to start extension: {}, {:?}", extension.name(), err);
eprintln!(
"Please check extension configuration for {}.",
extension.name()
);
process::exit(1);
}
}
// Create new session
let mut session = Session::new(agent, session_file.clone(), debug);
let mut session = Session::new(agent, session_file.clone(), session_config.debug);
// Add extensions if provided
for extension_str in extensions {
for extension_str in session_config.extensions {
if let Err(e) = session.add_extension(extension_str).await {
eprintln!("Failed to start extension: {}", e);
process::exit(1);
@@ -127,7 +148,7 @@ pub async fn build_session(
}
// Add remote extensions if provided
for extension_str in remote_extensions {
for extension_str in session_config.remote_extensions {
if let Err(e) = session.add_remote_extension(extension_str).await {
eprintln!("Failed to start extension: {}", e);
process::exit(1);
@@ -135,7 +156,7 @@ pub async fn build_session(
}
// Add builtin extensions
for builtin in builtins {
for builtin in session_config.builtins {
if let Err(e) = session.add_builtin(builtin).await {
eprintln!("Failed to start builtin extension: {}", e);
process::exit(1);
@@ -148,6 +169,10 @@ pub async fn build_session(
.extend_system_prompt(super::prompt::get_cli_prompt())
.await;
if let Some(additional_prompt) = session_config.additional_system_prompt {
session.agent.extend_system_prompt(additional_prompt).await;
}
// Only override system prompt if a system override exists
let system_prompt_file: Option<String> = config.get_param("GOOSE_SYSTEM_PROMPT_FILE_PATH").ok();
if let Some(ref path) = system_prompt_file {
@@ -156,6 +181,6 @@ pub async fn build_session(
session.agent.override_system_prompt(override_prompt).await;
}
output::display_session_info(resume, &provider_name, &model, &session_file);
output::display_session_info(session_config.resume, &provider_name, &model, &session_file);
session
}