pub mod error;
pub mod event_log;
pub mod migration;
pub mod progress;
pub mod query;
pub mod retention;
pub use error::SessionStoreError;
pub use event_log::{SessionEventLog, SessionManifest, TurnIndex, TurnIndexEntry};
pub use migration::{MigrationReport, migrate_legacy};
pub use progress::{
Milestone, MilestoneStatus, ProgressLedger, load_progress, progress_path, save_progress,
};
pub use query::{FactRecord, SessionSummary, query_facts, recent_sessions};
pub use retention::{RetentionPolicy, apply_retention, gc_legacy};
use std::path::{Path, PathBuf};
pub const SESSIONS_DIR: &str = ".vtcode/sessions";
pub const DERIVED_DIR: &str = "derived";
pub const SESSION_STORE_SCHEMA_VERSION: u32 = 1;
#[must_use]
pub fn sessions_root(workspace: &Path) -> PathBuf {
workspace.join(SESSIONS_DIR)
}
#[must_use]
pub fn session_dir(workspace: &Path, session_id: &str) -> PathBuf {
sessions_root(workspace).join(sanitize_id(session_id))
}
pub fn open(workspace: &Path, session_id: &str) -> Result<SessionEventLog, SessionStoreError> {
SessionEventLog::open(workspace, session_id)
}
fn sanitize_id(id: &str) -> String {
let mut out = String::with_capacity(id.len());
for c in id.chars() {
if c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' {
out.push(c);
} else {
out.push('_');
}
}
let out = out.trim_start_matches('.').to_string();
if out.is_empty() {
"session".to_string()
} else {
out
}
}
#[cfg(test)]
mod tests;