tracevault_core/
session.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::path::PathBuf;
5
6use crate::hooks::HookEvent;
7
8#[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 pub pre_edit_hashes: HashMap<String, String>,
18 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 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 pub fn session_dir(&self) -> PathBuf {
71 PathBuf::from(&self.cwd)
72 .join(".tracevault")
73 .join("sessions")
74 .join(&self.session_id)
75 }
76}