fix: session resume — skip current empty session, unify cross-workspace loading

Three improvements to the /resume command:

1. /resume latest now skips the current empty session
   When a new session is created on startup (with 0 messages), /resume
   latest previously returned that empty session. Now it skips sessions
   with message_count == 0 and excludes the current session ID via the
   new exclude_id parameter, so it finds the previous session with
   actual conversation history.

2. Unified load_session_excluding() replaces load_session_loose()
   The previous load_session_loose() only handled cross-workspace
   resume for aliases. The new load_session_excluding() combines the
   loose workspace validation logic with the exclude_id parameter,
   simplifying the call chain and ensuring all resume paths skip the
   current empty session when appropriate.

3. All existing session scanning paths (global root + project-local
   .claw/sessions/) are already in place from prior commits, and now
   the exclude_id filter is applied consistently across both local
   and global session scans.

Changes:
- session_control.rs: Add resolve_reference_excluding() that delegates
  from resolve_reference(), adding optional exclude_id filtering for
  alias references.
- session_control.rs: Add latest_session_excluding() that delegates
  from latest_session(), filtering out excluded session IDs and
  sessions with 0 messages in both local and global scan paths.
- session_control.rs: Add load_session_excluding() that replaces
  load_session_loose(), combining cross-workspace alias handling with
  the exclude_id parameter.
- main.rs: Add load_session_reference_excluding() that delegates from
  load_session_reference(), using the new store method.
- main.rs: Wire LiveCli::resume_session() to pass the current session
  ID as the exclude_id so /resume latest skips the current empty
  session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
TheArchitectit
2026-06-02 15:49:21 -05:00
parent 4d3dc5b873
commit e459a727e9
2 changed files with 78 additions and 28 deletions

View File

@@ -6364,7 +6364,8 @@ impl LiveCli {
return Ok(false);
};
let (handle, session) = load_session_reference(&session_ref)?;
let (handle, session) =
load_session_reference_excluding(&session_ref, Some(&self.session.id))?;
let message_count = session.messages.len();
let session_id = session.session_id.clone();
let runtime = build_runtime(
@@ -7059,17 +7060,18 @@ fn latest_managed_session() -> Result<ManagedSessionSummary, Box<dyn std::error:
fn load_session_reference(
reference: &str,
) -> Result<(SessionHandle, Session), Box<dyn std::error::Error>> {
load_session_reference_excluding(reference, None)
}
fn load_session_reference_excluding(
reference: &str,
exclude_id: Option<&str>,
) -> Result<(SessionHandle, Session), Box<dyn std::error::Error>> {
let store = current_session_store()?;
// For alias references ("latest", "last", "recent"), allow cross-workspace
// resume so /resume latest finds the most recent session globally.
// For explicit references, workspace validation is enforced.
let result = if runtime::session_control::is_session_reference_alias(reference) {
store.load_session_loose(reference)
} else {
store.load_session(reference)
};
let loaded = result.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
let loaded = store
.load_session_excluding(reference, exclude_id)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
Ok((
SessionHandle {
id: loaded.handle.id,