vtcode_session_store/
query.rs1use std::path::Path;
4
5use crate::error::SessionStoreError;
6use crate::sessions_root;
7
8#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
10pub struct SessionSummary {
11 pub session_id: String,
13 pub turn_count: u64,
15 pub event_count: u64,
17 pub status: String,
19 pub updated_at: String,
21}
22
23#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
25pub struct FactRecord {
26 pub fact: String,
28 pub session_id: String,
30}
31
32#[must_use]
34pub fn recent_sessions(workspace: &Path, n: usize) -> Vec<SessionSummary> {
35 let root = sessions_root(workspace);
36 if !root.exists() {
37 return Vec::new();
38 }
39 let mut out = Vec::new();
40 let entries = match std::fs::read_dir(&root) {
41 Ok(e) => e,
42 Err(_) => return Vec::new(),
43 };
44 for entry in entries.filter_map(Result::ok) {
45 let manifest = entry.path().join("manifest.json");
46 if let Ok(bytes) = std::fs::read(&manifest) {
47 if let Ok(s) = serde_json::from_slice::<SessionSummary>(&bytes) {
48 out.push(s);
49 }
50 }
51 }
52 out.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
53 out.truncate(n);
54 out
55}
56
57pub fn query_facts(workspace: &Path, limit: usize) -> Result<Vec<FactRecord>, SessionStoreError> {
61 let root = sessions_root(workspace);
62 if !root.exists() {
63 return Ok(Vec::new());
64 }
65 let mut facts: Vec<FactRecord> = Vec::new();
66 let entries = std::fs::read_dir(&root).map_err(|e| SessionStoreError::io(root.clone(), e))?;
67 for entry in entries.filter_map(Result::ok) {
68 let memory = entry.path().join(crate::DERIVED_DIR).join("memory.json");
69 let Ok(bytes) = std::fs::read(&memory) else {
70 continue;
71 };
72 let Ok(value) = serde_json::from_slice::<serde_json::Value>(&bytes) else {
73 continue;
74 };
75 let session_id = entry.file_name().to_string_lossy().to_string();
76 if let Some(arr) = value.get("grounded_facts").and_then(|v| v.as_array()) {
77 for item in arr {
78 if let Some(fact) = item.get("fact").and_then(|f| f.as_str()) {
79 facts.push(FactRecord {
80 fact: fact.to_string(),
81 session_id: session_id.clone(),
82 });
83 }
84 }
85 }
86 }
87 facts.truncate(limit);
88 Ok(facts)
89}