claude-memory-layer 1.0.6 → 1.0.8

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 (45) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/.claude-plugin/plugin.json +3 -3
  3. package/.history/package_20260201142928.json +46 -0
  4. package/.history/package_20260201192048.json +47 -0
  5. package/README.md +26 -26
  6. package/dist/.claude-plugin/plugin.json +3 -3
  7. package/dist/cli/index.js +1109 -25
  8. package/dist/cli/index.js.map +4 -4
  9. package/dist/core/index.js +192 -5
  10. package/dist/core/index.js.map +4 -4
  11. package/dist/hooks/session-end.js +262 -18
  12. package/dist/hooks/session-end.js.map +4 -4
  13. package/dist/hooks/session-start.js +262 -18
  14. package/dist/hooks/session-start.js.map +4 -4
  15. package/dist/hooks/stop.js +262 -18
  16. package/dist/hooks/stop.js.map +4 -4
  17. package/dist/hooks/user-prompt-submit.js +262 -18
  18. package/dist/hooks/user-prompt-submit.js.map +4 -4
  19. package/dist/server/api/index.js +4728 -0
  20. package/dist/server/api/index.js.map +7 -0
  21. package/dist/server/index.js +4790 -0
  22. package/dist/server/index.js.map +7 -0
  23. package/dist/services/memory-service.js +269 -18
  24. package/dist/services/memory-service.js.map +4 -4
  25. package/dist/ui/index.html +1225 -0
  26. package/package.json +4 -2
  27. package/scripts/build.ts +33 -3
  28. package/src/cli/index.ts +311 -6
  29. package/src/core/db-wrapper.ts +8 -1
  30. package/src/core/event-store.ts +52 -3
  31. package/src/core/graduation-worker.ts +171 -0
  32. package/src/core/graduation.ts +15 -2
  33. package/src/core/index.ts +1 -0
  34. package/src/core/retriever.ts +18 -0
  35. package/src/core/types.ts +1 -1
  36. package/src/mcp/index.ts +2 -2
  37. package/src/mcp/tools.ts +1 -1
  38. package/src/server/api/citations.ts +7 -3
  39. package/src/server/api/events.ts +7 -3
  40. package/src/server/api/search.ts +7 -3
  41. package/src/server/api/sessions.ts +7 -3
  42. package/src/server/api/stats.ts +175 -5
  43. package/src/server/index.ts +18 -9
  44. package/src/services/memory-service.ts +107 -19
  45. package/src/ui/index.html +1225 -0
@@ -67,7 +67,10 @@ function toDate(value) {
67
67
  return new Date(value);
68
68
  return new Date(String(value));
69
69
  }
70
- function createDatabase(path2) {
70
+ function createDatabase(path2, options) {
71
+ if (options?.readOnly) {
72
+ return new duckdb.Database(path2, { access_mode: "READ_ONLY" });
73
+ }
71
74
  return new duckdb.Database(path2);
72
75
  }
73
76
  function dbRun(db, sql, params = []) {
@@ -121,18 +124,24 @@ function dbClose(db) {
121
124
 
122
125
  // src/core/event-store.ts
123
126
  var EventStore = class {
124
- constructor(dbPath) {
127
+ constructor(dbPath, options) {
125
128
  this.dbPath = dbPath;
126
- this.db = createDatabase(dbPath);
129
+ this.readOnly = options?.readOnly ?? false;
130
+ this.db = createDatabase(dbPath, { readOnly: this.readOnly });
127
131
  }
128
132
  db;
129
133
  initialized = false;
134
+ readOnly;
130
135
  /**
131
136
  * Initialize database schema
132
137
  */
133
138
  async initialize() {
134
139
  if (this.initialized)
135
140
  return;
141
+ if (this.readOnly) {
142
+ this.initialized = true;
143
+ return;
144
+ }
136
145
  await dbRun(this.db, `
137
146
  CREATE TABLE IF NOT EXISTS events (
138
147
  id VARCHAR PRIMARY KEY,
@@ -609,6 +618,36 @@ var EventStore = class {
609
618
  );
610
619
  return rows;
611
620
  }
621
+ /**
622
+ * Get events by memory level
623
+ */
624
+ async getEventsByLevel(level, options) {
625
+ await this.initialize();
626
+ const limit = options?.limit || 50;
627
+ const offset = options?.offset || 0;
628
+ const rows = await dbAll(
629
+ this.db,
630
+ `SELECT e.* FROM events e
631
+ INNER JOIN memory_levels ml ON e.id = ml.event_id
632
+ WHERE ml.level = ?
633
+ ORDER BY e.timestamp DESC
634
+ LIMIT ? OFFSET ?`,
635
+ [level, limit, offset]
636
+ );
637
+ return rows.map((row) => this.rowToEvent(row));
638
+ }
639
+ /**
640
+ * Get memory level for a specific event
641
+ */
642
+ async getEventLevel(eventId) {
643
+ await this.initialize();
644
+ const rows = await dbAll(
645
+ this.db,
646
+ `SELECT level FROM memory_levels WHERE event_id = ?`,
647
+ [eventId]
648
+ );
649
+ return rows.length > 0 ? rows[0].level : null;
650
+ }
612
651
  // ============================================================
613
652
  // Endless Mode Helper Methods
614
653
  // ============================================================
@@ -1191,6 +1230,7 @@ var Retriever = class {
1191
1230
  matcher;
1192
1231
  sharedStore;
1193
1232
  sharedVectorStore;
1233
+ graduation;
1194
1234
  constructor(eventStore, vectorStore, embedder, matcher, sharedOptions) {
1195
1235
  this.eventStore = eventStore;
1196
1236
  this.vectorStore = vectorStore;
@@ -1199,6 +1239,12 @@ var Retriever = class {
1199
1239
  this.sharedStore = sharedOptions?.sharedStore;
1200
1240
  this.sharedVectorStore = sharedOptions?.sharedVectorStore;
1201
1241
  }
1242
+ /**
1243
+ * Set graduation pipeline for access tracking
1244
+ */
1245
+ setGraduationPipeline(graduation) {
1246
+ this.graduation = graduation;
1247
+ }
1202
1248
  /**
1203
1249
  * Set shared stores after construction
1204
1250
  */
@@ -1321,6 +1367,13 @@ var Retriever = class {
1321
1367
  const event = await this.eventStore.getEvent(result.eventId);
1322
1368
  if (!event)
1323
1369
  continue;
1370
+ if (this.graduation) {
1371
+ this.graduation.recordAccess(
1372
+ event.id,
1373
+ options.sessionId || "unknown",
1374
+ result.score
1375
+ );
1376
+ }
1324
1377
  let sessionContext;
1325
1378
  if (options.includeSessionContext) {
1326
1379
  sessionContext = await this.getSessionContext(event.sessionId, event.id);
@@ -1442,15 +1495,26 @@ var GraduationPipeline = class {
1442
1495
  L3toL4: { ...DEFAULT_CRITERIA.L3toL4, ...criteria.L3toL4 }
1443
1496
  };
1444
1497
  }
1498
+ // Track which sessions have accessed each event
1499
+ sessionAccesses = /* @__PURE__ */ new Map();
1445
1500
  /**
1446
1501
  * Record an access to an event (used for graduation scoring)
1447
1502
  */
1448
1503
  recordAccess(eventId, fromSessionId, confidence = 1) {
1449
1504
  const existing = this.metrics.get(eventId);
1505
+ if (!this.sessionAccesses.has(eventId)) {
1506
+ this.sessionAccesses.set(eventId, /* @__PURE__ */ new Set());
1507
+ }
1508
+ const sessions = this.sessionAccesses.get(eventId);
1509
+ const isNewSession = !sessions.has(fromSessionId);
1510
+ sessions.add(fromSessionId);
1450
1511
  if (existing) {
1451
1512
  existing.accessCount++;
1452
1513
  existing.lastAccessed = /* @__PURE__ */ new Date();
1453
1514
  existing.confidence = Math.max(existing.confidence, confidence);
1515
+ if (isNewSession && sessions.size > 1) {
1516
+ existing.crossSessionRefs = sessions.size - 1;
1517
+ }
1454
1518
  } else {
1455
1519
  this.metrics.set(eventId, {
1456
1520
  eventId,
@@ -3338,6 +3402,127 @@ function createContinuityManager(eventStore, config) {
3338
3402
  return new ContinuityManager(eventStore, config);
3339
3403
  }
3340
3404
 
3405
+ // src/core/graduation-worker.ts
3406
+ var DEFAULT_CONFIG4 = {
3407
+ evaluationIntervalMs: 3e5,
3408
+ // 5 minutes
3409
+ batchSize: 50,
3410
+ cooldownMs: 36e5
3411
+ // 1 hour cooldown between evaluations
3412
+ };
3413
+ var GraduationWorker = class {
3414
+ constructor(eventStore, graduation, config = DEFAULT_CONFIG4) {
3415
+ this.eventStore = eventStore;
3416
+ this.graduation = graduation;
3417
+ this.config = config;
3418
+ }
3419
+ running = false;
3420
+ timeout = null;
3421
+ lastEvaluated = /* @__PURE__ */ new Map();
3422
+ /**
3423
+ * Start the graduation worker
3424
+ */
3425
+ start() {
3426
+ if (this.running)
3427
+ return;
3428
+ this.running = true;
3429
+ this.scheduleNext();
3430
+ }
3431
+ /**
3432
+ * Stop the graduation worker
3433
+ */
3434
+ stop() {
3435
+ this.running = false;
3436
+ if (this.timeout) {
3437
+ clearTimeout(this.timeout);
3438
+ this.timeout = null;
3439
+ }
3440
+ }
3441
+ /**
3442
+ * Check if currently running
3443
+ */
3444
+ isRunning() {
3445
+ return this.running;
3446
+ }
3447
+ /**
3448
+ * Force a graduation evaluation run
3449
+ */
3450
+ async forceRun() {
3451
+ return await this.runGraduation();
3452
+ }
3453
+ /**
3454
+ * Schedule the next graduation check
3455
+ */
3456
+ scheduleNext() {
3457
+ if (!this.running)
3458
+ return;
3459
+ this.timeout = setTimeout(
3460
+ () => this.run(),
3461
+ this.config.evaluationIntervalMs
3462
+ );
3463
+ }
3464
+ /**
3465
+ * Run graduation evaluation
3466
+ */
3467
+ async run() {
3468
+ if (!this.running)
3469
+ return;
3470
+ try {
3471
+ await this.runGraduation();
3472
+ } catch (error) {
3473
+ console.error("Graduation error:", error);
3474
+ }
3475
+ this.scheduleNext();
3476
+ }
3477
+ /**
3478
+ * Perform graduation evaluation across all levels
3479
+ */
3480
+ async runGraduation() {
3481
+ const result = {
3482
+ evaluated: 0,
3483
+ graduated: 0,
3484
+ byLevel: {}
3485
+ };
3486
+ const levels = ["L0", "L1", "L2", "L3"];
3487
+ const now = Date.now();
3488
+ for (const level of levels) {
3489
+ const events = await this.eventStore.getEventsByLevel(level, {
3490
+ limit: this.config.batchSize
3491
+ });
3492
+ let levelGraduated = 0;
3493
+ for (const event of events) {
3494
+ const lastEval = this.lastEvaluated.get(event.id);
3495
+ if (lastEval && now - lastEval < this.config.cooldownMs) {
3496
+ continue;
3497
+ }
3498
+ result.evaluated++;
3499
+ this.lastEvaluated.set(event.id, now);
3500
+ const gradResult = await this.graduation.evaluateGraduation(event.id, level);
3501
+ if (gradResult.success) {
3502
+ result.graduated++;
3503
+ levelGraduated++;
3504
+ }
3505
+ }
3506
+ if (levelGraduated > 0) {
3507
+ result.byLevel[level] = levelGraduated;
3508
+ }
3509
+ }
3510
+ if (this.lastEvaluated.size > 1e3) {
3511
+ const entries = Array.from(this.lastEvaluated.entries());
3512
+ entries.sort((a, b) => b[1] - a[1]);
3513
+ this.lastEvaluated = new Map(entries.slice(0, 1e3));
3514
+ }
3515
+ return result;
3516
+ }
3517
+ };
3518
+ function createGraduationWorker(eventStore, graduation, config) {
3519
+ return new GraduationWorker(
3520
+ eventStore,
3521
+ graduation,
3522
+ { ...DEFAULT_CONFIG4, ...config }
3523
+ );
3524
+ }
3525
+
3341
3526
  // src/services/memory-service.ts
3342
3527
  function normalizePath(projectPath) {
3343
3528
  const expanded = projectPath.startsWith("~") ? path.join(os.homedir(), projectPath.slice(1)) : projectPath;
@@ -3401,6 +3586,7 @@ var MemoryService = class {
3401
3586
  retriever;
3402
3587
  graduation;
3403
3588
  vectorWorker = null;
3589
+ graduationWorker = null;
3404
3590
  initialized = false;
3405
3591
  // Endless Mode components
3406
3592
  workingSetStore = null;
@@ -3415,14 +3601,16 @@ var MemoryService = class {
3415
3601
  sharedPromoter = null;
3416
3602
  sharedStoreConfig = null;
3417
3603
  projectHash = null;
3604
+ readOnly;
3418
3605
  constructor(config) {
3419
3606
  const storagePath = this.expandPath(config.storagePath);
3420
- if (!fs.existsSync(storagePath)) {
3607
+ this.readOnly = config.readOnly ?? false;
3608
+ if (!this.readOnly && !fs.existsSync(storagePath)) {
3421
3609
  fs.mkdirSync(storagePath, { recursive: true });
3422
3610
  }
3423
3611
  this.projectHash = config.projectHash || null;
3424
3612
  this.sharedStoreConfig = config.sharedStoreConfig ?? { enabled: true };
3425
- this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"));
3613
+ this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"), { readOnly: this.readOnly });
3426
3614
  this.vectorStore = new VectorStore(path.join(storagePath, "vectors"));
3427
3615
  this.embedder = config.embeddingModel ? new Embedder(config.embeddingModel) : getDefaultEmbedder();
3428
3616
  this.matcher = getDefaultMatcher();
@@ -3443,19 +3631,27 @@ var MemoryService = class {
3443
3631
  await this.eventStore.initialize();
3444
3632
  await this.vectorStore.initialize();
3445
3633
  await this.embedder.initialize();
3446
- this.vectorWorker = createVectorWorker(
3447
- this.eventStore,
3448
- this.vectorStore,
3449
- this.embedder
3450
- );
3451
- this.vectorWorker.start();
3452
- const savedMode = await this.eventStore.getEndlessConfig("mode");
3453
- if (savedMode === "endless") {
3454
- this.endlessMode = "endless";
3455
- await this.initializeEndlessMode();
3456
- }
3457
- if (this.sharedStoreConfig?.enabled !== false) {
3458
- await this.initializeSharedStore();
3634
+ if (!this.readOnly) {
3635
+ this.vectorWorker = createVectorWorker(
3636
+ this.eventStore,
3637
+ this.vectorStore,
3638
+ this.embedder
3639
+ );
3640
+ this.vectorWorker.start();
3641
+ this.retriever.setGraduationPipeline(this.graduation);
3642
+ this.graduationWorker = createGraduationWorker(
3643
+ this.eventStore,
3644
+ this.graduation
3645
+ );
3646
+ this.graduationWorker.start();
3647
+ const savedMode = await this.eventStore.getEndlessConfig("mode");
3648
+ if (savedMode === "endless") {
3649
+ this.endlessMode = "endless";
3650
+ await this.initializeEndlessMode();
3651
+ }
3652
+ if (this.sharedStoreConfig?.enabled !== false) {
3653
+ await this.initializeSharedStore();
3654
+ }
3459
3655
  }
3460
3656
  this.initialized = true;
3461
3657
  }
@@ -3636,6 +3832,20 @@ var MemoryService = class {
3636
3832
  }
3637
3833
  return 0;
3638
3834
  }
3835
+ /**
3836
+ * Get events by memory level
3837
+ */
3838
+ async getEventsByLevel(level, options) {
3839
+ await this.initialize();
3840
+ return this.eventStore.getEventsByLevel(level, options);
3841
+ }
3842
+ /**
3843
+ * Get memory level for a specific event
3844
+ */
3845
+ async getEventLevel(eventId) {
3846
+ await this.initialize();
3847
+ return this.eventStore.getEventLevel(eventId);
3848
+ }
3639
3849
  /**
3640
3850
  * Format retrieval results as context for Claude
3641
3851
  */
@@ -3818,6 +4028,22 @@ var MemoryService = class {
3818
4028
  return [];
3819
4029
  return this.consolidatedStore.getAll({ limit });
3820
4030
  }
4031
+ /**
4032
+ * Get most accessed consolidated memories
4033
+ */
4034
+ async getMostAccessedMemories(limit = 10) {
4035
+ if (!this.consolidatedStore)
4036
+ return [];
4037
+ return this.consolidatedStore.getMostAccessed(limit);
4038
+ }
4039
+ /**
4040
+ * Mark a consolidated memory as accessed
4041
+ */
4042
+ async markMemoryAccessed(memoryId) {
4043
+ if (!this.consolidatedStore)
4044
+ return;
4045
+ await this.consolidatedStore.markAccessed(memoryId);
4046
+ }
3821
4047
  /**
3822
4048
  * Calculate continuity score for current context
3823
4049
  */
@@ -3905,10 +4131,28 @@ var MemoryService = class {
3905
4131
  }
3906
4132
  return parts.join("\n");
3907
4133
  }
4134
+ /**
4135
+ * Force a graduation evaluation run
4136
+ */
4137
+ async forceGraduation() {
4138
+ if (!this.graduationWorker) {
4139
+ return { evaluated: 0, graduated: 0, byLevel: {} };
4140
+ }
4141
+ return this.graduationWorker.forceRun();
4142
+ }
4143
+ /**
4144
+ * Record access to a memory event (for graduation scoring)
4145
+ */
4146
+ recordMemoryAccess(eventId, sessionId, confidence = 1) {
4147
+ this.graduation.recordAccess(eventId, sessionId, confidence);
4148
+ }
3908
4149
  /**
3909
4150
  * Shutdown service
3910
4151
  */
3911
4152
  async shutdown() {
4153
+ if (this.graduationWorker) {
4154
+ this.graduationWorker.stop();
4155
+ }
3912
4156
  if (this.consolidationWorker) {
3913
4157
  this.consolidationWorker.stop();
3914
4158
  }