claude-memory-layer 1.0.7 → 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 (38) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/.history/package_20260201192048.json +47 -0
  3. package/dist/cli/index.js +569 -39
  4. package/dist/cli/index.js.map +4 -4
  5. package/dist/core/index.js +192 -5
  6. package/dist/core/index.js.map +4 -4
  7. package/dist/hooks/session-end.js +262 -18
  8. package/dist/hooks/session-end.js.map +4 -4
  9. package/dist/hooks/session-start.js +262 -18
  10. package/dist/hooks/session-start.js.map +4 -4
  11. package/dist/hooks/stop.js +262 -18
  12. package/dist/hooks/stop.js.map +4 -4
  13. package/dist/hooks/user-prompt-submit.js +262 -18
  14. package/dist/hooks/user-prompt-submit.js.map +4 -4
  15. package/dist/server/api/index.js +404 -39
  16. package/dist/server/api/index.js.map +4 -4
  17. package/dist/server/index.js +413 -46
  18. package/dist/server/index.js.map +4 -4
  19. package/dist/services/memory-service.js +269 -18
  20. package/dist/services/memory-service.js.map +4 -4
  21. package/dist/ui/index.html +495 -15
  22. package/package.json +2 -1
  23. package/scripts/build.ts +3 -2
  24. package/src/cli/index.ts +226 -0
  25. package/src/core/db-wrapper.ts +8 -1
  26. package/src/core/event-store.ts +52 -3
  27. package/src/core/graduation-worker.ts +171 -0
  28. package/src/core/graduation.ts +15 -2
  29. package/src/core/index.ts +1 -0
  30. package/src/core/retriever.ts +18 -0
  31. package/src/server/api/citations.ts +7 -3
  32. package/src/server/api/events.ts +7 -3
  33. package/src/server/api/search.ts +7 -3
  34. package/src/server/api/sessions.ts +7 -3
  35. package/src/server/api/stats.ts +129 -12
  36. package/src/server/index.ts +18 -9
  37. package/src/services/memory-service.ts +107 -19
  38. package/src/ui/index.html +495 -15
@@ -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;
@@ -3380,6 +3565,7 @@ var MemoryService = class {
3380
3565
  retriever;
3381
3566
  graduation;
3382
3567
  vectorWorker = null;
3568
+ graduationWorker = null;
3383
3569
  initialized = false;
3384
3570
  // Endless Mode components
3385
3571
  workingSetStore = null;
@@ -3394,14 +3580,16 @@ var MemoryService = class {
3394
3580
  sharedPromoter = null;
3395
3581
  sharedStoreConfig = null;
3396
3582
  projectHash = null;
3583
+ readOnly;
3397
3584
  constructor(config) {
3398
3585
  const storagePath = this.expandPath(config.storagePath);
3399
- if (!fs.existsSync(storagePath)) {
3586
+ this.readOnly = config.readOnly ?? false;
3587
+ if (!this.readOnly && !fs.existsSync(storagePath)) {
3400
3588
  fs.mkdirSync(storagePath, { recursive: true });
3401
3589
  }
3402
3590
  this.projectHash = config.projectHash || null;
3403
3591
  this.sharedStoreConfig = config.sharedStoreConfig ?? { enabled: true };
3404
- this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"));
3592
+ this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"), { readOnly: this.readOnly });
3405
3593
  this.vectorStore = new VectorStore(path.join(storagePath, "vectors"));
3406
3594
  this.embedder = config.embeddingModel ? new Embedder(config.embeddingModel) : getDefaultEmbedder();
3407
3595
  this.matcher = getDefaultMatcher();
@@ -3422,19 +3610,27 @@ var MemoryService = class {
3422
3610
  await this.eventStore.initialize();
3423
3611
  await this.vectorStore.initialize();
3424
3612
  await this.embedder.initialize();
3425
- this.vectorWorker = createVectorWorker(
3426
- this.eventStore,
3427
- this.vectorStore,
3428
- this.embedder
3429
- );
3430
- this.vectorWorker.start();
3431
- const savedMode = await this.eventStore.getEndlessConfig("mode");
3432
- if (savedMode === "endless") {
3433
- this.endlessMode = "endless";
3434
- await this.initializeEndlessMode();
3435
- }
3436
- if (this.sharedStoreConfig?.enabled !== false) {
3437
- await this.initializeSharedStore();
3613
+ if (!this.readOnly) {
3614
+ this.vectorWorker = createVectorWorker(
3615
+ this.eventStore,
3616
+ this.vectorStore,
3617
+ this.embedder
3618
+ );
3619
+ this.vectorWorker.start();
3620
+ this.retriever.setGraduationPipeline(this.graduation);
3621
+ this.graduationWorker = createGraduationWorker(
3622
+ this.eventStore,
3623
+ this.graduation
3624
+ );
3625
+ this.graduationWorker.start();
3626
+ const savedMode = await this.eventStore.getEndlessConfig("mode");
3627
+ if (savedMode === "endless") {
3628
+ this.endlessMode = "endless";
3629
+ await this.initializeEndlessMode();
3630
+ }
3631
+ if (this.sharedStoreConfig?.enabled !== false) {
3632
+ await this.initializeSharedStore();
3633
+ }
3438
3634
  }
3439
3635
  this.initialized = true;
3440
3636
  }
@@ -3615,6 +3811,20 @@ var MemoryService = class {
3615
3811
  }
3616
3812
  return 0;
3617
3813
  }
3814
+ /**
3815
+ * Get events by memory level
3816
+ */
3817
+ async getEventsByLevel(level, options) {
3818
+ await this.initialize();
3819
+ return this.eventStore.getEventsByLevel(level, options);
3820
+ }
3821
+ /**
3822
+ * Get memory level for a specific event
3823
+ */
3824
+ async getEventLevel(eventId) {
3825
+ await this.initialize();
3826
+ return this.eventStore.getEventLevel(eventId);
3827
+ }
3618
3828
  /**
3619
3829
  * Format retrieval results as context for Claude
3620
3830
  */
@@ -3797,6 +4007,22 @@ var MemoryService = class {
3797
4007
  return [];
3798
4008
  return this.consolidatedStore.getAll({ limit });
3799
4009
  }
4010
+ /**
4011
+ * Get most accessed consolidated memories
4012
+ */
4013
+ async getMostAccessedMemories(limit = 10) {
4014
+ if (!this.consolidatedStore)
4015
+ return [];
4016
+ return this.consolidatedStore.getMostAccessed(limit);
4017
+ }
4018
+ /**
4019
+ * Mark a consolidated memory as accessed
4020
+ */
4021
+ async markMemoryAccessed(memoryId) {
4022
+ if (!this.consolidatedStore)
4023
+ return;
4024
+ await this.consolidatedStore.markAccessed(memoryId);
4025
+ }
3800
4026
  /**
3801
4027
  * Calculate continuity score for current context
3802
4028
  */
@@ -3884,10 +4110,28 @@ var MemoryService = class {
3884
4110
  }
3885
4111
  return parts.join("\n");
3886
4112
  }
4113
+ /**
4114
+ * Force a graduation evaluation run
4115
+ */
4116
+ async forceGraduation() {
4117
+ if (!this.graduationWorker) {
4118
+ return { evaluated: 0, graduated: 0, byLevel: {} };
4119
+ }
4120
+ return this.graduationWorker.forceRun();
4121
+ }
4122
+ /**
4123
+ * Record access to a memory event (for graduation scoring)
4124
+ */
4125
+ recordMemoryAccess(eventId, sessionId, confidence = 1) {
4126
+ this.graduation.recordAccess(eventId, sessionId, confidence);
4127
+ }
3887
4128
  /**
3888
4129
  * Shutdown service
3889
4130
  */
3890
4131
  async shutdown() {
4132
+ if (this.graduationWorker) {
4133
+ this.graduationWorker.stop();
4134
+ }
3891
4135
  if (this.consolidationWorker) {
3892
4136
  this.consolidationWorker.stop();
3893
4137
  }