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.
- package/.claude/settings.local.json +4 -1
- package/.claude-plugin/plugin.json +3 -3
- package/.history/package_20260201142928.json +46 -0
- package/.history/package_20260201192048.json +47 -0
- package/README.md +26 -26
- package/dist/.claude-plugin/plugin.json +3 -3
- package/dist/cli/index.js +1109 -25
- 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 +4728 -0
- package/dist/server/api/index.js.map +7 -0
- package/dist/server/index.js +4790 -0
- package/dist/server/index.js.map +7 -0
- package/dist/services/memory-service.js +269 -18
- package/dist/services/memory-service.js.map +4 -4
- package/dist/ui/index.html +1225 -0
- package/package.json +4 -2
- package/scripts/build.ts +33 -3
- package/src/cli/index.ts +311 -6
- 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/core/types.ts +1 -1
- package/src/mcp/index.ts +2 -2
- package/src/mcp/tools.ts +1 -1
- 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 +175 -5
- package/src/server/index.ts +18 -9
- package/src/services/memory-service.ts +107 -19
- 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.
|
|
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
|
-
|
|
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.
|
|
3426
|
-
this.
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
this.
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
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
|
}
|