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.
- package/.claude/settings.local.json +4 -1
- package/.history/package_20260201192048.json +47 -0
- package/dist/cli/index.js +569 -39
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +192 -5
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/session-end.js +262 -18
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +262 -18
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +262 -18
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +262 -18
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/server/api/index.js +404 -39
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +413 -46
- package/dist/server/index.js.map +4 -4
- package/dist/services/memory-service.js +269 -18
- package/dist/services/memory-service.js.map +4 -4
- package/dist/ui/index.html +495 -15
- package/package.json +2 -1
- package/scripts/build.ts +3 -2
- package/src/cli/index.ts +226 -0
- package/src/core/db-wrapper.ts +8 -1
- package/src/core/event-store.ts +52 -3
- package/src/core/graduation-worker.ts +171 -0
- package/src/core/graduation.ts +15 -2
- package/src/core/index.ts +1 -0
- package/src/core/retriever.ts +18 -0
- package/src/server/api/citations.ts +7 -3
- package/src/server/api/events.ts +7 -3
- package/src/server/api/search.ts +7 -3
- package/src/server/api/sessions.ts +7 -3
- package/src/server/api/stats.ts +129 -12
- package/src/server/index.ts +18 -9
- package/src/services/memory-service.ts +107 -19
- 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.
|
|
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
|
-
|
|
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.
|
|
3447
|
-
this.
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
this.
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
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
|
}
|