Skip to main content

tracevault_core/
session.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::path::PathBuf;
5
6use crate::hooks::HookEvent;
7
8/// Tracks file states during an active AI session
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SessionState {
11    pub session_id: String,
12    pub transcript_path: String,
13    pub cwd: String,
14    pub started_at: DateTime<Utc>,
15    pub events: Vec<SessionEvent>,
16    /// file path -> content hash before AI modification
17    pub pre_edit_hashes: HashMap<String, String>,
18    /// files modified by AI during this session
19    pub ai_modified_files: Vec<String>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct SessionEvent {
24    pub timestamp: DateTime<Utc>,
25    pub event_type: String,
26    pub tool_name: Option<String>,
27    pub file_path: Option<String>,
28    pub details: Option<serde_json::Value>,
29}
30
31impl SessionState {
32    pub fn new(event: &HookEvent) -> Self {
33        Self {
34            session_id: event.session_id.clone(),
35            transcript_path: event.transcript_path.clone(),
36            cwd: event.cwd.clone(),
37            started_at: Utc::now(),
38            events: vec![],
39            pre_edit_hashes: HashMap::new(),
40            ai_modified_files: vec![],
41        }
42    }
43
44    pub fn record_event(&mut self, event: &HookEvent) {
45        self.events.push(SessionEvent {
46            timestamp: Utc::now(),
47            event_type: event.hook_event_name.clone(),
48            tool_name: event.tool_name.clone(),
49            file_path: event.file_path(),
50            details: event.tool_input.clone(),
51        });
52
53        // Track AI-modified files
54        if event.hook_event_name == "PostToolUse" && event.is_file_modification() {
55            if let Some(path) = event.file_path() {
56                if !self.ai_modified_files.contains(&path) {
57                    self.ai_modified_files.push(path);
58                }
59            }
60        }
61    }
62
63    pub fn record_pre_edit_hash(&mut self, file_path: &str, hash: &str) {
64        self.pre_edit_hashes
65            .entry(file_path.to_string())
66            .or_insert_with(|| hash.to_string());
67    }
68
69    /// Path to session data directory: .tracevault/sessions/<session_id>/
70    pub fn session_dir(&self) -> PathBuf {
71        PathBuf::from(&self.cwd)
72            .join(".tracevault")
73            .join("sessions")
74            .join(&self.session_id)
75    }
76}