mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-19 07:04:21 +01:00
Clean up session file optionality for --no-session (#3230)
This commit is contained in:
@@ -18,7 +18,7 @@ pub struct BenchAgentError {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait BenchBaseSession: Send + Sync {
|
pub trait BenchBaseSession: Send + Sync {
|
||||||
async fn headless(&mut self, message: String) -> anyhow::Result<()>;
|
async fn headless(&mut self, message: String) -> anyhow::Result<()>;
|
||||||
fn session_file(&self) -> PathBuf;
|
fn session_file(&self) -> Option<PathBuf>;
|
||||||
fn message_history(&self) -> Vec<Message>;
|
fn message_history(&self) -> Vec<Message>;
|
||||||
fn get_total_token_usage(&self) -> anyhow::Result<Option<i32>>;
|
fn get_total_token_usage(&self) -> anyhow::Result<Option<i32>>;
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ impl BenchAgent {
|
|||||||
pub(crate) async fn get_token_usage(&self) -> Option<i32> {
|
pub(crate) async fn get_token_usage(&self) -> Option<i32> {
|
||||||
self.session.get_total_token_usage().ok().flatten()
|
self.session.get_total_token_usage().ok().flatten()
|
||||||
}
|
}
|
||||||
pub(crate) fn session_file(&self) -> PathBuf {
|
pub(crate) fn session_file(&self) -> Option<PathBuf> {
|
||||||
self.session.session_file()
|
self.session.session_file()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,8 +155,15 @@ impl EvalRunner {
|
|||||||
.canonicalize()
|
.canonicalize()
|
||||||
.context("Failed to canonicalize current directory path")?;
|
.context("Failed to canonicalize current directory path")?;
|
||||||
|
|
||||||
BenchmarkWorkDir::deep_copy(agent.session_file().as_path(), here.as_path(), false)
|
BenchmarkWorkDir::deep_copy(
|
||||||
.context("Failed to copy session file to evaluation directory")?;
|
agent
|
||||||
|
.session_file()
|
||||||
|
.expect("Failed to get session file")
|
||||||
|
.as_path(),
|
||||||
|
here.as_path(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.context("Failed to copy session file to evaluation directory")?;
|
||||||
|
|
||||||
tracing::info!("Evaluation completed successfully");
|
tracing::info!("Evaluation completed successfully");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -691,7 +691,11 @@ pub async fn cli() -> Result<()> {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
setup_logging(
|
setup_logging(
|
||||||
session.session_file().file_stem().and_then(|s| s.to_str()),
|
session
|
||||||
|
.session_file()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|p| p.file_stem())
|
||||||
|
.and_then(|s| s.to_str()),
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -831,7 +835,11 @@ pub async fn cli() -> Result<()> {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
setup_logging(
|
setup_logging(
|
||||||
session.session_file().file_stem().and_then(|s| s.to_str()),
|
session
|
||||||
|
.session_file()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|p| p.file_stem())
|
||||||
|
.and_then(|s| s.to_str()),
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -950,7 +958,11 @@ pub async fn cli() -> Result<()> {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
setup_logging(
|
setup_logging(
|
||||||
session.session_file().file_stem().and_then(|s| s.to_str()),
|
session
|
||||||
|
.session_file()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|p| p.file_stem())
|
||||||
|
.and_then(|s| s.to_str()),
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
if let Err(e) = session.interactive(None).await {
|
if let Err(e) = session.interactive(None).await {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ impl BenchBaseSession for Session {
|
|||||||
async fn headless(&mut self, message: String) -> anyhow::Result<()> {
|
async fn headless(&mut self, message: String) -> anyhow::Result<()> {
|
||||||
self.headless(message).await
|
self.headless(message).await
|
||||||
}
|
}
|
||||||
fn session_file(&self) -> PathBuf {
|
fn session_file(&self) -> Option<PathBuf> {
|
||||||
self.session_file()
|
self.session_file()
|
||||||
}
|
}
|
||||||
fn message_history(&self) -> Vec<Message> {
|
fn message_history(&self) -> Vec<Message> {
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ async fn offer_extension_debugging_help(
|
|||||||
std::env::temp_dir().join(format!("goose_debug_extension_{}.jsonl", extension_name));
|
std::env::temp_dir().join(format!("goose_debug_extension_{}.jsonl", extension_name));
|
||||||
|
|
||||||
// Create the debugging session
|
// Create the debugging session
|
||||||
let mut debug_session = Session::new(debug_agent, temp_session_file.clone(), false, None, true);
|
let mut debug_session = Session::new(debug_agent, Some(temp_session_file.clone()), false, None);
|
||||||
|
|
||||||
// Process the debugging request
|
// Process the debugging request
|
||||||
println!("{}", style("Analyzing the extension failure...").yellow());
|
println!("{}", style("Analyzing the extension failure...").yellow());
|
||||||
@@ -229,24 +229,16 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle session file resolution and resuming
|
// Handle session file resolution and resuming
|
||||||
let session_file: std::path::PathBuf = if session_config.no_session {
|
let session_file: Option<std::path::PathBuf> = if session_config.no_session {
|
||||||
// Use a temporary path that won't be written to
|
None
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
std::path::PathBuf::from("/dev/null")
|
|
||||||
}
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
std::path::PathBuf::from("NUL")
|
|
||||||
}
|
|
||||||
} else if session_config.resume {
|
} else if session_config.resume {
|
||||||
if let Some(identifier) = session_config.identifier {
|
if let Some(identifier) = session_config.identifier {
|
||||||
let session_file = match session::get_path(identifier) {
|
let session_file = match session::get_path(identifier) {
|
||||||
Ok(path) => path,
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
output::render_error(&format!("Invalid session identifier: {}", e));
|
output::render_error(&format!("Invalid session identifier: {}", e));
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
Ok(path) => path,
|
||||||
};
|
};
|
||||||
if !session_file.exists() {
|
if !session_file.exists() {
|
||||||
output::render_error(&format!(
|
output::render_error(&format!(
|
||||||
@@ -256,11 +248,11 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session {
|
|||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
session_file
|
Some(session_file)
|
||||||
} else {
|
} else {
|
||||||
// Try to resume most recent session
|
// Try to resume most recent session
|
||||||
match session::get_most_recent_session() {
|
match session::get_most_recent_session() {
|
||||||
Ok(file) => file,
|
Ok(file) => Some(file),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
output::render_error("Cannot resume - no previous sessions found");
|
output::render_error("Cannot resume - no previous sessions found");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
@@ -276,7 +268,7 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session {
|
|||||||
|
|
||||||
// Just get the path - file will be created when needed
|
// Just get the path - file will be created when needed
|
||||||
match session::get_path(id) {
|
match session::get_path(id) {
|
||||||
Ok(path) => path,
|
Ok(path) => Some(path),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
output::render_error(&format!("Failed to create session path: {}", e));
|
output::render_error(&format!("Failed to create session path: {}", e));
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
@@ -284,32 +276,34 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if session_config.resume && !session_config.no_session {
|
if session_config.resume {
|
||||||
// Read the session metadata
|
if let Some(session_file) = session_file.as_ref() {
|
||||||
let metadata = session::read_metadata(&session_file).unwrap_or_else(|e| {
|
// Read the session metadata
|
||||||
output::render_error(&format!("Failed to read session metadata: {}", e));
|
let metadata = session::read_metadata(session_file).unwrap_or_else(|e| {
|
||||||
process::exit(1);
|
output::render_error(&format!("Failed to read session metadata: {}", e));
|
||||||
});
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
let current_workdir =
|
let current_workdir =
|
||||||
std::env::current_dir().expect("Failed to get current working directory");
|
std::env::current_dir().expect("Failed to get current working directory");
|
||||||
if current_workdir != metadata.working_dir {
|
if current_workdir != metadata.working_dir {
|
||||||
// Ask user if they want to change the working directory
|
// Ask user if they want to change the working directory
|
||||||
let change_workdir = cliclack::confirm(format!("{} The original working directory of this session was set to {}. Your current directory is {}. Do you want to switch back to the original working directory?", style("WARNING:").yellow(), style(metadata.working_dir.display()).cyan(), style(current_workdir.display()).cyan()))
|
let change_workdir = cliclack::confirm(format!("{} The original working directory of this session was set to {}. Your current directory is {}. Do you want to switch back to the original working directory?", style("WARNING:").yellow(), style(metadata.working_dir.display()).cyan(), style(current_workdir.display()).cyan()))
|
||||||
.initial_value(true)
|
.initial_value(true)
|
||||||
.interact().expect("Failed to get user input");
|
.interact().expect("Failed to get user input");
|
||||||
|
|
||||||
if change_workdir {
|
if change_workdir {
|
||||||
if !metadata.working_dir.exists() {
|
if !metadata.working_dir.exists() {
|
||||||
output::render_error(&format!(
|
output::render_error(&format!(
|
||||||
"Cannot switch to original working directory - {} no longer exists",
|
"Cannot switch to original working directory - {} no longer exists",
|
||||||
style(metadata.working_dir.display()).cyan()
|
style(metadata.working_dir.display()).cyan()
|
||||||
));
|
));
|
||||||
} else if let Err(e) = std::env::set_current_dir(&metadata.working_dir) {
|
} else if let Err(e) = std::env::set_current_dir(&metadata.working_dir) {
|
||||||
output::render_error(&format!(
|
output::render_error(&format!(
|
||||||
"Failed to switch to original working directory: {}",
|
"Failed to switch to original working directory: {}",
|
||||||
e
|
e
|
||||||
));
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,7 +367,6 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session {
|
|||||||
session_file.clone(),
|
session_file.clone(),
|
||||||
session_config.debug,
|
session_config.debug,
|
||||||
session_config.scheduled_job_id.clone(),
|
session_config.scheduled_job_id.clone(),
|
||||||
!session_config.no_session, // save_session is the inverse of no_session
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add extensions if provided
|
// Add extensions if provided
|
||||||
|
|||||||
@@ -46,13 +46,12 @@ pub enum RunMode {
|
|||||||
pub struct Session {
|
pub struct Session {
|
||||||
agent: Agent,
|
agent: Agent,
|
||||||
messages: Vec<Message>,
|
messages: Vec<Message>,
|
||||||
session_file: PathBuf,
|
session_file: Option<PathBuf>,
|
||||||
// Cache for completion data - using std::sync for thread safety without async
|
// Cache for completion data - using std::sync for thread safety without async
|
||||||
completion_cache: Arc<std::sync::RwLock<CompletionCache>>,
|
completion_cache: Arc<std::sync::RwLock<CompletionCache>>,
|
||||||
debug: bool, // New field for debug mode
|
debug: bool, // New field for debug mode
|
||||||
run_mode: RunMode,
|
run_mode: RunMode,
|
||||||
scheduled_job_id: Option<String>, // ID of the scheduled job that triggered this session
|
scheduled_job_id: Option<String>, // ID of the scheduled job that triggered this session
|
||||||
save_session: bool, // Whether to save session to file
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache structure for completion data
|
// Cache structure for completion data
|
||||||
@@ -111,13 +110,12 @@ pub async fn classify_planner_response(
|
|||||||
impl Session {
|
impl Session {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
agent: Agent,
|
agent: Agent,
|
||||||
session_file: PathBuf,
|
session_file: Option<PathBuf>,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
scheduled_job_id: Option<String>,
|
scheduled_job_id: Option<String>,
|
||||||
save_session: bool,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let messages = if save_session {
|
let messages = if let Some(session_file) = &session_file {
|
||||||
match session::read_messages(&session_file) {
|
match session::read_messages(session_file) {
|
||||||
Ok(msgs) => msgs,
|
Ok(msgs) => msgs,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Warning: Failed to load message history: {}", e);
|
eprintln!("Warning: Failed to load message history: {}", e);
|
||||||
@@ -137,7 +135,6 @@ impl Session {
|
|||||||
debug,
|
debug,
|
||||||
run_mode: RunMode::Normal,
|
run_mode: RunMode::Normal,
|
||||||
scheduled_job_id,
|
scheduled_job_id,
|
||||||
save_session,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,19 +319,21 @@ impl Session {
|
|||||||
let provider = self.agent.provider().await?;
|
let provider = self.agent.provider().await?;
|
||||||
|
|
||||||
// Persist messages with provider for automatic description generation
|
// Persist messages with provider for automatic description generation
|
||||||
session::persist_messages_with_schedule_id(
|
if let Some(session_file) = &self.session_file {
|
||||||
&self.session_file,
|
session::persist_messages_with_schedule_id(
|
||||||
&self.messages,
|
session_file,
|
||||||
Some(provider),
|
&self.messages,
|
||||||
self.scheduled_job_id.clone(),
|
Some(provider),
|
||||||
self.save_session,
|
self.scheduled_job_id.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
// Track the current directory and last instruction in projects.json
|
// Track the current directory and last instruction in projects.json
|
||||||
let session_id = self
|
let session_id = self
|
||||||
.session_file
|
.session_file
|
||||||
.file_stem()
|
.as_ref()
|
||||||
|
.and_then(|p| p.file_stem())
|
||||||
.and_then(|s| s.to_str())
|
.and_then(|s| s.to_str())
|
||||||
.map(|s| s.to_string());
|
.map(|s| s.to_string());
|
||||||
|
|
||||||
@@ -420,7 +419,8 @@ impl Session {
|
|||||||
// Track the current directory and last instruction in projects.json
|
// Track the current directory and last instruction in projects.json
|
||||||
let session_id = self
|
let session_id = self
|
||||||
.session_file
|
.session_file
|
||||||
.file_stem()
|
.as_ref()
|
||||||
|
.and_then(|p| p.file_stem())
|
||||||
.and_then(|s| s.to_str())
|
.and_then(|s| s.to_str())
|
||||||
.map(|s| s.to_string());
|
.map(|s| s.to_string());
|
||||||
|
|
||||||
@@ -435,14 +435,15 @@ impl Session {
|
|||||||
let provider = self.agent.provider().await?;
|
let provider = self.agent.provider().await?;
|
||||||
|
|
||||||
// Persist messages with provider for automatic description generation
|
// Persist messages with provider for automatic description generation
|
||||||
session::persist_messages_with_schedule_id(
|
if let Some(session_file) = &self.session_file {
|
||||||
&self.session_file,
|
session::persist_messages_with_schedule_id(
|
||||||
&self.messages,
|
session_file,
|
||||||
Some(provider),
|
&self.messages,
|
||||||
self.scheduled_job_id.clone(),
|
Some(provider),
|
||||||
self.save_session,
|
self.scheduled_job_id.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
output::show_thinking();
|
output::show_thinking();
|
||||||
self.process_agent_response(true).await?;
|
self.process_agent_response(true).await?;
|
||||||
@@ -624,14 +625,15 @@ impl Session {
|
|||||||
self.messages = summarized_messages;
|
self.messages = summarized_messages;
|
||||||
|
|
||||||
// Persist the summarized messages
|
// Persist the summarized messages
|
||||||
session::persist_messages_with_schedule_id(
|
if let Some(session_file) = &self.session_file {
|
||||||
&self.session_file,
|
session::persist_messages_with_schedule_id(
|
||||||
&self.messages,
|
session_file,
|
||||||
Some(provider),
|
&self.messages,
|
||||||
self.scheduled_job_id.clone(),
|
Some(provider),
|
||||||
self.save_session,
|
self.scheduled_job_id.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
output::hide_thinking();
|
output::hide_thinking();
|
||||||
println!(
|
println!(
|
||||||
@@ -655,8 +657,11 @@ impl Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"\nClosing session. Recorded to {}",
|
"\nClosing session.{}",
|
||||||
self.session_file.display()
|
self.session_file
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| format!(" Recorded to {}", p.display()))
|
||||||
|
.unwrap_or_default()
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -744,19 +749,19 @@ impl Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn process_agent_response(&mut self, interactive: bool) -> Result<()> {
|
async fn process_agent_response(&mut self, interactive: bool) -> Result<()> {
|
||||||
let session_id = session::Identifier::Path(self.session_file.clone());
|
let session_config = self.session_file.as_ref().map(|s| {
|
||||||
|
let session_id = session::Identifier::Path(s.clone());
|
||||||
|
SessionConfig {
|
||||||
|
id: session_id.clone(),
|
||||||
|
working_dir: std::env::current_dir()
|
||||||
|
.expect("failed to get current session working directory"),
|
||||||
|
schedule_id: self.scheduled_job_id.clone(),
|
||||||
|
execution_mode: None,
|
||||||
|
}
|
||||||
|
});
|
||||||
let mut stream = self
|
let mut stream = self
|
||||||
.agent
|
.agent
|
||||||
.reply(
|
.reply(&self.messages, session_config.clone())
|
||||||
&self.messages,
|
|
||||||
Some(SessionConfig {
|
|
||||||
id: session_id.clone(),
|
|
||||||
working_dir: std::env::current_dir()
|
|
||||||
.expect("failed to get current session working directory"),
|
|
||||||
schedule_id: self.scheduled_job_id.clone(),
|
|
||||||
execution_mode: None,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut progress_bars = output::McpSpinners::new();
|
let mut progress_bars = output::McpSpinners::new();
|
||||||
@@ -803,7 +808,15 @@ impl Session {
|
|||||||
Err(ToolError::ExecutionError("Tool call cancelled by user".to_string()))
|
Err(ToolError::ExecutionError("Tool call cancelled by user".to_string()))
|
||||||
));
|
));
|
||||||
self.messages.push(response_message);
|
self.messages.push(response_message);
|
||||||
session::persist_messages_with_schedule_id(&self.session_file, &self.messages, None, self.scheduled_job_id.clone(), self.save_session).await?;
|
if let Some(session_file) = &self.session_file {
|
||||||
|
session::persist_messages_with_schedule_id(
|
||||||
|
session_file,
|
||||||
|
&self.messages,
|
||||||
|
None,
|
||||||
|
self.scheduled_job_id.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
drop(stream);
|
drop(stream);
|
||||||
break;
|
break;
|
||||||
@@ -885,13 +898,7 @@ impl Session {
|
|||||||
.agent
|
.agent
|
||||||
.reply(
|
.reply(
|
||||||
&self.messages,
|
&self.messages,
|
||||||
Some(SessionConfig {
|
session_config.clone(),
|
||||||
id: session_id.clone(),
|
|
||||||
working_dir: std::env::current_dir()
|
|
||||||
.expect("failed to get current session working directory"),
|
|
||||||
schedule_id: self.scheduled_job_id.clone(),
|
|
||||||
execution_mode: None,
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
@@ -900,7 +907,15 @@ impl Session {
|
|||||||
self.messages.push(message.clone());
|
self.messages.push(message.clone());
|
||||||
|
|
||||||
// No need to update description on assistant messages
|
// No need to update description on assistant messages
|
||||||
session::persist_messages_with_schedule_id(&self.session_file, &self.messages, None, self.scheduled_job_id.clone(), self.save_session).await?;
|
if let Some(session_file) = &self.session_file {
|
||||||
|
session::persist_messages_with_schedule_id(
|
||||||
|
session_file,
|
||||||
|
&self.messages,
|
||||||
|
None,
|
||||||
|
self.scheduled_job_id.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
if interactive {output::hide_thinking()};
|
if interactive {output::hide_thinking()};
|
||||||
let _ = progress_bars.hide();
|
let _ = progress_bars.hide();
|
||||||
@@ -1099,14 +1114,15 @@ impl Session {
|
|||||||
self.messages.push(response_message);
|
self.messages.push(response_message);
|
||||||
|
|
||||||
// No need for description update here
|
// No need for description update here
|
||||||
session::persist_messages_with_schedule_id(
|
if let Some(session_file) = &self.session_file {
|
||||||
&self.session_file,
|
session::persist_messages_with_schedule_id(
|
||||||
&self.messages,
|
session_file,
|
||||||
None,
|
&self.messages,
|
||||||
self.scheduled_job_id.clone(),
|
None,
|
||||||
self.save_session,
|
self.scheduled_job_id.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
let prompt = format!(
|
let prompt = format!(
|
||||||
"The existing call to {} was interrupted. How would you like to proceed?",
|
"The existing call to {} was interrupted. How would you like to proceed?",
|
||||||
@@ -1115,14 +1131,15 @@ impl Session {
|
|||||||
self.messages.push(Message::assistant().with_text(&prompt));
|
self.messages.push(Message::assistant().with_text(&prompt));
|
||||||
|
|
||||||
// No need for description update here
|
// No need for description update here
|
||||||
session::persist_messages_with_schedule_id(
|
if let Some(session_file) = &self.session_file {
|
||||||
&self.session_file,
|
session::persist_messages_with_schedule_id(
|
||||||
&self.messages,
|
session_file,
|
||||||
None,
|
&self.messages,
|
||||||
self.scheduled_job_id.clone(),
|
None,
|
||||||
self.save_session,
|
self.scheduled_job_id.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
output::render_message(&Message::assistant().with_text(&prompt), self.debug);
|
output::render_message(&Message::assistant().with_text(&prompt), self.debug);
|
||||||
} else {
|
} else {
|
||||||
@@ -1136,14 +1153,15 @@ impl Session {
|
|||||||
self.messages.push(Message::assistant().with_text(prompt));
|
self.messages.push(Message::assistant().with_text(prompt));
|
||||||
|
|
||||||
// No need for description update here
|
// No need for description update here
|
||||||
session::persist_messages_with_schedule_id(
|
if let Some(session_file) = &self.session_file {
|
||||||
&self.session_file,
|
session::persist_messages_with_schedule_id(
|
||||||
&self.messages,
|
session_file,
|
||||||
None,
|
&self.messages,
|
||||||
self.scheduled_job_id.clone(),
|
None,
|
||||||
self.save_session,
|
self.scheduled_job_id.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
output::render_message(
|
output::render_message(
|
||||||
&Message::assistant().with_text(prompt),
|
&Message::assistant().with_text(prompt),
|
||||||
@@ -1167,7 +1185,7 @@ impl Session {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn session_file(&self) -> PathBuf {
|
pub fn session_file(&self) -> Option<PathBuf> {
|
||||||
self.session_file.clone()
|
self.session_file.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1243,11 +1261,11 @@ impl Session {
|
|||||||
|
|
||||||
/// Get the session metadata
|
/// Get the session metadata
|
||||||
pub fn get_metadata(&self) -> Result<session::SessionMetadata> {
|
pub fn get_metadata(&self) -> Result<session::SessionMetadata> {
|
||||||
if !self.session_file.exists() {
|
if !self.session_file.as_ref().is_some_and(|f| f.exists()) {
|
||||||
return Err(anyhow::anyhow!("Session file does not exist"));
|
return Err(anyhow::anyhow!("Session file does not exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
session::read_metadata(&self.session_file)
|
session::read_metadata(self.session_file.as_ref().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the session's total token usage
|
// Get the session's total token usage
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use serde_json::Value;
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Error;
|
use std::io::Error;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -550,12 +550,12 @@ pub fn display_session_info(
|
|||||||
resume: bool,
|
resume: bool,
|
||||||
provider: &str,
|
provider: &str,
|
||||||
model: &str,
|
model: &str,
|
||||||
session_file: &Path,
|
session_file: &Option<PathBuf>,
|
||||||
provider_instance: Option<&Arc<dyn goose::providers::base::Provider>>,
|
provider_instance: Option<&Arc<dyn goose::providers::base::Provider>>,
|
||||||
) {
|
) {
|
||||||
let start_session_msg = if resume {
|
let start_session_msg = if resume {
|
||||||
"resuming session |"
|
"resuming session |"
|
||||||
} else if session_file.to_str() == Some("/dev/null") || session_file.to_str() == Some("NUL") {
|
} else if session_file.is_none() {
|
||||||
"running without session |"
|
"running without session |"
|
||||||
} else {
|
} else {
|
||||||
"starting session |"
|
"starting session |"
|
||||||
@@ -597,7 +597,7 @@ pub fn display_session_info(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if session_file.to_str() != Some("/dev/null") && session_file.to_str() != Some("NUL") {
|
if let Some(session_file) = session_file {
|
||||||
println!(
|
println!(
|
||||||
" {} {}",
|
" {} {}",
|
||||||
style("logging to").dim(),
|
style("logging to").dim(),
|
||||||
|
|||||||
@@ -1248,7 +1248,6 @@ async fn run_scheduled_job_internal(
|
|||||||
&session_file_path,
|
&session_file_path,
|
||||||
&updated_metadata,
|
&updated_metadata,
|
||||||
&all_session_messages,
|
&all_session_messages,
|
||||||
true,
|
|
||||||
) {
|
) {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
"[Job {}] Failed to persist final messages: {}",
|
"[Job {}] Failed to persist final messages: {}",
|
||||||
@@ -1279,7 +1278,6 @@ async fn run_scheduled_job_internal(
|
|||||||
&session_file_path,
|
&session_file_path,
|
||||||
&fallback_metadata,
|
&fallback_metadata,
|
||||||
&all_session_messages,
|
&all_session_messages,
|
||||||
true,
|
|
||||||
) {
|
) {
|
||||||
tracing::error!("[Job {}] Failed to persist final messages with fallback metadata: {}", job.id, e_fb);
|
tracing::error!("[Job {}] Failed to persist final messages with fallback metadata: {}", job.id, e_fb);
|
||||||
}
|
}
|
||||||
@@ -1306,12 +1304,9 @@ async fn run_scheduled_job_internal(
|
|||||||
message_count: 0,
|
message_count: 0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if let Err(e) = crate::session::storage::save_messages_with_metadata(
|
if let Err(e) =
|
||||||
&session_file_path,
|
crate::session::storage::save_messages_with_metadata(&session_file_path, &metadata, &[])
|
||||||
&metadata,
|
{
|
||||||
&[],
|
|
||||||
true,
|
|
||||||
) {
|
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
"[Job {}] Failed to persist metadata for empty job: {}",
|
"[Job {}] Failed to persist metadata for empty job: {}",
|
||||||
job.id,
|
job.id,
|
||||||
|
|||||||
@@ -158,14 +158,6 @@ pub fn get_path(id: Identifier) -> Result<PathBuf> {
|
|||||||
session_dir.join(format!("{}.jsonl", name))
|
session_dir.join(format!("{}.jsonl", name))
|
||||||
}
|
}
|
||||||
Identifier::Path(path) => {
|
Identifier::Path(path) => {
|
||||||
// Allow special paths for no-session mode
|
|
||||||
if let Some(path_str) = path.to_str() {
|
|
||||||
if path_str == "/dev/null" || path_str == "NUL" {
|
|
||||||
// These are special paths used for --no-session mode
|
|
||||||
return Ok(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In test mode, allow temporary directory paths
|
// In test mode, allow temporary directory paths
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
{
|
{
|
||||||
@@ -199,13 +191,9 @@ pub fn get_path(id: Identifier) -> Result<PathBuf> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Additional security check for file extension (skip for special no-session paths)
|
// Additional security check for file extension (skip for special no-session paths)
|
||||||
if let Some(path_str) = path.to_str() {
|
if let Some(ext) = path.extension() {
|
||||||
if path_str != "/dev/null" && path_str != "NUL" {
|
if ext != "jsonl" {
|
||||||
if let Some(ext) = path.extension() {
|
return Err(anyhow::anyhow!("Invalid file extension"));
|
||||||
if ext != "jsonl" {
|
|
||||||
return Err(anyhow::anyhow!("Invalid file extension"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1054,7 +1042,7 @@ pub async fn persist_messages(
|
|||||||
messages: &[Message],
|
messages: &[Message],
|
||||||
provider: Option<Arc<dyn Provider>>,
|
provider: Option<Arc<dyn Provider>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
persist_messages_with_schedule_id(session_file, messages, provider, None, true).await
|
persist_messages_with_schedule_id(session_file, messages, provider, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write messages to a session file with metadata, including an optional scheduled job ID
|
/// Write messages to a session file with metadata, including an optional scheduled job ID
|
||||||
@@ -1071,13 +1059,7 @@ pub async fn persist_messages_with_schedule_id(
|
|||||||
messages: &[Message],
|
messages: &[Message],
|
||||||
provider: Option<Arc<dyn Provider>>,
|
provider: Option<Arc<dyn Provider>>,
|
||||||
schedule_id: Option<String>,
|
schedule_id: Option<String>,
|
||||||
save_session: bool,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if !save_session {
|
|
||||||
tracing::debug!("Skipping session persistence (save_session=false)");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the session file path for security
|
// Validate the session file path for security
|
||||||
let secure_path = get_path(Identifier::Path(session_file.to_path_buf()))?;
|
let secure_path = get_path(Identifier::Path(session_file.to_path_buf()))?;
|
||||||
|
|
||||||
@@ -1097,14 +1079,8 @@ pub async fn persist_messages_with_schedule_id(
|
|||||||
match provider {
|
match provider {
|
||||||
Some(provider) if user_message_count < 4 => {
|
Some(provider) if user_message_count < 4 => {
|
||||||
//generate_description is responsible for writing the messages
|
//generate_description is responsible for writing the messages
|
||||||
generate_description_with_schedule_id(
|
generate_description_with_schedule_id(&secure_path, messages, provider, schedule_id)
|
||||||
&secure_path,
|
.await
|
||||||
messages,
|
|
||||||
provider,
|
|
||||||
schedule_id,
|
|
||||||
save_session,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Read existing metadata
|
// Read existing metadata
|
||||||
@@ -1114,7 +1090,7 @@ pub async fn persist_messages_with_schedule_id(
|
|||||||
metadata.schedule_id = schedule_id;
|
metadata.schedule_id = schedule_id;
|
||||||
}
|
}
|
||||||
// Write the file with metadata and messages
|
// Write the file with metadata and messages
|
||||||
save_messages_with_metadata(&secure_path, &metadata, messages, save_session)
|
save_messages_with_metadata(&secure_path, &metadata, messages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1136,13 +1112,7 @@ pub fn save_messages_with_metadata(
|
|||||||
session_file: &Path,
|
session_file: &Path,
|
||||||
metadata: &SessionMetadata,
|
metadata: &SessionMetadata,
|
||||||
messages: &[Message],
|
messages: &[Message],
|
||||||
save_session: bool,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if !save_session {
|
|
||||||
tracing::debug!("Skipping session file write (save_session=false)");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
use fs2::FileExt;
|
use fs2::FileExt;
|
||||||
|
|
||||||
// Validate the path for security
|
// Validate the path for security
|
||||||
@@ -1257,7 +1227,7 @@ pub async fn generate_description(
|
|||||||
messages: &[Message],
|
messages: &[Message],
|
||||||
provider: Arc<dyn Provider>,
|
provider: Arc<dyn Provider>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
generate_description_with_schedule_id(session_file, messages, provider, None, true).await
|
generate_description_with_schedule_id(session_file, messages, provider, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a description for the session using the provider, including an optional scheduled job ID
|
/// Generate a description for the session using the provider, including an optional scheduled job ID
|
||||||
@@ -1274,13 +1244,7 @@ pub async fn generate_description_with_schedule_id(
|
|||||||
messages: &[Message],
|
messages: &[Message],
|
||||||
provider: Arc<dyn Provider>,
|
provider: Arc<dyn Provider>,
|
||||||
schedule_id: Option<String>,
|
schedule_id: Option<String>,
|
||||||
save_session: bool,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if !save_session {
|
|
||||||
tracing::debug!("Skipping description generation (save_session=false)");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the path for security
|
// Validate the path for security
|
||||||
let secure_path = get_path(Identifier::Path(session_file.to_path_buf()))?;
|
let secure_path = get_path(Identifier::Path(session_file.to_path_buf()))?;
|
||||||
|
|
||||||
@@ -1355,7 +1319,7 @@ pub async fn generate_description_with_schedule_id(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the file with the new metadata and existing messages
|
// Update the file with the new metadata and existing messages
|
||||||
save_messages_with_metadata(&secure_path, &metadata, messages, save_session)
|
save_messages_with_metadata(&secure_path, &metadata, messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update only the metadata in a session file, preserving all messages
|
/// Update only the metadata in a session file, preserving all messages
|
||||||
@@ -1371,7 +1335,7 @@ pub async fn update_metadata(session_file: &Path, metadata: &SessionMetadata) ->
|
|||||||
let messages = read_messages(&secure_path)?;
|
let messages = read_messages(&secure_path)?;
|
||||||
|
|
||||||
// Rewrite the file with the new metadata and existing messages
|
// Rewrite the file with the new metadata and existing messages
|
||||||
save_messages_with_metadata(&secure_path, metadata, &messages, true)
|
save_messages_with_metadata(&secure_path, metadata, &messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -1686,7 +1650,7 @@ mod tests {
|
|||||||
let messages = vec![Message::user().with_text("test")];
|
let messages = vec![Message::user().with_text("test")];
|
||||||
|
|
||||||
// Write with special metadata
|
// Write with special metadata
|
||||||
save_messages_with_metadata(&file_path, &metadata, &messages, true)?;
|
save_messages_with_metadata(&file_path, &metadata, &messages)?;
|
||||||
|
|
||||||
// Read back metadata
|
// Read back metadata
|
||||||
let read_metadata = read_metadata(&file_path)?;
|
let read_metadata = read_metadata(&file_path)?;
|
||||||
@@ -1710,7 +1674,7 @@ mod tests {
|
|||||||
|
|
||||||
// Test deserialization of invalid directory
|
// Test deserialization of invalid directory
|
||||||
let messages = vec![Message::user().with_text("test")];
|
let messages = vec![Message::user().with_text("test")];
|
||||||
save_messages_with_metadata(&file_path, &metadata, &messages, true)?;
|
save_messages_with_metadata(&file_path, &metadata, &messages)?;
|
||||||
|
|
||||||
// Modify the file to include invalid directory
|
// Modify the file to include invalid directory
|
||||||
let contents = fs::read_to_string(&file_path)?;
|
let contents = fs::read_to_string(&file_path)?;
|
||||||
@@ -1792,15 +1756,8 @@ mod tests {
|
|||||||
|
|
||||||
let metadata = SessionMetadata::default();
|
let metadata = SessionMetadata::default();
|
||||||
|
|
||||||
// Test with save_session = false - should not create file
|
|
||||||
save_messages_with_metadata(&file_path, &metadata, &messages, false)?;
|
|
||||||
assert!(
|
|
||||||
!file_path.exists(),
|
|
||||||
"File should not be created when save_session=false"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test with save_session = true - should create file
|
// Test with save_session = true - should create file
|
||||||
save_messages_with_metadata(&file_path, &metadata, &messages, true)?;
|
save_messages_with_metadata(&file_path, &metadata, &messages)?;
|
||||||
assert!(
|
assert!(
|
||||||
file_path.exists(),
|
file_path.exists(),
|
||||||
"File should be created when save_session=true"
|
"File should be created when save_session=true"
|
||||||
@@ -1823,28 +1780,12 @@ mod tests {
|
|||||||
Message::assistant().with_text("Test response"),
|
Message::assistant().with_text("Test response"),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Test persist_messages_with_schedule_id with save_session = false
|
|
||||||
persist_messages_with_schedule_id(
|
|
||||||
&file_path,
|
|
||||||
&messages,
|
|
||||||
None,
|
|
||||||
Some("test_schedule".to_string()),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
!file_path.exists(),
|
|
||||||
"File should not be created when save_session=false"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test persist_messages_with_schedule_id with save_session = true
|
// Test persist_messages_with_schedule_id with save_session = true
|
||||||
persist_messages_with_schedule_id(
|
persist_messages_with_schedule_id(
|
||||||
&file_path,
|
&file_path,
|
||||||
&messages,
|
&messages,
|
||||||
None,
|
None,
|
||||||
Some("test_schedule".to_string()),
|
Some("test_schedule".to_string()),
|
||||||
true,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -788,7 +788,7 @@ async fn test_schedule_tool_session_content_action_with_real_session() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Save the session file
|
// Save the session file
|
||||||
goose::session::storage::save_messages_with_metadata(&session_path, &metadata, &messages, true)
|
goose::session::storage::save_messages_with_metadata(&session_path, &metadata, &messages)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Test the session_content action
|
// Test the session_content action
|
||||||
|
|||||||
Reference in New Issue
Block a user