@swestash/agent-lens 0.11.0 → 0.12.0

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.
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { existsSync as existsSync10, writeFileSync as writeFileSync3 } from "fs";
5
- import { join as join12 } from "path";
6
- import { fileURLToPath as fileURLToPath4 } from "url";
4
+ import { existsSync as existsSync10, writeFileSync as writeFileSync3 } from "node:fs";
5
+ import { join as join12 } from "node:path";
6
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
7
7
 
8
8
  // ../../node_modules/.pnpm/cac@6.7.14/node_modules/cac/dist/index.mjs
9
9
  import { EventEmitter } from "events";
@@ -921,6 +921,29 @@ END;
921
921
  `
922
922
  );
923
923
 
924
+ // ../core/dist/sqlite.js
925
+ function runNamed(stmt, row) {
926
+ return stmt.run(row);
927
+ }
928
+ var savepointSeq = 0;
929
+ function transaction(db, fn) {
930
+ return (...args) => {
931
+ const savepoint = db.isTransaction ? `agent_lens_sp_${savepointSeq++}` : "";
932
+ db.exec(savepoint ? `SAVEPOINT ${savepoint}` : "BEGIN");
933
+ try {
934
+ const result = fn(...args);
935
+ db.exec(savepoint ? `RELEASE ${savepoint}` : "COMMIT");
936
+ return result;
937
+ } catch (err) {
938
+ try {
939
+ db.exec(savepoint ? `ROLLBACK TO ${savepoint}; RELEASE ${savepoint}` : "ROLLBACK");
940
+ } catch {
941
+ }
942
+ throw err;
943
+ }
944
+ };
945
+ }
946
+
924
947
  // ../core/dist/security.js
925
948
  var SEVERITY_ORDER = ["info", "low", "medium", "high", "critical"];
926
949
  function severityRank(s) {
@@ -1076,7 +1099,7 @@ function scrubUsernames(text, users) {
1076
1099
  }
1077
1100
 
1078
1101
  // ../core/dist/redact-export.js
1079
- import { createHash } from "crypto";
1102
+ import { createHash } from "node:crypto";
1080
1103
 
1081
1104
  // ../core/dist/markdown.js
1082
1105
  function truncate(s) {
@@ -1313,6 +1336,9 @@ var activeTable = PRICE_TABLE;
1313
1336
  function usePricing(table) {
1314
1337
  activeTable = table;
1315
1338
  }
1339
+ function activePricing() {
1340
+ return activeTable;
1341
+ }
1316
1342
  function rateForModel(model) {
1317
1343
  if (!model)
1318
1344
  return null;
@@ -1333,25 +1359,24 @@ function costForUsage(model, u) {
1333
1359
  }
1334
1360
 
1335
1361
  // ../core/dist/rawjson.js
1336
- import { gzipSync, gunzipSync } from "zlib";
1362
+ import { gzipSync, gunzipSync } from "node:zlib";
1337
1363
  function packRaw(json) {
1338
1364
  return gzipSync(Buffer.from(json, "utf8"));
1339
1365
  }
1340
1366
  function unpackRaw(value) {
1341
- if (Buffer.isBuffer(value)) {
1342
- if (value.length >= 2 && value[0] === 31 && value[1] === 139) {
1343
- return gunzipSync(value).toString("utf8");
1344
- }
1345
- return value.toString("utf8");
1367
+ if (typeof value === "string")
1368
+ return value;
1369
+ if (value.length >= 2 && value[0] === 31 && value[1] === 139) {
1370
+ return gunzipSync(value).toString("utf8");
1346
1371
  }
1347
- return value;
1372
+ return Buffer.from(value).toString("utf8");
1348
1373
  }
1349
1374
 
1350
1375
  // ../core/dist/paths.js
1351
- import { existsSync, readFileSync } from "fs";
1352
- import { dirname, join, resolve } from "path";
1353
- import { fileURLToPath } from "url";
1354
- import { homedir, platform } from "os";
1376
+ import { existsSync, readFileSync } from "node:fs";
1377
+ import { dirname, join, resolve } from "node:path";
1378
+ import { fileURLToPath } from "node:url";
1379
+ import { homedir, platform } from "node:os";
1355
1380
  function expandPath(p) {
1356
1381
  if (!p)
1357
1382
  return p;
@@ -1429,8 +1454,8 @@ function resolveWebDist(fromDir, repoRoot = findRepoRoot(fromDir)) {
1429
1454
  }
1430
1455
 
1431
1456
  // ../core/dist/config.js
1432
- import { readFileSync as readFileSync2 } from "fs";
1433
- import { join as join2 } from "path";
1457
+ import { readFileSync as readFileSync2 } from "node:fs";
1458
+ import { join as join2 } from "node:path";
1434
1459
  var DEFAULT_PORT = 4477;
1435
1460
  var DEFAULT_HOST = "127.0.0.1";
1436
1461
  var DEFAULT_DB_NAME = "agent-lens.db";
@@ -1512,10 +1537,10 @@ function resolveServerConfig(overrides = {}, cfg = readConfigFile()) {
1512
1537
  }
1513
1538
 
1514
1539
  // ../core/dist/version.js
1515
- import { spawnSync } from "child_process";
1516
- import { readFileSync as readFileSync3 } from "fs";
1517
- import { dirname as dirname2, join as join3, parse } from "path";
1518
- import { fileURLToPath as fileURLToPath2 } from "url";
1540
+ import { spawnSync } from "node:child_process";
1541
+ import { readFileSync as readFileSync3 } from "node:fs";
1542
+ import { dirname as dirname2, join as join3, parse } from "node:path";
1543
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
1519
1544
  var moduleDir2 = dirname2(fileURLToPath2(import.meta.url));
1520
1545
  function isPlaceholder(v) {
1521
1546
  return v.startsWith("0.0.0");
@@ -1617,9 +1642,9 @@ function encodeProjectPath(p) {
1617
1642
  }
1618
1643
 
1619
1644
  // ../core/dist/collect.js
1620
- import { appendFileSync, closeSync, copyFileSync, existsSync as existsSync2, mkdirSync, openSync, readSync, readdirSync, statSync, unlinkSync, utimesSync, writeSync, chmodSync } from "fs";
1621
- import { basename, dirname as dirname3, join as join4, relative, sep } from "path";
1622
- import { platform as platform2 } from "os";
1645
+ import { appendFileSync, closeSync, copyFileSync, existsSync as existsSync2, mkdirSync, openSync, readSync, readdirSync, statSync, unlinkSync, utimesSync, writeSync, chmodSync } from "node:fs";
1646
+ import { basename, dirname as dirname3, join as join4, relative, sep } from "node:path";
1647
+ import { platform as platform2 } from "node:os";
1623
1648
  var POSIX = platform2() !== "win32";
1624
1649
  var DIR_MODE = 448;
1625
1650
  var FILE_MODE = 384;
@@ -1838,9 +1863,9 @@ function collectAll(opts = {}) {
1838
1863
  }
1839
1864
 
1840
1865
  // ../core/dist/lock.js
1841
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync } from "fs";
1842
- import { dirname as dirname4 } from "path";
1843
- import { hostname } from "os";
1866
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
1867
+ import { dirname as dirname4 } from "node:path";
1868
+ import { hostname } from "node:os";
1844
1869
  function isAlive(pid) {
1845
1870
  if (!pid)
1846
1871
  return false;
@@ -1880,10 +1905,10 @@ function acquireLock(lockPath, staleMs = 6 * 60 * 60 * 1e3) {
1880
1905
  }
1881
1906
 
1882
1907
  // ../core/dist/service.js
1883
- import { spawnSync as spawnSync2 } from "child_process";
1884
- import { mkdirSync as mkdirSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
1885
- import { homedir as homedir2, platform as platform3, userInfo } from "os";
1886
- import { join as join5 } from "path";
1908
+ import { spawnSync as spawnSync2 } from "node:child_process";
1909
+ import { mkdirSync as mkdirSync3, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
1910
+ import { homedir as homedir2, platform as platform3, userInfo } from "node:os";
1911
+ import { join as join5 } from "node:path";
1887
1912
  var DEFAULT_HOURS = [9, 13, 17, 21];
1888
1913
  function currentUser() {
1889
1914
  try {
@@ -2208,15 +2233,15 @@ function runService(action, params) {
2208
2233
  }
2209
2234
 
2210
2235
  // ../ingest/dist/run.js
2211
- import { readFileSync as readFileSync6, statSync as statSync3, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
2212
- import { dirname as dirname6, join as join9 } from "path";
2236
+ import { readFileSync as readFileSync6, statSync as statSync3, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "node:fs";
2237
+ import { dirname as dirname6, join as join9 } from "node:path";
2213
2238
 
2214
2239
  // ../ingest/dist/db.js
2215
- import Database from "better-sqlite3";
2240
+ import { DatabaseSync } from "node:sqlite";
2216
2241
  function openRaw(file) {
2217
- const db = new Database(file);
2218
- db.pragma("journal_mode = WAL");
2219
- db.pragma("foreign_keys = ON");
2242
+ const db = new DatabaseSync(file);
2243
+ db.exec("PRAGMA journal_mode = WAL");
2244
+ db.exec("PRAGMA foreign_keys = ON");
2220
2245
  return db;
2221
2246
  }
2222
2247
  function readSchemaVersion(db) {
@@ -2236,37 +2261,40 @@ function openDb(file) {
2236
2261
  return db;
2237
2262
  }
2238
2263
  function resetSchema(db) {
2239
- db.pragma("foreign_keys = OFF");
2240
- db.exec(`
2241
- DROP TRIGGER IF EXISTS events_au;
2242
- DROP TRIGGER IF EXISTS events_ad;
2243
- DROP TRIGGER IF EXISTS events_ai;
2244
- DROP TABLE IF EXISTS events_fts;
2245
- DROP TABLE IF EXISTS file_changes;
2246
- DROP TABLE IF EXISTS findings;
2247
- DROP TABLE IF EXISTS classifications;
2248
- DROP TABLE IF EXISTS token_usage;
2249
- DROP TABLE IF EXISTS tool_calls;
2250
- DROP TABLE IF EXISTS skills;
2251
- DROP TABLE IF EXISTS events;
2252
- DROP TABLE IF EXISTS turns;
2253
- DROP TABLE IF EXISTS sessions;
2254
- DROP TABLE IF EXISTS projects;
2255
- DROP TABLE IF EXISTS sources;
2256
- DROP TABLE IF EXISTS agents;
2257
- DROP TABLE IF EXISTS workflow_results;
2258
- DROP TABLE IF EXISTS session_meta;
2259
- DROP TABLE IF EXISTS tool_results;
2260
- DROP TABLE IF EXISTS ingest_state;
2261
- DROP TABLE IF EXISTS meta;
2262
- `);
2263
- applySchema(db);
2264
- db.pragma("foreign_keys = ON");
2264
+ db.exec("PRAGMA foreign_keys = OFF");
2265
+ try {
2266
+ db.exec(`
2267
+ DROP TRIGGER IF EXISTS events_au;
2268
+ DROP TRIGGER IF EXISTS events_ad;
2269
+ DROP TRIGGER IF EXISTS events_ai;
2270
+ DROP TABLE IF EXISTS events_fts;
2271
+ DROP TABLE IF EXISTS file_changes;
2272
+ DROP TABLE IF EXISTS findings;
2273
+ DROP TABLE IF EXISTS classifications;
2274
+ DROP TABLE IF EXISTS token_usage;
2275
+ DROP TABLE IF EXISTS tool_calls;
2276
+ DROP TABLE IF EXISTS skills;
2277
+ DROP TABLE IF EXISTS events;
2278
+ DROP TABLE IF EXISTS turns;
2279
+ DROP TABLE IF EXISTS sessions;
2280
+ DROP TABLE IF EXISTS projects;
2281
+ DROP TABLE IF EXISTS sources;
2282
+ DROP TABLE IF EXISTS agents;
2283
+ DROP TABLE IF EXISTS workflow_results;
2284
+ DROP TABLE IF EXISTS session_meta;
2285
+ DROP TABLE IF EXISTS tool_results;
2286
+ DROP TABLE IF EXISTS ingest_state;
2287
+ DROP TABLE IF EXISTS meta;
2288
+ `);
2289
+ applySchema(db);
2290
+ } finally {
2291
+ db.exec("PRAGMA foreign_keys = ON");
2292
+ }
2265
2293
  }
2266
2294
 
2267
2295
  // ../ingest/dist/adapters/claude-code.js
2268
- import { readdirSync as readdirSync2 } from "fs";
2269
- import { basename as basename2, join as join6 } from "path";
2296
+ import { readdirSync as readdirSync2 } from "node:fs";
2297
+ import { basename as basename2, join as join6 } from "node:path";
2270
2298
  function flattenText(content) {
2271
2299
  if (content == null)
2272
2300
  return null;
@@ -2527,7 +2555,7 @@ function createDirtySet(db, name, ids) {
2527
2555
  db.exec(`DROP TABLE IF EXISTS ${name}`);
2528
2556
  db.exec(`CREATE TEMP TABLE ${name} (id TEXT PRIMARY KEY)`);
2529
2557
  const ins = db.prepare(`INSERT OR IGNORE INTO ${name} (id) VALUES (?)`);
2530
- db.transaction((xs) => {
2558
+ transaction(db, (xs) => {
2531
2559
  for (const id of xs)
2532
2560
  ins.run(id);
2533
2561
  })(ids);
@@ -2727,7 +2755,7 @@ function classify(db, dirty) {
2727
2755
  category=excluded.category, complexity_score=excluded.complexity_score,
2728
2756
  complexity_band=excluded.complexity_band, signals_json=excluded.signals_json,
2729
2757
  classifier_version=excluded.classifier_version`);
2730
- const tx = db.transaction(() => {
2758
+ const tx = transaction(db, () => {
2731
2759
  for (const sess of sessions) {
2732
2760
  const toolCounts = toolMix.get(sess.id) ?? {};
2733
2761
  const skills = skillMix.get(sess.id) ?? {};
@@ -2798,8 +2826,8 @@ function classify(db, dirty) {
2798
2826
  }
2799
2827
 
2800
2828
  // ../ingest/dist/detect.js
2801
- import { createHash as createHash2 } from "crypto";
2802
- import { posix as path } from "path";
2829
+ import { createHash as createHash2 } from "node:crypto";
2830
+ import { posix as path } from "node:path";
2803
2831
  var DETECTOR_VERSION = 8;
2804
2832
  function evidence(s, max = 200) {
2805
2833
  const one = s.replace(/\s+/g, " ").trim();
@@ -3264,7 +3292,7 @@ function detect(db, dirty) {
3264
3292
  VALUES (@id, @session_id, @tool_call_id, @event_uuid, @turn_id, @rule_id, @category, @framework_ref, @severity, @title, @evidence, @signals_json, @detector_version)
3265
3293
  ON CONFLICT(id) DO NOTHING`);
3266
3294
  const delScope = incremental ? "DELETE FROM findings WHERE session_id IN (SELECT id FROM _dirty_sec)" : "DELETE FROM findings";
3267
- const tx = db.transaction(() => {
3295
+ const tx = transaction(db, () => {
3268
3296
  db.exec(delScope);
3269
3297
  for (const row of rows) {
3270
3298
  const ctx = buildContext(row, projPath.get(row.session_id) ?? null, ownedConfigDirs);
@@ -3324,7 +3352,7 @@ function classifyErrors(db, dirty) {
3324
3352
  createDirtySet(db, "_dirty_err", dirty);
3325
3353
  const rows = db.prepare(`SELECT id, result_summary FROM tool_calls WHERE status = 'error'${scope}`).all();
3326
3354
  const update = db.prepare("UPDATE tool_calls SET error_type = ? WHERE id = ?");
3327
- const tx = db.transaction(() => {
3355
+ const tx = transaction(db, () => {
3328
3356
  db.exec(`UPDATE tool_calls SET error_type = NULL WHERE error_type IS NOT NULL${scope}`);
3329
3357
  for (const r of rows)
3330
3358
  update.run(classifyToolError(r.result_summary).type, r.id);
@@ -3337,8 +3365,8 @@ function classifyErrors(db, dirty) {
3337
3365
  }
3338
3366
 
3339
3367
  // ../ingest/dist/filechanges.js
3340
- import { createHash as createHash3 } from "crypto";
3341
- import { posix as path2 } from "path";
3368
+ import { createHash as createHash3 } from "node:crypto";
3369
+ import { posix as path2 } from "node:path";
3342
3370
  var FILECHANGES_VERSION = 1;
3343
3371
  var FILE_TOOLS = ["Edit", "Write", "NotebookEdit"];
3344
3372
  function fileChangeId(toolCallId, filePath) {
@@ -3370,7 +3398,7 @@ function deriveFileChanges(db, dirty) {
3370
3398
  db.exec("DROP TABLE IF EXISTS _dirty_fc");
3371
3399
  db.exec("CREATE TEMP TABLE _dirty_fc (id TEXT PRIMARY KEY)");
3372
3400
  const ins = db.prepare("INSERT OR IGNORE INTO _dirty_fc (id) VALUES (?)");
3373
- db.transaction((ids) => {
3401
+ transaction(db, (ids) => {
3374
3402
  for (const id of ids)
3375
3403
  ins.run(id);
3376
3404
  })(dirty);
@@ -3387,7 +3415,7 @@ function deriveFileChanges(db, dirty) {
3387
3415
  VALUES (@id, @tool_call_id, @session_id, @turn_id, @event_uuid, @project_id, @file_path, @tool_name, @lines_added, @lines_removed, @timestamp, @derive_version)
3388
3416
  ON CONFLICT(id) DO NOTHING`);
3389
3417
  const delScope = incremental ? "DELETE FROM file_changes WHERE session_id IN (SELECT id FROM _dirty_fc)" : "DELETE FROM file_changes";
3390
- const tx = db.transaction(() => {
3418
+ const tx = transaction(db, () => {
3391
3419
  db.exec(delScope);
3392
3420
  for (const row of rows) {
3393
3421
  if (row.status === "error")
@@ -3436,10 +3464,10 @@ function deriveFileChanges(db, dirty) {
3436
3464
  }
3437
3465
 
3438
3466
  // ../ingest/dist/canonicalize.js
3439
- import { existsSync as existsSync4 } from "fs";
3440
- import { homedir as homedir3 } from "os";
3441
- import { createHash as createHash4 } from "crypto";
3442
- import { dirname as dirname5, join as join7 } from "path";
3467
+ import { existsSync as existsSync4 } from "node:fs";
3468
+ import { homedir as homedir3 } from "node:os";
3469
+ import { createHash as createHash4 } from "node:crypto";
3470
+ import { dirname as dirname5, join as join7 } from "node:path";
3443
3471
  function projectId(agentId, path3) {
3444
3472
  return createHash4("sha1").update(`${agentId}\0${path3}`).digest("hex").slice(0, 16);
3445
3473
  }
@@ -3495,7 +3523,7 @@ function canonicalizeProjects(db, opts = {}) {
3495
3523
  const repointSessions = db.prepare("UPDATE sessions SET project_id = ? WHERE project_id = ?");
3496
3524
  const repointFileChanges = db.prepare("UPDATE file_changes SET project_id = ? WHERE project_id = ?");
3497
3525
  const touched = /* @__PURE__ */ new Set();
3498
- db.transaction(() => {
3526
+ transaction(db, () => {
3499
3527
  for (const r of rows) {
3500
3528
  const target = canon(r.path);
3501
3529
  if (target === strip(r.path))
@@ -3519,12 +3547,12 @@ function canonicalizeProjects(db, opts = {}) {
3519
3547
  for (const id of touched)
3520
3548
  refresh.run(id);
3521
3549
  })();
3522
- const removed = db.prepare("DELETE FROM projects WHERE id NOT IN (SELECT DISTINCT project_id FROM sessions WHERE project_id IS NOT NULL)").run().changes;
3550
+ const removed = Number(db.prepare("DELETE FROM projects WHERE id NOT IN (SELECT DISTINCT project_id FROM sessions WHERE project_id IS NOT NULL)").run().changes);
3523
3551
  return { merged, removed };
3524
3552
  }
3525
3553
 
3526
3554
  // ../ingest/dist/pipeline.js
3527
- import { createHash as createHash5 } from "crypto";
3555
+ import { createHash as createHash5 } from "node:crypto";
3528
3556
  function newStats() {
3529
3557
  return { files: 0, skipped: 0, malformed: 0, newEvents: 0 };
3530
3558
  }
@@ -3588,20 +3616,23 @@ function pruneExcluded(db, excludedPaths) {
3588
3616
  return 0;
3589
3617
  }
3590
3618
  const files = db.prepare("SELECT DISTINCT source_file f FROM events WHERE session_id IN (SELECT id FROM _prune) AND source_file IS NOT NULL").all().map((r) => r.f);
3591
- db.pragma("foreign_keys = OFF");
3592
- db.transaction(() => {
3593
- db.exec("DELETE FROM findings WHERE session_id IN (SELECT id FROM _prune)");
3594
- db.exec("DELETE FROM file_changes WHERE session_id IN (SELECT id FROM _prune)");
3595
- for (const t of ["token_usage", "tool_calls", "turns", "events"])
3596
- db.exec(`DELETE FROM ${t} WHERE session_id IN (SELECT id FROM _prune)`);
3597
- db.exec("DELETE FROM classifications WHERE scope = 'session' AND target_id IN (SELECT id FROM _prune)");
3598
- db.exec("DELETE FROM sessions WHERE id IN (SELECT id FROM _prune)");
3599
- const delState = db.prepare("DELETE FROM ingest_state WHERE file_path = ?");
3600
- for (const f of files)
3601
- delState.run(f);
3602
- db.exec("DELETE FROM projects WHERE id NOT IN (SELECT DISTINCT project_id FROM sessions WHERE project_id IS NOT NULL)");
3603
- })();
3604
- db.pragma("foreign_keys = ON");
3619
+ db.exec("PRAGMA foreign_keys = OFF");
3620
+ try {
3621
+ transaction(db, () => {
3622
+ db.exec("DELETE FROM findings WHERE session_id IN (SELECT id FROM _prune)");
3623
+ db.exec("DELETE FROM file_changes WHERE session_id IN (SELECT id FROM _prune)");
3624
+ for (const t of ["token_usage", "tool_calls", "turns", "events"])
3625
+ db.exec(`DELETE FROM ${t} WHERE session_id IN (SELECT id FROM _prune)`);
3626
+ db.exec("DELETE FROM classifications WHERE scope = 'session' AND target_id IN (SELECT id FROM _prune)");
3627
+ db.exec("DELETE FROM sessions WHERE id IN (SELECT id FROM _prune)");
3628
+ const delState = db.prepare("DELETE FROM ingest_state WHERE file_path = ?");
3629
+ for (const f of files)
3630
+ delState.run(f);
3631
+ db.exec("DELETE FROM projects WHERE id NOT IN (SELECT DISTINCT project_id FROM sessions WHERE project_id IS NOT NULL)");
3632
+ })();
3633
+ } finally {
3634
+ db.exec("PRAGMA foreign_keys = ON");
3635
+ }
3605
3636
  db.exec("DROP TABLE IF EXISTS _prune");
3606
3637
  return total;
3607
3638
  }
@@ -3683,12 +3714,12 @@ function rebuildTurns(db, scope, sessionIds) {
3683
3714
  const insTurn = db.prepare(`INSERT INTO turns (id, session_id, seq, user_event_uuid, prompt_preview, model, started_at, ended_at, duration_ms)
3684
3715
  VALUES (@id, @session_id, @seq, @user_event_uuid, @prompt_preview, @model, @started_at, @ended_at, @duration_ms)`);
3685
3716
  const updEventTurn = db.prepare("UPDATE events SET turn_id = ? WHERE uuid = ?");
3686
- const tx = db.transaction(() => {
3717
+ const tx = transaction(db, () => {
3687
3718
  for (const { id } of sessionIds) {
3688
3719
  const rows = selEvents.all(id);
3689
3720
  const { turns, eventTurn } = buildTurns(id, rows);
3690
3721
  for (const t of turns)
3691
- insTurn.run(t);
3722
+ runNamed(insTurn, t);
3692
3723
  for (const [uuid, turnId] of eventTurn)
3693
3724
  updEventTurn.run(turnId, uuid);
3694
3725
  }
@@ -3707,7 +3738,7 @@ function linkSkillVersions(db, sessionIds) {
3707
3738
  first_seen = MIN(first_seen, excluded.first_seen),
3708
3739
  base_dir = excluded.base_dir`);
3709
3740
  const linkCall = db.prepare("UPDATE tool_calls SET skill_id = ? WHERE id = ?");
3710
- const linkTx = db.transaction(() => {
3741
+ const linkTx = transaction(db, () => {
3711
3742
  for (const { id: sid } of sessionIds) {
3712
3743
  const calls = selSkillCalls.all(sid);
3713
3744
  if (!calls.length)
@@ -3905,7 +3936,7 @@ function prepareStatements(db) {
3905
3936
  function ingestFile(db, stmts, adapter, file, lines, meta, now2, stats) {
3906
3937
  const sessionMeta = {};
3907
3938
  let eventsInFile = 0;
3908
- const tx = db.transaction(() => {
3939
+ const tx = transaction(db, () => {
3909
3940
  stmts.insSessionStub.run(file.sessionId, adapter.agentId, file.sourceId);
3910
3941
  let seq = 0;
3911
3942
  for (const line of lines) {
@@ -3929,10 +3960,10 @@ function ingestFile(db, stmts, adapter, file, lines, meta, now2, stats) {
3929
3960
  }
3930
3961
  }
3931
3962
  if (parsed.tokenUsage)
3932
- stmts.insTokens.run(parsed.tokenUsage);
3963
+ runNamed(stmts.insTokens, parsed.tokenUsage);
3933
3964
  if (parsed.toolCalls)
3934
3965
  for (const tc of parsed.toolCalls)
3935
- stmts.insTool.run(tc);
3966
+ runNamed(stmts.insTool, tc);
3936
3967
  if (parsed.toolResults)
3937
3968
  for (const tr of parsed.toolResults)
3938
3969
  stmts.patchTool.run({
@@ -3986,14 +4017,13 @@ function ingestFile(db, stmts, adapter, file, lines, meta, now2, stats) {
3986
4017
  }
3987
4018
 
3988
4019
  // ../ingest/dist/workflows.js
3989
- import { basename as basename3 } from "path";
4020
+ import { basename as basename3 } from "node:path";
3990
4021
 
3991
4022
  // ../ingest/dist/sidecar.js
3992
- import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
3993
- import { join as join8 } from "path";
4023
+ import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
4024
+ import { join as join8 } from "node:path";
3994
4025
 
3995
4026
  // ../ingest/dist/redact.js
3996
- import { createHash as createHash6, randomBytes } from "crypto";
3997
4027
  function parseExcludes(csv) {
3998
4028
  return (csv ?? "").split(",").map((s) => s.trim()).filter(Boolean).map(encodeProjectPath);
3999
4029
  }
@@ -4002,17 +4032,17 @@ function isExcludedArchivePath(filePath, excluded) {
4002
4032
  }
4003
4033
 
4004
4034
  // ../ingest/dist/fileread.js
4005
- import { createHash as createHash7 } from "crypto";
4006
- import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
4007
- import { StringDecoder } from "string_decoder";
4035
+ import { createHash as createHash6 } from "node:crypto";
4036
+ import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "node:fs";
4037
+ import { StringDecoder } from "node:string_decoder";
4008
4038
  var STREAM_THRESHOLD = 8 * 1024 * 1024;
4009
4039
  var CHUNK2 = 64 * 1024;
4010
4040
  function sha256(buf) {
4011
- return createHash7("sha256").update(buf).digest("hex");
4041
+ return createHash6("sha256").update(buf).digest("hex");
4012
4042
  }
4013
4043
  function sha256File(path3) {
4014
4044
  const fd = openSync2(path3, "r");
4015
- const h = createHash7("sha256");
4045
+ const h = createHash6("sha256");
4016
4046
  const buf = Buffer.allocUnsafe(CHUNK2);
4017
4047
  try {
4018
4048
  let n;
@@ -4205,7 +4235,7 @@ function ingestWorkflowResults(db, sourceArchiveDir, sourceId, excludedDirs, now
4205
4235
  }
4206
4236
 
4207
4237
  // ../ingest/dist/meta.js
4208
- import { basename as basename4 } from "path";
4238
+ import { basename as basename4 } from "node:path";
4209
4239
  var newMetaStats = newSidecarStats;
4210
4240
  function ingestSubagentMeta(db, sourceArchiveDir, sourceId, excludedDirs, now2, stats, full) {
4211
4241
  const upsert = db.prepare(`INSERT INTO session_meta
@@ -4244,7 +4274,7 @@ function ingestSubagentMeta(db, sourceArchiveDir, sourceId, excludedDirs, now2,
4244
4274
  }
4245
4275
 
4246
4276
  // ../ingest/dist/toolResults.js
4247
- import { basename as basename5 } from "path";
4277
+ import { basename as basename5 } from "node:path";
4248
4278
  var newToolResultStats = newSidecarStats;
4249
4279
  function ingestToolResults(db, sourceArchiveDir, excludedDirs, now2, stats, full) {
4250
4280
  const upsert = db.prepare(`INSERT INTO tool_results (session_id, name, path, bytes, text, ingested_at)
@@ -4435,22 +4465,23 @@ function runMetrics(argv = process.argv.slice(2)) {
4435
4465
  }
4436
4466
 
4437
4467
  // ../server/dist/run.js
4438
- import { existsSync as existsSync8 } from "fs";
4439
- import { dirname as dirname7 } from "path";
4440
- import { fileURLToPath as fileURLToPath3 } from "url";
4468
+ import { existsSync as existsSync8 } from "node:fs";
4469
+ import { dirname as dirname7 } from "node:path";
4470
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
4441
4471
 
4442
4472
  // ../server/dist/db.js
4443
- import Database2 from "better-sqlite3";
4473
+ import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
4444
4474
 
4445
4475
  // ../server/dist/sql-util.js
4476
+ var bind = (params) => params;
4446
4477
  function tableExists(db, name) {
4447
4478
  return !!db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?").get(name);
4448
4479
  }
4449
4480
  function queryAll(db, sql, ...params) {
4450
- return db.prepare(sql).all(...params);
4481
+ return db.prepare(sql).all(...bind(params));
4451
4482
  }
4452
4483
  function queryGet(db, sql, ...params) {
4453
- return db.prepare(sql).get(...params);
4484
+ return db.prepare(sql).get(...bind(params));
4454
4485
  }
4455
4486
  function pushDateRange(clauses, params, col, from, to) {
4456
4487
  if (from) {
@@ -4526,8 +4557,8 @@ function costOf(rows) {
4526
4557
 
4527
4558
  // ../server/dist/db.js
4528
4559
  function openReadonly(file) {
4529
- const db = new Database2(file, { readonly: true, fileMustExist: true });
4530
- db.pragma("query_only = ON");
4560
+ const db = new DatabaseSync2(file, { readOnly: true });
4561
+ db.exec("PRAGMA query_only = ON");
4531
4562
  return db;
4532
4563
  }
4533
4564
  function schemaStatus(db) {
@@ -4575,6 +4606,32 @@ function listModels(db) {
4575
4606
  function toFtsQuery(raw) {
4576
4607
  return raw.trim().split(/\s+/).filter(Boolean).map((t) => `"${t.replace(/"/g, '""')}"`).join(" ");
4577
4608
  }
4609
+ var TOKENS_SORT_SQL = `(SELECT COALESCE(SUM(t.input_tokens + t.output_tokens
4610
+ + t.cache_creation_input_tokens + t.cache_read_input_tokens), 0)
4611
+ FROM token_usage t WHERE t.session_id = s.id)`;
4612
+ var SECURITY_SORT_SQL = `(CASE worst_severity
4613
+ WHEN 'critical' THEN 5 WHEN 'high' THEN 4 WHEN 'medium' THEN 3 WHEN 'low' THEN 2 WHEN 'info' THEN 1
4614
+ ELSE 0 END) * 1000000 + finding_count`;
4615
+ var COST_SORT_SQL = `(SELECT COALESCE(SUM((tu.input_tokens * mr.i_rate + tu.output_tokens * mr.o_rate
4616
+ + tu.cache_creation_input_tokens * mr.cw_rate + tu.cache_read_input_tokens * mr.cr_rate) / 1000000.0), 0)
4617
+ FROM token_usage tu LEFT JOIN model_rate mr ON mr.model IS tu.model
4618
+ WHERE tu.session_id = s.id)`;
4619
+ function ratesCte() {
4620
+ const entries = Object.entries(activePricing());
4621
+ return {
4622
+ // `model_rate` resolves the prefix match ONCE per distinct model id, not once per token_usage row.
4623
+ // Inlining the lookup into the cost expression instead re-ran it for every usage row of every
4624
+ // candidate session — ~35× slower on a real corpus, which would have made this "fix" a regression.
4625
+ sql: `WITH rates(prefix, i_rate, o_rate, cw_rate, cr_rate) AS (VALUES ${entries.map(() => "(?,?,?,?,?)").join(",")}),
4626
+ model_rate AS (
4627
+ SELECT m.model, r.i_rate, r.o_rate, r.cw_rate, r.cr_rate
4628
+ FROM (SELECT DISTINCT model FROM token_usage) m
4629
+ LEFT JOIN rates r ON r.prefix = (
4630
+ SELECT p.prefix FROM rates p WHERE substr(m.model, 1, length(p.prefix)) = p.prefix
4631
+ ORDER BY length(p.prefix) DESC LIMIT 1)) `,
4632
+ params: entries.flatMap(([prefix, r]) => [prefix, r.input, r.output, r.cacheWrite, r.cacheRead])
4633
+ };
4634
+ }
4578
4635
  function listSessions(db, f) {
4579
4636
  const where = [];
4580
4637
  const params = [];
@@ -4619,41 +4676,19 @@ function listSessions(db, f) {
4619
4676
  LIMIT 1) AS worst_severity
4620
4677
  FROM sessions s LEFT JOIN projects p ON p.id = s.project_id
4621
4678
  ${whereSql}`;
4622
- const NATIVE_ORDER = {
4679
+ const ORDER = {
4623
4680
  started: "s.started_at",
4624
4681
  title: "COALESCE(s.ai_title, s.slug)",
4625
4682
  turns: "s.turn_count",
4626
- duration: "s.duration_ms"
4683
+ duration: "s.duration_ms",
4684
+ errors: "tool_error_count",
4685
+ tokens: TOKENS_SORT_SQL,
4686
+ cost: COST_SORT_SQL,
4687
+ security: SECURITY_SORT_SQL
4627
4688
  };
4628
4689
  const sortKey = f.sort ?? "started";
4629
- const JS_SORT = /* @__PURE__ */ new Set(["tokens", "cost", "errors", "security"]);
4630
- let sessions;
4631
- if (JS_SORT.has(sortKey)) {
4632
- const all = attachSessionCost(db, queryAll(db, baseSelect, ...params));
4633
- const sign = f.dir === "asc" ? 1 : -1;
4634
- const metric = (r) => {
4635
- switch (sortKey) {
4636
- case "tokens":
4637
- return r.tokens;
4638
- case "cost":
4639
- return r.cost;
4640
- case "errors":
4641
- return r.tool_error_count ?? 0;
4642
- // Security: rank by worst severity (info=0 … critical=4; none = -1), finding count as tiebreak.
4643
- case "security":
4644
- return (severityRank(r.worst_severity ?? "") + 1) * 1e6 + (r.finding_count ?? 0);
4645
- default:
4646
- return 0;
4647
- }
4648
- };
4649
- all.sort((a, b) => {
4650
- const c = (metric(a) - metric(b)) * sign;
4651
- return c !== 0 ? c : String(a.id).localeCompare(String(b.id));
4652
- });
4653
- sessions = all.slice(f.offset, f.offset + f.limit);
4654
- } else {
4655
- sessions = attachSessionCost(db, queryAll(db, `${baseSelect} ORDER BY ${orderBy(NATIVE_ORDER, sortKey, "started", f.dir)}, s.id ASC LIMIT ? OFFSET ?`, ...params, f.limit, f.offset));
4656
- }
4690
+ const rates = sortKey === "cost" ? ratesCte() : null;
4691
+ const sessions = attachSessionCost(db, queryAll(db, `${rates?.sql ?? ""}${baseSelect} ORDER BY ${orderBy(ORDER, sortKey, "started", f.dir)}, s.id ASC LIMIT ? OFFSET ?`, ...rates?.params ?? [], ...params, f.limit, f.offset));
4657
4692
  return { total, sessions };
4658
4693
  }
4659
4694
  function attachSessionCost(db, rows) {
@@ -5216,7 +5251,7 @@ function securitySummary(db) {
5216
5251
  }
5217
5252
 
5218
5253
  // ../server/dist/app.js
5219
- import { existsSync as existsSync7 } from "fs";
5254
+ import { existsSync as existsSync7 } from "node:fs";
5220
5255
  import Fastify from "fastify";
5221
5256
  import fastifyStatic from "@fastify/static";
5222
5257
 
@@ -5274,11 +5309,18 @@ var usageForCost = (u) => ({
5274
5309
  cache_creation_input_tokens: u.cw ?? 0,
5275
5310
  cache_read_input_tokens: u.cr ?? 0
5276
5311
  });
5277
- function pct(sorted, p) {
5278
- if (sorted.length === 0)
5279
- return 0;
5280
- const rank = Math.ceil(p / 100 * sorted.length) - 1;
5281
- return sorted[Math.min(Math.max(rank, 0), sorted.length - 1)];
5312
+ function percentiles(db, values, params) {
5313
+ const at = (p) => `(SELECT COALESCE((SELECT v FROM vals ORDER BY v
5314
+ LIMIT 1 OFFSET MAX(0, CAST(ceil(${p} / 100.0 * (SELECT c FROM n)) AS INTEGER) - 1)), 0))`;
5315
+ const row = queryGet(
5316
+ db,
5317
+ // MATERIALIZED matters: the three selects below each reference `vals`, and without the hint
5318
+ // SQLite inlines the CTE and re-runs the underlying scan once per percentile.
5319
+ `WITH vals AS MATERIALIZED (${values}), n AS (SELECT COUNT(*) c FROM vals)
5320
+ SELECT ${at(50)} AS p50, ${at(95)} AS p95, (SELECT c FROM n) AS count`,
5321
+ ...params
5322
+ );
5323
+ return row ?? { p50: 0, p95: 0, count: 0 };
5282
5324
  }
5283
5325
  function workflowWhere(f) {
5284
5326
  const where = [];
@@ -5345,10 +5387,10 @@ function dashboardOverview(db, f) {
5345
5387
  unpriced2.push(u.model);
5346
5388
  }
5347
5389
  const totalTokens = tokens.input + tokens.output + tokens.cache_creation + tokens.cache_read;
5348
- const durs = queryAll(db, `SELECT tn.duration_ms d FROM turns tn JOIN sessions s ON s.id = tn.session_id
5349
- ${w.sql ? w.sql + " AND" : "WHERE"} tn.duration_ms IS NOT NULL ORDER BY tn.duration_ms`, ...w.params).map((r) => r.d);
5350
- const sessDurs = queryAll(db, `SELECT s.duration_ms d FROM sessions s
5351
- ${w.sql ? w.sql + " AND" : "WHERE"} s.is_sidechain = 0 AND s.duration_ms IS NOT NULL ORDER BY s.duration_ms`, ...w.params).map((r) => r.d);
5390
+ const turnDur = percentiles(db, `SELECT tn.duration_ms v FROM turns tn JOIN sessions s ON s.id = tn.session_id
5391
+ ${w.sql ? w.sql + " AND" : "WHERE"} tn.duration_ms IS NOT NULL`, w.params);
5392
+ const sessDur = percentiles(db, `SELECT s.duration_ms v FROM sessions s
5393
+ ${w.sql ? w.sql + " AND" : "WHERE"} s.is_sidechain = 0 AND s.duration_ms IS NOT NULL`, w.params);
5352
5394
  return {
5353
5395
  range: { from: f.from ?? null, to: f.to ?? null, source: f.source ?? null },
5354
5396
  sessions: counts.sessions ?? 0,
@@ -5363,8 +5405,8 @@ function dashboardOverview(db, f) {
5363
5405
  cache_read_ratio: totalTokens ? tokens.cache_read / totalTokens : 0,
5364
5406
  cost: Number(cost.toFixed(4)),
5365
5407
  unpriced_models: [...new Set(unpriced2)].sort(),
5366
- turn_duration_ms: { p50: pct(durs, 50), p95: pct(durs, 95), count: durs.length },
5367
- session_duration_ms: { p50: pct(sessDurs, 50), p95: pct(sessDurs, 95), count: sessDurs.length },
5408
+ turn_duration_ms: turnDur,
5409
+ session_duration_ms: sessDur,
5368
5410
  workflows: workflowAgg(db, f)
5369
5411
  };
5370
5412
  }
@@ -5529,8 +5571,8 @@ function dashboardBreakdowns(db, f) {
5529
5571
  }
5530
5572
 
5531
5573
  // ../server/dist/refresh.js
5532
- import { existsSync as existsSync6, mkdirSync as mkdirSync5 } from "fs";
5533
- import { join as join10 } from "path";
5574
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5 } from "node:fs";
5575
+ import { join as join10 } from "node:path";
5534
5576
  var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "[::1]"]);
5535
5577
  function originAllowed(origin) {
5536
5578
  if (!origin)
@@ -5567,7 +5609,7 @@ function runRefresh() {
5567
5609
  }
5568
5610
 
5569
5611
  // ../server/dist/triage.js
5570
- import Database3 from "better-sqlite3";
5612
+ import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
5571
5613
  var TRIAGE_SCHEMA_SQL = (
5572
5614
  /* sql */
5573
5615
  `
@@ -5595,8 +5637,8 @@ CREATE TABLE IF NOT EXISTS muted_rules (
5595
5637
  `
5596
5638
  );
5597
5639
  function openTriage(file) {
5598
- const db = new Database3(file);
5599
- db.pragma("journal_mode = WAL");
5640
+ const db = new DatabaseSync3(file);
5641
+ db.exec("PRAGMA journal_mode = WAL");
5600
5642
  db.exec(TRIAGE_SCHEMA_SQL);
5601
5643
  return db;
5602
5644
  }
@@ -5605,7 +5647,7 @@ function dismiss(db, ids, note) {
5605
5647
  const stmt = db.prepare(`INSERT INTO dismissed_findings (finding_id, note, dismissed_at) VALUES (?, ?, ?)
5606
5648
  ON CONFLICT(finding_id) DO UPDATE SET note = excluded.note, dismissed_at = excluded.dismissed_at`);
5607
5649
  const ts = now();
5608
- const tx = db.transaction((list) => {
5650
+ const tx = transaction(db, (list) => {
5609
5651
  for (const id of list)
5610
5652
  stmt.run(id, note ?? null, ts);
5611
5653
  });
@@ -5615,9 +5657,9 @@ function dismiss(db, ids, note) {
5615
5657
  function reopen(db, ids) {
5616
5658
  const stmt = db.prepare("DELETE FROM dismissed_findings WHERE finding_id = ?");
5617
5659
  let n = 0;
5618
- const tx = db.transaction((list) => {
5660
+ const tx = transaction(db, (list) => {
5619
5661
  for (const id of list)
5620
- n += stmt.run(id).changes;
5662
+ n += Number(stmt.run(id).changes);
5621
5663
  });
5622
5664
  tx(ids);
5623
5665
  return n;
@@ -5627,14 +5669,14 @@ function muteRule(db, ruleId, scope = "global", scopeId = "", note) {
5627
5669
  ON CONFLICT(rule_id, scope, scope_id) DO UPDATE SET note = excluded.note, muted_at = excluded.muted_at`).run(ruleId, scope, scope === "global" ? "" : scopeId, note ?? null, now());
5628
5670
  }
5629
5671
  function unmute(db, ruleId, scope = "global", scopeId = "") {
5630
- return db.prepare("DELETE FROM muted_rules WHERE rule_id = ? AND scope = ? AND scope_id = ?").run(ruleId, scope, scope === "global" ? "" : scopeId).changes;
5672
+ return Number(db.prepare("DELETE FROM muted_rules WHERE rule_id = ? AND scope = ? AND scope_id = ?").run(ruleId, scope, scope === "global" ? "" : scopeId).changes);
5631
5673
  }
5632
5674
  function listMutes(db) {
5633
5675
  return db.prepare("SELECT rule_id, scope, scope_id, note, muted_at FROM muted_rules ORDER BY muted_at DESC").all();
5634
5676
  }
5635
5677
 
5636
5678
  // ../server/dist/about.js
5637
- import { statSync as statSync4 } from "fs";
5679
+ import { statSync as statSync4 } from "node:fs";
5638
5680
  function storage(db, dbPath) {
5639
5681
  const row = tableExists(db, "ingest_state") ? queryGet(db, "SELECT COUNT(*) files, SUM(size) bytes, MAX(ingested_at) last FROM ingest_state") : void 0;
5640
5682
  let db_bytes = null;
@@ -6018,8 +6060,8 @@ async function startServer(overrides = {}) {
6018
6060
  }
6019
6061
 
6020
6062
  // src/watch.ts
6021
- import { existsSync as existsSync9 } from "fs";
6022
- import { join as join11 } from "path";
6063
+ import { existsSync as existsSync9 } from "node:fs";
6064
+ import { join as join11 } from "node:path";
6023
6065
  import chokidar from "chokidar";
6024
6066
  async function runWatch(opts = {}) {
6025
6067
  const sources = loadSources();