claude-memory-layer 1.0.7 → 1.0.9

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.
Files changed (53) hide show
  1. package/.claude/settings.local.json +10 -1
  2. package/.claude-memory/test.sqlite +0 -0
  3. package/.history/package_20260201192048.json +47 -0
  4. package/.history/package_20260202114053.json +49 -0
  5. package/HANDOFF.md +92 -0
  6. package/dist/cli/index.js +1711 -102
  7. package/dist/cli/index.js.map +4 -4
  8. package/dist/core/index.js +1257 -84
  9. package/dist/core/index.js.map +4 -4
  10. package/dist/hooks/post-tool-use.js +5589 -0
  11. package/dist/hooks/post-tool-use.js.map +7 -0
  12. package/dist/hooks/session-end.js +1382 -85
  13. package/dist/hooks/session-end.js.map +4 -4
  14. package/dist/hooks/session-start.js +1377 -84
  15. package/dist/hooks/session-start.js.map +4 -4
  16. package/dist/hooks/stop.js +1383 -86
  17. package/dist/hooks/stop.js.map +4 -4
  18. package/dist/hooks/user-prompt-submit.js +1412 -84
  19. package/dist/hooks/user-prompt-submit.js.map +4 -4
  20. package/dist/server/api/index.js +1576 -136
  21. package/dist/server/api/index.js.map +4 -4
  22. package/dist/server/index.js +1585 -143
  23. package/dist/server/index.js.map +4 -4
  24. package/dist/services/memory-service.js +1392 -84
  25. package/dist/services/memory-service.js.map +4 -4
  26. package/dist/ui/app.js +304 -0
  27. package/dist/ui/index.html +202 -715
  28. package/dist/ui/style.css +595 -0
  29. package/package.json +4 -1
  30. package/scripts/build.ts +5 -2
  31. package/src/cli/index.ts +226 -0
  32. package/src/core/db-wrapper.ts +8 -1
  33. package/src/core/event-store.ts +70 -3
  34. package/src/core/graduation-worker.ts +171 -0
  35. package/src/core/graduation.ts +15 -2
  36. package/src/core/index.ts +4 -0
  37. package/src/core/retriever.ts +21 -0
  38. package/src/core/sqlite-event-store.ts +849 -0
  39. package/src/core/sqlite-wrapper.ts +108 -0
  40. package/src/core/sync-worker.ts +228 -0
  41. package/src/core/vector-worker.ts +44 -14
  42. package/src/hooks/user-prompt-submit.ts +53 -4
  43. package/src/server/api/citations.ts +7 -3
  44. package/src/server/api/events.ts +7 -3
  45. package/src/server/api/search.ts +7 -3
  46. package/src/server/api/sessions.ts +7 -3
  47. package/src/server/api/stats.ts +159 -12
  48. package/src/server/index.ts +18 -9
  49. package/src/services/memory-service.ts +263 -46
  50. package/src/ui/app.js +304 -0
  51. package/src/ui/index.html +202 -715
  52. package/src/ui/style.css +595 -0
  53. package/test_access.js +49 -0
@@ -639,7 +639,10 @@ function toDate(value) {
639
639
  return new Date(value);
640
640
  return new Date(String(value));
641
641
  }
642
- function createDatabase(path) {
642
+ function createDatabase(path, options) {
643
+ if (options?.readOnly) {
644
+ return new duckdb.Database(path, { access_mode: "READ_ONLY" });
645
+ }
643
646
  return new duckdb.Database(path);
644
647
  }
645
648
  function dbRun(db, sql, params = []) {
@@ -693,18 +696,24 @@ function dbClose(db) {
693
696
 
694
697
  // src/core/event-store.ts
695
698
  var EventStore = class {
696
- constructor(dbPath) {
699
+ constructor(dbPath, options) {
697
700
  this.dbPath = dbPath;
698
- this.db = createDatabase(dbPath);
701
+ this.readOnly = options?.readOnly ?? false;
702
+ this.db = createDatabase(dbPath, { readOnly: this.readOnly });
699
703
  }
700
704
  db;
701
705
  initialized = false;
706
+ readOnly;
702
707
  /**
703
708
  * Initialize database schema
704
709
  */
705
710
  async initialize() {
706
711
  if (this.initialized)
707
712
  return;
713
+ if (this.readOnly) {
714
+ this.initialized = true;
715
+ return;
716
+ }
708
717
  await dbRun(this.db, `
709
718
  CREATE TABLE IF NOT EXISTS events (
710
719
  id VARCHAR PRIMARY KEY,
@@ -1076,20 +1085,761 @@ var EventStore = class {
1076
1085
  const row = rows[0];
1077
1086
  return {
1078
1087
  id: row.id,
1079
- startedAt: toDate(row.started_at),
1080
- endedAt: row.ended_at ? toDate(row.ended_at) : void 0,
1088
+ startedAt: toDate(row.started_at),
1089
+ endedAt: row.ended_at ? toDate(row.ended_at) : void 0,
1090
+ projectPath: row.project_path,
1091
+ summary: row.summary,
1092
+ tags: row.tags ? JSON.parse(row.tags) : void 0
1093
+ };
1094
+ }
1095
+ /**
1096
+ * Add to embedding outbox (Single-Writer Pattern)
1097
+ */
1098
+ async enqueueForEmbedding(eventId, content) {
1099
+ await this.initialize();
1100
+ const id = randomUUID();
1101
+ await dbRun(
1102
+ this.db,
1103
+ `INSERT INTO embedding_outbox (id, event_id, content, status, retry_count)
1104
+ VALUES (?, ?, ?, 'pending', 0)`,
1105
+ [id, eventId, content]
1106
+ );
1107
+ return id;
1108
+ }
1109
+ /**
1110
+ * Get pending outbox items
1111
+ */
1112
+ async getPendingOutboxItems(limit = 32) {
1113
+ await this.initialize();
1114
+ const pending = await dbAll(
1115
+ this.db,
1116
+ `SELECT * FROM embedding_outbox
1117
+ WHERE status = 'pending'
1118
+ ORDER BY created_at
1119
+ LIMIT ?`,
1120
+ [limit]
1121
+ );
1122
+ if (pending.length === 0)
1123
+ return [];
1124
+ const ids = pending.map((r) => r.id);
1125
+ const placeholders = ids.map(() => "?").join(",");
1126
+ await dbRun(
1127
+ this.db,
1128
+ `UPDATE embedding_outbox SET status = 'processing' WHERE id IN (${placeholders})`,
1129
+ ids
1130
+ );
1131
+ return pending.map((row) => ({
1132
+ id: row.id,
1133
+ eventId: row.event_id,
1134
+ content: row.content,
1135
+ status: "processing",
1136
+ retryCount: row.retry_count,
1137
+ createdAt: toDate(row.created_at),
1138
+ errorMessage: row.error_message
1139
+ }));
1140
+ }
1141
+ /**
1142
+ * Mark outbox items as done
1143
+ */
1144
+ async completeOutboxItems(ids) {
1145
+ if (ids.length === 0)
1146
+ return;
1147
+ const placeholders = ids.map(() => "?").join(",");
1148
+ await dbRun(
1149
+ this.db,
1150
+ `DELETE FROM embedding_outbox WHERE id IN (${placeholders})`,
1151
+ ids
1152
+ );
1153
+ }
1154
+ /**
1155
+ * Mark outbox items as failed
1156
+ */
1157
+ async failOutboxItems(ids, error) {
1158
+ if (ids.length === 0)
1159
+ return;
1160
+ const placeholders = ids.map(() => "?").join(",");
1161
+ await dbRun(
1162
+ this.db,
1163
+ `UPDATE embedding_outbox
1164
+ SET status = CASE WHEN retry_count >= 3 THEN 'failed' ELSE 'pending' END,
1165
+ retry_count = retry_count + 1,
1166
+ error_message = ?
1167
+ WHERE id IN (${placeholders})`,
1168
+ [error, ...ids]
1169
+ );
1170
+ }
1171
+ /**
1172
+ * Update memory level
1173
+ */
1174
+ async updateMemoryLevel(eventId, level) {
1175
+ await this.initialize();
1176
+ await dbRun(
1177
+ this.db,
1178
+ `UPDATE memory_levels SET level = ?, promoted_at = CURRENT_TIMESTAMP WHERE event_id = ?`,
1179
+ [level, eventId]
1180
+ );
1181
+ }
1182
+ /**
1183
+ * Get memory level statistics
1184
+ */
1185
+ async getLevelStats() {
1186
+ await this.initialize();
1187
+ const rows = await dbAll(
1188
+ this.db,
1189
+ `SELECT level, COUNT(*) as count FROM memory_levels GROUP BY level`
1190
+ );
1191
+ return rows;
1192
+ }
1193
+ /**
1194
+ * Get events by memory level
1195
+ */
1196
+ async getEventsByLevel(level, options) {
1197
+ await this.initialize();
1198
+ const limit = options?.limit || 50;
1199
+ const offset = options?.offset || 0;
1200
+ const rows = await dbAll(
1201
+ this.db,
1202
+ `SELECT e.* FROM events e
1203
+ INNER JOIN memory_levels ml ON e.id = ml.event_id
1204
+ WHERE ml.level = ?
1205
+ ORDER BY e.timestamp DESC
1206
+ LIMIT ? OFFSET ?`,
1207
+ [level, limit, offset]
1208
+ );
1209
+ return rows.map((row) => this.rowToEvent(row));
1210
+ }
1211
+ /**
1212
+ * Get memory level for a specific event
1213
+ */
1214
+ async getEventLevel(eventId) {
1215
+ await this.initialize();
1216
+ const rows = await dbAll(
1217
+ this.db,
1218
+ `SELECT level FROM memory_levels WHERE event_id = ?`,
1219
+ [eventId]
1220
+ );
1221
+ return rows.length > 0 ? rows[0].level : null;
1222
+ }
1223
+ // ============================================================
1224
+ // Endless Mode Helper Methods
1225
+ // ============================================================
1226
+ /**
1227
+ * Get database instance for Endless Mode stores
1228
+ */
1229
+ getDatabase() {
1230
+ return this.db;
1231
+ }
1232
+ /**
1233
+ * Get config value for endless mode
1234
+ */
1235
+ async getEndlessConfig(key) {
1236
+ await this.initialize();
1237
+ const rows = await dbAll(
1238
+ this.db,
1239
+ `SELECT value FROM endless_config WHERE key = ?`,
1240
+ [key]
1241
+ );
1242
+ if (rows.length === 0)
1243
+ return null;
1244
+ return JSON.parse(rows[0].value);
1245
+ }
1246
+ /**
1247
+ * Set config value for endless mode
1248
+ */
1249
+ async setEndlessConfig(key, value) {
1250
+ await this.initialize();
1251
+ await dbRun(
1252
+ this.db,
1253
+ `INSERT OR REPLACE INTO endless_config (key, value, updated_at)
1254
+ VALUES (?, ?, CURRENT_TIMESTAMP)`,
1255
+ [key, JSON.stringify(value)]
1256
+ );
1257
+ }
1258
+ /**
1259
+ * Get all sessions
1260
+ */
1261
+ async getAllSessions() {
1262
+ await this.initialize();
1263
+ const rows = await dbAll(
1264
+ this.db,
1265
+ `SELECT * FROM sessions ORDER BY started_at DESC`
1266
+ );
1267
+ return rows.map((row) => ({
1268
+ id: row.id,
1269
+ startedAt: toDate(row.started_at),
1270
+ endedAt: row.ended_at ? toDate(row.ended_at) : void 0,
1271
+ projectPath: row.project_path,
1272
+ summary: row.summary,
1273
+ tags: row.tags ? JSON.parse(row.tags) : void 0
1274
+ }));
1275
+ }
1276
+ /**
1277
+ * Increment access count for events (stub for compatibility)
1278
+ */
1279
+ async incrementAccessCount(eventIds) {
1280
+ return Promise.resolve();
1281
+ }
1282
+ /**
1283
+ * Get most accessed memories (stub for compatibility)
1284
+ */
1285
+ async getMostAccessed(limit = 10) {
1286
+ return [];
1287
+ }
1288
+ /**
1289
+ * Close database connection
1290
+ */
1291
+ async close() {
1292
+ await dbClose(this.db);
1293
+ }
1294
+ /**
1295
+ * Convert database row to MemoryEvent
1296
+ */
1297
+ rowToEvent(row) {
1298
+ return {
1299
+ id: row.id,
1300
+ eventType: row.event_type,
1301
+ sessionId: row.session_id,
1302
+ timestamp: toDate(row.timestamp),
1303
+ content: row.content,
1304
+ canonicalKey: row.canonical_key,
1305
+ dedupeKey: row.dedupe_key,
1306
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0
1307
+ };
1308
+ }
1309
+ };
1310
+
1311
+ // src/core/sqlite-wrapper.ts
1312
+ import Database from "better-sqlite3";
1313
+ function createSQLiteDatabase(path, options) {
1314
+ const db = new Database(path, {
1315
+ readonly: options?.readonly ?? false
1316
+ });
1317
+ if (!options?.readonly && (options?.walMode ?? true)) {
1318
+ db.pragma("journal_mode = WAL");
1319
+ db.pragma("synchronous = NORMAL");
1320
+ db.pragma("busy_timeout = 5000");
1321
+ }
1322
+ return db;
1323
+ }
1324
+ function sqliteRun(db, sql, params = []) {
1325
+ const stmt = db.prepare(sql);
1326
+ return stmt.run(...params);
1327
+ }
1328
+ function sqliteAll(db, sql, params = []) {
1329
+ const stmt = db.prepare(sql);
1330
+ return stmt.all(...params);
1331
+ }
1332
+ function sqliteGet(db, sql, params = []) {
1333
+ const stmt = db.prepare(sql);
1334
+ return stmt.get(...params);
1335
+ }
1336
+ function sqliteExec(db, sql) {
1337
+ db.exec(sql);
1338
+ }
1339
+ function sqliteClose(db) {
1340
+ db.close();
1341
+ }
1342
+ function sqliteTransaction(db, fn) {
1343
+ return db.transaction(fn)();
1344
+ }
1345
+ function toDateFromSQLite(value) {
1346
+ if (value instanceof Date)
1347
+ return value;
1348
+ if (typeof value === "string")
1349
+ return new Date(value);
1350
+ if (typeof value === "number")
1351
+ return new Date(value);
1352
+ return new Date(String(value));
1353
+ }
1354
+ function toSQLiteTimestamp(date) {
1355
+ return date.toISOString();
1356
+ }
1357
+
1358
+ // src/core/sqlite-event-store.ts
1359
+ import { randomUUID as randomUUID2 } from "crypto";
1360
+ var SQLiteEventStore = class {
1361
+ constructor(dbPath, options) {
1362
+ this.dbPath = dbPath;
1363
+ this.readOnly = options?.readonly ?? false;
1364
+ this.db = createSQLiteDatabase(dbPath, {
1365
+ readonly: this.readOnly,
1366
+ walMode: !this.readOnly
1367
+ });
1368
+ }
1369
+ db;
1370
+ initialized = false;
1371
+ readOnly;
1372
+ /**
1373
+ * Initialize database schema
1374
+ */
1375
+ async initialize() {
1376
+ if (this.initialized)
1377
+ return;
1378
+ if (this.readOnly) {
1379
+ this.initialized = true;
1380
+ return;
1381
+ }
1382
+ sqliteExec(this.db, `
1383
+ -- L0 EventStore: Single Source of Truth (immutable, append-only)
1384
+ CREATE TABLE IF NOT EXISTS events (
1385
+ id TEXT PRIMARY KEY,
1386
+ event_type TEXT NOT NULL,
1387
+ session_id TEXT NOT NULL,
1388
+ timestamp TEXT NOT NULL,
1389
+ content TEXT NOT NULL,
1390
+ canonical_key TEXT NOT NULL,
1391
+ dedupe_key TEXT UNIQUE,
1392
+ metadata TEXT,
1393
+ access_count INTEGER DEFAULT 0,
1394
+ last_accessed_at TEXT
1395
+ );
1396
+
1397
+ -- Dedup table for idempotency
1398
+ CREATE TABLE IF NOT EXISTS event_dedup (
1399
+ dedupe_key TEXT PRIMARY KEY,
1400
+ event_id TEXT NOT NULL,
1401
+ created_at TEXT DEFAULT (datetime('now'))
1402
+ );
1403
+
1404
+ -- Session metadata
1405
+ CREATE TABLE IF NOT EXISTS sessions (
1406
+ id TEXT PRIMARY KEY,
1407
+ started_at TEXT NOT NULL,
1408
+ ended_at TEXT,
1409
+ project_path TEXT,
1410
+ summary TEXT,
1411
+ tags TEXT
1412
+ );
1413
+
1414
+ -- Insights (derived data, rebuildable)
1415
+ CREATE TABLE IF NOT EXISTS insights (
1416
+ id TEXT PRIMARY KEY,
1417
+ insight_type TEXT NOT NULL,
1418
+ content TEXT NOT NULL,
1419
+ canonical_key TEXT NOT NULL,
1420
+ confidence REAL,
1421
+ source_events TEXT,
1422
+ created_at TEXT,
1423
+ last_updated TEXT
1424
+ );
1425
+
1426
+ -- Embedding Outbox (Single-Writer Pattern)
1427
+ CREATE TABLE IF NOT EXISTS embedding_outbox (
1428
+ id TEXT PRIMARY KEY,
1429
+ event_id TEXT NOT NULL,
1430
+ content TEXT NOT NULL,
1431
+ status TEXT DEFAULT 'pending',
1432
+ retry_count INTEGER DEFAULT 0,
1433
+ created_at TEXT DEFAULT (datetime('now')),
1434
+ processed_at TEXT,
1435
+ error_message TEXT
1436
+ );
1437
+
1438
+ -- Projection offset tracking
1439
+ CREATE TABLE IF NOT EXISTS projection_offsets (
1440
+ projection_name TEXT PRIMARY KEY,
1441
+ last_event_id TEXT,
1442
+ last_timestamp TEXT,
1443
+ updated_at TEXT DEFAULT (datetime('now'))
1444
+ );
1445
+
1446
+ -- Memory level tracking
1447
+ CREATE TABLE IF NOT EXISTS memory_levels (
1448
+ event_id TEXT PRIMARY KEY,
1449
+ level TEXT NOT NULL DEFAULT 'L0',
1450
+ promoted_at TEXT DEFAULT (datetime('now'))
1451
+ );
1452
+
1453
+ -- Entries (immutable memory units)
1454
+ CREATE TABLE IF NOT EXISTS entries (
1455
+ entry_id TEXT PRIMARY KEY,
1456
+ created_ts TEXT NOT NULL,
1457
+ entry_type TEXT NOT NULL,
1458
+ title TEXT NOT NULL,
1459
+ content_json TEXT NOT NULL,
1460
+ stage TEXT NOT NULL DEFAULT 'raw',
1461
+ status TEXT DEFAULT 'active',
1462
+ superseded_by TEXT,
1463
+ build_id TEXT,
1464
+ evidence_json TEXT,
1465
+ canonical_key TEXT,
1466
+ created_at TEXT DEFAULT (datetime('now'))
1467
+ );
1468
+
1469
+ -- Entities (task/condition/artifact)
1470
+ CREATE TABLE IF NOT EXISTS entities (
1471
+ entity_id TEXT PRIMARY KEY,
1472
+ entity_type TEXT NOT NULL,
1473
+ canonical_key TEXT NOT NULL,
1474
+ title TEXT NOT NULL,
1475
+ stage TEXT NOT NULL DEFAULT 'raw',
1476
+ status TEXT NOT NULL DEFAULT 'active',
1477
+ current_json TEXT NOT NULL,
1478
+ title_norm TEXT,
1479
+ search_text TEXT,
1480
+ created_at TEXT DEFAULT (datetime('now')),
1481
+ updated_at TEXT DEFAULT (datetime('now'))
1482
+ );
1483
+
1484
+ -- Entity aliases for canonical key lookup
1485
+ CREATE TABLE IF NOT EXISTS entity_aliases (
1486
+ entity_type TEXT NOT NULL,
1487
+ canonical_key TEXT NOT NULL,
1488
+ entity_id TEXT NOT NULL,
1489
+ is_primary INTEGER DEFAULT 0,
1490
+ created_at TEXT DEFAULT (datetime('now')),
1491
+ PRIMARY KEY(entity_type, canonical_key)
1492
+ );
1493
+
1494
+ -- Edges (relationships between entries/entities)
1495
+ CREATE TABLE IF NOT EXISTS edges (
1496
+ edge_id TEXT PRIMARY KEY,
1497
+ src_type TEXT NOT NULL,
1498
+ src_id TEXT NOT NULL,
1499
+ rel_type TEXT NOT NULL,
1500
+ dst_type TEXT NOT NULL,
1501
+ dst_id TEXT NOT NULL,
1502
+ meta_json TEXT,
1503
+ created_at TEXT DEFAULT (datetime('now'))
1504
+ );
1505
+
1506
+ -- Vector Outbox V2 Table
1507
+ CREATE TABLE IF NOT EXISTS vector_outbox (
1508
+ job_id TEXT PRIMARY KEY,
1509
+ item_kind TEXT NOT NULL,
1510
+ item_id TEXT NOT NULL,
1511
+ embedding_version TEXT NOT NULL,
1512
+ status TEXT NOT NULL DEFAULT 'pending',
1513
+ retry_count INTEGER DEFAULT 0,
1514
+ error TEXT,
1515
+ created_at TEXT DEFAULT (datetime('now')),
1516
+ updated_at TEXT DEFAULT (datetime('now')),
1517
+ UNIQUE(item_kind, item_id, embedding_version)
1518
+ );
1519
+
1520
+ -- Build Runs
1521
+ CREATE TABLE IF NOT EXISTS build_runs (
1522
+ build_id TEXT PRIMARY KEY,
1523
+ started_at TEXT NOT NULL,
1524
+ finished_at TEXT,
1525
+ extractor_model TEXT NOT NULL,
1526
+ extractor_prompt_hash TEXT NOT NULL,
1527
+ embedder_model TEXT NOT NULL,
1528
+ embedding_version TEXT NOT NULL,
1529
+ idris_version TEXT NOT NULL,
1530
+ schema_version TEXT NOT NULL,
1531
+ status TEXT NOT NULL DEFAULT 'running',
1532
+ error TEXT
1533
+ );
1534
+
1535
+ -- Pipeline Metrics
1536
+ CREATE TABLE IF NOT EXISTS pipeline_metrics (
1537
+ id TEXT PRIMARY KEY,
1538
+ ts TEXT NOT NULL,
1539
+ stage TEXT NOT NULL,
1540
+ latency_ms REAL NOT NULL,
1541
+ success INTEGER NOT NULL,
1542
+ error TEXT,
1543
+ session_id TEXT
1544
+ );
1545
+
1546
+ -- Working Set table (active memory window)
1547
+ CREATE TABLE IF NOT EXISTS working_set (
1548
+ id TEXT PRIMARY KEY,
1549
+ event_id TEXT NOT NULL,
1550
+ added_at TEXT DEFAULT (datetime('now')),
1551
+ relevance_score REAL DEFAULT 1.0,
1552
+ topics TEXT,
1553
+ expires_at TEXT
1554
+ );
1555
+
1556
+ -- Consolidated Memories table (long-term integrated memories)
1557
+ CREATE TABLE IF NOT EXISTS consolidated_memories (
1558
+ memory_id TEXT PRIMARY KEY,
1559
+ summary TEXT NOT NULL,
1560
+ topics TEXT,
1561
+ source_events TEXT,
1562
+ confidence REAL DEFAULT 0.5,
1563
+ created_at TEXT DEFAULT (datetime('now')),
1564
+ accessed_at TEXT,
1565
+ access_count INTEGER DEFAULT 0
1566
+ );
1567
+
1568
+ -- Continuity Log table (tracks context transitions)
1569
+ CREATE TABLE IF NOT EXISTS continuity_log (
1570
+ log_id TEXT PRIMARY KEY,
1571
+ from_context_id TEXT,
1572
+ to_context_id TEXT,
1573
+ continuity_score REAL,
1574
+ transition_type TEXT,
1575
+ created_at TEXT DEFAULT (datetime('now'))
1576
+ );
1577
+
1578
+ -- Endless Mode Config table
1579
+ CREATE TABLE IF NOT EXISTS endless_config (
1580
+ key TEXT PRIMARY KEY,
1581
+ value TEXT,
1582
+ updated_at TEXT DEFAULT (datetime('now'))
1583
+ );
1584
+
1585
+ -- Sync position tracking (for SQLite -> DuckDB sync)
1586
+ CREATE TABLE IF NOT EXISTS sync_positions (
1587
+ target_name TEXT PRIMARY KEY,
1588
+ last_event_id TEXT,
1589
+ last_timestamp TEXT,
1590
+ updated_at TEXT DEFAULT (datetime('now'))
1591
+ );
1592
+
1593
+ -- Create indexes
1594
+ CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
1595
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
1596
+ CREATE INDEX IF NOT EXISTS idx_entries_type ON entries(entry_type);
1597
+ CREATE INDEX IF NOT EXISTS idx_entries_stage ON entries(stage);
1598
+ CREATE INDEX IF NOT EXISTS idx_entries_canonical ON entries(canonical_key);
1599
+ CREATE INDEX IF NOT EXISTS idx_entities_type_key ON entities(entity_type, canonical_key);
1600
+ CREATE INDEX IF NOT EXISTS idx_entities_status ON entities(status);
1601
+ CREATE INDEX IF NOT EXISTS idx_edges_src ON edges(src_id, rel_type);
1602
+ CREATE INDEX IF NOT EXISTS idx_edges_dst ON edges(dst_id, rel_type);
1603
+ CREATE INDEX IF NOT EXISTS idx_edges_rel ON edges(rel_type);
1604
+ CREATE INDEX IF NOT EXISTS idx_outbox_status ON vector_outbox(status);
1605
+ CREATE INDEX IF NOT EXISTS idx_working_set_expires ON working_set(expires_at);
1606
+ CREATE INDEX IF NOT EXISTS idx_working_set_relevance ON working_set(relevance_score);
1607
+ CREATE INDEX IF NOT EXISTS idx_consolidated_confidence ON consolidated_memories(confidence);
1608
+ CREATE INDEX IF NOT EXISTS idx_continuity_created ON continuity_log(created_at);
1609
+ CREATE INDEX IF NOT EXISTS idx_embedding_outbox_status ON embedding_outbox(status);
1610
+ `);
1611
+ const tableInfo = sqliteAll(this.db, "PRAGMA table_info(events)", []);
1612
+ const columnNames = tableInfo.map((col) => col.name);
1613
+ if (!columnNames.includes("access_count")) {
1614
+ try {
1615
+ sqliteExec(this.db, `
1616
+ ALTER TABLE events ADD COLUMN access_count INTEGER DEFAULT 0;
1617
+ `);
1618
+ } catch (err) {
1619
+ console.error("Error adding access_count column:", err);
1620
+ }
1621
+ }
1622
+ if (!columnNames.includes("last_accessed_at")) {
1623
+ try {
1624
+ sqliteExec(this.db, `
1625
+ ALTER TABLE events ADD COLUMN last_accessed_at TEXT;
1626
+ `);
1627
+ } catch (err) {
1628
+ console.error("Error adding last_accessed_at column:", err);
1629
+ }
1630
+ }
1631
+ try {
1632
+ sqliteExec(this.db, `
1633
+ CREATE INDEX IF NOT EXISTS idx_events_access_count ON events(access_count DESC);
1634
+ `);
1635
+ } catch (err) {
1636
+ }
1637
+ try {
1638
+ sqliteExec(this.db, `
1639
+ CREATE INDEX IF NOT EXISTS idx_events_last_accessed ON events(last_accessed_at DESC);
1640
+ `);
1641
+ } catch (err) {
1642
+ }
1643
+ this.initialized = true;
1644
+ }
1645
+ /**
1646
+ * Append event to store (Append-only, Idempotent)
1647
+ */
1648
+ async append(input) {
1649
+ await this.initialize();
1650
+ const canonicalKey = makeCanonicalKey(input.content);
1651
+ const dedupeKey = makeDedupeKey(input.content, input.sessionId);
1652
+ const existing = sqliteGet(
1653
+ this.db,
1654
+ `SELECT event_id FROM event_dedup WHERE dedupe_key = ?`,
1655
+ [dedupeKey]
1656
+ );
1657
+ if (existing) {
1658
+ return {
1659
+ success: true,
1660
+ eventId: existing.event_id,
1661
+ isDuplicate: true
1662
+ };
1663
+ }
1664
+ const id = randomUUID2();
1665
+ const timestamp = toSQLiteTimestamp(input.timestamp);
1666
+ try {
1667
+ const insertEvent = this.db.prepare(`
1668
+ INSERT INTO events (id, event_type, session_id, timestamp, content, canonical_key, dedupe_key, metadata)
1669
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1670
+ `);
1671
+ const insertDedup = this.db.prepare(`
1672
+ INSERT INTO event_dedup (dedupe_key, event_id) VALUES (?, ?)
1673
+ `);
1674
+ const insertLevel = this.db.prepare(`
1675
+ INSERT INTO memory_levels (event_id, level) VALUES (?, 'L0')
1676
+ `);
1677
+ const transaction = this.db.transaction(() => {
1678
+ insertEvent.run(
1679
+ id,
1680
+ input.eventType,
1681
+ input.sessionId,
1682
+ timestamp,
1683
+ input.content,
1684
+ canonicalKey,
1685
+ dedupeKey,
1686
+ JSON.stringify(input.metadata || {})
1687
+ );
1688
+ insertDedup.run(dedupeKey, id);
1689
+ insertLevel.run(id);
1690
+ });
1691
+ transaction();
1692
+ return { success: true, eventId: id, isDuplicate: false };
1693
+ } catch (error) {
1694
+ return {
1695
+ success: false,
1696
+ error: error instanceof Error ? error.message : String(error)
1697
+ };
1698
+ }
1699
+ }
1700
+ /**
1701
+ * Get events by session ID
1702
+ */
1703
+ async getSessionEvents(sessionId) {
1704
+ await this.initialize();
1705
+ const rows = sqliteAll(
1706
+ this.db,
1707
+ `SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC`,
1708
+ [sessionId]
1709
+ );
1710
+ return rows.map(this.rowToEvent);
1711
+ }
1712
+ /**
1713
+ * Get recent events
1714
+ */
1715
+ async getRecentEvents(limit = 100) {
1716
+ await this.initialize();
1717
+ const rows = sqliteAll(
1718
+ this.db,
1719
+ `SELECT * FROM events ORDER BY timestamp DESC LIMIT ?`,
1720
+ [limit]
1721
+ );
1722
+ return rows.map(this.rowToEvent);
1723
+ }
1724
+ /**
1725
+ * Get event by ID
1726
+ */
1727
+ async getEvent(id) {
1728
+ await this.initialize();
1729
+ const row = sqliteGet(
1730
+ this.db,
1731
+ `SELECT * FROM events WHERE id = ?`,
1732
+ [id]
1733
+ );
1734
+ if (!row)
1735
+ return null;
1736
+ return this.rowToEvent(row);
1737
+ }
1738
+ /**
1739
+ * Get events since a timestamp (for sync)
1740
+ */
1741
+ async getEventsSince(timestamp, limit = 1e3) {
1742
+ await this.initialize();
1743
+ const rows = sqliteAll(
1744
+ this.db,
1745
+ `SELECT * FROM events WHERE timestamp > ? ORDER BY timestamp ASC LIMIT ?`,
1746
+ [timestamp, limit]
1747
+ );
1748
+ return rows.map(this.rowToEvent);
1749
+ }
1750
+ /**
1751
+ * Create or update session
1752
+ */
1753
+ async upsertSession(session) {
1754
+ await this.initialize();
1755
+ const existing = sqliteGet(
1756
+ this.db,
1757
+ `SELECT id FROM sessions WHERE id = ?`,
1758
+ [session.id]
1759
+ );
1760
+ if (!existing) {
1761
+ sqliteRun(
1762
+ this.db,
1763
+ `INSERT INTO sessions (id, started_at, project_path, tags)
1764
+ VALUES (?, ?, ?, ?)`,
1765
+ [
1766
+ session.id,
1767
+ toSQLiteTimestamp(session.startedAt || /* @__PURE__ */ new Date()),
1768
+ session.projectPath || null,
1769
+ JSON.stringify(session.tags || [])
1770
+ ]
1771
+ );
1772
+ } else {
1773
+ const updates = [];
1774
+ const values = [];
1775
+ if (session.endedAt) {
1776
+ updates.push("ended_at = ?");
1777
+ values.push(toSQLiteTimestamp(session.endedAt));
1778
+ }
1779
+ if (session.summary) {
1780
+ updates.push("summary = ?");
1781
+ values.push(session.summary);
1782
+ }
1783
+ if (session.tags) {
1784
+ updates.push("tags = ?");
1785
+ values.push(JSON.stringify(session.tags));
1786
+ }
1787
+ if (updates.length > 0) {
1788
+ values.push(session.id);
1789
+ sqliteRun(
1790
+ this.db,
1791
+ `UPDATE sessions SET ${updates.join(", ")} WHERE id = ?`,
1792
+ values
1793
+ );
1794
+ }
1795
+ }
1796
+ }
1797
+ /**
1798
+ * Get session by ID
1799
+ */
1800
+ async getSession(id) {
1801
+ await this.initialize();
1802
+ const row = sqliteGet(
1803
+ this.db,
1804
+ `SELECT * FROM sessions WHERE id = ?`,
1805
+ [id]
1806
+ );
1807
+ if (!row)
1808
+ return null;
1809
+ return {
1810
+ id: row.id,
1811
+ startedAt: toDateFromSQLite(row.started_at),
1812
+ endedAt: row.ended_at ? toDateFromSQLite(row.ended_at) : void 0,
1813
+ projectPath: row.project_path,
1814
+ summary: row.summary,
1815
+ tags: row.tags ? JSON.parse(row.tags) : void 0
1816
+ };
1817
+ }
1818
+ /**
1819
+ * Get all sessions
1820
+ */
1821
+ async getAllSessions() {
1822
+ await this.initialize();
1823
+ const rows = sqliteAll(
1824
+ this.db,
1825
+ `SELECT * FROM sessions ORDER BY started_at DESC`
1826
+ );
1827
+ return rows.map((row) => ({
1828
+ id: row.id,
1829
+ startedAt: toDateFromSQLite(row.started_at),
1830
+ endedAt: row.ended_at ? toDateFromSQLite(row.ended_at) : void 0,
1081
1831
  projectPath: row.project_path,
1082
1832
  summary: row.summary,
1083
1833
  tags: row.tags ? JSON.parse(row.tags) : void 0
1084
- };
1834
+ }));
1085
1835
  }
1086
1836
  /**
1087
- * Add to embedding outbox (Single-Writer Pattern)
1837
+ * Add to embedding outbox
1088
1838
  */
1089
1839
  async enqueueForEmbedding(eventId, content) {
1090
1840
  await this.initialize();
1091
- const id = randomUUID();
1092
- await dbRun(
1841
+ const id = randomUUID2();
1842
+ sqliteRun(
1093
1843
  this.db,
1094
1844
  `INSERT INTO embedding_outbox (id, event_id, content, status, retry_count)
1095
1845
  VALUES (?, ?, ?, 'pending', 0)`,
@@ -1102,7 +1852,7 @@ var EventStore = class {
1102
1852
  */
1103
1853
  async getPendingOutboxItems(limit = 32) {
1104
1854
  await this.initialize();
1105
- const pending = await dbAll(
1855
+ const pending = sqliteAll(
1106
1856
  this.db,
1107
1857
  `SELECT * FROM embedding_outbox
1108
1858
  WHERE status = 'pending'
@@ -1114,7 +1864,7 @@ var EventStore = class {
1114
1864
  return [];
1115
1865
  const ids = pending.map((r) => r.id);
1116
1866
  const placeholders = ids.map(() => "?").join(",");
1117
- await dbRun(
1867
+ sqliteRun(
1118
1868
  this.db,
1119
1869
  `UPDATE embedding_outbox SET status = 'processing' WHERE id IN (${placeholders})`,
1120
1870
  ids
@@ -1125,7 +1875,7 @@ var EventStore = class {
1125
1875
  content: row.content,
1126
1876
  status: "processing",
1127
1877
  retryCount: row.retry_count,
1128
- createdAt: toDate(row.created_at),
1878
+ createdAt: toDateFromSQLite(row.created_at),
1129
1879
  errorMessage: row.error_message
1130
1880
  }));
1131
1881
  }
@@ -1136,7 +1886,7 @@ var EventStore = class {
1136
1886
  if (ids.length === 0)
1137
1887
  return;
1138
1888
  const placeholders = ids.map(() => "?").join(",");
1139
- await dbRun(
1889
+ sqliteRun(
1140
1890
  this.db,
1141
1891
  `DELETE FROM embedding_outbox WHERE id IN (${placeholders})`,
1142
1892
  ids
@@ -1149,7 +1899,7 @@ var EventStore = class {
1149
1899
  if (ids.length === 0)
1150
1900
  return;
1151
1901
  const placeholders = ids.map(() => "?").join(",");
1152
- await dbRun(
1902
+ sqliteRun(
1153
1903
  this.db,
1154
1904
  `UPDATE embedding_outbox
1155
1905
  SET status = CASE WHEN retry_count >= 3 THEN 'failed' ELSE 'pending' END,
@@ -1164,9 +1914,9 @@ var EventStore = class {
1164
1914
  */
1165
1915
  async updateMemoryLevel(eventId, level) {
1166
1916
  await this.initialize();
1167
- await dbRun(
1917
+ sqliteRun(
1168
1918
  this.db,
1169
- `UPDATE memory_levels SET level = ?, promoted_at = CURRENT_TIMESTAMP WHERE event_id = ?`,
1919
+ `UPDATE memory_levels SET level = ?, promoted_at = datetime('now') WHERE event_id = ?`,
1170
1920
  [level, eventId]
1171
1921
  );
1172
1922
  }
@@ -1175,90 +1925,329 @@ var EventStore = class {
1175
1925
  */
1176
1926
  async getLevelStats() {
1177
1927
  await this.initialize();
1178
- const rows = await dbAll(
1928
+ const rows = sqliteAll(
1179
1929
  this.db,
1180
1930
  `SELECT level, COUNT(*) as count FROM memory_levels GROUP BY level`
1181
1931
  );
1182
1932
  return rows;
1183
1933
  }
1184
- // ============================================================
1185
- // Endless Mode Helper Methods
1186
- // ============================================================
1187
1934
  /**
1188
- * Get database instance for Endless Mode stores
1935
+ * Get events by memory level
1189
1936
  */
1190
- getDatabase() {
1191
- return this.db;
1937
+ async getEventsByLevel(level, options) {
1938
+ await this.initialize();
1939
+ const limit = options?.limit || 50;
1940
+ const offset = options?.offset || 0;
1941
+ const rows = sqliteAll(
1942
+ this.db,
1943
+ `SELECT e.* FROM events e
1944
+ INNER JOIN memory_levels ml ON e.id = ml.event_id
1945
+ WHERE ml.level = ?
1946
+ ORDER BY e.timestamp DESC
1947
+ LIMIT ? OFFSET ?`,
1948
+ [level, limit, offset]
1949
+ );
1950
+ return rows.map((row) => this.rowToEvent(row));
1951
+ }
1952
+ /**
1953
+ * Get memory level for a specific event
1954
+ */
1955
+ async getEventLevel(eventId) {
1956
+ await this.initialize();
1957
+ const row = sqliteGet(
1958
+ this.db,
1959
+ `SELECT level FROM memory_levels WHERE event_id = ?`,
1960
+ [eventId]
1961
+ );
1962
+ return row ? row.level : null;
1963
+ }
1964
+ /**
1965
+ * Get sync position for a target
1966
+ */
1967
+ async getSyncPosition(targetName) {
1968
+ await this.initialize();
1969
+ const row = sqliteGet(
1970
+ this.db,
1971
+ `SELECT last_event_id, last_timestamp FROM sync_positions WHERE target_name = ?`,
1972
+ [targetName]
1973
+ );
1974
+ return {
1975
+ lastEventId: row?.last_event_id ?? null,
1976
+ lastTimestamp: row?.last_timestamp ?? null
1977
+ };
1978
+ }
1979
+ /**
1980
+ * Update sync position for a target
1981
+ */
1982
+ async updateSyncPosition(targetName, lastEventId, lastTimestamp) {
1983
+ await this.initialize();
1984
+ sqliteRun(
1985
+ this.db,
1986
+ `INSERT OR REPLACE INTO sync_positions (target_name, last_event_id, last_timestamp, updated_at)
1987
+ VALUES (?, ?, ?, datetime('now'))`,
1988
+ [targetName, lastEventId, lastTimestamp]
1989
+ );
1192
1990
  }
1193
1991
  /**
1194
1992
  * Get config value for endless mode
1195
1993
  */
1196
1994
  async getEndlessConfig(key) {
1197
1995
  await this.initialize();
1198
- const rows = await dbAll(
1996
+ const row = sqliteGet(
1199
1997
  this.db,
1200
1998
  `SELECT value FROM endless_config WHERE key = ?`,
1201
1999
  [key]
1202
2000
  );
1203
- if (rows.length === 0)
2001
+ if (!row)
1204
2002
  return null;
1205
- return JSON.parse(rows[0].value);
2003
+ return JSON.parse(row.value);
1206
2004
  }
1207
2005
  /**
1208
2006
  * Set config value for endless mode
1209
2007
  */
1210
2008
  async setEndlessConfig(key, value) {
1211
2009
  await this.initialize();
1212
- await dbRun(
2010
+ sqliteRun(
1213
2011
  this.db,
1214
2012
  `INSERT OR REPLACE INTO endless_config (key, value, updated_at)
1215
- VALUES (?, ?, CURRENT_TIMESTAMP)`,
2013
+ VALUES (?, ?, datetime('now'))`,
1216
2014
  [key, JSON.stringify(value)]
1217
2015
  );
1218
2016
  }
1219
2017
  /**
1220
- * Get all sessions
2018
+ * Increment access count for events
1221
2019
  */
1222
- async getAllSessions() {
2020
+ async incrementAccessCount(eventIds) {
2021
+ if (eventIds.length === 0 || this.readOnly)
2022
+ return;
1223
2023
  await this.initialize();
1224
- const rows = await dbAll(
2024
+ const placeholders = eventIds.map(() => "?").join(",");
2025
+ const currentTime = toSQLiteTimestamp(/* @__PURE__ */ new Date());
2026
+ sqliteRun(
1225
2027
  this.db,
1226
- `SELECT * FROM sessions ORDER BY started_at DESC`
2028
+ `UPDATE events
2029
+ SET access_count = access_count + 1,
2030
+ last_accessed_at = ?
2031
+ WHERE id IN (${placeholders})`,
2032
+ [currentTime, ...eventIds]
1227
2033
  );
1228
- return rows.map((row) => ({
1229
- id: row.id,
1230
- startedAt: toDate(row.started_at),
1231
- endedAt: row.ended_at ? toDate(row.ended_at) : void 0,
1232
- projectPath: row.project_path,
1233
- summary: row.summary,
1234
- tags: row.tags ? JSON.parse(row.tags) : void 0
1235
- }));
2034
+ }
2035
+ /**
2036
+ * Get most accessed memories
2037
+ */
2038
+ async getMostAccessed(limit = 10) {
2039
+ await this.initialize();
2040
+ const rows = sqliteAll(
2041
+ this.db,
2042
+ `SELECT * FROM events
2043
+ WHERE access_count > 0
2044
+ ORDER BY access_count DESC, last_accessed_at DESC
2045
+ LIMIT ?`,
2046
+ [limit]
2047
+ );
2048
+ return rows.map((row) => this.rowToEvent(row));
2049
+ }
2050
+ /**
2051
+ * Get database instance for direct access
2052
+ */
2053
+ getDatabase() {
2054
+ return this.db;
1236
2055
  }
1237
2056
  /**
1238
2057
  * Close database connection
1239
2058
  */
1240
2059
  async close() {
1241
- await dbClose(this.db);
2060
+ sqliteClose(this.db);
1242
2061
  }
1243
2062
  /**
1244
2063
  * Convert database row to MemoryEvent
1245
2064
  */
1246
2065
  rowToEvent(row) {
1247
- return {
2066
+ const event = {
1248
2067
  id: row.id,
1249
2068
  eventType: row.event_type,
1250
2069
  sessionId: row.session_id,
1251
- timestamp: toDate(row.timestamp),
2070
+ timestamp: toDateFromSQLite(row.timestamp),
1252
2071
  content: row.content,
1253
2072
  canonicalKey: row.canonical_key,
1254
2073
  dedupeKey: row.dedupe_key,
1255
2074
  metadata: row.metadata ? JSON.parse(row.metadata) : void 0
1256
2075
  };
2076
+ if (row.access_count !== void 0) {
2077
+ event.access_count = row.access_count;
2078
+ }
2079
+ if (row.last_accessed_at !== void 0) {
2080
+ event.last_accessed_at = row.last_accessed_at;
2081
+ }
2082
+ return event;
2083
+ }
2084
+ };
2085
+
2086
+ // src/core/sync-worker.ts
2087
+ var DEFAULT_CONFIG = {
2088
+ intervalMs: 3e4,
2089
+ batchSize: 500,
2090
+ maxRetries: 3,
2091
+ retryDelayMs: 5e3
2092
+ };
2093
+ var SyncWorker = class {
2094
+ constructor(sqliteStore, duckdbStore, config) {
2095
+ this.sqliteStore = sqliteStore;
2096
+ this.duckdbStore = duckdbStore;
2097
+ this.config = { ...DEFAULT_CONFIG, ...config };
2098
+ }
2099
+ config;
2100
+ intervalHandle = null;
2101
+ running = false;
2102
+ stats = {
2103
+ lastSyncAt: null,
2104
+ eventsSynced: 0,
2105
+ sessionsSynced: 0,
2106
+ errors: 0,
2107
+ status: "idle"
2108
+ };
2109
+ /**
2110
+ * Start the sync worker
2111
+ */
2112
+ start() {
2113
+ if (this.running)
2114
+ return;
2115
+ this.running = true;
2116
+ this.stats.status = "idle";
2117
+ this.syncNow().catch((err) => {
2118
+ console.error("[SyncWorker] Initial sync failed:", err);
2119
+ });
2120
+ this.intervalHandle = setInterval(() => {
2121
+ this.syncNow().catch((err) => {
2122
+ console.error("[SyncWorker] Periodic sync failed:", err);
2123
+ });
2124
+ }, this.config.intervalMs);
2125
+ }
2126
+ /**
2127
+ * Stop the sync worker
2128
+ */
2129
+ stop() {
2130
+ this.running = false;
2131
+ this.stats.status = "stopped";
2132
+ if (this.intervalHandle) {
2133
+ clearInterval(this.intervalHandle);
2134
+ this.intervalHandle = null;
2135
+ }
2136
+ }
2137
+ /**
2138
+ * Trigger immediate sync
2139
+ */
2140
+ async syncNow() {
2141
+ if (this.stats.status === "syncing") {
2142
+ return;
2143
+ }
2144
+ this.stats.status = "syncing";
2145
+ try {
2146
+ await this.syncEvents();
2147
+ await this.syncSessions();
2148
+ this.stats.lastSyncAt = /* @__PURE__ */ new Date();
2149
+ this.stats.status = "idle";
2150
+ } catch (error) {
2151
+ this.stats.errors++;
2152
+ this.stats.status = "error";
2153
+ throw error;
2154
+ }
2155
+ }
2156
+ /**
2157
+ * Sync events from SQLite to DuckDB
2158
+ */
2159
+ async syncEvents() {
2160
+ const targetName = "duckdb_analytics";
2161
+ const position = await this.sqliteStore.getSyncPosition(targetName);
2162
+ const lastTimestamp = position.lastTimestamp || "1970-01-01T00:00:00.000Z";
2163
+ let hasMore = true;
2164
+ let totalSynced = 0;
2165
+ while (hasMore) {
2166
+ const events = await this.sqliteStore.getEventsSince(lastTimestamp, this.config.batchSize);
2167
+ if (events.length === 0) {
2168
+ hasMore = false;
2169
+ break;
2170
+ }
2171
+ await this.retryWithBackoff(async () => {
2172
+ for (const event of events) {
2173
+ await this.insertEventToDuckDB(event);
2174
+ }
2175
+ });
2176
+ totalSynced += events.length;
2177
+ const lastEvent = events[events.length - 1];
2178
+ await this.sqliteStore.updateSyncPosition(
2179
+ targetName,
2180
+ lastEvent.id,
2181
+ lastEvent.timestamp.toISOString()
2182
+ );
2183
+ hasMore = events.length === this.config.batchSize;
2184
+ }
2185
+ this.stats.eventsSynced += totalSynced;
2186
+ }
2187
+ /**
2188
+ * Sync sessions from SQLite to DuckDB
2189
+ */
2190
+ async syncSessions() {
2191
+ const sessions = await this.sqliteStore.getAllSessions();
2192
+ for (const session of sessions) {
2193
+ await this.retryWithBackoff(async () => {
2194
+ await this.duckdbStore.upsertSession(session);
2195
+ });
2196
+ }
2197
+ this.stats.sessionsSynced = sessions.length;
2198
+ }
2199
+ /**
2200
+ * Insert a single event into DuckDB
2201
+ */
2202
+ async insertEventToDuckDB(event) {
2203
+ await this.duckdbStore.append({
2204
+ eventType: event.eventType,
2205
+ sessionId: event.sessionId,
2206
+ timestamp: event.timestamp,
2207
+ content: event.content,
2208
+ metadata: event.metadata
2209
+ });
2210
+ }
2211
+ /**
2212
+ * Retry operation with exponential backoff
2213
+ */
2214
+ async retryWithBackoff(fn) {
2215
+ let lastError = null;
2216
+ for (let attempt = 0; attempt < this.config.maxRetries; attempt++) {
2217
+ try {
2218
+ return await fn();
2219
+ } catch (error) {
2220
+ lastError = error instanceof Error ? error : new Error(String(error));
2221
+ if (attempt < this.config.maxRetries - 1) {
2222
+ const delay = this.config.retryDelayMs * Math.pow(2, attempt);
2223
+ await this.sleep(delay);
2224
+ }
2225
+ }
2226
+ }
2227
+ throw lastError;
2228
+ }
2229
+ /**
2230
+ * Sleep utility
2231
+ */
2232
+ sleep(ms) {
2233
+ return new Promise((resolve) => setTimeout(resolve, ms));
2234
+ }
2235
+ /**
2236
+ * Get sync statistics
2237
+ */
2238
+ getStats() {
2239
+ return { ...this.stats };
2240
+ }
2241
+ /**
2242
+ * Check if worker is running
2243
+ */
2244
+ isRunning() {
2245
+ return this.running;
1257
2246
  }
1258
2247
  };
1259
2248
 
1260
2249
  // src/core/entity-repo.ts
1261
- import { randomUUID as randomUUID2 } from "crypto";
2250
+ import { randomUUID as randomUUID3 } from "crypto";
1262
2251
  var EntityRepo = class {
1263
2252
  constructor(db) {
1264
2253
  this.db = db;
@@ -1267,7 +2256,7 @@ var EntityRepo = class {
1267
2256
  * Create a new entity
1268
2257
  */
1269
2258
  async create(input) {
1270
- const entityId = randomUUID2();
2259
+ const entityId = randomUUID3();
1271
2260
  const canonicalKey = makeEntityCanonicalKey(input.entityType, input.title, {
1272
2261
  project: input.project
1273
2262
  });
@@ -1521,7 +2510,7 @@ var EntityRepo = class {
1521
2510
  };
1522
2511
 
1523
2512
  // src/core/edge-repo.ts
1524
- import { randomUUID as randomUUID3 } from "crypto";
2513
+ import { randomUUID as randomUUID4 } from "crypto";
1525
2514
  var EdgeRepo = class {
1526
2515
  constructor(db) {
1527
2516
  this.db = db;
@@ -1530,7 +2519,7 @@ var EdgeRepo = class {
1530
2519
  * Create a new edge (idempotent - ignores duplicates)
1531
2520
  */
1532
2521
  async create(input) {
1533
- const edgeId = randomUUID3();
2522
+ const edgeId = randomUUID4();
1534
2523
  const now = /* @__PURE__ */ new Date();
1535
2524
  await dbRun(
1536
2525
  this.db,
@@ -2003,8 +2992,8 @@ function getDefaultEmbedder() {
2003
2992
  }
2004
2993
 
2005
2994
  // src/core/vector-outbox.ts
2006
- import { randomUUID as randomUUID4 } from "crypto";
2007
- var DEFAULT_CONFIG = {
2995
+ import { randomUUID as randomUUID5 } from "crypto";
2996
+ var DEFAULT_CONFIG2 = {
2008
2997
  embeddingVersion: "v1",
2009
2998
  maxRetries: 3,
2010
2999
  stuckThresholdMs: 5 * 60 * 1e3,
@@ -2014,7 +3003,7 @@ var DEFAULT_CONFIG = {
2014
3003
  var VectorOutbox = class {
2015
3004
  constructor(db, config) {
2016
3005
  this.db = db;
2017
- this.config = { ...DEFAULT_CONFIG, ...config };
3006
+ this.config = { ...DEFAULT_CONFIG2, ...config };
2018
3007
  }
2019
3008
  config;
2020
3009
  /**
@@ -2022,7 +3011,7 @@ var VectorOutbox = class {
2022
3011
  */
2023
3012
  async enqueue(itemKind, itemId, embeddingVersion) {
2024
3013
  const version = embeddingVersion ?? this.config.embeddingVersion;
2025
- const jobId = randomUUID4();
3014
+ const jobId = randomUUID5();
2026
3015
  const now = (/* @__PURE__ */ new Date()).toISOString();
2027
3016
  await dbRun(
2028
3017
  this.db,
@@ -2242,7 +3231,7 @@ var VectorOutbox = class {
2242
3231
  };
2243
3232
 
2244
3233
  // src/core/vector-worker.ts
2245
- var DEFAULT_CONFIG2 = {
3234
+ var DEFAULT_CONFIG3 = {
2246
3235
  batchSize: 32,
2247
3236
  pollIntervalMs: 1e3,
2248
3237
  maxRetries: 3
@@ -2253,12 +3242,13 @@ var VectorWorker = class {
2253
3242
  embedder;
2254
3243
  config;
2255
3244
  running = false;
3245
+ stopping = false;
2256
3246
  pollTimeout = null;
2257
3247
  constructor(eventStore, vectorStore, embedder, config = {}) {
2258
3248
  this.eventStore = eventStore;
2259
3249
  this.vectorStore = vectorStore;
2260
3250
  this.embedder = embedder;
2261
- this.config = { ...DEFAULT_CONFIG2, ...config };
3251
+ this.config = { ...DEFAULT_CONFIG3, ...config };
2262
3252
  }
2263
3253
  /**
2264
3254
  * Start the worker polling loop
@@ -2267,6 +3257,7 @@ var VectorWorker = class {
2267
3257
  if (this.running)
2268
3258
  return;
2269
3259
  this.running = true;
3260
+ this.stopping = false;
2270
3261
  this.poll();
2271
3262
  }
2272
3263
  /**
@@ -2274,6 +3265,7 @@ var VectorWorker = class {
2274
3265
  */
2275
3266
  stop() {
2276
3267
  this.running = false;
3268
+ this.stopping = true;
2277
3269
  if (this.pollTimeout) {
2278
3270
  clearTimeout(this.pollTimeout);
2279
3271
  this.pollTimeout = null;
@@ -2323,9 +3315,15 @@ var VectorWorker = class {
2323
3315
  }
2324
3316
  return successful.length;
2325
3317
  } catch (error) {
2326
- const allIds = items.map((i) => i.id);
2327
- const errorMessage = error instanceof Error ? error.message : String(error);
2328
- await this.eventStore.failOutboxItems(allIds, errorMessage);
3318
+ if (!this.stopping) {
3319
+ try {
3320
+ const allIds = items.map((i) => i.id);
3321
+ const errorMessage = error instanceof Error ? error.message : String(error);
3322
+ await this.eventStore.failOutboxItems(allIds, errorMessage);
3323
+ } catch (failError) {
3324
+ console.warn("Could not mark outbox items as failed (database may be closed)");
3325
+ }
3326
+ }
2329
3327
  throw error;
2330
3328
  }
2331
3329
  }
@@ -2333,14 +3331,18 @@ var VectorWorker = class {
2333
3331
  * Poll for new items
2334
3332
  */
2335
3333
  async poll() {
2336
- if (!this.running)
3334
+ if (!this.running || this.stopping)
2337
3335
  return;
2338
3336
  try {
2339
3337
  await this.processBatch();
2340
3338
  } catch (error) {
2341
- console.error("Vector worker error:", error);
3339
+ if (!this.stopping) {
3340
+ console.error("Vector worker error:", error);
3341
+ }
3342
+ }
3343
+ if (this.running && !this.stopping) {
3344
+ this.pollTimeout = setTimeout(() => this.poll(), this.config.pollIntervalMs);
2342
3345
  }
2343
- this.pollTimeout = setTimeout(() => this.poll(), this.config.pollIntervalMs);
2344
3346
  }
2345
3347
  /**
2346
3348
  * Process all pending items (blocking)
@@ -2450,6 +3452,7 @@ var VectorWorkerV2 = class {
2450
3452
  contentProvider;
2451
3453
  config;
2452
3454
  running = false;
3455
+ stopping = false;
2453
3456
  pollTimeout = null;
2454
3457
  constructor(db, vectorStore, embedder, config = {}, contentProvider) {
2455
3458
  this.outbox = new VectorOutbox(db, {
@@ -2468,6 +3471,7 @@ var VectorWorkerV2 = class {
2468
3471
  if (this.running)
2469
3472
  return;
2470
3473
  this.running = true;
3474
+ this.stopping = false;
2471
3475
  this.poll();
2472
3476
  }
2473
3477
  /**
@@ -2475,6 +3479,7 @@ var VectorWorkerV2 = class {
2475
3479
  */
2476
3480
  stop() {
2477
3481
  this.running = false;
3482
+ this.stopping = true;
2478
3483
  if (this.pollTimeout) {
2479
3484
  clearTimeout(this.pollTimeout);
2480
3485
  this.pollTimeout = null;
@@ -2495,8 +3500,13 @@ var VectorWorkerV2 = class {
2495
3500
  await this.outbox.markDone(job.jobId);
2496
3501
  successCount++;
2497
3502
  } catch (error) {
2498
- const errorMessage = error instanceof Error ? error.message : String(error);
2499
- await this.outbox.markFailed(job.jobId, errorMessage);
3503
+ if (!this.stopping) {
3504
+ try {
3505
+ const errorMessage = error instanceof Error ? error.message : String(error);
3506
+ await this.outbox.markFailed(job.jobId, errorMessage);
3507
+ } catch {
3508
+ }
3509
+ }
2500
3510
  }
2501
3511
  }
2502
3512
  return successCount;
@@ -2529,14 +3539,18 @@ var VectorWorkerV2 = class {
2529
3539
  * Poll for new jobs
2530
3540
  */
2531
3541
  async poll() {
2532
- if (!this.running)
3542
+ if (!this.running || this.stopping)
2533
3543
  return;
2534
3544
  try {
2535
3545
  await this.processBatch();
2536
3546
  } catch (error) {
2537
- console.error("Vector worker V2 error:", error);
3547
+ if (!this.stopping) {
3548
+ console.error("Vector worker V2 error:", error);
3549
+ }
3550
+ }
3551
+ if (this.running && !this.stopping) {
3552
+ this.pollTimeout = setTimeout(() => this.poll(), this.config.pollIntervalMs);
2538
3553
  }
2539
- this.pollTimeout = setTimeout(() => this.poll(), this.config.pollIntervalMs);
2540
3554
  }
2541
3555
  /**
2542
3556
  * Process all pending jobs (blocking)
@@ -2580,7 +3594,7 @@ function createVectorWorkerV2(db, vectorStore, embedder, config) {
2580
3594
  }
2581
3595
 
2582
3596
  // src/core/matcher.ts
2583
- var DEFAULT_CONFIG3 = {
3597
+ var DEFAULT_CONFIG4 = {
2584
3598
  weights: {
2585
3599
  semanticSimilarity: 0.4,
2586
3600
  ftsScore: 0.25,
@@ -2595,9 +3609,9 @@ var Matcher = class {
2595
3609
  config;
2596
3610
  constructor(config = {}) {
2597
3611
  this.config = {
2598
- ...DEFAULT_CONFIG3,
3612
+ ...DEFAULT_CONFIG4,
2599
3613
  ...config,
2600
- weights: { ...DEFAULT_CONFIG3.weights, ...config.weights }
3614
+ weights: { ...DEFAULT_CONFIG4.weights, ...config.weights }
2601
3615
  };
2602
3616
  }
2603
3617
  /**
@@ -3167,6 +4181,7 @@ var Retriever = class {
3167
4181
  matcher;
3168
4182
  sharedStore;
3169
4183
  sharedVectorStore;
4184
+ graduation;
3170
4185
  constructor(eventStore, vectorStore, embedder, matcher, sharedOptions) {
3171
4186
  this.eventStore = eventStore;
3172
4187
  this.vectorStore = vectorStore;
@@ -3175,6 +4190,12 @@ var Retriever = class {
3175
4190
  this.sharedStore = sharedOptions?.sharedStore;
3176
4191
  this.sharedVectorStore = sharedOptions?.sharedVectorStore;
3177
4192
  }
4193
+ /**
4194
+ * Set graduation pipeline for access tracking
4195
+ */
4196
+ setGraduationPipeline(graduation) {
4197
+ this.graduation = graduation;
4198
+ }
3178
4199
  /**
3179
4200
  * Set shared stores after construction
3180
4201
  */
@@ -3297,6 +4318,13 @@ var Retriever = class {
3297
4318
  const event = await this.eventStore.getEvent(result.eventId);
3298
4319
  if (!event)
3299
4320
  continue;
4321
+ if (this.graduation) {
4322
+ this.graduation.recordAccess(
4323
+ event.id,
4324
+ options.sessionId || "unknown",
4325
+ result.score
4326
+ );
4327
+ }
3300
4328
  let sessionContext;
3301
4329
  if (options.includeSessionContext) {
3302
4330
  sessionContext = await this.getSessionContext(event.sessionId, event.id);
@@ -3418,15 +4446,26 @@ var GraduationPipeline = class {
3418
4446
  L3toL4: { ...DEFAULT_CRITERIA.L3toL4, ...criteria.L3toL4 }
3419
4447
  };
3420
4448
  }
4449
+ // Track which sessions have accessed each event
4450
+ sessionAccesses = /* @__PURE__ */ new Map();
3421
4451
  /**
3422
4452
  * Record an access to an event (used for graduation scoring)
3423
4453
  */
3424
4454
  recordAccess(eventId, fromSessionId, confidence = 1) {
3425
4455
  const existing = this.metrics.get(eventId);
4456
+ if (!this.sessionAccesses.has(eventId)) {
4457
+ this.sessionAccesses.set(eventId, /* @__PURE__ */ new Set());
4458
+ }
4459
+ const sessions = this.sessionAccesses.get(eventId);
4460
+ const isNewSession = !sessions.has(fromSessionId);
4461
+ sessions.add(fromSessionId);
3426
4462
  if (existing) {
3427
4463
  existing.accessCount++;
3428
4464
  existing.lastAccessed = /* @__PURE__ */ new Date();
3429
4465
  existing.confidence = Math.max(existing.confidence, confidence);
4466
+ if (isNewSession && sessions.size > 1) {
4467
+ existing.crossSessionRefs = sessions.size - 1;
4468
+ }
3430
4469
  } else {
3431
4470
  this.metrics.set(eventId, {
3432
4471
  eventId,
@@ -3631,8 +4670,129 @@ function createGraduationPipeline(eventStore) {
3631
4670
  return new GraduationPipeline(eventStore);
3632
4671
  }
3633
4672
 
4673
+ // src/core/graduation-worker.ts
4674
+ var DEFAULT_CONFIG5 = {
4675
+ evaluationIntervalMs: 3e5,
4676
+ // 5 minutes
4677
+ batchSize: 50,
4678
+ cooldownMs: 36e5
4679
+ // 1 hour cooldown between evaluations
4680
+ };
4681
+ var GraduationWorker = class {
4682
+ constructor(eventStore, graduation, config = DEFAULT_CONFIG5) {
4683
+ this.eventStore = eventStore;
4684
+ this.graduation = graduation;
4685
+ this.config = config;
4686
+ }
4687
+ running = false;
4688
+ timeout = null;
4689
+ lastEvaluated = /* @__PURE__ */ new Map();
4690
+ /**
4691
+ * Start the graduation worker
4692
+ */
4693
+ start() {
4694
+ if (this.running)
4695
+ return;
4696
+ this.running = true;
4697
+ this.scheduleNext();
4698
+ }
4699
+ /**
4700
+ * Stop the graduation worker
4701
+ */
4702
+ stop() {
4703
+ this.running = false;
4704
+ if (this.timeout) {
4705
+ clearTimeout(this.timeout);
4706
+ this.timeout = null;
4707
+ }
4708
+ }
4709
+ /**
4710
+ * Check if currently running
4711
+ */
4712
+ isRunning() {
4713
+ return this.running;
4714
+ }
4715
+ /**
4716
+ * Force a graduation evaluation run
4717
+ */
4718
+ async forceRun() {
4719
+ return await this.runGraduation();
4720
+ }
4721
+ /**
4722
+ * Schedule the next graduation check
4723
+ */
4724
+ scheduleNext() {
4725
+ if (!this.running)
4726
+ return;
4727
+ this.timeout = setTimeout(
4728
+ () => this.run(),
4729
+ this.config.evaluationIntervalMs
4730
+ );
4731
+ }
4732
+ /**
4733
+ * Run graduation evaluation
4734
+ */
4735
+ async run() {
4736
+ if (!this.running)
4737
+ return;
4738
+ try {
4739
+ await this.runGraduation();
4740
+ } catch (error) {
4741
+ console.error("Graduation error:", error);
4742
+ }
4743
+ this.scheduleNext();
4744
+ }
4745
+ /**
4746
+ * Perform graduation evaluation across all levels
4747
+ */
4748
+ async runGraduation() {
4749
+ const result = {
4750
+ evaluated: 0,
4751
+ graduated: 0,
4752
+ byLevel: {}
4753
+ };
4754
+ const levels = ["L0", "L1", "L2", "L3"];
4755
+ const now = Date.now();
4756
+ for (const level of levels) {
4757
+ const events = await this.eventStore.getEventsByLevel(level, {
4758
+ limit: this.config.batchSize
4759
+ });
4760
+ let levelGraduated = 0;
4761
+ for (const event of events) {
4762
+ const lastEval = this.lastEvaluated.get(event.id);
4763
+ if (lastEval && now - lastEval < this.config.cooldownMs) {
4764
+ continue;
4765
+ }
4766
+ result.evaluated++;
4767
+ this.lastEvaluated.set(event.id, now);
4768
+ const gradResult = await this.graduation.evaluateGraduation(event.id, level);
4769
+ if (gradResult.success) {
4770
+ result.graduated++;
4771
+ levelGraduated++;
4772
+ }
4773
+ }
4774
+ if (levelGraduated > 0) {
4775
+ result.byLevel[level] = levelGraduated;
4776
+ }
4777
+ }
4778
+ if (this.lastEvaluated.size > 1e3) {
4779
+ const entries = Array.from(this.lastEvaluated.entries());
4780
+ entries.sort((a, b) => b[1] - a[1]);
4781
+ this.lastEvaluated = new Map(entries.slice(0, 1e3));
4782
+ }
4783
+ return result;
4784
+ }
4785
+ };
4786
+ function createGraduationWorker(eventStore, graduation, config) {
4787
+ return new GraduationWorker(
4788
+ eventStore,
4789
+ graduation,
4790
+ { ...DEFAULT_CONFIG5, ...config }
4791
+ );
4792
+ }
4793
+
3634
4794
  // src/core/task/task-matcher.ts
3635
- var DEFAULT_CONFIG4 = {
4795
+ var DEFAULT_CONFIG6 = {
3636
4796
  minCombinedScore: MATCH_THRESHOLDS.minCombinedScore,
3637
4797
  minGap: MATCH_THRESHOLDS.minGap,
3638
4798
  suggestionThreshold: MATCH_THRESHOLDS.suggestionThreshold,
@@ -3641,7 +4801,7 @@ var DEFAULT_CONFIG4 = {
3641
4801
  var TaskMatcher = class {
3642
4802
  constructor(db, config) {
3643
4803
  this.db = db;
3644
- this.config = { ...DEFAULT_CONFIG4, ...config };
4804
+ this.config = { ...DEFAULT_CONFIG6, ...config };
3645
4805
  }
3646
4806
  config;
3647
4807
  /**
@@ -3805,7 +4965,7 @@ var TaskMatcher = class {
3805
4965
  };
3806
4966
 
3807
4967
  // src/core/task/blocker-resolver.ts
3808
- import { randomUUID as randomUUID5 } from "crypto";
4968
+ import { randomUUID as randomUUID6 } from "crypto";
3809
4969
  var URL_PATTERN = /^https?:\/\/.+/;
3810
4970
  var JIRA_PATTERN = /^[A-Z]+-\d+$/;
3811
4971
  var GITHUB_ISSUE_PATTERN = /^[^\/]+\/[^#]+#\d+$/;
@@ -3942,7 +5102,7 @@ var BlockerResolver = class {
3942
5102
  * Declare a new condition entity
3943
5103
  */
3944
5104
  async declareCondition(text, canonicalKey, candidates) {
3945
- const entityId = randomUUID5();
5105
+ const entityId = randomUUID6();
3946
5106
  const now = (/* @__PURE__ */ new Date()).toISOString();
3947
5107
  const currentJson = {
3948
5108
  text,
@@ -3985,7 +5145,7 @@ var BlockerResolver = class {
3985
5145
  * Declare a new artifact entity
3986
5146
  */
3987
5147
  async declareArtifact(identifier, canonicalKey) {
3988
- const entityId = randomUUID5();
5148
+ const entityId = randomUUID6();
3989
5149
  const now = (/* @__PURE__ */ new Date()).toISOString();
3990
5150
  let artifactType = "generic";
3991
5151
  if (URL_PATTERN.test(identifier)) {
@@ -4050,7 +5210,7 @@ var BlockerResolver = class {
4050
5210
  };
4051
5211
 
4052
5212
  // src/core/task/task-resolver.ts
4053
- import { randomUUID as randomUUID6 } from "crypto";
5213
+ import { randomUUID as randomUUID7 } from "crypto";
4054
5214
  var VALID_TRANSITIONS = {
4055
5215
  pending: ["in_progress", "cancelled"],
4056
5216
  in_progress: ["blocked", "done", "cancelled"],
@@ -4125,7 +5285,7 @@ var TaskResolver = class {
4125
5285
  isNew: false
4126
5286
  };
4127
5287
  }
4128
- const taskId = randomUUID6();
5288
+ const taskId = randomUUID7();
4129
5289
  const canonicalKey = makeEntityCanonicalKey("task", extracted.title, {
4130
5290
  project: extracted.project
4131
5291
  });
@@ -4278,7 +5438,7 @@ var TaskResolver = class {
4278
5438
  * Emit task event to events table
4279
5439
  */
4280
5440
  async emitTaskEvent(eventType, payload) {
4281
- const eventId = randomUUID6();
5441
+ const eventId = randomUUID7();
4282
5442
  const now = /* @__PURE__ */ new Date();
4283
5443
  const dedupeKey = makeTaskEventDedupeKey(
4284
5444
  eventType,
@@ -4336,14 +5496,14 @@ var TaskResolver = class {
4336
5496
  `INSERT INTO edges (edge_id, src_type, src_id, rel_type, dst_type, dst_id, meta_json)
4337
5497
  VALUES (?, 'entity', ?, 'resolves_to', 'entity', ?, ?)
4338
5498
  ON CONFLICT DO NOTHING`,
4339
- [randomUUID6(), conditionId, taskId, JSON.stringify({ resolved_at: (/* @__PURE__ */ new Date()).toISOString() })]
5499
+ [randomUUID7(), conditionId, taskId, JSON.stringify({ resolved_at: (/* @__PURE__ */ new Date()).toISOString() })]
4340
5500
  );
4341
5501
  return eventId;
4342
5502
  }
4343
5503
  };
4344
5504
 
4345
5505
  // src/core/task/task-projector.ts
4346
- import { randomUUID as randomUUID7 } from "crypto";
5506
+ import { randomUUID as randomUUID8 } from "crypto";
4347
5507
  var PROJECTOR_NAME = "task_projector";
4348
5508
  var TASK_EVENT_TYPES = [
4349
5509
  "task_created",
@@ -4539,7 +5699,7 @@ var TaskProjector = class {
4539
5699
  * Create blocker edge
4540
5700
  */
4541
5701
  async createBlockerEdge(taskId, blocker, relType) {
4542
- const edgeId = randomUUID7();
5702
+ const edgeId = randomUUID8();
4543
5703
  await dbRun(
4544
5704
  this.db,
4545
5705
  `INSERT INTO edges (edge_id, src_type, src_id, rel_type, dst_type, dst_id, meta_json, created_at)
@@ -4577,7 +5737,7 @@ var TaskProjector = class {
4577
5737
  * Enqueue entity for vectorization
4578
5738
  */
4579
5739
  async enqueueForVectorization(itemId, itemKind) {
4580
- const jobId = randomUUID7();
5740
+ const jobId = randomUUID8();
4581
5741
  const embeddingVersion = "v1";
4582
5742
  await dbRun(
4583
5743
  this.db,
@@ -4697,7 +5857,7 @@ function createSharedEventStore(dbPath) {
4697
5857
  }
4698
5858
 
4699
5859
  // src/core/shared-store.ts
4700
- import { randomUUID as randomUUID8 } from "crypto";
5860
+ import { randomUUID as randomUUID9 } from "crypto";
4701
5861
  var SharedStore = class {
4702
5862
  constructor(sharedEventStore) {
4703
5863
  this.sharedEventStore = sharedEventStore;
@@ -4709,7 +5869,7 @@ var SharedStore = class {
4709
5869
  * Promote a verified troubleshooting entry to shared storage
4710
5870
  */
4711
5871
  async promoteEntry(input) {
4712
- const entryId = randomUUID8();
5872
+ const entryId = randomUUID9();
4713
5873
  await dbRun(
4714
5874
  this.db,
4715
5875
  `INSERT INTO shared_troubleshooting (
@@ -5074,7 +6234,7 @@ function createSharedVectorStore(dbPath) {
5074
6234
  }
5075
6235
 
5076
6236
  // src/core/shared-promoter.ts
5077
- import { randomUUID as randomUUID9 } from "crypto";
6237
+ import { randomUUID as randomUUID10 } from "crypto";
5078
6238
  var SharedPromoter = class {
5079
6239
  constructor(sharedStore, sharedVectorStore, embedder, config) {
5080
6240
  this.sharedStore = sharedStore;
@@ -5142,7 +6302,7 @@ var SharedPromoter = class {
5142
6302
  const embeddingContent = this.createEmbeddingContent(input);
5143
6303
  const embedding = await this.embedder.embed(embeddingContent);
5144
6304
  await this.sharedVectorStore.upsert({
5145
- id: randomUUID9(),
6305
+ id: randomUUID10(),
5146
6306
  entryId,
5147
6307
  entryType: "troubleshooting",
5148
6308
  content: embeddingContent,
@@ -5291,6 +6451,7 @@ export {
5291
6451
  FullDetailSchema,
5292
6452
  GraduationPipeline,
5293
6453
  GraduationResultSchema,
6454
+ GraduationWorker,
5294
6455
  InsightSchema,
5295
6456
  InsightTypeSchema,
5296
6457
  MATCH_THRESHOLDS,
@@ -5311,6 +6472,7 @@ export {
5311
6472
  ProgressiveSearchResultSchema,
5312
6473
  RelationTypeSchema,
5313
6474
  Retriever,
6475
+ SQLiteEventStore,
5314
6476
  SearchIndexItemSchema,
5315
6477
  SessionSchema,
5316
6478
  SharedEntryTypeSchema,
@@ -5320,6 +6482,7 @@ export {
5320
6482
  SharedStoreConfigSchema,
5321
6483
  SharedTroubleshootingEntrySchema,
5322
6484
  SharedVectorStore,
6485
+ SyncWorker,
5323
6486
  TaskBlockersSetPayloadSchema,
5324
6487
  TaskCreatedPayloadSchema,
5325
6488
  TaskCurrentJsonSchema,
@@ -5341,7 +6504,9 @@ export {
5341
6504
  VectorWorkerV2,
5342
6505
  WorkingSetItemSchema,
5343
6506
  createGraduationPipeline,
6507
+ createGraduationWorker,
5344
6508
  createRetriever,
6509
+ createSQLiteDatabase,
5345
6510
  createSharedEventStore,
5346
6511
  createSharedPromoter,
5347
6512
  createSharedStore,
@@ -5358,6 +6523,14 @@ export {
5358
6523
  makeDedupeKey,
5359
6524
  makeEntityCanonicalKey,
5360
6525
  makeTaskEventDedupeKey,
5361
- parseEntityCanonicalKey
6526
+ parseEntityCanonicalKey,
6527
+ sqliteAll,
6528
+ sqliteClose,
6529
+ sqliteExec,
6530
+ sqliteGet,
6531
+ sqliteRun,
6532
+ sqliteTransaction,
6533
+ toDateFromSQLite,
6534
+ toSQLiteTimestamp
5362
6535
  };
5363
6536
  //# sourceMappingURL=index.js.map