2026-03-26 23:24:25 -04:00
|
|
|
// session.rs — Session state and transcript info
|
2026-03-25 01:26:03 -04:00
|
|
|
//
|
2026-03-26 23:24:25 -04:00
|
|
|
// Session: per-session state (seen set, state directory) across hook
|
2026-03-25 01:26:03 -04:00
|
|
|
// invocations. Created from hook JSON input or POC_SESSION_ID env var.
|
2026-03-26 23:24:25 -04:00
|
|
|
//
|
|
|
|
|
// TranscriptInfo: cached metadata about the current session's transcript
|
|
|
|
|
// file — size, path, compaction offset. Read once, used by all callers.
|
2026-03-25 01:26:03 -04:00
|
|
|
|
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
use std::fs;
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
|
|
pub struct Session {
|
|
|
|
|
pub session_id: String,
|
|
|
|
|
pub transcript_path: String,
|
|
|
|
|
pub hook_event: String,
|
|
|
|
|
pub state_dir: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Session {
|
|
|
|
|
pub fn from_json(input: &str) -> Option<Self> {
|
|
|
|
|
let state_dir = PathBuf::from("/tmp/claude-memory-search");
|
|
|
|
|
fs::create_dir_all(&state_dir).ok();
|
|
|
|
|
|
|
|
|
|
let json: serde_json::Value = serde_json::from_str(input).ok()?;
|
|
|
|
|
let session_id = json["session_id"].as_str().unwrap_or("").to_string();
|
|
|
|
|
if session_id.is_empty() { return None; }
|
|
|
|
|
let transcript_path = json["transcript_path"].as_str().unwrap_or("").to_string();
|
|
|
|
|
let hook_event = json["hook_event_name"].as_str().unwrap_or("").to_string();
|
|
|
|
|
|
|
|
|
|
Some(Session { session_id, transcript_path, hook_event, state_dir })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn path(&self, prefix: &str) -> PathBuf {
|
|
|
|
|
self.state_dir.join(format!("{}-{}", prefix, self.session_id))
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 14:22:29 -04:00
|
|
|
/// Load from a session ID string
|
|
|
|
|
pub fn from_id(session_id: String) -> Option<Self> {
|
2026-03-25 01:26:03 -04:00
|
|
|
if session_id.is_empty() { return None; }
|
|
|
|
|
let state_dir = PathBuf::from("/tmp/claude-memory-search");
|
|
|
|
|
Some(Session {
|
|
|
|
|
session_id,
|
|
|
|
|
transcript_path: String::new(),
|
|
|
|
|
hook_event: String::new(),
|
|
|
|
|
state_dir,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 14:22:29 -04:00
|
|
|
/// Load from POC_SESSION_ID environment variable
|
|
|
|
|
pub fn from_env() -> Option<Self> {
|
|
|
|
|
Self::from_id(std::env::var("POC_SESSION_ID").ok()?)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 01:26:03 -04:00
|
|
|
/// Get the seen set for this session
|
|
|
|
|
pub fn seen(&self) -> HashSet<String> {
|
|
|
|
|
super::hippocampus::memory_search::load_seen(&self.state_dir, &self.session_id)
|
|
|
|
|
}
|
2026-03-26 23:24:25 -04:00
|
|
|
|
|
|
|
|
/// Get transcript metadata, resolving the path if needed.
|
|
|
|
|
pub fn transcript(&self) -> TranscriptInfo {
|
|
|
|
|
if !self.transcript_path.is_empty() {
|
|
|
|
|
return TranscriptInfo::from_path(&self.transcript_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find transcript by session ID in projects dir
|
|
|
|
|
let projects = crate::config::get().projects_dir.clone();
|
|
|
|
|
if let Ok(dirs) = fs::read_dir(&projects) {
|
|
|
|
|
for dir in dirs.filter_map(|e| e.ok()) {
|
|
|
|
|
let path = dir.path().join(format!("{}.jsonl", self.session_id));
|
|
|
|
|
if path.exists() {
|
|
|
|
|
return TranscriptInfo::from_path(&path.to_string_lossy());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TranscriptInfo::empty()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Cached transcript metadata — read once, use everywhere.
|
|
|
|
|
pub struct TranscriptInfo {
|
|
|
|
|
pub path: String,
|
|
|
|
|
pub size: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TranscriptInfo {
|
|
|
|
|
pub fn from_path(path: &str) -> Self {
|
|
|
|
|
let size = fs::metadata(path).map(|m| m.len()).unwrap_or(0);
|
|
|
|
|
TranscriptInfo {
|
|
|
|
|
path: path.to_string(),
|
|
|
|
|
size,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn empty() -> Self {
|
|
|
|
|
TranscriptInfo {
|
|
|
|
|
path: String::new(),
|
|
|
|
|
size: 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn exists(&self) -> bool {
|
|
|
|
|
!self.path.is_empty() && self.size > 0
|
|
|
|
|
}
|
2026-03-25 01:26:03 -04:00
|
|
|
}
|