2025-02-11 21:16:58 -08:00
use console ::style ;
use goose ::agents ::extension ::ExtensionError ;
use goose ::agents ::AgentFactory ;
use goose ::config ::{ Config , ExtensionManager } ;
2025-03-03 11:49:15 -05:00
use goose ::session ;
use goose ::session ::Identifier ;
2025-02-11 21:16:58 -08:00
use mcp_client ::transport ::Error as McpClientError ;
use std ::process ;
use super ::output ;
use super ::Session ;
pub async fn build_session (
2025-03-03 11:49:15 -05:00
identifier : Option < Identifier > ,
2025-02-11 21:16:58 -08:00
resume : bool ,
extensions : Vec < String > ,
builtins : Vec < String > ,
2025-03-06 18:58:25 -08:00
debug : bool ,
2025-02-11 21:16:58 -08:00
) -> Session {
// Load config and get provider/model
let config = Config ::global ( ) ;
let provider_name : String = config
2025-03-10 09:51:54 -07:00
. get_param ( " GOOSE_PROVIDER " )
2025-02-11 21:16:58 -08:00
. expect ( " No provider configured. Run 'goose configure' first " ) ;
let model : String = config
2025-03-10 09:51:54 -07:00
. get_param ( " GOOSE_MODEL " )
2025-02-11 21:16:58 -08:00
. expect ( " No model configured. Run 'goose configure' first " ) ;
let model_config = goose ::model ::ModelConfig ::new ( model . clone ( ) ) ;
let provider =
goose ::providers ::create ( & provider_name , model_config ) . expect ( " Failed to create provider " ) ;
// Create the agent
2025-03-07 01:03:37 +08:00
let mut agent = AgentFactory ::create ( & AgentFactory ::configured_version ( ) , provider )
. expect ( " Failed to create agent " ) ;
2025-02-11 21:16:58 -08:00
// Handle session file resolution and resuming
let session_file = if resume {
2025-02-27 18:31:14 +08:00
if let Some ( identifier ) = identifier {
2025-03-03 11:49:15 -05:00
let session_file = session ::get_path ( identifier ) ;
2025-02-11 21:16:58 -08:00
if ! session_file . exists ( ) {
output ::render_error ( & format! (
" Cannot resume session {} - no such session exists " ,
2025-02-27 18:31:14 +08:00
style ( session_file . display ( ) ) . cyan ( )
2025-02-11 21:16:58 -08:00
) ) ;
process ::exit ( 1 ) ;
}
2025-03-07 11:12:57 -05:00
2025-02-11 21:16:58 -08:00
session_file
} else {
// Try to resume most recent session
2025-03-03 11:49:15 -05:00
match session ::get_most_recent_session ( ) {
2025-02-11 21:16:58 -08:00
Ok ( file ) = > file ,
Err ( _ ) = > {
output ::render_error ( " Cannot resume - no previous sessions found " ) ;
process ::exit ( 1 ) ;
}
}
}
} else {
2025-02-27 18:31:14 +08:00
// Create new session with provided name/path or generated name
let id = match identifier {
Some ( identifier ) = > identifier ,
2025-03-03 11:49:15 -05:00
None = > Identifier ::Name ( session ::generate_session_id ( ) ) ,
2025-02-27 18:31:14 +08:00
} ;
2025-03-03 11:49:15 -05:00
// Just get the path - file will be created when needed
session ::get_path ( id )
2025-02-11 21:16:58 -08:00
} ;
2025-03-07 11:12:57 -05:00
if 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 ) ) ;
process ::exit ( 1 ) ;
} ) ;
2025-03-20 07:42:26 +11:00
let current_workdir =
std ::env ::current_dir ( ) . expect ( " Failed to get current working directory " ) ;
if current_workdir ! = metadata . working_dir {
// Ask user if they want to change the working directory
let change_workdir = cliclack ::confirm ( format! ( " {} The working directory of this session was set to {} . It does not match the current working directory. Would you like to change it? " , style ( " WARNING: " ) . yellow ( ) , style ( metadata . working_dir . display ( ) ) . cyan ( ) ) )
. initial_value ( true )
. interact ( ) . expect ( " Failed to get user input " ) ;
2025-03-07 11:12:57 -05:00
2025-03-20 07:42:26 +11:00
if change_workdir {
std ::env ::set_current_dir ( metadata . working_dir ) . unwrap ( ) ;
}
2025-03-07 11:12:57 -05:00
}
}
// 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 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 ) ;
} ) ;
}
}
2025-02-11 21:16:58 -08:00
// Create new session
2025-03-06 18:58:25 -08:00
let mut session = Session ::new ( agent , session_file . clone ( ) , debug ) ;
2025-02-11 21:16:58 -08:00
// Add extensions if provided
for extension_str in extensions {
if let Err ( e ) = session . add_extension ( extension_str ) . await {
eprintln! ( " Failed to start extension: {} " , e ) ;
process ::exit ( 1 ) ;
}
}
// Add builtin extensions
for builtin in builtins {
if let Err ( e ) = session . add_builtin ( builtin ) . await {
eprintln! ( " Failed to start builtin extension: {} " , e ) ;
process ::exit ( 1 ) ;
}
}
// Add CLI-specific system prompt extension
session
. agent
. extend_system_prompt ( super ::prompt ::get_cli_prompt ( ) )
. await ;
2025-02-20 11:19:44 -05:00
// Only override system prompt if a system override exists
2025-03-10 09:51:54 -07:00
let system_prompt_file : Option < String > = config . get_param ( " GOOSE_SYSTEM_PROMPT_FILE_PATH " ) . ok ( ) ;
2025-02-20 11:19:44 -05:00
if let Some ( ref path ) = system_prompt_file {
let override_prompt =
std ::fs ::read_to_string ( path ) . expect ( " Failed to read system prompt file " ) ;
session . agent . override_system_prompt ( override_prompt ) . await ;
}
2025-02-11 21:16:58 -08:00
output ::display_session_info ( resume , & provider_name , & model , & session_file ) ;
session
}