-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathsession.ts
More file actions
86 lines (72 loc) · 2.67 KB
/
Copy pathsession.ts
File metadata and controls
86 lines (72 loc) · 2.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Uses Node.js 22+ built-in sqlite — no native compilation, no npm package required
import { DatabaseSync } from "node:sqlite";
import { createHash } from "node:crypto";
import { mkdirSync, existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { Config } from "./config.js";
import { runMigrations } from "./migrations.js";
import { projectHash as scopedProjectHash } from "./store.js";
export type EventType = "file_write" | "file_edit" | "task_complete" | "error" | "fetch" | "session_ended";
export interface SessionEvent {
event_type: EventType;
file_path?: string;
task_name?: string;
error_type?: string;
created_at?: string;
// SECURITY: No content/output fields — we never store file contents or command output
}
function getEventLogPath(projectPath: string): string {
const hash = scopedProjectHash(projectPath);
return join(Config.DB_DIR, `${hash}.events.jsonl`);
}
function dbPath(projectPath: string): string {
const hash = scopedProjectHash(projectPath);
return join(Config.DB_DIR, `${hash}.db`);
}
function openDb(projectPath: string): DatabaseSync {
mkdirSync(Config.DB_DIR, { recursive: true });
const db = new DatabaseSync(dbPath(projectPath));
// WAL mode for concurrent multi-agent access + busy wait instead of immediate fail
db.exec("PRAGMA journal_mode = WAL");
db.exec("PRAGMA busy_timeout = 5000");
db.exec(`
CREATE TABLE IF NOT EXISTS sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_hash TEXT NOT NULL,
created_at TEXT NOT NULL,
last_active TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id INTEGER NOT NULL,
event_type TEXT NOT NULL,
file_path TEXT,
task_name TEXT,
error_type TEXT,
created_at TEXT NOT NULL,
FOREIGN KEY(session_id) REFERENCES sessions(id)
);
`);
// Apply all pending schema migrations
runMigrations(db);
return db;
}
export function getRecentEvents(projectPath: string, limit = 50): SessionEvent[] {
// Read from the JSONL event log written by hooks (posttooluse.mjs, stop.mjs).
// Hooks write minimal metadata events; this is the source of truth for zc_recall_context().
const logPath = getEventLogPath(projectPath);
if (!existsSync(logPath)) return [];
try {
const lines = readFileSync(logPath, "utf8").split("\n").filter(Boolean);
return lines
.slice(-limit)
.reverse()
.map((line) => {
try { return JSON.parse(line) as SessionEvent; }
catch { return null; }
})
.filter((e): e is SessionEvent => e !== null);
} catch {
return [];
}
}