Files
goose/crates/goose-cli/src/commands/session.rs
Bradley Axen 1c9a7c0b05 feat: V1.0 (#734)
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>
2025-01-24 13:04:43 -08:00

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(),
);
}