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/index.js
CHANGED
|
@@ -9,7 +9,8 @@ const __dirname = dirname(__filename);
|
|
|
9
9
|
import { Hono as Hono7 } from "hono";
|
|
10
10
|
import { cors } from "hono/cors";
|
|
11
11
|
import { logger } from "hono/logger";
|
|
12
|
-
import {
|
|
12
|
+
import { serve } from "@hono/node-server";
|
|
13
|
+
import { serveStatic } from "@hono/node-server/serve-static";
|
|
13
14
|
import * as path2 from "path";
|
|
14
15
|
import * as fs2 from "fs";
|
|
15
16
|
|
|
@@ -80,7 +81,10 @@ function toDate(value) {
|
|
|
80
81
|
return new Date(value);
|
|
81
82
|
return new Date(String(value));
|
|
82
83
|
}
|
|
83
|
-
function createDatabase(path3) {
|
|
84
|
+
function createDatabase(path3, options) {
|
|
85
|
+
if (options?.readOnly) {
|
|
86
|
+
return new duckdb.Database(path3, { access_mode: "READ_ONLY" });
|
|
87
|
+
}
|
|
84
88
|
return new duckdb.Database(path3);
|
|
85
89
|
}
|
|
86
90
|
function dbRun(db, sql, params = []) {
|
|
@@ -134,18 +138,24 @@ function dbClose(db) {
|
|
|
134
138
|
|
|
135
139
|
// src/core/event-store.ts
|
|
136
140
|
var EventStore = class {
|
|
137
|
-
constructor(dbPath) {
|
|
141
|
+
constructor(dbPath, options) {
|
|
138
142
|
this.dbPath = dbPath;
|
|
139
|
-
this.
|
|
143
|
+
this.readOnly = options?.readOnly ?? false;
|
|
144
|
+
this.db = createDatabase(dbPath, { readOnly: this.readOnly });
|
|
140
145
|
}
|
|
141
146
|
db;
|
|
142
147
|
initialized = false;
|
|
148
|
+
readOnly;
|
|
143
149
|
/**
|
|
144
150
|
* Initialize database schema
|
|
145
151
|
*/
|
|
146
152
|
async initialize() {
|
|
147
153
|
if (this.initialized)
|
|
148
154
|
return;
|
|
155
|
+
if (this.readOnly) {
|
|
156
|
+
this.initialized = true;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
149
159
|
await dbRun(this.db, `
|
|
150
160
|
CREATE TABLE IF NOT EXISTS events (
|
|
151
161
|
id VARCHAR PRIMARY KEY,
|
|
@@ -622,6 +632,36 @@ var EventStore = class {
|
|
|
622
632
|
);
|
|
623
633
|
return rows;
|
|
624
634
|
}
|
|
635
|
+
/**
|
|
636
|
+
* Get events by memory level
|
|
637
|
+
*/
|
|
638
|
+
async getEventsByLevel(level, options) {
|
|
639
|
+
await this.initialize();
|
|
640
|
+
const limit = options?.limit || 50;
|
|
641
|
+
const offset = options?.offset || 0;
|
|
642
|
+
const rows = await dbAll(
|
|
643
|
+
this.db,
|
|
644
|
+
`SELECT e.* FROM events e
|
|
645
|
+
INNER JOIN memory_levels ml ON e.id = ml.event_id
|
|
646
|
+
WHERE ml.level = ?
|
|
647
|
+
ORDER BY e.timestamp DESC
|
|
648
|
+
LIMIT ? OFFSET ?`,
|
|
649
|
+
[level, limit, offset]
|
|
650
|
+
);
|
|
651
|
+
return rows.map((row) => this.rowToEvent(row));
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Get memory level for a specific event
|
|
655
|
+
*/
|
|
656
|
+
async getEventLevel(eventId) {
|
|
657
|
+
await this.initialize();
|
|
658
|
+
const rows = await dbAll(
|
|
659
|
+
this.db,
|
|
660
|
+
`SELECT level FROM memory_levels WHERE event_id = ?`,
|
|
661
|
+
[eventId]
|
|
662
|
+
);
|
|
663
|
+
return rows.length > 0 ? rows[0].level : null;
|
|
664
|
+
}
|
|
625
665
|
// ============================================================
|
|
626
666
|
// Endless Mode Helper Methods
|
|
627
667
|
// ============================================================
|
|
@@ -1204,6 +1244,7 @@ var Retriever = class {
|
|
|
1204
1244
|
matcher;
|
|
1205
1245
|
sharedStore;
|
|
1206
1246
|
sharedVectorStore;
|
|
1247
|
+
graduation;
|
|
1207
1248
|
constructor(eventStore, vectorStore, embedder, matcher, sharedOptions) {
|
|
1208
1249
|
this.eventStore = eventStore;
|
|
1209
1250
|
this.vectorStore = vectorStore;
|
|
@@ -1212,6 +1253,12 @@ var Retriever = class {
|
|
|
1212
1253
|
this.sharedStore = sharedOptions?.sharedStore;
|
|
1213
1254
|
this.sharedVectorStore = sharedOptions?.sharedVectorStore;
|
|
1214
1255
|
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Set graduation pipeline for access tracking
|
|
1258
|
+
*/
|
|
1259
|
+
setGraduationPipeline(graduation) {
|
|
1260
|
+
this.graduation = graduation;
|
|
1261
|
+
}
|
|
1215
1262
|
/**
|
|
1216
1263
|
* Set shared stores after construction
|
|
1217
1264
|
*/
|
|
@@ -1334,6 +1381,13 @@ var Retriever = class {
|
|
|
1334
1381
|
const event = await this.eventStore.getEvent(result.eventId);
|
|
1335
1382
|
if (!event)
|
|
1336
1383
|
continue;
|
|
1384
|
+
if (this.graduation) {
|
|
1385
|
+
this.graduation.recordAccess(
|
|
1386
|
+
event.id,
|
|
1387
|
+
options.sessionId || "unknown",
|
|
1388
|
+
result.score
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1337
1391
|
let sessionContext;
|
|
1338
1392
|
if (options.includeSessionContext) {
|
|
1339
1393
|
sessionContext = await this.getSessionContext(event.sessionId, event.id);
|
|
@@ -1455,15 +1509,26 @@ var GraduationPipeline = class {
|
|
|
1455
1509
|
L3toL4: { ...DEFAULT_CRITERIA.L3toL4, ...criteria.L3toL4 }
|
|
1456
1510
|
};
|
|
1457
1511
|
}
|
|
1512
|
+
// Track which sessions have accessed each event
|
|
1513
|
+
sessionAccesses = /* @__PURE__ */ new Map();
|
|
1458
1514
|
/**
|
|
1459
1515
|
* Record an access to an event (used for graduation scoring)
|
|
1460
1516
|
*/
|
|
1461
1517
|
recordAccess(eventId, fromSessionId, confidence = 1) {
|
|
1462
1518
|
const existing = this.metrics.get(eventId);
|
|
1519
|
+
if (!this.sessionAccesses.has(eventId)) {
|
|
1520
|
+
this.sessionAccesses.set(eventId, /* @__PURE__ */ new Set());
|
|
1521
|
+
}
|
|
1522
|
+
const sessions = this.sessionAccesses.get(eventId);
|
|
1523
|
+
const isNewSession = !sessions.has(fromSessionId);
|
|
1524
|
+
sessions.add(fromSessionId);
|
|
1463
1525
|
if (existing) {
|
|
1464
1526
|
existing.accessCount++;
|
|
1465
1527
|
existing.lastAccessed = /* @__PURE__ */ new Date();
|
|
1466
1528
|
existing.confidence = Math.max(existing.confidence, confidence);
|
|
1529
|
+
if (isNewSession && sessions.size > 1) {
|
|
1530
|
+
existing.crossSessionRefs = sessions.size - 1;
|
|
1531
|
+
}
|
|
1467
1532
|
} else {
|
|
1468
1533
|
this.metrics.set(eventId, {
|
|
1469
1534
|
eventId,
|
|
@@ -3351,6 +3416,127 @@ function createContinuityManager(eventStore, config) {
|
|
|
3351
3416
|
return new ContinuityManager(eventStore, config);
|
|
3352
3417
|
}
|
|
3353
3418
|
|
|
3419
|
+
// src/core/graduation-worker.ts
|
|
3420
|
+
var DEFAULT_CONFIG4 = {
|
|
3421
|
+
evaluationIntervalMs: 3e5,
|
|
3422
|
+
// 5 minutes
|
|
3423
|
+
batchSize: 50,
|
|
3424
|
+
cooldownMs: 36e5
|
|
3425
|
+
// 1 hour cooldown between evaluations
|
|
3426
|
+
};
|
|
3427
|
+
var GraduationWorker = class {
|
|
3428
|
+
constructor(eventStore, graduation, config = DEFAULT_CONFIG4) {
|
|
3429
|
+
this.eventStore = eventStore;
|
|
3430
|
+
this.graduation = graduation;
|
|
3431
|
+
this.config = config;
|
|
3432
|
+
}
|
|
3433
|
+
running = false;
|
|
3434
|
+
timeout = null;
|
|
3435
|
+
lastEvaluated = /* @__PURE__ */ new Map();
|
|
3436
|
+
/**
|
|
3437
|
+
* Start the graduation worker
|
|
3438
|
+
*/
|
|
3439
|
+
start() {
|
|
3440
|
+
if (this.running)
|
|
3441
|
+
return;
|
|
3442
|
+
this.running = true;
|
|
3443
|
+
this.scheduleNext();
|
|
3444
|
+
}
|
|
3445
|
+
/**
|
|
3446
|
+
* Stop the graduation worker
|
|
3447
|
+
*/
|
|
3448
|
+
stop() {
|
|
3449
|
+
this.running = false;
|
|
3450
|
+
if (this.timeout) {
|
|
3451
|
+
clearTimeout(this.timeout);
|
|
3452
|
+
this.timeout = null;
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
/**
|
|
3456
|
+
* Check if currently running
|
|
3457
|
+
*/
|
|
3458
|
+
isRunning() {
|
|
3459
|
+
return this.running;
|
|
3460
|
+
}
|
|
3461
|
+
/**
|
|
3462
|
+
* Force a graduation evaluation run
|
|
3463
|
+
*/
|
|
3464
|
+
async forceRun() {
|
|
3465
|
+
return await this.runGraduation();
|
|
3466
|
+
}
|
|
3467
|
+
/**
|
|
3468
|
+
* Schedule the next graduation check
|
|
3469
|
+
*/
|
|
3470
|
+
scheduleNext() {
|
|
3471
|
+
if (!this.running)
|
|
3472
|
+
return;
|
|
3473
|
+
this.timeout = setTimeout(
|
|
3474
|
+
() => this.run(),
|
|
3475
|
+
this.config.evaluationIntervalMs
|
|
3476
|
+
);
|
|
3477
|
+
}
|
|
3478
|
+
/**
|
|
3479
|
+
* Run graduation evaluation
|
|
3480
|
+
*/
|
|
3481
|
+
async run() {
|
|
3482
|
+
if (!this.running)
|
|
3483
|
+
return;
|
|
3484
|
+
try {
|
|
3485
|
+
await this.runGraduation();
|
|
3486
|
+
} catch (error) {
|
|
3487
|
+
console.error("Graduation error:", error);
|
|
3488
|
+
}
|
|
3489
|
+
this.scheduleNext();
|
|
3490
|
+
}
|
|
3491
|
+
/**
|
|
3492
|
+
* Perform graduation evaluation across all levels
|
|
3493
|
+
*/
|
|
3494
|
+
async runGraduation() {
|
|
3495
|
+
const result = {
|
|
3496
|
+
evaluated: 0,
|
|
3497
|
+
graduated: 0,
|
|
3498
|
+
byLevel: {}
|
|
3499
|
+
};
|
|
3500
|
+
const levels = ["L0", "L1", "L2", "L3"];
|
|
3501
|
+
const now = Date.now();
|
|
3502
|
+
for (const level of levels) {
|
|
3503
|
+
const events = await this.eventStore.getEventsByLevel(level, {
|
|
3504
|
+
limit: this.config.batchSize
|
|
3505
|
+
});
|
|
3506
|
+
let levelGraduated = 0;
|
|
3507
|
+
for (const event of events) {
|
|
3508
|
+
const lastEval = this.lastEvaluated.get(event.id);
|
|
3509
|
+
if (lastEval && now - lastEval < this.config.cooldownMs) {
|
|
3510
|
+
continue;
|
|
3511
|
+
}
|
|
3512
|
+
result.evaluated++;
|
|
3513
|
+
this.lastEvaluated.set(event.id, now);
|
|
3514
|
+
const gradResult = await this.graduation.evaluateGraduation(event.id, level);
|
|
3515
|
+
if (gradResult.success) {
|
|
3516
|
+
result.graduated++;
|
|
3517
|
+
levelGraduated++;
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
if (levelGraduated > 0) {
|
|
3521
|
+
result.byLevel[level] = levelGraduated;
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
if (this.lastEvaluated.size > 1e3) {
|
|
3525
|
+
const entries = Array.from(this.lastEvaluated.entries());
|
|
3526
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
3527
|
+
this.lastEvaluated = new Map(entries.slice(0, 1e3));
|
|
3528
|
+
}
|
|
3529
|
+
return result;
|
|
3530
|
+
}
|
|
3531
|
+
};
|
|
3532
|
+
function createGraduationWorker(eventStore, graduation, config) {
|
|
3533
|
+
return new GraduationWorker(
|
|
3534
|
+
eventStore,
|
|
3535
|
+
graduation,
|
|
3536
|
+
{ ...DEFAULT_CONFIG4, ...config }
|
|
3537
|
+
);
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3354
3540
|
// src/services/memory-service.ts
|
|
3355
3541
|
function normalizePath(projectPath) {
|
|
3356
3542
|
const expanded = projectPath.startsWith("~") ? path.join(os.homedir(), projectPath.slice(1)) : projectPath;
|
|
@@ -3378,6 +3564,7 @@ var MemoryService = class {
|
|
|
3378
3564
|
retriever;
|
|
3379
3565
|
graduation;
|
|
3380
3566
|
vectorWorker = null;
|
|
3567
|
+
graduationWorker = null;
|
|
3381
3568
|
initialized = false;
|
|
3382
3569
|
// Endless Mode components
|
|
3383
3570
|
workingSetStore = null;
|
|
@@ -3392,14 +3579,16 @@ var MemoryService = class {
|
|
|
3392
3579
|
sharedPromoter = null;
|
|
3393
3580
|
sharedStoreConfig = null;
|
|
3394
3581
|
projectHash = null;
|
|
3582
|
+
readOnly;
|
|
3395
3583
|
constructor(config) {
|
|
3396
3584
|
const storagePath = this.expandPath(config.storagePath);
|
|
3397
|
-
|
|
3585
|
+
this.readOnly = config.readOnly ?? false;
|
|
3586
|
+
if (!this.readOnly && !fs.existsSync(storagePath)) {
|
|
3398
3587
|
fs.mkdirSync(storagePath, { recursive: true });
|
|
3399
3588
|
}
|
|
3400
3589
|
this.projectHash = config.projectHash || null;
|
|
3401
3590
|
this.sharedStoreConfig = config.sharedStoreConfig ?? { enabled: true };
|
|
3402
|
-
this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"));
|
|
3591
|
+
this.eventStore = new EventStore(path.join(storagePath, "events.duckdb"), { readOnly: this.readOnly });
|
|
3403
3592
|
this.vectorStore = new VectorStore(path.join(storagePath, "vectors"));
|
|
3404
3593
|
this.embedder = config.embeddingModel ? new Embedder(config.embeddingModel) : getDefaultEmbedder();
|
|
3405
3594
|
this.matcher = getDefaultMatcher();
|
|
@@ -3420,19 +3609,27 @@ var MemoryService = class {
|
|
|
3420
3609
|
await this.eventStore.initialize();
|
|
3421
3610
|
await this.vectorStore.initialize();
|
|
3422
3611
|
await this.embedder.initialize();
|
|
3423
|
-
this.
|
|
3424
|
-
this.
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
this.
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3612
|
+
if (!this.readOnly) {
|
|
3613
|
+
this.vectorWorker = createVectorWorker(
|
|
3614
|
+
this.eventStore,
|
|
3615
|
+
this.vectorStore,
|
|
3616
|
+
this.embedder
|
|
3617
|
+
);
|
|
3618
|
+
this.vectorWorker.start();
|
|
3619
|
+
this.retriever.setGraduationPipeline(this.graduation);
|
|
3620
|
+
this.graduationWorker = createGraduationWorker(
|
|
3621
|
+
this.eventStore,
|
|
3622
|
+
this.graduation
|
|
3623
|
+
);
|
|
3624
|
+
this.graduationWorker.start();
|
|
3625
|
+
const savedMode = await this.eventStore.getEndlessConfig("mode");
|
|
3626
|
+
if (savedMode === "endless") {
|
|
3627
|
+
this.endlessMode = "endless";
|
|
3628
|
+
await this.initializeEndlessMode();
|
|
3629
|
+
}
|
|
3630
|
+
if (this.sharedStoreConfig?.enabled !== false) {
|
|
3631
|
+
await this.initializeSharedStore();
|
|
3632
|
+
}
|
|
3436
3633
|
}
|
|
3437
3634
|
this.initialized = true;
|
|
3438
3635
|
}
|
|
@@ -3613,6 +3810,20 @@ var MemoryService = class {
|
|
|
3613
3810
|
}
|
|
3614
3811
|
return 0;
|
|
3615
3812
|
}
|
|
3813
|
+
/**
|
|
3814
|
+
* Get events by memory level
|
|
3815
|
+
*/
|
|
3816
|
+
async getEventsByLevel(level, options) {
|
|
3817
|
+
await this.initialize();
|
|
3818
|
+
return this.eventStore.getEventsByLevel(level, options);
|
|
3819
|
+
}
|
|
3820
|
+
/**
|
|
3821
|
+
* Get memory level for a specific event
|
|
3822
|
+
*/
|
|
3823
|
+
async getEventLevel(eventId) {
|
|
3824
|
+
await this.initialize();
|
|
3825
|
+
return this.eventStore.getEventLevel(eventId);
|
|
3826
|
+
}
|
|
3616
3827
|
/**
|
|
3617
3828
|
* Format retrieval results as context for Claude
|
|
3618
3829
|
*/
|
|
@@ -3795,6 +4006,22 @@ var MemoryService = class {
|
|
|
3795
4006
|
return [];
|
|
3796
4007
|
return this.consolidatedStore.getAll({ limit });
|
|
3797
4008
|
}
|
|
4009
|
+
/**
|
|
4010
|
+
* Get most accessed consolidated memories
|
|
4011
|
+
*/
|
|
4012
|
+
async getMostAccessedMemories(limit = 10) {
|
|
4013
|
+
if (!this.consolidatedStore)
|
|
4014
|
+
return [];
|
|
4015
|
+
return this.consolidatedStore.getMostAccessed(limit);
|
|
4016
|
+
}
|
|
4017
|
+
/**
|
|
4018
|
+
* Mark a consolidated memory as accessed
|
|
4019
|
+
*/
|
|
4020
|
+
async markMemoryAccessed(memoryId) {
|
|
4021
|
+
if (!this.consolidatedStore)
|
|
4022
|
+
return;
|
|
4023
|
+
await this.consolidatedStore.markAccessed(memoryId);
|
|
4024
|
+
}
|
|
3798
4025
|
/**
|
|
3799
4026
|
* Calculate continuity score for current context
|
|
3800
4027
|
*/
|
|
@@ -3882,10 +4109,28 @@ var MemoryService = class {
|
|
|
3882
4109
|
}
|
|
3883
4110
|
return parts.join("\n");
|
|
3884
4111
|
}
|
|
4112
|
+
/**
|
|
4113
|
+
* Force a graduation evaluation run
|
|
4114
|
+
*/
|
|
4115
|
+
async forceGraduation() {
|
|
4116
|
+
if (!this.graduationWorker) {
|
|
4117
|
+
return { evaluated: 0, graduated: 0, byLevel: {} };
|
|
4118
|
+
}
|
|
4119
|
+
return this.graduationWorker.forceRun();
|
|
4120
|
+
}
|
|
4121
|
+
/**
|
|
4122
|
+
* Record access to a memory event (for graduation scoring)
|
|
4123
|
+
*/
|
|
4124
|
+
recordMemoryAccess(eventId, sessionId, confidence = 1) {
|
|
4125
|
+
this.graduation.recordAccess(eventId, sessionId, confidence);
|
|
4126
|
+
}
|
|
3885
4127
|
/**
|
|
3886
4128
|
* Shutdown service
|
|
3887
4129
|
*/
|
|
3888
4130
|
async shutdown() {
|
|
4131
|
+
if (this.graduationWorker) {
|
|
4132
|
+
this.graduationWorker.stop();
|
|
4133
|
+
}
|
|
3889
4134
|
if (this.consolidationWorker) {
|
|
3890
4135
|
this.consolidationWorker.stop();
|
|
3891
4136
|
}
|
|
@@ -3908,14 +4153,11 @@ var MemoryService = class {
|
|
|
3908
4153
|
}
|
|
3909
4154
|
};
|
|
3910
4155
|
var serviceCache = /* @__PURE__ */ new Map();
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
}));
|
|
3917
|
-
}
|
|
3918
|
-
return serviceCache.get(GLOBAL_KEY);
|
|
4156
|
+
function getReadOnlyMemoryService() {
|
|
4157
|
+
return new MemoryService({
|
|
4158
|
+
storagePath: "~/.claude-code/memory",
|
|
4159
|
+
readOnly: true
|
|
4160
|
+
});
|
|
3919
4161
|
}
|
|
3920
4162
|
function getMemoryServiceForProject(projectPath, sharedStoreConfig) {
|
|
3921
4163
|
const hash = hashProjectPath(projectPath);
|
|
@@ -3935,8 +4177,8 @@ var sessionsRouter = new Hono();
|
|
|
3935
4177
|
sessionsRouter.get("/", async (c) => {
|
|
3936
4178
|
const page = parseInt(c.req.query("page") || "1", 10);
|
|
3937
4179
|
const pageSize = parseInt(c.req.query("pageSize") || "20", 10);
|
|
4180
|
+
const memoryService = getReadOnlyMemoryService();
|
|
3938
4181
|
try {
|
|
3939
|
-
const memoryService = getDefaultMemoryService();
|
|
3940
4182
|
await memoryService.initialize();
|
|
3941
4183
|
const recentEvents = await memoryService.getRecentEvents(1e3);
|
|
3942
4184
|
const sessionMap = /* @__PURE__ */ new Map();
|
|
@@ -3973,12 +4215,14 @@ sessionsRouter.get("/", async (c) => {
|
|
|
3973
4215
|
});
|
|
3974
4216
|
} catch (error) {
|
|
3975
4217
|
return c.json({ error: error.message }, 500);
|
|
4218
|
+
} finally {
|
|
4219
|
+
await memoryService.shutdown();
|
|
3976
4220
|
}
|
|
3977
4221
|
});
|
|
3978
4222
|
sessionsRouter.get("/:id", async (c) => {
|
|
3979
4223
|
const { id } = c.req.param();
|
|
4224
|
+
const memoryService = getReadOnlyMemoryService();
|
|
3980
4225
|
try {
|
|
3981
|
-
const memoryService = getDefaultMemoryService();
|
|
3982
4226
|
await memoryService.initialize();
|
|
3983
4227
|
const events = await memoryService.getSessionHistory(id);
|
|
3984
4228
|
if (events.length === 0) {
|
|
@@ -4007,6 +4251,8 @@ sessionsRouter.get("/:id", async (c) => {
|
|
|
4007
4251
|
});
|
|
4008
4252
|
} catch (error) {
|
|
4009
4253
|
return c.json({ error: error.message }, 500);
|
|
4254
|
+
} finally {
|
|
4255
|
+
await memoryService.shutdown();
|
|
4010
4256
|
}
|
|
4011
4257
|
});
|
|
4012
4258
|
|
|
@@ -4018,8 +4264,8 @@ eventsRouter.get("/", async (c) => {
|
|
|
4018
4264
|
const eventType = c.req.query("type");
|
|
4019
4265
|
const limit = parseInt(c.req.query("limit") || "100", 10);
|
|
4020
4266
|
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
4267
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4021
4268
|
try {
|
|
4022
|
-
const memoryService = getDefaultMemoryService();
|
|
4023
4269
|
await memoryService.initialize();
|
|
4024
4270
|
let events = await memoryService.getRecentEvents(limit + offset + 1e3);
|
|
4025
4271
|
if (sessionId) {
|
|
@@ -4046,12 +4292,14 @@ eventsRouter.get("/", async (c) => {
|
|
|
4046
4292
|
});
|
|
4047
4293
|
} catch (error) {
|
|
4048
4294
|
return c.json({ error: error.message }, 500);
|
|
4295
|
+
} finally {
|
|
4296
|
+
await memoryService.shutdown();
|
|
4049
4297
|
}
|
|
4050
4298
|
});
|
|
4051
4299
|
eventsRouter.get("/:id", async (c) => {
|
|
4052
4300
|
const { id } = c.req.param();
|
|
4301
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4053
4302
|
try {
|
|
4054
|
-
const memoryService = getDefaultMemoryService();
|
|
4055
4303
|
await memoryService.initialize();
|
|
4056
4304
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4057
4305
|
const event = recentEvents.find((e) => e.id === id);
|
|
@@ -4081,6 +4329,8 @@ eventsRouter.get("/:id", async (c) => {
|
|
|
4081
4329
|
});
|
|
4082
4330
|
} catch (error) {
|
|
4083
4331
|
return c.json({ error: error.message }, 500);
|
|
4332
|
+
} finally {
|
|
4333
|
+
await memoryService.shutdown();
|
|
4084
4334
|
}
|
|
4085
4335
|
});
|
|
4086
4336
|
|
|
@@ -4088,12 +4338,12 @@ eventsRouter.get("/:id", async (c) => {
|
|
|
4088
4338
|
import { Hono as Hono3 } from "hono";
|
|
4089
4339
|
var searchRouter = new Hono3();
|
|
4090
4340
|
searchRouter.post("/", async (c) => {
|
|
4341
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4091
4342
|
try {
|
|
4092
4343
|
const body = await c.req.json();
|
|
4093
4344
|
if (!body.query) {
|
|
4094
4345
|
return c.json({ error: "Query is required" }, 400);
|
|
4095
4346
|
}
|
|
4096
|
-
const memoryService = getDefaultMemoryService();
|
|
4097
4347
|
await memoryService.initialize();
|
|
4098
4348
|
const startTime = Date.now();
|
|
4099
4349
|
const result = await memoryService.retrieveMemories(body.query, {
|
|
@@ -4122,6 +4372,8 @@ searchRouter.post("/", async (c) => {
|
|
|
4122
4372
|
});
|
|
4123
4373
|
} catch (error) {
|
|
4124
4374
|
return c.json({ error: error.message }, 500);
|
|
4375
|
+
} finally {
|
|
4376
|
+
await memoryService.shutdown();
|
|
4125
4377
|
}
|
|
4126
4378
|
});
|
|
4127
4379
|
searchRouter.get("/", async (c) => {
|
|
@@ -4130,8 +4382,8 @@ searchRouter.get("/", async (c) => {
|
|
|
4130
4382
|
return c.json({ error: 'Query parameter "q" is required' }, 400);
|
|
4131
4383
|
}
|
|
4132
4384
|
const topK = parseInt(c.req.query("topK") || "5", 10);
|
|
4385
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4133
4386
|
try {
|
|
4134
|
-
const memoryService = getDefaultMemoryService();
|
|
4135
4387
|
await memoryService.initialize();
|
|
4136
4388
|
const result = await memoryService.retrieveMemories(query, { topK });
|
|
4137
4389
|
return c.json({
|
|
@@ -4149,6 +4401,8 @@ searchRouter.get("/", async (c) => {
|
|
|
4149
4401
|
});
|
|
4150
4402
|
} catch (error) {
|
|
4151
4403
|
return c.json({ error: error.message }, 500);
|
|
4404
|
+
} finally {
|
|
4405
|
+
await memoryService.shutdown();
|
|
4152
4406
|
}
|
|
4153
4407
|
});
|
|
4154
4408
|
|
|
@@ -4156,8 +4410,8 @@ searchRouter.get("/", async (c) => {
|
|
|
4156
4410
|
import { Hono as Hono4 } from "hono";
|
|
4157
4411
|
var statsRouter = new Hono4();
|
|
4158
4412
|
statsRouter.get("/shared", async (c) => {
|
|
4413
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4159
4414
|
try {
|
|
4160
|
-
const memoryService = getDefaultMemoryService();
|
|
4161
4415
|
await memoryService.initialize();
|
|
4162
4416
|
const sharedStats = await memoryService.getSharedStoreStats();
|
|
4163
4417
|
return c.json({
|
|
@@ -4175,12 +4429,14 @@ statsRouter.get("/shared", async (c) => {
|
|
|
4175
4429
|
totalUsageCount: 0,
|
|
4176
4430
|
lastUpdated: null
|
|
4177
4431
|
});
|
|
4432
|
+
} finally {
|
|
4433
|
+
await memoryService.shutdown();
|
|
4178
4434
|
}
|
|
4179
4435
|
});
|
|
4180
4436
|
statsRouter.get("/endless", async (c) => {
|
|
4437
|
+
const projectPath = c.req.query("project") || process.cwd();
|
|
4438
|
+
const memoryService = getMemoryServiceForProject(projectPath);
|
|
4181
4439
|
try {
|
|
4182
|
-
const projectPath = c.req.query("project") || process.cwd();
|
|
4183
|
-
const memoryService = getMemoryServiceForProject(projectPath);
|
|
4184
4440
|
await memoryService.initialize();
|
|
4185
4441
|
const status = await memoryService.getEndlessModeStatus();
|
|
4186
4442
|
return c.json({
|
|
@@ -4198,11 +4454,48 @@ statsRouter.get("/endless", async (c) => {
|
|
|
4198
4454
|
consolidatedCount: 0,
|
|
4199
4455
|
lastConsolidation: null
|
|
4200
4456
|
});
|
|
4457
|
+
} finally {
|
|
4458
|
+
await memoryService.shutdown();
|
|
4459
|
+
}
|
|
4460
|
+
});
|
|
4461
|
+
statsRouter.get("/levels/:level", async (c) => {
|
|
4462
|
+
const { level } = c.req.param();
|
|
4463
|
+
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
4464
|
+
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
4465
|
+
const validLevels = ["L0", "L1", "L2", "L3", "L4"];
|
|
4466
|
+
if (!validLevels.includes(level)) {
|
|
4467
|
+
return c.json({ error: `Invalid level. Must be one of: ${validLevels.join(", ")}` }, 400);
|
|
4468
|
+
}
|
|
4469
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4470
|
+
try {
|
|
4471
|
+
await memoryService.initialize();
|
|
4472
|
+
const events = await memoryService.getEventsByLevel(level, { limit, offset });
|
|
4473
|
+
const stats = await memoryService.getStats();
|
|
4474
|
+
const levelStat = stats.levelStats.find((s) => s.level === level);
|
|
4475
|
+
return c.json({
|
|
4476
|
+
level,
|
|
4477
|
+
events: events.map((e) => ({
|
|
4478
|
+
id: e.id,
|
|
4479
|
+
eventType: e.eventType,
|
|
4480
|
+
sessionId: e.sessionId,
|
|
4481
|
+
timestamp: e.timestamp.toISOString(),
|
|
4482
|
+
content: e.content.slice(0, 500) + (e.content.length > 500 ? "..." : ""),
|
|
4483
|
+
metadata: e.metadata
|
|
4484
|
+
})),
|
|
4485
|
+
total: levelStat?.count || 0,
|
|
4486
|
+
limit,
|
|
4487
|
+
offset,
|
|
4488
|
+
hasMore: events.length === limit
|
|
4489
|
+
});
|
|
4490
|
+
} catch (error) {
|
|
4491
|
+
return c.json({ error: error.message }, 500);
|
|
4492
|
+
} finally {
|
|
4493
|
+
await memoryService.shutdown();
|
|
4201
4494
|
}
|
|
4202
4495
|
});
|
|
4203
4496
|
statsRouter.get("/", async (c) => {
|
|
4497
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4204
4498
|
try {
|
|
4205
|
-
const memoryService = getDefaultMemoryService();
|
|
4206
4499
|
await memoryService.initialize();
|
|
4207
4500
|
const stats = await memoryService.getStats();
|
|
4208
4501
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
@@ -4239,12 +4532,43 @@ statsRouter.get("/", async (c) => {
|
|
|
4239
4532
|
});
|
|
4240
4533
|
} catch (error) {
|
|
4241
4534
|
return c.json({ error: error.message }, 500);
|
|
4535
|
+
} finally {
|
|
4536
|
+
await memoryService.shutdown();
|
|
4537
|
+
}
|
|
4538
|
+
});
|
|
4539
|
+
statsRouter.get("/most-accessed", async (c) => {
|
|
4540
|
+
const limit = parseInt(c.req.query("limit") || "10", 10);
|
|
4541
|
+
const projectPath = c.req.query("project") || process.cwd();
|
|
4542
|
+
const memoryService = getMemoryServiceForProject(projectPath);
|
|
4543
|
+
try {
|
|
4544
|
+
await memoryService.initialize();
|
|
4545
|
+
const memories = await memoryService.getMostAccessedMemories(limit);
|
|
4546
|
+
return c.json({
|
|
4547
|
+
memories: memories.map((m) => ({
|
|
4548
|
+
memoryId: m.memoryId,
|
|
4549
|
+
summary: m.summary,
|
|
4550
|
+
topics: m.topics,
|
|
4551
|
+
accessCount: m.accessCount,
|
|
4552
|
+
lastAccessed: m.accessedAt?.toISOString() || null,
|
|
4553
|
+
confidence: m.confidence,
|
|
4554
|
+
createdAt: m.createdAt.toISOString()
|
|
4555
|
+
})),
|
|
4556
|
+
total: memories.length
|
|
4557
|
+
});
|
|
4558
|
+
} catch (error) {
|
|
4559
|
+
return c.json({
|
|
4560
|
+
memories: [],
|
|
4561
|
+
total: 0,
|
|
4562
|
+
error: error.message
|
|
4563
|
+
});
|
|
4564
|
+
} finally {
|
|
4565
|
+
await memoryService.shutdown();
|
|
4242
4566
|
}
|
|
4243
4567
|
});
|
|
4244
4568
|
statsRouter.get("/timeline", async (c) => {
|
|
4245
4569
|
const days = parseInt(c.req.query("days") || "7", 10);
|
|
4570
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4246
4571
|
try {
|
|
4247
|
-
const memoryService = getDefaultMemoryService();
|
|
4248
4572
|
await memoryService.initialize();
|
|
4249
4573
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4250
4574
|
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
|
|
@@ -4269,8 +4593,46 @@ statsRouter.get("/timeline", async (c) => {
|
|
|
4269
4593
|
});
|
|
4270
4594
|
} catch (error) {
|
|
4271
4595
|
return c.json({ error: error.message }, 500);
|
|
4596
|
+
} finally {
|
|
4597
|
+
await memoryService.shutdown();
|
|
4272
4598
|
}
|
|
4273
4599
|
});
|
|
4600
|
+
statsRouter.post("/graduation/run", async (c) => {
|
|
4601
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4602
|
+
try {
|
|
4603
|
+
await memoryService.initialize();
|
|
4604
|
+
const result = await memoryService.forceGraduation();
|
|
4605
|
+
return c.json({
|
|
4606
|
+
success: true,
|
|
4607
|
+
evaluated: result.evaluated,
|
|
4608
|
+
graduated: result.graduated,
|
|
4609
|
+
byLevel: result.byLevel
|
|
4610
|
+
});
|
|
4611
|
+
} catch (error) {
|
|
4612
|
+
return c.json({
|
|
4613
|
+
success: false,
|
|
4614
|
+
error: error.message
|
|
4615
|
+
}, 500);
|
|
4616
|
+
} finally {
|
|
4617
|
+
await memoryService.shutdown();
|
|
4618
|
+
}
|
|
4619
|
+
});
|
|
4620
|
+
statsRouter.get("/graduation", async (c) => {
|
|
4621
|
+
return c.json({
|
|
4622
|
+
criteria: {
|
|
4623
|
+
L0toL1: { minAccessCount: 1, minConfidence: 0.5, minCrossSessionRefs: 0, maxAgeDays: 30 },
|
|
4624
|
+
L1toL2: { minAccessCount: 3, minConfidence: 0.7, minCrossSessionRefs: 1, maxAgeDays: 60 },
|
|
4625
|
+
L2toL3: { minAccessCount: 5, minConfidence: 0.85, minCrossSessionRefs: 2, maxAgeDays: 90 },
|
|
4626
|
+
L3toL4: { minAccessCount: 10, minConfidence: 0.92, minCrossSessionRefs: 3, maxAgeDays: 180 }
|
|
4627
|
+
},
|
|
4628
|
+
description: {
|
|
4629
|
+
accessCount: "Number of times the memory was retrieved/referenced",
|
|
4630
|
+
confidence: "Match confidence score when retrieved (0.0-1.0)",
|
|
4631
|
+
crossSessionRefs: "Number of different sessions that referenced this memory",
|
|
4632
|
+
maxAgeDays: "Maximum days since last access (prevents stale promotion)"
|
|
4633
|
+
}
|
|
4634
|
+
});
|
|
4635
|
+
});
|
|
4274
4636
|
|
|
4275
4637
|
// src/server/api/citations.ts
|
|
4276
4638
|
import { Hono as Hono5 } from "hono";
|
|
@@ -4297,8 +4659,8 @@ var citationsRouter = new Hono5();
|
|
|
4297
4659
|
citationsRouter.get("/:id", async (c) => {
|
|
4298
4660
|
const { id } = c.req.param();
|
|
4299
4661
|
const citationId = parseCitationId(id) || id;
|
|
4662
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4300
4663
|
try {
|
|
4301
|
-
const memoryService = getDefaultMemoryService();
|
|
4302
4664
|
await memoryService.initialize();
|
|
4303
4665
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4304
4666
|
const event = recentEvents.find((e) => {
|
|
@@ -4324,13 +4686,15 @@ citationsRouter.get("/:id", async (c) => {
|
|
|
4324
4686
|
});
|
|
4325
4687
|
} catch (error) {
|
|
4326
4688
|
return c.json({ error: error.message }, 500);
|
|
4689
|
+
} finally {
|
|
4690
|
+
await memoryService.shutdown();
|
|
4327
4691
|
}
|
|
4328
4692
|
});
|
|
4329
4693
|
citationsRouter.get("/:id/related", async (c) => {
|
|
4330
4694
|
const { id } = c.req.param();
|
|
4331
4695
|
const citationId = parseCitationId(id) || id;
|
|
4696
|
+
const memoryService = getReadOnlyMemoryService();
|
|
4332
4697
|
try {
|
|
4333
|
-
const memoryService = getDefaultMemoryService();
|
|
4334
4698
|
await memoryService.initialize();
|
|
4335
4699
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4336
4700
|
const event = recentEvents.find((e) => {
|
|
@@ -4360,6 +4724,8 @@ citationsRouter.get("/:id/related", async (c) => {
|
|
|
4360
4724
|
});
|
|
4361
4725
|
} catch (error) {
|
|
4362
4726
|
return c.json({ error: error.message }, 500);
|
|
4727
|
+
} finally {
|
|
4728
|
+
await memoryService.shutdown();
|
|
4363
4729
|
}
|
|
4364
4730
|
});
|
|
4365
4731
|
|
|
@@ -4372,7 +4738,7 @@ app.use("/*", cors());
|
|
|
4372
4738
|
app.use("/*", logger());
|
|
4373
4739
|
app.route("/api", apiRouter);
|
|
4374
4740
|
app.get("/health", (c) => c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
4375
|
-
var uiPath = path2.join(
|
|
4741
|
+
var uiPath = path2.join(__dirname, "../../dist/ui");
|
|
4376
4742
|
if (fs2.existsSync(uiPath)) {
|
|
4377
4743
|
app.use("/*", serveStatic({ root: uiPath }));
|
|
4378
4744
|
}
|
|
@@ -4388,17 +4754,17 @@ function startServer(port = 37777) {
|
|
|
4388
4754
|
if (serverInstance) {
|
|
4389
4755
|
return serverInstance;
|
|
4390
4756
|
}
|
|
4391
|
-
serverInstance =
|
|
4392
|
-
|
|
4757
|
+
serverInstance = serve({
|
|
4758
|
+
fetch: app.fetch,
|
|
4393
4759
|
port,
|
|
4394
|
-
|
|
4760
|
+
hostname: "127.0.0.1"
|
|
4395
4761
|
});
|
|
4396
4762
|
console.log(`\u{1F9E0} Code Memory viewer started at http://localhost:${port}`);
|
|
4397
4763
|
return serverInstance;
|
|
4398
4764
|
}
|
|
4399
4765
|
function stopServer() {
|
|
4400
4766
|
if (serverInstance) {
|
|
4401
|
-
serverInstance.
|
|
4767
|
+
serverInstance.close();
|
|
4402
4768
|
serverInstance = null;
|
|
4403
4769
|
}
|
|
4404
4770
|
}
|
|
@@ -4410,7 +4776,8 @@ async function isServerRunning(port = 37777) {
|
|
|
4410
4776
|
return false;
|
|
4411
4777
|
}
|
|
4412
4778
|
}
|
|
4413
|
-
|
|
4779
|
+
var isMainModule = process.argv[1]?.includes("server/index") || process.argv[1]?.endsWith("server.js");
|
|
4780
|
+
if (isMainModule) {
|
|
4414
4781
|
const port = parseInt(process.env.PORT || "37777", 10);
|
|
4415
4782
|
startServer(port);
|
|
4416
4783
|
}
|