agentacta 2026.3.27 → 2026.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,9 +22,9 @@ npx agentacta
22
22
 
23
23
  ## Why this exists
24
24
 
25
- Agents move fast. Your memory of what happened doesnt.
25
+ Agents move fast. Your memory of what happened doesn't.
26
26
 
27
- When you need to answer what changed, when, and why,” youre usually scraping logs, scrolling transcripts, or asking the same assistant that forgot 20 minutes ago.
27
+ When you need to answer "what changed, when, and why," you're usually scraping logs, scrolling transcripts, or asking the same assistant that forgot 20 minutes ago.
28
28
 
29
29
  AgentActa gives you one place to inspect the full trail.
30
30
 
@@ -40,7 +40,7 @@ AgentActa gives you one place to inspect the full trail.
40
40
  - 📊 Stats for sessions, messages, tools, and tokens
41
41
  - ⚡ Live indexing via file watching
42
42
  - 📱 Mobile-optimized UI with floating navigation
43
- - 🏥 Session health scoring reliability scores, issue detection, and per-signal breakdowns
43
+ - 🏥 Session health scoring - reliability scores, issue detection, and per-signal breakdowns
44
44
  - 💡 Search suggestions based on real data
45
45
  - ⌨️ Command palette (⌘K / Ctrl+K) for quick navigation
46
46
  - 🎨 Theme settings (system, light, dark, OLED)
@@ -95,9 +95,9 @@ Pick a date, see everything that happened, newest first. Today's view updates li
95
95
 
96
96
  The Insights tab surfaces session health across your entire history.
97
97
 
98
- It tracks five issue signals repeated tool loops, sessions that produced no output, high error rates, vague instructions, and incomplete sessions. Each signal is severity-scaled so scores reflect how bad the problem actually was, not just whether it occurred.
98
+ It tracks five issue signals - repeated tool loops, sessions that produced no output, high error rates, vague instructions, and incomplete sessions. Each signal is severity-scaled so scores reflect how bad the problem actually was, not just whether it occurred.
99
99
 
100
- The reliability score (0100) is the inverse of the confusion score: higher means the agent completed work cleanly. The issue rate shows what percentage of possible signal types were detected in a session.
100
+ The reliability score (0-100) is the inverse of the confusion score: higher means the agent completed work cleanly. The issue rate shows what percentage of possible signal types were detected in a session.
101
101
 
102
102
  ### File Activity
103
103
 
@@ -131,7 +131,7 @@ On first run, AgentActa creates:
131
131
  - `~/.config/agentacta/config.json`
132
132
  - or `agentacta.config.json` in current directory (if present)
133
133
 
134
- Default config (auto-generated on first run session directories are detected automatically):
134
+ Default config (auto-generated on first run - session directories are detected automatically):
135
135
 
136
136
  ```json
137
137
  {
@@ -176,7 +176,7 @@ Default config (auto-generated on first run — session directories are detected
176
176
  | `GET /api/timeline/stream?after=<ts>` | SSE stream for live timeline updates |
177
177
  | `POST /api/maintenance` | VACUUM + WAL checkpoint (returns size before/after) |
178
178
  | `GET /api/health` | Server status, version, uptime, session count |
179
- | `GET /api/insights` | Session health summary reliability scores, issue counts, top flagged sessions |
179
+ | `GET /api/insights` | Session health summary - reliability scores, issue counts, top flagged sessions |
180
180
  | `GET /api/export/search?q=<query>&format=md` | Export search results |
181
181
 
182
182
  ### Context API
@@ -189,7 +189,7 @@ The Context API gives agents historical context before they start working. Inste
189
189
  | `GET /api/context/repo?path=<repo-path>` | Aggregates for a repo/project |
190
190
  | `GET /api/context/agent?name=<agent-name>` | Stats for a specific agent |
191
191
 
192
- **File context** how many sessions touched this file, when it was last modified, recent change summaries, operation breakdown (reads vs edits), related files, and recent errors:
192
+ **File context** - how many sessions touched this file, when it was last modified, recent change summaries, operation breakdown (reads vs edits), related files, and recent errors:
193
193
 
194
194
  ```bash
195
195
  curl http://localhost:4003/api/context/file?path=/home/user/project/server.js
@@ -206,7 +206,7 @@ curl http://localhost:4003/api/context/file?path=/home/user/project/server.js
206
206
  }
207
207
  ```
208
208
 
209
- **Agent context** total sessions, cost, average duration, most-used tools, recent work:
209
+ **Agent context** - total sessions, cost, average duration, most-used tools, recent work:
210
210
 
211
211
  ```bash
212
212
  curl http://localhost:4003/api/context/agent?name=claude-code
@@ -223,7 +223,7 @@ curl http://localhost:4003/api/context/agent?name=claude-code
223
223
  }
224
224
  ```
225
225
 
226
- **Repo context** aggregate cost, tokens, distinct agents, most-touched files, common tools:
226
+ **Repo context** - aggregate cost, tokens, distinct agents, most-touched files, common tools:
227
227
 
228
228
  ```bash
229
229
  curl http://localhost:4003/api/context/repo?path=agentacta
@@ -292,13 +292,13 @@ Your session history stays local.
292
292
 
293
293
  PRs welcome.
294
294
 
295
- See [CONTRIBUTING.md](CONTRIBUTING.md). If youre adding a new agent format, start in `indexer.js`.
295
+ See [CONTRIBUTING.md](CONTRIBUTING.md). If you're adding a new agent format, start in `src/indexer.ts`.
296
296
 
297
297
  ## Name
298
298
 
299
- *Acta* is Latin for things done.”
299
+ *Acta* is Latin for "things done."
300
300
 
301
- Thats the job here: keep a readable record of what your agents actually did.
301
+ That's the job here: keep a readable record of what your agents actually did.
302
302
 
303
303
  ## License
304
304
 
@@ -0,0 +1,4 @@
1
+ import type { AgentActaConfig } from './types.js';
2
+ declare const CONFIG_FILE: string;
3
+ declare function loadConfig(): AgentActaConfig;
4
+ export { loadConfig, CONFIG_FILE };
package/dist/config.js ADDED
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CONFIG_FILE = void 0;
7
+ exports.loadConfig = loadConfig;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const node_os_1 = __importDefault(require("node:os"));
11
+ const CWD_CONFIG_FILE = node_path_1.default.join(process.cwd(), 'agentacta.config.json');
12
+ const XDG_CONFIG_DIR = node_path_1.default.join(process.env.XDG_CONFIG_HOME || node_path_1.default.join(node_os_1.default.homedir(), '.config'), 'agentacta');
13
+ const XDG_CONFIG_FILE = node_path_1.default.join(XDG_CONFIG_DIR, 'config.json');
14
+ // Resolve config file: CWD first (backward compat), then XDG default
15
+ function resolveConfigFile() {
16
+ if (node_fs_1.default.existsSync(CWD_CONFIG_FILE))
17
+ return CWD_CONFIG_FILE;
18
+ return XDG_CONFIG_FILE;
19
+ }
20
+ const CONFIG_FILE = resolveConfigFile();
21
+ exports.CONFIG_FILE = CONFIG_FILE;
22
+ const KNOWN_SESSION_DIRS = [
23
+ node_path_1.default.join(node_os_1.default.homedir(), '.claude', 'projects'), // Claude Code
24
+ node_path_1.default.join(node_os_1.default.homedir(), '.codex', 'sessions'), // Codex CLI
25
+ node_path_1.default.join(node_os_1.default.homedir(), '.openclaw', 'sessions'), // OpenClaw
26
+ ];
27
+ const DEFAULTS = {
28
+ port: 4003,
29
+ storage: 'reference',
30
+ sessionsPath: null,
31
+ dbPath: './agentacta.db',
32
+ projectAliases: {}
33
+ };
34
+ function detectSessionDirs() {
35
+ const found = KNOWN_SESSION_DIRS.filter((d) => node_fs_1.default.existsSync(d));
36
+ return found.length > 0 ? found : null;
37
+ }
38
+ function loadConfig() {
39
+ let fileConfig = {};
40
+ if (node_fs_1.default.existsSync(CONFIG_FILE)) {
41
+ try {
42
+ fileConfig = JSON.parse(node_fs_1.default.readFileSync(CONFIG_FILE, 'utf8'));
43
+ }
44
+ catch (err) {
45
+ console.error(`Warning: Could not parse ${CONFIG_FILE}:`, err.message);
46
+ }
47
+ }
48
+ else {
49
+ // First-run: create default config with auto-detected session dirs
50
+ const detected = detectSessionDirs();
51
+ const firstRunDefaults = { ...DEFAULTS, sessionsPath: detected };
52
+ const dir = node_path_1.default.dirname(CONFIG_FILE);
53
+ if (!node_fs_1.default.existsSync(dir))
54
+ node_fs_1.default.mkdirSync(dir, { recursive: true });
55
+ node_fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(firstRunDefaults, null, 2) + '\n');
56
+ // Apply to in-memory config so this run also benefits
57
+ fileConfig = firstRunDefaults;
58
+ console.log(`Created default config: ${CONFIG_FILE}`);
59
+ if (detected) {
60
+ console.log(`Auto-detected session directories:\n${detected.map((d) => ` - ${d}`).join('\n')}`);
61
+ }
62
+ }
63
+ // In demo mode, ignore file-based sessionsPath so live data doesn't bleed in
64
+ if (process.env.AGENTACTA_DEMO_MODE)
65
+ delete fileConfig.sessionsPath;
66
+ const config = { ...DEFAULTS, ...fileConfig };
67
+ // Env var overrides (highest priority)
68
+ if (process.env.PORT)
69
+ config.port = parseInt(process.env.PORT);
70
+ if (process.env.AGENTACTA_STORAGE)
71
+ config.storage = process.env.AGENTACTA_STORAGE;
72
+ if (process.env.AGENTACTA_SESSIONS_PATH)
73
+ config.sessionsPath = process.env.AGENTACTA_SESSIONS_PATH;
74
+ if (process.env.AGENTACTA_DB_PATH)
75
+ config.dbPath = process.env.AGENTACTA_DB_PATH;
76
+ if (process.env.AGENTACTA_PROJECT_ALIASES_JSON) {
77
+ try {
78
+ config.projectAliases = JSON.parse(process.env.AGENTACTA_PROJECT_ALIASES_JSON);
79
+ }
80
+ catch (err) {
81
+ console.error('Warning: Could not parse AGENTACTA_PROJECT_ALIASES_JSON:', err.message);
82
+ }
83
+ }
84
+ // Resolve dbPath relative to cwd
85
+ config.dbPath = node_path_1.default.resolve(config.dbPath);
86
+ if (!config.projectAliases || typeof config.projectAliases !== 'object')
87
+ config.projectAliases = {};
88
+ return config;
89
+ }
90
+ // v1.1.3
91
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;AAoFS,gCAAU;AApFnB,sDAAyB;AACzB,0DAA6B;AAC7B,sDAAyB;AAGzB,MAAM,eAAe,GAAW,mBAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,uBAAuB,CAAC,CAAC;AAClF,MAAM,cAAc,GAAW,mBAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,mBAAI,CAAC,IAAI,CAAC,iBAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;AACzH,MAAM,eAAe,GAAW,mBAAI,CAAC,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;AAEzE,qEAAqE;AACrE,SAAS,iBAAiB;IACxB,IAAI,iBAAE,CAAC,UAAU,CAAC,eAAe,CAAC;QAAE,OAAO,eAAe,CAAC;IAC3D,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,MAAM,WAAW,GAAW,iBAAiB,EAAE,CAAC;AAqE3B,kCAAW;AAnEhC,MAAM,kBAAkB,GAAa;IACnC,mBAAI,CAAC,IAAI,CAAC,iBAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,EAAK,cAAc;IACjE,mBAAI,CAAC,IAAI,CAAC,iBAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAM,YAAY;IAC/D,mBAAI,CAAC,IAAI,CAAC,iBAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,UAAU,CAAC,EAAG,WAAW;CAC/D,CAAC;AAEF,MAAM,QAAQ,GAAoB;IAChC,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,WAAW;IACpB,YAAY,EAAE,IAAI;IAClB,MAAM,EAAE,gBAAgB;IACxB,cAAc,EAAE,EAAE;CACnB,CAAC;AAEF,SAAS,iBAAiB;IACxB,MAAM,KAAK,GAAa,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,iBAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,UAAU,GAA6B,EAAE,CAAC;IAE9C,IAAI,iBAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAA6B,CAAC;QAC5F,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,WAAW,GAAG,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;SAAM,CAAC;QACN,mEAAmE;QACnE,MAAM,QAAQ,GAAoB,iBAAiB,EAAE,CAAC;QACtD,MAAM,gBAAgB,GAAoB,EAAE,GAAG,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;QAClF,MAAM,GAAG,GAAW,mBAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC9C,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,iBAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,iBAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAChF,sDAAsD;QACtD,UAAU,GAAG,gBAAgB,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,2BAA2B,WAAW,EAAE,CAAC,CAAC;QACtD,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,CAAC,uCAAuC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3G,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAAE,OAAO,UAAU,CAAC,YAAY,CAAC;IACpE,MAAM,MAAM,GAAoB,EAAE,GAAG,QAAQ,EAAE,GAAG,UAAU,EAAE,CAAC;IAE/D,uCAAuC;IACvC,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI;QAAE,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAClF,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB;QAAE,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IACnG,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAAE,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACjF,IAAI,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,CAAC;QAC/C,IAAI,CAAC;YACH,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAA2B,CAAC;QAC3G,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,0DAA0D,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QACpG,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,MAAM,CAAC,MAAM,GAAG,mBAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,OAAO,MAAM,CAAC,cAAc,KAAK,QAAQ;QAAE,MAAM,CAAC,cAAc,GAAG,EAAE,CAAC;IAEpG,OAAO,MAAM,CAAC;AAChB,CAAC;AAGD,SAAS"}
package/dist/db.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import Database from 'better-sqlite3';
2
+ import type { PreparedStatements } from './types.js';
3
+ export declare function open(dbPath?: string): Database.Database;
4
+ export declare function init(dbPath?: string): void;
5
+ export declare function createStmts(db: Database.Database): PreparedStatements;
package/dist/db.js ADDED
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.open = open;
7
+ exports.init = init;
8
+ exports.createStmts = createStmts;
9
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
10
+ const config_js_1 = require("./config.js");
11
+ let _config = null;
12
+ function getConfig() {
13
+ if (!_config)
14
+ _config = (0, config_js_1.loadConfig)();
15
+ return _config;
16
+ }
17
+ function open(dbPath) {
18
+ const p = dbPath || getConfig().dbPath;
19
+ const db = new better_sqlite3_1.default(p);
20
+ db.pragma('journal_mode = WAL');
21
+ db.pragma('foreign_keys = ON');
22
+ return db;
23
+ }
24
+ function init(dbPath) {
25
+ const db = open(dbPath);
26
+ db.exec(`
27
+ CREATE TABLE IF NOT EXISTS sessions (
28
+ id TEXT PRIMARY KEY,
29
+ start_time TEXT NOT NULL,
30
+ end_time TEXT,
31
+ message_count INTEGER DEFAULT 0,
32
+ tool_count INTEGER DEFAULT 0,
33
+ model TEXT,
34
+ summary TEXT,
35
+ agent TEXT,
36
+ session_type TEXT,
37
+ total_cost REAL DEFAULT 0,
38
+ total_tokens INTEGER DEFAULT 0,
39
+ input_tokens INTEGER DEFAULT 0,
40
+ output_tokens INTEGER DEFAULT 0,
41
+ cache_read_tokens INTEGER DEFAULT 0,
42
+ cache_write_tokens INTEGER DEFAULT 0,
43
+ initial_prompt TEXT,
44
+ first_message_id TEXT,
45
+ first_message_timestamp TEXT
46
+ );
47
+
48
+ CREATE TABLE IF NOT EXISTS events (
49
+ id TEXT PRIMARY KEY,
50
+ session_id TEXT NOT NULL,
51
+ timestamp TEXT NOT NULL,
52
+ type TEXT NOT NULL,
53
+ role TEXT,
54
+ content TEXT,
55
+ tool_name TEXT,
56
+ tool_args TEXT,
57
+ tool_result TEXT,
58
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
59
+ );
60
+
61
+ CREATE INDEX IF NOT EXISTS idx_sessions_start_time ON sessions(start_time DESC);
62
+ CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
63
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
64
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
65
+ CREATE INDEX IF NOT EXISTS idx_events_tool_name ON events(tool_name);
66
+
67
+ CREATE VIRTUAL TABLE IF NOT EXISTS events_fts USING fts5(
68
+ content, tool_name, tool_args,
69
+ content='events',
70
+ content_rowid='rowid'
71
+ );
72
+
73
+ CREATE TRIGGER IF NOT EXISTS events_ai AFTER INSERT ON events BEGIN
74
+ INSERT INTO events_fts(rowid, content, tool_name, tool_args)
75
+ VALUES (new.rowid, new.content, new.tool_name, new.tool_args);
76
+ END;
77
+
78
+ CREATE TRIGGER IF NOT EXISTS events_ad AFTER DELETE ON events BEGIN
79
+ INSERT INTO events_fts(events_fts, rowid, content, tool_name, tool_args)
80
+ VALUES ('delete', old.rowid, old.content, old.tool_name, old.tool_args);
81
+ END;
82
+
83
+ CREATE TABLE IF NOT EXISTS index_state (
84
+ file_path TEXT PRIMARY KEY,
85
+ last_offset INTEGER DEFAULT 0,
86
+ last_modified TEXT
87
+ );
88
+
89
+ CREATE TABLE IF NOT EXISTS file_activity (
90
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
91
+ session_id TEXT NOT NULL,
92
+ file_path TEXT NOT NULL,
93
+ operation TEXT NOT NULL,
94
+ timestamp TEXT,
95
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
96
+ );
97
+
98
+ CREATE INDEX IF NOT EXISTS idx_file_activity_path ON file_activity(file_path);
99
+ CREATE INDEX IF NOT EXISTS idx_file_activity_session ON file_activity(session_id);
100
+
101
+ CREATE TABLE IF NOT EXISTS archive (
102
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
103
+ session_id TEXT NOT NULL,
104
+ line_number INTEGER NOT NULL,
105
+ raw_json TEXT NOT NULL,
106
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
107
+ );
108
+
109
+ CREATE INDEX IF NOT EXISTS idx_archive_session ON archive(session_id);
110
+
111
+ CREATE TABLE IF NOT EXISTS session_insights (
112
+ session_id TEXT PRIMARY KEY,
113
+ signals TEXT,
114
+ confusion_score INTEGER DEFAULT 0,
115
+ flagged INTEGER DEFAULT 0,
116
+ computed_at TEXT,
117
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
118
+ );
119
+
120
+ CREATE INDEX IF NOT EXISTS idx_insights_flagged ON session_insights(flagged);
121
+ CREATE INDEX IF NOT EXISTS idx_insights_score ON session_insights(confusion_score DESC);
122
+ `);
123
+ // Add columns if missing (migration)
124
+ const cols = db.prepare("PRAGMA table_info(sessions)").all();
125
+ const colNames = cols.map((c) => c.name);
126
+ if (!colNames.includes('agent'))
127
+ db.exec("ALTER TABLE sessions ADD COLUMN agent TEXT");
128
+ if (!colNames.includes('session_type'))
129
+ db.exec("ALTER TABLE sessions ADD COLUMN session_type TEXT");
130
+ if (!colNames.includes('total_cost'))
131
+ db.exec("ALTER TABLE sessions ADD COLUMN total_cost REAL DEFAULT 0");
132
+ if (!colNames.includes('total_tokens'))
133
+ db.exec("ALTER TABLE sessions ADD COLUMN total_tokens INTEGER DEFAULT 0");
134
+ if (!colNames.includes('input_tokens'))
135
+ db.exec("ALTER TABLE sessions ADD COLUMN input_tokens INTEGER DEFAULT 0");
136
+ if (!colNames.includes('output_tokens'))
137
+ db.exec("ALTER TABLE sessions ADD COLUMN output_tokens INTEGER DEFAULT 0");
138
+ if (!colNames.includes('cache_read_tokens'))
139
+ db.exec("ALTER TABLE sessions ADD COLUMN cache_read_tokens INTEGER DEFAULT 0");
140
+ if (!colNames.includes('cache_write_tokens'))
141
+ db.exec("ALTER TABLE sessions ADD COLUMN cache_write_tokens INTEGER DEFAULT 0");
142
+ if (!colNames.includes('models'))
143
+ db.exec("ALTER TABLE sessions ADD COLUMN models TEXT");
144
+ if (!colNames.includes('projects'))
145
+ db.exec("ALTER TABLE sessions ADD COLUMN projects TEXT");
146
+ db.close();
147
+ }
148
+ function createStmts(db) {
149
+ return {
150
+ getState: db.prepare('SELECT * FROM index_state WHERE file_path = ?'),
151
+ getSession: db.prepare('SELECT id FROM sessions WHERE id = ?'),
152
+ deleteEvents: db.prepare('DELETE FROM events WHERE session_id = ?'),
153
+ deleteSession: db.prepare('DELETE FROM sessions WHERE id = ?'),
154
+ deleteFileActivity: db.prepare('DELETE FROM file_activity WHERE session_id = ?'),
155
+ insertEvent: db.prepare(`INSERT OR REPLACE INTO events (id, session_id, timestamp, type, role, content, tool_name, tool_args, tool_result) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),
156
+ upsertSession: db.prepare(`INSERT OR REPLACE INTO sessions (id, start_time, end_time, message_count, tool_count, model, summary, agent, session_type, total_cost, total_tokens, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, initial_prompt, first_message_id, first_message_timestamp, models, projects) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
157
+ upsertState: db.prepare(`INSERT OR REPLACE INTO index_state (file_path, last_offset, last_modified) VALUES (?, ?, ?)`),
158
+ insertFileActivity: db.prepare(`INSERT INTO file_activity (session_id, file_path, operation, timestamp) VALUES (?, ?, ?, ?)`),
159
+ deleteArchive: db.prepare('DELETE FROM archive WHERE session_id = ?'),
160
+ insertArchive: db.prepare('INSERT INTO archive (session_id, line_number, raw_json) VALUES (?, ?, ?)')
161
+ };
162
+ }
163
+ //# sourceMappingURL=db.js.map
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type Database from 'better-sqlite3';
2
+ import type { EventRow } from './types.js';
3
+ export declare function extractCallBaseId(id: string | null | undefined): string;
4
+ export declare function loadDeltaAttributionContext(db: Database.Database, sessionId: string, rows: EventRow[]): EventRow[];
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractCallBaseId = extractCallBaseId;
4
+ exports.loadDeltaAttributionContext = loadDeltaAttributionContext;
5
+ function extractCallBaseId(id) {
6
+ if (!id)
7
+ return '';
8
+ return String(id).replace(/:(call|result)$/, '');
9
+ }
10
+ function loadDeltaAttributionContext(db, sessionId, rows) {
11
+ if (!db || !Array.isArray(rows) || !rows.length)
12
+ return [];
13
+ const ordered = [...rows].sort((a, b) => {
14
+ const ta = Date.parse(a?.timestamp || '0') || 0;
15
+ const tb = Date.parse(b?.timestamp || '0') || 0;
16
+ if (ta !== tb)
17
+ return ta - tb;
18
+ return String(a?.id || '').localeCompare(String(b?.id || ''));
19
+ });
20
+ const first = ordered[0];
21
+ const firstTs = first?.timestamp || '1970-01-01T00:00:00.000Z';
22
+ const firstId = first?.id || '';
23
+ const neighborhoodRows = db.prepare(`SELECT * FROM events
24
+ WHERE session_id = ?
25
+ AND (timestamp < ? OR (timestamp = ? AND id < ?))
26
+ ORDER BY timestamp DESC, id DESC
27
+ LIMIT 12`).all(sessionId, firstTs, firstTs, firstId).reverse();
28
+ const callIds = [...new Set(rows
29
+ .filter((row) => row != null && row.type === 'tool_result')
30
+ .map((row) => extractCallBaseId(row.id))
31
+ .filter(Boolean)
32
+ .map((base) => `${base}:call`))];
33
+ if (!callIds.length)
34
+ return neighborhoodRows;
35
+ const placeholders = callIds.map(() => '?').join(',');
36
+ const linkedCallRows = db.prepare(`SELECT * FROM events
37
+ WHERE session_id = ?
38
+ AND type = 'tool_call'
39
+ AND id IN (${placeholders})`).all(sessionId, ...callIds);
40
+ const merged = [];
41
+ const seen = new Set();
42
+ for (const row of [...neighborhoodRows, ...linkedCallRows]) {
43
+ if (!row || !row.id || seen.has(row.id))
44
+ continue;
45
+ seen.add(row.id);
46
+ merged.push(row);
47
+ }
48
+ return merged;
49
+ }
50
+ //# sourceMappingURL=delta-attribution-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta-attribution-context.js","sourceRoot":"","sources":["../src/delta-attribution-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};