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
@@ -66,7 +66,10 @@ function toDate(value) {
66
66
  return new Date(value);
67
67
  return new Date(String(value));
68
68
  }
69
- function createDatabase(path2) {
69
+ function createDatabase(path2, options) {
70
+ if (options?.readOnly) {
71
+ return new duckdb.Database(path2, { access_mode: "READ_ONLY" });
72
+ }
70
73
  return new duckdb.Database(path2);
71
74
  }
72
75
  function dbRun(db, sql, params = []) {
@@ -120,18 +123,24 @@ function dbClose(db) {
120
123
 
121
124
  // src/core/event-store.ts
122
125
  var EventStore = class {
123
- constructor(dbPath) {
126
+ constructor(dbPath, options) {
124
127
  this.dbPath = dbPath;
125
- this.db = createDatabase(dbPath);
128
+ this.readOnly = options?.readOnly ?? false;
129
+ this.db = createDatabase(dbPath, { readOnly: this.readOnly });
126
130
  }
127
131
  db;
128
132
  initialized = false;
133
+ readOnly;
129
134
  /**
130
135
  * Initialize database schema
131
136
  */
132
137
  async initialize() {
133
138
  if (this.initialized)
134
139
  return;
140
+ if (this.readOnly) {
141
+ this.initialized = true;
142
+ return;
143
+ }
135
144
  await dbRun(this.db, `
136
145
  CREATE TABLE IF NOT EXISTS events (
137
146
  id VARCHAR PRIMARY KEY,
@@ -608,6 +617,36 @@ var EventStore = class {
608
617
  );
609
618
  return rows;
610
619
  }
620
+ /**
621
+ * Get events by memory level
622
+ */
623
+ async getEventsByLevel(level, options) {
624
+ await this.initialize();
625
+ const limit = options?.limit || 50;
626
+ const offset = options?.offset || 0;
627
+ const rows = await dbAll(
628
+ this.db,
629
+ `SELECT e.* FROM events e
630
+ INNER JOIN memory_levels ml ON e.id = ml.event_id
631
+ WHERE ml.level = ?
632
+ ORDER BY e.timestamp DESC
633
+ LIMIT ? OFFSET ?`,
634
+ [level, limit, offset]
635
+ );
636
+ return rows.map((row) => this.rowToEvent(row));
637
+ }
638
+ /**
639
+ * Get memory level for a specific event
640
+ */
641
+ async getEventLevel(eventId) {
642
+ await this.initialize();
643
+ const rows = await dbAll(
644
+ this.db,
645
+ `SELECT level FROM memory_levels WHERE event_id = ?`,
646
+ [eventId]
647
+ );
648
+ return rows.length > 0 ? rows[0].level : null;
649
+ }
611
650
  // ============================================================
612
651
  // Endless Mode Helper Methods
613
652
  // ============================================================
@@ -1190,6 +1229,7 @@ var Retriever = class {
1190
1229
  matcher;
1191
1230
  sharedStore;
1192
1231
  sharedVectorStore;
1232
+ graduation;
1193
1233
  constructor(eventStore, vectorStore, embedder, matcher, sharedOptions) {
1194
1234
  this.eventStore = eventStore;
1195
1235
  this.vectorStore = vectorStore;
@@ -1198,6 +1238,12 @@ var Retriever = class {
1198
1238
  this.sharedStore = sharedOptions?.sharedStore;
1199
1239
  this.sharedVectorStore = sharedOptions?.sharedVectorStore;
1200
1240
  }
1241
+ /**
1242
+ * Set graduation pipeline for access tracking
1243
+ */
1244
+ setGraduationPipeline(graduation) {
1245
+ this.graduation = graduation;
1246
+ }
1201
1247
  /**
1202
1248
  * Set shared stores after construction
1203
1249
  */
@@ -1320,6 +1366,13 @@ var Retriever = class {
1320
1366
  const event = await this.eventStore.getEvent(result.eventId);
1321
1367
  if (!event)
1322
1368
  continue;
1369
+ if (this.graduation) {
1370
+ this.graduation.recordAccess(
1371
+ event.id,
1372
+ options.sessionId || "unknown",
1373
+ result.score
1374
+ );
1375
+ }
1323
1376
  let sessionContext;
1324
1377
  if (options.includeSessionContext) {
1325
1378
  sessionContext = await this.getSessionContext(event.sessionId, event.id);
@@ -1441,15 +1494,26 @@ var GraduationPipeline = class {
1441
1494
  L3toL4: { ...DEFAULT_CRITERIA.L3toL4, ...criteria.L3toL4 }
1442
1495
  };
1443
1496
  }
1497
+ // Track which sessions have accessed each event
1498
+ sessionAccesses = /* @__PURE__ */ new Map();
1444
1499
  /**
1445
1500
  * Record an access to an event (used for graduation scoring)
1446
1501
  */
1447
1502
  recordAccess(eventId, fromSessionId, confidence = 1) {
1448
1503
  const existing = this.metrics.get(eventId);
1504
+ if (!this.sessionAccesses.has(eventId)) {
1505
+ this.sessionAccesses.set(eventId, /* @__PURE__ */ new Set());
1506
+ }
1507
+ const sessions = this.sessionAccesses.get(eventId);
1508
+ const isNewSession = !sessions.has(fromSessionId);
1509
+ sessions.add(fromSessionId);
1449
1510
  if (existing) {
1450
1511
  existing.accessCount++;
1451
1512
  existing.lastAccessed = /* @__PURE__ */ new Date();
1452
1513
  existing.confidence = Math.max(existing.confidence, confidence);
1514
+ if (isNewSession && sessions.size > 1) {
1515
+ existing.crossSessionRefs = sessions.size - 1;
1516
+ }
1453
1517
  } else {
1454
1518
  this.metrics.set(eventId, {
1455
1519
  eventId,
@@ -3337,6 +3401,127 @@ function createContinuityManager(eventStore, config) {
3337
3401
  return new ContinuityManager(eventStore, config);
3338
3402
  }
3339
3403
 
3404
+ // src/core/graduation-worker.ts
3405
+ var DEFAULT_CONFIG4 = {
3406
+ evaluationIntervalMs: 3e5,
3407
+ // 5 minutes
3408
+ batchSize: 50,
3409
+ cooldownMs: 36e5
3410
+ // 1 hour cooldown between evaluations
3411
+ };
3412
+ var GraduationWorker = class {
3413
+ constructor(eventStore, graduation, config = DEFAULT_CONFIG4) {
3414
+ this.eventStore = eventStore;
3415
+ this.graduation = graduation;
3416
+ this.config = config;
3417
+ }
3418
+ running = false;
3419
+ timeout = null;
3420
+ lastEvaluated = /* @__PURE__ */ new Map();
3421
+ /**
3422
+ * Start the graduation worker
3423
+ */
3424
+ start() {
3425
+ if (this.running)
3426
+ return;
3427
+ this.running = true;
3428
+ this.scheduleNext();
3429
+ }
3430
+ /**
3431
+ * Stop the graduation worker
3432
+ */
3433
+ stop() {
3434
+ this.running = false;
3435
+ if (this.timeout) {
3436
+ clearTimeout(this.timeout);
3437
+ this.timeout = null;
3438
+ }
3439
+ }
3440
+ /**
3441
+ * Check if currently running
3442
+ */
3443
+ isRunning() {
3444
+ return this.running;
3445
+ }
3446
+ /**
3447
+ * Force a graduation evaluation run
3448
+ */
3449
+ async forceRun() {
3450
+ return await this.runGraduation();
3451
+ }
3452
+ /**
3453
+ * Schedule the next graduation check
3454
+ */
3455
+ scheduleNext() {
3456
+ if (!this.running)
3457
+ return;
3458
+ this.timeout = setTimeout(
3459
+ () => this.run(),
3460
+ this.config.evaluationIntervalMs
3461
+ );
3462
+ }
3463
+ /**
3464
+ * Run graduation evaluation
3465
+ */
3466
+ async run() {
3467
+ if (!this.running)
3468
+ return;
3469
+ try {
3470
+ await this.runGraduation();
3471
+ } catch (error) {
3472
+ console.error("Graduation error:", error);
3473
+ }
3474
+ this.scheduleNext();
3475
+ }
3476
+ /**
3477
+ * Perform graduation evaluation across all levels
3478
+ */
3479
+ async runGraduation() {
3480
+ const result = {
3481
+ evaluated: 0,
3482
+ graduated: 0,
3483
+ byLevel: {}
3484
+ };
3485
+ const levels = ["L0", "L1", "L2", "L3"];
3486
+ const now = Date.now();
3487
+ for (const level of levels) {
3488
+ const events = await this.eventStore.getEventsByLevel(level, {
3489
+ limit: this.config.batchSize
3490
+ });
3491
+ let levelGraduated = 0;
3492
+ for (const event of events) {
3493
+ const lastEval = this.lastEvaluated.get(event.id);
3494
+ if (lastEval && now - lastEval < this.config.cooldownMs) {
3495
+ continue;
3496
+ }
3497
+ result.evaluated++;
3498
+ this.lastEvaluated.set(event.id, now);
3499
+ const gradResult = await this.graduation.evaluateGraduation(event.id, level);
3500
+ if (gradResult.success) {
3501
+ result.graduated++;
3502
+ levelGraduated++;
3503
+ }
3504
+ }
3505
+ if (levelGraduated > 0) {
3506
+ result.byLevel[level] = levelGraduated;
3507
+ }
3508
+ }
3509
+ if (this.lastEvaluated.size > 1e3) {
3510
+ const entries = Array.from(this.lastEvaluated.entries());
3511
+ entries.sort((a, b) => b[1] - a[1]);
3512
+ this.lastEvaluated = new Map(entries.slice(0, 1e3));
3513
+ }
3514
+ return result;
3515
+ }
3516
+ };
3517
+ function createGraduationWorker(eventStore, graduation, config) {
3518
+ return new GraduationWorker(
3519
+ eventStore,
3520
+ graduation,
3521
+ { ...DEFAULT_CONFIG4, ...config }
3522
+ );
3523
+ }
3524
+
3340
3525
  // src/services/memory-service.ts
3341
3526
  function normalizePath(projectPath) {
3342
3527
  const expanded = projectPath.startsWith("~") ? path.join(os.homedir(), projectPath.slice(1)) : projectPath;
@@ -3404,6 +3589,7 @@ var MemoryService = class {
3404
3589
  retriever;
3405
3590
  graduation;
3406
3591
  vectorWorker = null;
3592
+ graduationWorker = null;
3407
3593
  initialized = false;
3408
3594
  // Endless Mode components
3409
3595
  workingSetStore = null;
@@ -3418,14 +3604,16 @@ var MemoryService = class {
3418
3604
  sharedPromoter = null;
3419
3605
  sharedStoreConfig = null;
3420
3606
  projectHash = null;
3607
+ readOnly;
3421
3608
  constructor(config) {
3422
3609
  const storagePath = this.expandPath(config.storagePath);
3423
- if (!fs.existsSync(storagePath)) {
3610
+ this.readOnly = config.readOnly ?? false;
3611
+ if (!this.readOnly && !fs.existsSync(storagePath)) {
3424
3612
  fs.mkdirSync(storagePath, { recursive: true });
3425
3613
  }
3426
3614
  this.projectHash = config.projectHash || null;
3427
3615
  this.sharedStoreConfig = config.sharedStoreConfig ?? { enabled: true };
3428
- this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"));
3616
+ this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"), { readOnly: this.readOnly });
3429
3617
  this.vectorStore = new VectorStore(path.join(storagePath, "vectors"));
3430
3618
  this.embedder = config.embeddingModel ? new Embedder(config.embeddingModel) : getDefaultEmbedder();
3431
3619
  this.matcher = getDefaultMatcher();
@@ -3446,19 +3634,27 @@ var MemoryService = class {
3446
3634
  await this.eventStore.initialize();
3447
3635
  await this.vectorStore.initialize();
3448
3636
  await this.embedder.initialize();
3449
- this.vectorWorker = createVectorWorker(
3450
- this.eventStore,
3451
- this.vectorStore,
3452
- this.embedder
3453
- );
3454
- this.vectorWorker.start();
3455
- const savedMode = await this.eventStore.getEndlessConfig("mode");
3456
- if (savedMode === "endless") {
3457
- this.endlessMode = "endless";
3458
- await this.initializeEndlessMode();
3459
- }
3460
- if (this.sharedStoreConfig?.enabled !== false) {
3461
- await this.initializeSharedStore();
3637
+ if (!this.readOnly) {
3638
+ this.vectorWorker = createVectorWorker(
3639
+ this.eventStore,
3640
+ this.vectorStore,
3641
+ this.embedder
3642
+ );
3643
+ this.vectorWorker.start();
3644
+ this.retriever.setGraduationPipeline(this.graduation);
3645
+ this.graduationWorker = createGraduationWorker(
3646
+ this.eventStore,
3647
+ this.graduation
3648
+ );
3649
+ this.graduationWorker.start();
3650
+ const savedMode = await this.eventStore.getEndlessConfig("mode");
3651
+ if (savedMode === "endless") {
3652
+ this.endlessMode = "endless";
3653
+ await this.initializeEndlessMode();
3654
+ }
3655
+ if (this.sharedStoreConfig?.enabled !== false) {
3656
+ await this.initializeSharedStore();
3657
+ }
3462
3658
  }
3463
3659
  this.initialized = true;
3464
3660
  }
@@ -3639,6 +3835,20 @@ var MemoryService = class {
3639
3835
  }
3640
3836
  return 0;
3641
3837
  }
3838
+ /**
3839
+ * Get events by memory level
3840
+ */
3841
+ async getEventsByLevel(level, options) {
3842
+ await this.initialize();
3843
+ return this.eventStore.getEventsByLevel(level, options);
3844
+ }
3845
+ /**
3846
+ * Get memory level for a specific event
3847
+ */
3848
+ async getEventLevel(eventId) {
3849
+ await this.initialize();
3850
+ return this.eventStore.getEventLevel(eventId);
3851
+ }
3642
3852
  /**
3643
3853
  * Format retrieval results as context for Claude
3644
3854
  */
@@ -3821,6 +4031,22 @@ var MemoryService = class {
3821
4031
  return [];
3822
4032
  return this.consolidatedStore.getAll({ limit });
3823
4033
  }
4034
+ /**
4035
+ * Get most accessed consolidated memories
4036
+ */
4037
+ async getMostAccessedMemories(limit = 10) {
4038
+ if (!this.consolidatedStore)
4039
+ return [];
4040
+ return this.consolidatedStore.getMostAccessed(limit);
4041
+ }
4042
+ /**
4043
+ * Mark a consolidated memory as accessed
4044
+ */
4045
+ async markMemoryAccessed(memoryId) {
4046
+ if (!this.consolidatedStore)
4047
+ return;
4048
+ await this.consolidatedStore.markAccessed(memoryId);
4049
+ }
3824
4050
  /**
3825
4051
  * Calculate continuity score for current context
3826
4052
  */
@@ -3908,10 +4134,28 @@ var MemoryService = class {
3908
4134
  }
3909
4135
  return parts.join("\n");
3910
4136
  }
4137
+ /**
4138
+ * Force a graduation evaluation run
4139
+ */
4140
+ async forceGraduation() {
4141
+ if (!this.graduationWorker) {
4142
+ return { evaluated: 0, graduated: 0, byLevel: {} };
4143
+ }
4144
+ return this.graduationWorker.forceRun();
4145
+ }
4146
+ /**
4147
+ * Record access to a memory event (for graduation scoring)
4148
+ */
4149
+ recordMemoryAccess(eventId, sessionId, confidence = 1) {
4150
+ this.graduation.recordAccess(eventId, sessionId, confidence);
4151
+ }
3911
4152
  /**
3912
4153
  * Shutdown service
3913
4154
  */
3914
4155
  async shutdown() {
4156
+ if (this.graduationWorker) {
4157
+ this.graduationWorker.stop();
4158
+ }
3915
4159
  if (this.consolidationWorker) {
3916
4160
  this.consolidationWorker.stop();
3917
4161
  }
@@ -3943,6 +4187,12 @@ function getDefaultMemoryService() {
3943
4187
  }
3944
4188
  return serviceCache.get(GLOBAL_KEY);
3945
4189
  }
4190
+ function getReadOnlyMemoryService() {
4191
+ return new MemoryService({
4192
+ storagePath: "~/.claude-code/memory",
4193
+ readOnly: true
4194
+ });
4195
+ }
3946
4196
  function getMemoryServiceForProject(projectPath, sharedStoreConfig) {
3947
4197
  const hash = hashProjectPath(projectPath);
3948
4198
  if (!serviceCache.has(hash)) {
@@ -3972,6 +4222,7 @@ export {
3972
4222
  getMemoryServiceForProject,
3973
4223
  getMemoryServiceForSession,
3974
4224
  getProjectStoragePath,
4225
+ getReadOnlyMemoryService,
3975
4226
  getSessionProject,
3976
4227
  hashProjectPath,
3977
4228
  registerSession