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
package/dist/server/api/index.js
CHANGED
|
@@ -72,7 +72,10 @@ function toDate(value) {
|
|
|
72
72
|
return new Date(value);
|
|
73
73
|
return new Date(String(value));
|
|
74
74
|
}
|
|
75
|
-
function createDatabase(path2) {
|
|
75
|
+
function createDatabase(path2, options) {
|
|
76
|
+
if (options?.readOnly) {
|
|
77
|
+
return new duckdb.Database(path2, { access_mode: "READ_ONLY" });
|
|
78
|
+
}
|
|
76
79
|
return new duckdb.Database(path2);
|
|
77
80
|
}
|
|
78
81
|
function dbRun(db, sql, params = []) {
|
|
@@ -126,18 +129,24 @@ function dbClose(db) {
|
|
|
126
129
|
|
|
127
130
|
// src/core/event-store.ts
|
|
128
131
|
var EventStore = class {
|
|
129
|
-
constructor(dbPath) {
|
|
132
|
+
constructor(dbPath, options) {
|
|
130
133
|
this.dbPath = dbPath;
|
|
131
|
-
this.
|
|
134
|
+
this.readOnly = options?.readOnly ?? false;
|
|
135
|
+
this.db = createDatabase(dbPath, { readOnly: this.readOnly });
|
|
132
136
|
}
|
|
133
137
|
db;
|
|
134
138
|
initialized = false;
|
|
139
|
+
readOnly;
|
|
135
140
|
/**
|
|
136
141
|
* Initialize database schema
|
|
137
142
|
*/
|
|
138
143
|
async initialize() {
|
|
139
144
|
if (this.initialized)
|
|
140
145
|
return;
|
|
146
|
+
if (this.readOnly) {
|
|
147
|
+
this.initialized = true;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
141
150
|
await dbRun(this.db, `
|
|
142
151
|
CREATE TABLE IF NOT EXISTS events (
|
|
143
152
|
id VARCHAR PRIMARY KEY,
|
|
@@ -614,6 +623,36 @@ var EventStore = class {
|
|
|
614
623
|
);
|
|
615
624
|
return rows;
|
|
616
625
|
}
|
|
626
|
+
/**
|
|
627
|
+
* Get events by memory level
|
|
628
|
+
*/
|
|
629
|
+
async getEventsByLevel(level, options) {
|
|
630
|
+
await this.initialize();
|
|
631
|
+
const limit = options?.limit || 50;
|
|
632
|
+
const offset = options?.offset || 0;
|
|
633
|
+
const rows = await dbAll(
|
|
634
|
+
this.db,
|
|
635
|
+
`SELECT e.* FROM events e
|
|
636
|
+
INNER JOIN memory_levels ml ON e.id = ml.event_id
|
|
637
|
+
WHERE ml.level = ?
|
|
638
|
+
ORDER BY e.timestamp DESC
|
|
639
|
+
LIMIT ? OFFSET ?`,
|
|
640
|
+
[level, limit, offset]
|
|
641
|
+
);
|
|
642
|
+
return rows.map((row) => this.rowToEvent(row));
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Get memory level for a specific event
|
|
646
|
+
*/
|
|
647
|
+
async getEventLevel(eventId) {
|
|
648
|
+
await this.initialize();
|
|
649
|
+
const rows = await dbAll(
|
|
650
|
+
this.db,
|
|
651
|
+
`SELECT level FROM memory_levels WHERE event_id = ?`,
|
|
652
|
+
[eventId]
|
|
653
|
+
);
|
|
654
|
+
return rows.length > 0 ? rows[0].level : null;
|
|
655
|
+
}
|
|
617
656
|
// ============================================================
|
|
618
657
|
// Endless Mode Helper Methods
|
|
619
658
|
// ============================================================
|
|
@@ -1196,6 +1235,7 @@ var Retriever = class {
|
|
|
1196
1235
|
matcher;
|
|
1197
1236
|
sharedStore;
|
|
1198
1237
|
sharedVectorStore;
|
|
1238
|
+
graduation;
|
|
1199
1239
|
constructor(eventStore, vectorStore, embedder, matcher, sharedOptions) {
|
|
1200
1240
|
this.eventStore = eventStore;
|
|
1201
1241
|
this.vectorStore = vectorStore;
|
|
@@ -1204,6 +1244,12 @@ var Retriever = class {
|
|
|
1204
1244
|
this.sharedStore = sharedOptions?.sharedStore;
|
|
1205
1245
|
this.sharedVectorStore = sharedOptions?.sharedVectorStore;
|
|
1206
1246
|
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Set graduation pipeline for access tracking
|
|
1249
|
+
*/
|
|
1250
|
+
setGraduationPipeline(graduation) {
|
|
1251
|
+
this.graduation = graduation;
|
|
1252
|
+
}
|
|
1207
1253
|
/**
|
|
1208
1254
|
* Set shared stores after construction
|
|
1209
1255
|
*/
|
|
@@ -1326,6 +1372,13 @@ var Retriever = class {
|
|
|
1326
1372
|
const event = await this.eventStore.getEvent(result.eventId);
|
|
1327
1373
|
if (!event)
|
|
1328
1374
|
continue;
|
|
1375
|
+
if (this.graduation) {
|
|
1376
|
+
this.graduation.recordAccess(
|
|
1377
|
+
event.id,
|
|
1378
|
+
options.sessionId || "unknown",
|
|
1379
|
+
result.score
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1329
1382
|
let sessionContext;
|
|
1330
1383
|
if (options.includeSessionContext) {
|
|
1331
1384
|
sessionContext = await this.getSessionContext(event.sessionId, event.id);
|
|
@@ -1447,15 +1500,26 @@ var GraduationPipeline = class {
|
|
|
1447
1500
|
L3toL4: { ...DEFAULT_CRITERIA.L3toL4, ...criteria.L3toL4 }
|
|
1448
1501
|
};
|
|
1449
1502
|
}
|
|
1503
|
+
// Track which sessions have accessed each event
|
|
1504
|
+
sessionAccesses = /* @__PURE__ */ new Map();
|
|
1450
1505
|
/**
|
|
1451
1506
|
* Record an access to an event (used for graduation scoring)
|
|
1452
1507
|
*/
|
|
1453
1508
|
recordAccess(eventId, fromSessionId, confidence = 1) {
|
|
1454
1509
|
const existing = this.metrics.get(eventId);
|
|
1510
|
+
if (!this.sessionAccesses.has(eventId)) {
|
|
1511
|
+
this.sessionAccesses.set(eventId, /* @__PURE__ */ new Set());
|
|
1512
|
+
}
|
|
1513
|
+
const sessions = this.sessionAccesses.get(eventId);
|
|
1514
|
+
const isNewSession = !sessions.has(fromSessionId);
|
|
1515
|
+
sessions.add(fromSessionId);
|
|
1455
1516
|
if (existing) {
|
|
1456
1517
|
existing.accessCount++;
|
|
1457
1518
|
existing.lastAccessed = /* @__PURE__ */ new Date();
|
|
1458
1519
|
existing.confidence = Math.max(existing.confidence, confidence);
|
|
1520
|
+
if (isNewSession && sessions.size > 1) {
|
|
1521
|
+
existing.crossSessionRefs = sessions.size - 1;
|
|
1522
|
+
}
|
|
1459
1523
|
} else {
|
|
1460
1524
|
this.metrics.set(eventId, {
|
|
1461
1525
|
eventId,
|
|
@@ -3343,6 +3407,127 @@ function createContinuityManager(eventStore, config) {
|
|
|
3343
3407
|
return new ContinuityManager(eventStore, config);
|
|
3344
3408
|
}
|
|
3345
3409
|
|
|
3410
|
+
// src/core/graduation-worker.ts
|
|
3411
|
+
var DEFAULT_CONFIG4 = {
|
|
3412
|
+
evaluationIntervalMs: 3e5,
|
|
3413
|
+
// 5 minutes
|
|
3414
|
+
batchSize: 50,
|
|
3415
|
+
cooldownMs: 36e5
|
|
3416
|
+
// 1 hour cooldown between evaluations
|
|
3417
|
+
};
|
|
3418
|
+
var GraduationWorker = class {
|
|
3419
|
+
constructor(eventStore, graduation, config = DEFAULT_CONFIG4) {
|
|
3420
|
+
this.eventStore = eventStore;
|
|
3421
|
+
this.graduation = graduation;
|
|
3422
|
+
this.config = config;
|
|
3423
|
+
}
|
|
3424
|
+
running = false;
|
|
3425
|
+
timeout = null;
|
|
3426
|
+
lastEvaluated = /* @__PURE__ */ new Map();
|
|
3427
|
+
/**
|
|
3428
|
+
* Start the graduation worker
|
|
3429
|
+
*/
|
|
3430
|
+
start() {
|
|
3431
|
+
if (this.running)
|
|
3432
|
+
return;
|
|
3433
|
+
this.running = true;
|
|
3434
|
+
this.scheduleNext();
|
|
3435
|
+
}
|
|
3436
|
+
/**
|
|
3437
|
+
* Stop the graduation worker
|
|
3438
|
+
*/
|
|
3439
|
+
stop() {
|
|
3440
|
+
this.running = false;
|
|
3441
|
+
if (this.timeout) {
|
|
3442
|
+
clearTimeout(this.timeout);
|
|
3443
|
+
this.timeout = null;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
/**
|
|
3447
|
+
* Check if currently running
|
|
3448
|
+
*/
|
|
3449
|
+
isRunning() {
|
|
3450
|
+
return this.running;
|
|
3451
|
+
}
|
|
3452
|
+
/**
|
|
3453
|
+
* Force a graduation evaluation run
|
|
3454
|
+
*/
|
|
3455
|
+
async forceRun() {
|
|
3456
|
+
return await this.runGraduation();
|
|
3457
|
+
}
|
|
3458
|
+
/**
|
|
3459
|
+
* Schedule the next graduation check
|
|
3460
|
+
*/
|
|
3461
|
+
scheduleNext() {
|
|
3462
|
+
if (!this.running)
|
|
3463
|
+
return;
|
|
3464
|
+
this.timeout = setTimeout(
|
|
3465
|
+
() => this.run(),
|
|
3466
|
+
this.config.evaluationIntervalMs
|
|
3467
|
+
);
|
|
3468
|
+
}
|
|
3469
|
+
/**
|
|
3470
|
+
* Run graduation evaluation
|
|
3471
|
+
*/
|
|
3472
|
+
async run() {
|
|
3473
|
+
if (!this.running)
|
|
3474
|
+
return;
|
|
3475
|
+
try {
|
|
3476
|
+
await this.runGraduation();
|
|
3477
|
+
} catch (error) {
|
|
3478
|
+
console.error("Graduation error:", error);
|
|
3479
|
+
}
|
|
3480
|
+
this.scheduleNext();
|
|
3481
|
+
}
|
|
3482
|
+
/**
|
|
3483
|
+
* Perform graduation evaluation across all levels
|
|
3484
|
+
*/
|
|
3485
|
+
async runGraduation() {
|
|
3486
|
+
const result = {
|
|
3487
|
+
evaluated: 0,
|
|
3488
|
+
graduated: 0,
|
|
3489
|
+
byLevel: {}
|
|
3490
|
+
};
|
|
3491
|
+
const levels = ["L0", "L1", "L2", "L3"];
|
|
3492
|
+
const now = Date.now();
|
|
3493
|
+
for (const level of levels) {
|
|
3494
|
+
const events = await this.eventStore.getEventsByLevel(level, {
|
|
3495
|
+
limit: this.config.batchSize
|
|
3496
|
+
});
|
|
3497
|
+
let levelGraduated = 0;
|
|
3498
|
+
for (const event of events) {
|
|
3499
|
+
const lastEval = this.lastEvaluated.get(event.id);
|
|
3500
|
+
if (lastEval && now - lastEval < this.config.cooldownMs) {
|
|
3501
|
+
continue;
|
|
3502
|
+
}
|
|
3503
|
+
result.evaluated++;
|
|
3504
|
+
this.lastEvaluated.set(event.id, now);
|
|
3505
|
+
const gradResult = await this.graduation.evaluateGraduation(event.id, level);
|
|
3506
|
+
if (gradResult.success) {
|
|
3507
|
+
result.graduated++;
|
|
3508
|
+
levelGraduated++;
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
if (levelGraduated > 0) {
|
|
3512
|
+
result.byLevel[level] = levelGraduated;
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3515
|
+
if (this.lastEvaluated.size > 1e3) {
|
|
3516
|
+
const entries = Array.from(this.lastEvaluated.entries());
|
|
3517
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
3518
|
+
this.lastEvaluated = new Map(entries.slice(0, 1e3));
|
|
3519
|
+
}
|
|
3520
|
+
return result;
|
|
3521
|
+
}
|
|
3522
|
+
};
|
|
3523
|
+
function createGraduationWorker(eventStore, graduation, config) {
|
|
3524
|
+
return new GraduationWorker(
|
|
3525
|
+
eventStore,
|
|
3526
|
+
graduation,
|
|
3527
|
+
{ ...DEFAULT_CONFIG4, ...config }
|
|
3528
|
+
);
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3346
3531
|
// src/services/memory-service.ts
|
|
3347
3532
|
function normalizePath(projectPath) {
|
|
3348
3533
|
const expanded = projectPath.startsWith("~") ? path.join(os.homedir(), projectPath.slice(1)) : projectPath;
|
|
@@ -3370,6 +3555,7 @@ var MemoryService = class {
|
|
|
3370
3555
|
retriever;
|
|
3371
3556
|
graduation;
|
|
3372
3557
|
vectorWorker = null;
|
|
3558
|
+
graduationWorker = null;
|
|
3373
3559
|
initialized = false;
|
|
3374
3560
|
// Endless Mode components
|
|
3375
3561
|
workingSetStore = null;
|
|
@@ -3384,14 +3570,16 @@ var MemoryService = class {
|
|
|
3384
3570
|
sharedPromoter = null;
|
|
3385
3571
|
sharedStoreConfig = null;
|
|
3386
3572
|
projectHash = null;
|
|
3573
|
+
readOnly;
|
|
3387
3574
|
constructor(config) {
|
|
3388
3575
|
const storagePath = this.expandPath(config.storagePath);
|
|
3389
|
-
|
|
3576
|
+
this.readOnly = config.readOnly ?? false;
|
|
3577
|
+
if (!this.readOnly && !fs.existsSync(storagePath)) {
|
|
3390
3578
|
fs.mkdirSync(storagePath, { recursive: true });
|
|
3391
3579
|
}
|
|
3392
3580
|
this.projectHash = config.projectHash || null;
|
|
3393
3581
|
this.sharedStoreConfig = config.sharedStoreConfig ?? { enabled: true };
|
|
3394
|
-
this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"));
|
|
3582
|
+
this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"), { readOnly: this.readOnly });
|
|
3395
3583
|
this.vectorStore = new VectorStore(path.join(storagePath, "vectors"));
|
|
3396
3584
|
this.embedder = config.embeddingModel ? new Embedder(config.embeddingModel) : getDefaultEmbedder();
|
|
3397
3585
|
this.matcher = getDefaultMatcher();
|
|
@@ -3412,19 +3600,27 @@ var MemoryService = class {
|
|
|
3412
3600
|
await this.eventStore.initialize();
|
|
3413
3601
|
await this.vectorStore.initialize();
|
|
3414
3602
|
await this.embedder.initialize();
|
|
3415
|
-
this.
|
|
3416
|
-
this.
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
this.
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3603
|
+
if (!this.readOnly) {
|
|
3604
|
+
this.vectorWorker = createVectorWorker(
|
|
3605
|
+
this.eventStore,
|
|
3606
|
+
this.vectorStore,
|
|
3607
|
+
this.embedder
|
|
3608
|
+
);
|
|
3609
|
+
this.vectorWorker.start();
|
|
3610
|
+
this.retriever.setGraduationPipeline(this.graduation);
|
|
3611
|
+
this.graduationWorker = createGraduationWorker(
|
|
3612
|
+
this.eventStore,
|
|
3613
|
+
this.graduation
|
|
3614
|
+
);
|
|
3615
|
+
this.graduationWorker.start();
|
|
3616
|
+
const savedMode = await this.eventStore.getEndlessConfig("mode");
|
|
3617
|
+
if (savedMode === "endless") {
|
|
3618
|
+
this.endlessMode = "endless";
|
|
3619
|
+
await this.initializeEndlessMode();
|
|
3620
|
+
}
|
|
3621
|
+
if (this.sharedStoreConfig?.enabled !== false) {
|
|
3622
|
+
await this.initializeSharedStore();
|
|
3623
|
+
}
|
|
3428
3624
|
}
|
|
3429
3625
|
this.initialized = true;
|
|
3430
3626
|
}
|
|
@@ -3605,6 +3801,20 @@ var MemoryService = class {
|
|
|
3605
3801
|
}
|
|
3606
3802
|
return 0;
|
|
3607
3803
|
}
|
|
3804
|
+
/**
|
|
3805
|
+
* Get events by memory level
|
|
3806
|
+
*/
|
|
3807
|
+
async getEventsByLevel(level, options) {
|
|
3808
|
+
await this.initialize();
|
|
3809
|
+
return this.eventStore.getEventsByLevel(level, options);
|
|
3810
|
+
}
|
|
3811
|
+
/**
|
|
3812
|
+
* Get memory level for a specific event
|
|
3813
|
+
*/
|
|
3814
|
+
async getEventLevel(eventId) {
|
|
3815
|
+
await this.initialize();
|
|
3816
|
+
return this.eventStore.getEventLevel(eventId);
|
|
3817
|
+
}
|
|
3608
3818
|
/**
|
|
3609
3819
|
* Format retrieval results as context for Claude
|
|
3610
3820
|
*/
|
|
@@ -3787,6 +3997,22 @@ var MemoryService = class {
|
|
|
3787
3997
|
return [];
|
|
3788
3998
|
return this.consolidatedStore.getAll({ limit });
|
|
3789
3999
|
}
|
|
4000
|
+
/**
|
|
4001
|
+
* Get most accessed consolidated memories
|
|
4002
|
+
*/
|
|
4003
|
+
async getMostAccessedMemories(limit = 10) {
|
|
4004
|
+
if (!this.consolidatedStore)
|
|
4005
|
+
return [];
|
|
4006
|
+
return this.consolidatedStore.getMostAccessed(limit);
|
|
4007
|
+
}
|
|
4008
|
+
/**
|
|
4009
|
+
* Mark a consolidated memory as accessed
|
|
4010
|
+
*/
|
|
4011
|
+
async markMemoryAccessed(memoryId) {
|
|
4012
|
+
if (!this.consolidatedStore)
|
|
4013
|
+
return;
|
|
4014
|
+
await this.consolidatedStore.markAccessed(memoryId);
|
|
4015
|
+
}
|
|
3790
4016
|
/**
|
|
3791
4017
|
* Calculate continuity score for current context
|
|
3792
4018
|
*/
|
|
@@ -3874,10 +4100,28 @@ var MemoryService = class {
|
|
|
3874
4100
|
}
|
|
3875
4101
|
return parts.join("\n");
|
|
3876
4102
|
}
|
|
4103
|
+
/**
|
|
4104
|
+
* Force a graduation evaluation run
|
|
4105
|
+
*/
|
|
4106
|
+
async forceGraduation() {
|
|
4107
|
+
if (!this.graduationWorker) {
|
|
4108
|
+
return { evaluated: 0, graduated: 0, byLevel: {} };
|
|
4109
|
+
}
|
|
4110
|
+
return this.graduationWorker.forceRun();
|
|
4111
|
+
}
|
|
4112
|
+
/**
|
|
4113
|
+
* Record access to a memory event (for graduation scoring)
|
|
4114
|
+
*/
|
|
4115
|
+
recordMemoryAccess(eventId, sessionId, confidence = 1) {
|
|
4116
|
+
this.graduation.recordAccess(eventId, sessionId, confidence);
|
|
4117
|
+
}
|
|
3877
4118
|
/**
|
|
3878
4119
|
* Shutdown service
|
|
3879
4120
|
*/
|
|
3880
4121
|
async shutdown() {
|
|
4122
|
+
if (this.graduationWorker) {
|
|
4123
|
+
this.graduationWorker.stop();
|
|
4124
|
+
}
|
|
3881
4125
|
if (this.consolidationWorker) {
|
|
3882
4126
|
this.consolidationWorker.stop();
|
|
3883
4127
|
}
|
|
@@ -3900,14 +4144,11 @@ var MemoryService = class {
|
|
|
3900
4144
|
}
|
|
3901
4145
|
};
|
|
3902
4146
|
var serviceCache = /* @__PURE__ */ new Map();
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
}));
|
|
3909
|
-
}
|
|
3910
|
-
return serviceCache.get(GLOBAL_KEY);
|
|
4147
|
+
function getReadOnlyMemoryService() {
|
|
4148
|
+
return new MemoryService({
|
|
4149
|
+
storagePath: "~/.claude-code/memory",
|
|
4150
|
+
readOnly: true
|
|
4151
|
+
});
|
|
3911
4152
|
}
|
|
3912
4153
|
function getMemoryServiceForProject(projectPath, sharedStoreConfig) {
|
|
3913
4154
|
const hash = hashProjectPath(projectPath);
|
|
@@ -3927,8 +4168,8 @@ var sessionsRouter = new Hono();
|
|
|
3927
4168
|
sessionsRouter.get("/", async (c) => {
|
|
3928
4169
|
const page = parseInt(c.req.query("page") || "1", 10);
|
|
3929
4170
|
const pageSize = parseInt(c.req.query("pageSize") || "20", 10);
|
|
4171
|
+
const memoryService = getReadOnlyMemoryService();
|
|
3930
4172
|
try {
|
|
3931
|
-
const memoryService = getDefaultMemoryService();
|
|
3932
4173
|
await memoryService.initialize();
|
|
3933
4174
|
const recentEvents = await memoryService.getRecentEvents(1e3);
|
|
3934
4175
|
const sessionMap = /* @__PURE__ */ new Map();
|
|
@@ -3965,12 +4206,14 @@ sessionsRouter.get("/", async (c) => {
|
|
|
3965
4206
|
});
|
|
3966
4207
|
} catch (error) {
|
|
3967
4208
|
return c.json({ error: error.message }, 500);
|
|
4209
|
+
} finally {
|
|
4210
|
+
await memoryService.shutdown();
|
|
3968
4211
|
}
|
|
3969
4212
|
});
|
|
3970
4213
|
sessionsRouter.get("/:id", async (c) => {
|
|
3971
4214
|
const { id } = c.req.param();
|
|
4215
|
+
const memoryService = getReadOnlyMemoryService();
|
|
3972
4216
|
try {
|
|
3973
|
-
const memoryService = getDefaultMemoryService();
|
|
3974
4217
|
await memoryService.initialize();
|
|
3975
4218
|
const events = await memoryService.getSessionHistory(id);
|
|
3976
4219
|
if (events.length === 0) {
|
|
@@ -3999,6 +4242,8 @@ sessionsRouter.get("/:id", async (c) => {
|
|
|
3999
4242
|
});
|
|
4000
4243
|
} catch (error) {
|
|
4001
4244
|
return c.json({ error: error.message }, 500);
|
|
4245
|
+
} finally {
|
|
4246
|
+
await memoryService.shutdown();
|
|
4002
4247
|
}
|
|
4003
4248
|
});
|
|
4004
4249
|
|
|
@@ -4010,8 +4255,8 @@ eventsRouter.get("/", async (c) => {
|
|
|
4010
4255
|
const eventType = c.req.query("type");
|
|
4011
4256
|
const limit = parseInt(c.req.query("limit") || "100", 10);
|
|
4012
4257
|
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
4258
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4013
4259
|
try {
|
|
4014
|
-
const memoryService = getDefaultMemoryService();
|
|
4015
4260
|
await memoryService.initialize();
|
|
4016
4261
|
let events = await memoryService.getRecentEvents(limit + offset + 1e3);
|
|
4017
4262
|
if (sessionId) {
|
|
@@ -4038,12 +4283,14 @@ eventsRouter.get("/", async (c) => {
|
|
|
4038
4283
|
});
|
|
4039
4284
|
} catch (error) {
|
|
4040
4285
|
return c.json({ error: error.message }, 500);
|
|
4286
|
+
} finally {
|
|
4287
|
+
await memoryService.shutdown();
|
|
4041
4288
|
}
|
|
4042
4289
|
});
|
|
4043
4290
|
eventsRouter.get("/:id", async (c) => {
|
|
4044
4291
|
const { id } = c.req.param();
|
|
4292
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4045
4293
|
try {
|
|
4046
|
-
const memoryService = getDefaultMemoryService();
|
|
4047
4294
|
await memoryService.initialize();
|
|
4048
4295
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4049
4296
|
const event = recentEvents.find((e) => e.id === id);
|
|
@@ -4073,6 +4320,8 @@ eventsRouter.get("/:id", async (c) => {
|
|
|
4073
4320
|
});
|
|
4074
4321
|
} catch (error) {
|
|
4075
4322
|
return c.json({ error: error.message }, 500);
|
|
4323
|
+
} finally {
|
|
4324
|
+
await memoryService.shutdown();
|
|
4076
4325
|
}
|
|
4077
4326
|
});
|
|
4078
4327
|
|
|
@@ -4080,12 +4329,12 @@ eventsRouter.get("/:id", async (c) => {
|
|
|
4080
4329
|
import { Hono as Hono3 } from "hono";
|
|
4081
4330
|
var searchRouter = new Hono3();
|
|
4082
4331
|
searchRouter.post("/", async (c) => {
|
|
4332
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4083
4333
|
try {
|
|
4084
4334
|
const body = await c.req.json();
|
|
4085
4335
|
if (!body.query) {
|
|
4086
4336
|
return c.json({ error: "Query is required" }, 400);
|
|
4087
4337
|
}
|
|
4088
|
-
const memoryService = getDefaultMemoryService();
|
|
4089
4338
|
await memoryService.initialize();
|
|
4090
4339
|
const startTime = Date.now();
|
|
4091
4340
|
const result = await memoryService.retrieveMemories(body.query, {
|
|
@@ -4114,6 +4363,8 @@ searchRouter.post("/", async (c) => {
|
|
|
4114
4363
|
});
|
|
4115
4364
|
} catch (error) {
|
|
4116
4365
|
return c.json({ error: error.message }, 500);
|
|
4366
|
+
} finally {
|
|
4367
|
+
await memoryService.shutdown();
|
|
4117
4368
|
}
|
|
4118
4369
|
});
|
|
4119
4370
|
searchRouter.get("/", async (c) => {
|
|
@@ -4122,8 +4373,8 @@ searchRouter.get("/", async (c) => {
|
|
|
4122
4373
|
return c.json({ error: 'Query parameter "q" is required' }, 400);
|
|
4123
4374
|
}
|
|
4124
4375
|
const topK = parseInt(c.req.query("topK") || "5", 10);
|
|
4376
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4125
4377
|
try {
|
|
4126
|
-
const memoryService = getDefaultMemoryService();
|
|
4127
4378
|
await memoryService.initialize();
|
|
4128
4379
|
const result = await memoryService.retrieveMemories(query, { topK });
|
|
4129
4380
|
return c.json({
|
|
@@ -4141,6 +4392,8 @@ searchRouter.get("/", async (c) => {
|
|
|
4141
4392
|
});
|
|
4142
4393
|
} catch (error) {
|
|
4143
4394
|
return c.json({ error: error.message }, 500);
|
|
4395
|
+
} finally {
|
|
4396
|
+
await memoryService.shutdown();
|
|
4144
4397
|
}
|
|
4145
4398
|
});
|
|
4146
4399
|
|
|
@@ -4148,8 +4401,8 @@ searchRouter.get("/", async (c) => {
|
|
|
4148
4401
|
import { Hono as Hono4 } from "hono";
|
|
4149
4402
|
var statsRouter = new Hono4();
|
|
4150
4403
|
statsRouter.get("/shared", async (c) => {
|
|
4404
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4151
4405
|
try {
|
|
4152
|
-
const memoryService = getDefaultMemoryService();
|
|
4153
4406
|
await memoryService.initialize();
|
|
4154
4407
|
const sharedStats = await memoryService.getSharedStoreStats();
|
|
4155
4408
|
return c.json({
|
|
@@ -4167,12 +4420,14 @@ statsRouter.get("/shared", async (c) => {
|
|
|
4167
4420
|
totalUsageCount: 0,
|
|
4168
4421
|
lastUpdated: null
|
|
4169
4422
|
});
|
|
4423
|
+
} finally {
|
|
4424
|
+
await memoryService.shutdown();
|
|
4170
4425
|
}
|
|
4171
4426
|
});
|
|
4172
4427
|
statsRouter.get("/endless", async (c) => {
|
|
4428
|
+
const projectPath = c.req.query("project") || process.cwd();
|
|
4429
|
+
const memoryService = getMemoryServiceForProject(projectPath);
|
|
4173
4430
|
try {
|
|
4174
|
-
const projectPath = c.req.query("project") || process.cwd();
|
|
4175
|
-
const memoryService = getMemoryServiceForProject(projectPath);
|
|
4176
4431
|
await memoryService.initialize();
|
|
4177
4432
|
const status = await memoryService.getEndlessModeStatus();
|
|
4178
4433
|
return c.json({
|
|
@@ -4190,11 +4445,48 @@ statsRouter.get("/endless", async (c) => {
|
|
|
4190
4445
|
consolidatedCount: 0,
|
|
4191
4446
|
lastConsolidation: null
|
|
4192
4447
|
});
|
|
4448
|
+
} finally {
|
|
4449
|
+
await memoryService.shutdown();
|
|
4450
|
+
}
|
|
4451
|
+
});
|
|
4452
|
+
statsRouter.get("/levels/:level", async (c) => {
|
|
4453
|
+
const { level } = c.req.param();
|
|
4454
|
+
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
4455
|
+
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
4456
|
+
const validLevels = ["L0", "L1", "L2", "L3", "L4"];
|
|
4457
|
+
if (!validLevels.includes(level)) {
|
|
4458
|
+
return c.json({ error: `Invalid level. Must be one of: ${validLevels.join(", ")}` }, 400);
|
|
4459
|
+
}
|
|
4460
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4461
|
+
try {
|
|
4462
|
+
await memoryService.initialize();
|
|
4463
|
+
const events = await memoryService.getEventsByLevel(level, { limit, offset });
|
|
4464
|
+
const stats = await memoryService.getStats();
|
|
4465
|
+
const levelStat = stats.levelStats.find((s) => s.level === level);
|
|
4466
|
+
return c.json({
|
|
4467
|
+
level,
|
|
4468
|
+
events: events.map((e) => ({
|
|
4469
|
+
id: e.id,
|
|
4470
|
+
eventType: e.eventType,
|
|
4471
|
+
sessionId: e.sessionId,
|
|
4472
|
+
timestamp: e.timestamp.toISOString(),
|
|
4473
|
+
content: e.content.slice(0, 500) + (e.content.length > 500 ? "..." : ""),
|
|
4474
|
+
metadata: e.metadata
|
|
4475
|
+
})),
|
|
4476
|
+
total: levelStat?.count || 0,
|
|
4477
|
+
limit,
|
|
4478
|
+
offset,
|
|
4479
|
+
hasMore: events.length === limit
|
|
4480
|
+
});
|
|
4481
|
+
} catch (error) {
|
|
4482
|
+
return c.json({ error: error.message }, 500);
|
|
4483
|
+
} finally {
|
|
4484
|
+
await memoryService.shutdown();
|
|
4193
4485
|
}
|
|
4194
4486
|
});
|
|
4195
4487
|
statsRouter.get("/", async (c) => {
|
|
4488
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4196
4489
|
try {
|
|
4197
|
-
const memoryService = getDefaultMemoryService();
|
|
4198
4490
|
await memoryService.initialize();
|
|
4199
4491
|
const stats = await memoryService.getStats();
|
|
4200
4492
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
@@ -4231,12 +4523,43 @@ statsRouter.get("/", async (c) => {
|
|
|
4231
4523
|
});
|
|
4232
4524
|
} catch (error) {
|
|
4233
4525
|
return c.json({ error: error.message }, 500);
|
|
4526
|
+
} finally {
|
|
4527
|
+
await memoryService.shutdown();
|
|
4528
|
+
}
|
|
4529
|
+
});
|
|
4530
|
+
statsRouter.get("/most-accessed", async (c) => {
|
|
4531
|
+
const limit = parseInt(c.req.query("limit") || "10", 10);
|
|
4532
|
+
const projectPath = c.req.query("project") || process.cwd();
|
|
4533
|
+
const memoryService = getMemoryServiceForProject(projectPath);
|
|
4534
|
+
try {
|
|
4535
|
+
await memoryService.initialize();
|
|
4536
|
+
const memories = await memoryService.getMostAccessedMemories(limit);
|
|
4537
|
+
return c.json({
|
|
4538
|
+
memories: memories.map((m) => ({
|
|
4539
|
+
memoryId: m.memoryId,
|
|
4540
|
+
summary: m.summary,
|
|
4541
|
+
topics: m.topics,
|
|
4542
|
+
accessCount: m.accessCount,
|
|
4543
|
+
lastAccessed: m.accessedAt?.toISOString() || null,
|
|
4544
|
+
confidence: m.confidence,
|
|
4545
|
+
createdAt: m.createdAt.toISOString()
|
|
4546
|
+
})),
|
|
4547
|
+
total: memories.length
|
|
4548
|
+
});
|
|
4549
|
+
} catch (error) {
|
|
4550
|
+
return c.json({
|
|
4551
|
+
memories: [],
|
|
4552
|
+
total: 0,
|
|
4553
|
+
error: error.message
|
|
4554
|
+
});
|
|
4555
|
+
} finally {
|
|
4556
|
+
await memoryService.shutdown();
|
|
4234
4557
|
}
|
|
4235
4558
|
});
|
|
4236
4559
|
statsRouter.get("/timeline", async (c) => {
|
|
4237
4560
|
const days = parseInt(c.req.query("days") || "7", 10);
|
|
4561
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4238
4562
|
try {
|
|
4239
|
-
const memoryService = getDefaultMemoryService();
|
|
4240
4563
|
await memoryService.initialize();
|
|
4241
4564
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4242
4565
|
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
|
|
@@ -4261,8 +4584,46 @@ statsRouter.get("/timeline", async (c) => {
|
|
|
4261
4584
|
});
|
|
4262
4585
|
} catch (error) {
|
|
4263
4586
|
return c.json({ error: error.message }, 500);
|
|
4587
|
+
} finally {
|
|
4588
|
+
await memoryService.shutdown();
|
|
4264
4589
|
}
|
|
4265
4590
|
});
|
|
4591
|
+
statsRouter.post("/graduation/run", async (c) => {
|
|
4592
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4593
|
+
try {
|
|
4594
|
+
await memoryService.initialize();
|
|
4595
|
+
const result = await memoryService.forceGraduation();
|
|
4596
|
+
return c.json({
|
|
4597
|
+
success: true,
|
|
4598
|
+
evaluated: result.evaluated,
|
|
4599
|
+
graduated: result.graduated,
|
|
4600
|
+
byLevel: result.byLevel
|
|
4601
|
+
});
|
|
4602
|
+
} catch (error) {
|
|
4603
|
+
return c.json({
|
|
4604
|
+
success: false,
|
|
4605
|
+
error: error.message
|
|
4606
|
+
}, 500);
|
|
4607
|
+
} finally {
|
|
4608
|
+
await memoryService.shutdown();
|
|
4609
|
+
}
|
|
4610
|
+
});
|
|
4611
|
+
statsRouter.get("/graduation", async (c) => {
|
|
4612
|
+
return c.json({
|
|
4613
|
+
criteria: {
|
|
4614
|
+
L0toL1: { minAccessCount: 1, minConfidence: 0.5, minCrossSessionRefs: 0, maxAgeDays: 30 },
|
|
4615
|
+
L1toL2: { minAccessCount: 3, minConfidence: 0.7, minCrossSessionRefs: 1, maxAgeDays: 60 },
|
|
4616
|
+
L2toL3: { minAccessCount: 5, minConfidence: 0.85, minCrossSessionRefs: 2, maxAgeDays: 90 },
|
|
4617
|
+
L3toL4: { minAccessCount: 10, minConfidence: 0.92, minCrossSessionRefs: 3, maxAgeDays: 180 }
|
|
4618
|
+
},
|
|
4619
|
+
description: {
|
|
4620
|
+
accessCount: "Number of times the memory was retrieved/referenced",
|
|
4621
|
+
confidence: "Match confidence score when retrieved (0.0-1.0)",
|
|
4622
|
+
crossSessionRefs: "Number of different sessions that referenced this memory",
|
|
4623
|
+
maxAgeDays: "Maximum days since last access (prevents stale promotion)"
|
|
4624
|
+
}
|
|
4625
|
+
});
|
|
4626
|
+
});
|
|
4266
4627
|
|
|
4267
4628
|
// src/server/api/citations.ts
|
|
4268
4629
|
import { Hono as Hono5 } from "hono";
|
|
@@ -4289,8 +4650,8 @@ var citationsRouter = new Hono5();
|
|
|
4289
4650
|
citationsRouter.get("/:id", async (c) => {
|
|
4290
4651
|
const { id } = c.req.param();
|
|
4291
4652
|
const citationId = parseCitationId(id) || id;
|
|
4653
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4292
4654
|
try {
|
|
4293
|
-
const memoryService = getDefaultMemoryService();
|
|
4294
4655
|
await memoryService.initialize();
|
|
4295
4656
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4296
4657
|
const event = recentEvents.find((e) => {
|
|
@@ -4316,13 +4677,15 @@ citationsRouter.get("/:id", async (c) => {
|
|
|
4316
4677
|
});
|
|
4317
4678
|
} catch (error) {
|
|
4318
4679
|
return c.json({ error: error.message }, 500);
|
|
4680
|
+
} finally {
|
|
4681
|
+
await memoryService.shutdown();
|
|
4319
4682
|
}
|
|
4320
4683
|
});
|
|
4321
4684
|
citationsRouter.get("/:id/related", async (c) => {
|
|
4322
4685
|
const { id } = c.req.param();
|
|
4323
4686
|
const citationId = parseCitationId(id) || id;
|
|
4687
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4324
4688
|
try {
|
|
4325
|
-
const memoryService = getDefaultMemoryService();
|
|
4326
4689
|
await memoryService.initialize();
|
|
4327
4690
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4328
4691
|
const event = recentEvents.find((e) => {
|
|
@@ -4352,6 +4715,8 @@ citationsRouter.get("/:id/related", async (c) => {
|
|
|
4352
4715
|
});
|
|
4353
4716
|
} catch (error) {
|
|
4354
4717
|
return c.json({ error: error.message }, 500);
|
|
4718
|
+
} finally {
|
|
4719
|
+
await memoryService.shutdown();
|
|
4355
4720
|
}
|
|
4356
4721
|
});
|
|
4357
4722
|
|