mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-17 06:04:23 +01:00
Co-authored-by: Michael Neale <michael.neale@gmail.com> Co-authored-by: Wendy Tang <wendytang@squareup.com> Co-authored-by: Jarrod Sibbison <72240382+jsibbison-square@users.noreply.github.com> Co-authored-by: Alex Hancock <alex.hancock@example.com> Co-authored-by: Alex Hancock <alexhancock@block.xyz> Co-authored-by: Lifei Zhou <lifei@squareup.com> Co-authored-by: Wes <141185334+wesrblock@users.noreply.github.com> Co-authored-by: Max Novich <maksymstepanenko1990@gmail.com> Co-authored-by: Zaki Ali <zaki@squareup.com> Co-authored-by: Salman Mohammed <smohammed@squareup.com> Co-authored-by: Kalvin C <kalvinnchau@users.noreply.github.com> Co-authored-by: Alec Thomas <alec@swapoff.org> Co-authored-by: lily-de <119957291+lily-de@users.noreply.github.com> Co-authored-by: kalvinnchau <kalvin@block.xyz> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Rizel Scarlett <rizel@squareup.com> Co-authored-by: bwrage <bwrage@squareup.com> Co-authored-by: Kalvin Chau <kalvin@squareup.com> Co-authored-by: Alice Hau <110418948+ahau-square@users.noreply.github.com> Co-authored-by: Alistair Gray <ajgray@stripe.com> Co-authored-by: Nahiyan Khan <nahiyan.khan@gmail.com> Co-authored-by: Alex Hancock <alexhancock@squareup.com> Co-authored-by: Nahiyan Khan <nahiyan@squareup.com> Co-authored-by: marcelle <1852848+laanak08@users.noreply.github.com> Co-authored-by: Yingjie He <yingjiehe@block.xyz> Co-authored-by: Yingjie He <yingjiehe@squareup.com> Co-authored-by: Lily Delalande <ldelalande@block.xyz> Co-authored-by: Adewale Abati <acekyd01@gmail.com> Co-authored-by: Ebony Louis <ebony774@gmail.com> Co-authored-by: Angie Jones <jones.angie@gmail.com> Co-authored-by: Ebony Louis <55366651+EbonyLouis@users.noreply.github.com>
179 lines
6.1 KiB
Rust
179 lines
6.1 KiB
Rust
use rand::{distributions::Alphanumeric, Rng};
|
|
use std::process;
|
|
|
|
use crate::prompt::rustyline::RustylinePrompt;
|
|
use crate::session::{ensure_session_dir, get_most_recent_session, Session};
|
|
use console::style;
|
|
use goose::agents::extension::{Envs, ExtensionError};
|
|
use goose::agents::AgentFactory;
|
|
use goose::config::{Config, ExtensionConfig, ExtensionManager};
|
|
use goose::providers::create;
|
|
use std::path::Path;
|
|
|
|
use mcp_client::transport::Error as McpClientError;
|
|
|
|
pub async fn build_session(
|
|
name: Option<String>,
|
|
resume: bool,
|
|
extension: Option<String>,
|
|
builtin: Option<String>,
|
|
) -> Session<'static> {
|
|
// Load config and get provider/model
|
|
let config = Config::global();
|
|
|
|
let provider_name: String = config
|
|
.get("GOOSE_PROVIDER")
|
|
.expect("No provider configured. Run 'goose configure' first");
|
|
let session_dir = ensure_session_dir().expect("Failed to create session directory");
|
|
|
|
let model: String = config
|
|
.get("GOOSE_MODEL")
|
|
.expect("No model configured. Run 'goose configure' first");
|
|
let model_config = goose::model::ModelConfig::new(model.clone());
|
|
let provider = create(&provider_name, model_config).expect("Failed to create provider");
|
|
|
|
// Create the agent
|
|
let agent_version: Option<String> = config.get("GOOSE_AGENT").ok();
|
|
let mut agent = match agent_version {
|
|
Some(version) => AgentFactory::create(&version, provider),
|
|
None => AgentFactory::create(AgentFactory::default_version(), provider),
|
|
}
|
|
.expect("Failed to create agent");
|
|
|
|
// Setup extensions for the agent
|
|
for extension in ExtensionManager::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);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Add extension if provided
|
|
if let Some(extension_str) = extension {
|
|
let mut parts: Vec<&str> = extension_str.split_whitespace().collect();
|
|
let mut envs = std::collections::HashMap::new();
|
|
|
|
// Parse environment variables (format: KEY=value)
|
|
while let Some(part) = parts.first() {
|
|
if !part.contains('=') {
|
|
break;
|
|
}
|
|
let env_part = parts.remove(0);
|
|
let (key, value) = env_part.split_once('=').unwrap();
|
|
envs.insert(key.to_string(), value.to_string());
|
|
}
|
|
|
|
if parts.is_empty() {
|
|
eprintln!("No command provided in extension string");
|
|
process::exit(1);
|
|
}
|
|
|
|
let cmd = parts.remove(0).to_string();
|
|
//this is an ephemeral extension so name does not matter
|
|
let name = rand::thread_rng()
|
|
.sample_iter(&Alphanumeric)
|
|
.take(8)
|
|
.map(char::from)
|
|
.collect();
|
|
let config = ExtensionConfig::Stdio {
|
|
name,
|
|
cmd,
|
|
args: parts.iter().map(|s| s.to_string()).collect(),
|
|
envs: Envs::new(envs),
|
|
};
|
|
|
|
agent.add_extension(config).await.unwrap_or_else(|e| {
|
|
eprintln!("Failed to start extension: {}", e);
|
|
process::exit(1);
|
|
});
|
|
}
|
|
|
|
// Add builtin extension if provided
|
|
if let Some(name) = builtin {
|
|
let config = ExtensionConfig::Builtin { name };
|
|
agent.add_extension(config).await.unwrap_or_else(|e| {
|
|
eprintln!("Failed to start builtin extension: {}", e);
|
|
process::exit(1);
|
|
});
|
|
}
|
|
|
|
// If resuming, try to find the session
|
|
if resume {
|
|
if let Some(ref session_name) = name {
|
|
// Try to resume specific session
|
|
let session_file = session_dir.join(format!("{}.jsonl", session_name));
|
|
if session_file.exists() {
|
|
let prompt = Box::new(RustylinePrompt::new());
|
|
return Session::new(agent, prompt, session_file);
|
|
} else {
|
|
eprintln!("Session '{}' not found, starting new session", session_name);
|
|
}
|
|
} else {
|
|
// Try to resume most recent session
|
|
if let Ok(session_file) = get_most_recent_session() {
|
|
let prompt = Box::new(RustylinePrompt::new());
|
|
return Session::new(agent, prompt, session_file);
|
|
} else {
|
|
eprintln!("No previous sessions found, starting new session");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate session name if not provided
|
|
let name = name.unwrap_or_else(|| {
|
|
rand::thread_rng()
|
|
.sample_iter(&Alphanumeric)
|
|
.take(8)
|
|
.map(char::from)
|
|
.collect()
|
|
});
|
|
|
|
let session_file = session_dir.join(format!("{}.jsonl", name));
|
|
if session_file.exists() {
|
|
eprintln!("Session '{}' already exists", name);
|
|
process::exit(1);
|
|
}
|
|
|
|
let prompt = Box::new(RustylinePrompt::new());
|
|
|
|
display_session_info(resume, &provider_name, &model, &session_file);
|
|
Session::new(agent, prompt, session_file)
|
|
}
|
|
|
|
fn display_session_info(resume: bool, provider: &str, model: &str, session_file: &Path) {
|
|
let start_session_msg = if resume {
|
|
"resuming session |"
|
|
} else {
|
|
"starting session |"
|
|
};
|
|
println!(
|
|
"{} {} {} {} {}",
|
|
style(start_session_msg).dim(),
|
|
style("provider:").dim(),
|
|
style(provider).cyan().dim(),
|
|
style("model:").dim(),
|
|
style(model).cyan().dim(),
|
|
);
|
|
println!(
|
|
" {} {}",
|
|
style("logging to").dim(),
|
|
style(session_file.display()).dim().cyan(),
|
|
);
|
|
}
|