Clean up session file optionality for --no-session (#3230)

This commit is contained in:
Jack Amadeo
2025-07-02 16:05:05 -04:00
committed by GitHub
parent 2e97621348
commit c77c1b364d
10 changed files with 179 additions and 213 deletions

View File

@@ -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()
} }
} }

View 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 {

View File

@@ -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 {

View File

@@ -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> {

View File

@@ -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

View File

@@ -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

View File

@@ -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(),

View File

@@ -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,

View File

@@ -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?;

View File

@@ -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