claude-memory-layer 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/.history/package_20260201192048.json +47 -0
  3. package/dist/cli/index.js +569 -39
  4. package/dist/cli/index.js.map +4 -4
  5. package/dist/core/index.js +192 -5
  6. package/dist/core/index.js.map +4 -4
  7. package/dist/hooks/session-end.js +262 -18
  8. package/dist/hooks/session-end.js.map +4 -4
  9. package/dist/hooks/session-start.js +262 -18
  10. package/dist/hooks/session-start.js.map +4 -4
  11. package/dist/hooks/stop.js +262 -18
  12. package/dist/hooks/stop.js.map +4 -4
  13. package/dist/hooks/user-prompt-submit.js +262 -18
  14. package/dist/hooks/user-prompt-submit.js.map +4 -4
  15. package/dist/server/api/index.js +404 -39
  16. package/dist/server/api/index.js.map +4 -4
  17. package/dist/server/index.js +413 -46
  18. package/dist/server/index.js.map +4 -4
  19. package/dist/services/memory-service.js +269 -18
  20. package/dist/services/memory-service.js.map +4 -4
  21. package/dist/ui/index.html +495 -15
  22. package/package.json +2 -1
  23. package/scripts/build.ts +3 -2
  24. package/src/cli/index.ts +226 -0
  25. package/src/core/db-wrapper.ts +8 -1
  26. package/src/core/event-store.ts +52 -3
  27. package/src/core/graduation-worker.ts +171 -0
  28. package/src/core/graduation.ts +15 -2
  29. package/src/core/index.ts +1 -0
  30. package/src/core/retriever.ts +18 -0
  31. package/src/server/api/citations.ts +7 -3
  32. package/src/server/api/events.ts +7 -3
  33. package/src/server/api/search.ts +7 -3
  34. package/src/server/api/sessions.ts +7 -3
  35. package/src/server/api/stats.ts +129 -12
  36. package/src/server/index.ts +18 -9
  37. package/src/services/memory-service.ts +107 -19
  38. package/src/ui/index.html +495 -15
@@ -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 { serveStatic } from "hono/bun";
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.db = createDatabase(dbPath);
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
- if (!fs.existsSync(storagePath)) {
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.vectorWorker = createVectorWorker(
3424
- this.eventStore,
3425
- this.vectorStore,
3426
- this.embedder
3427
- );
3428
- this.vectorWorker.start();
3429
- const savedMode = await this.eventStore.getEndlessConfig("mode");
3430
- if (savedMode === "endless") {
3431
- this.endlessMode = "endless";
3432
- await this.initializeEndlessMode();
3433
- }
3434
- if (this.sharedStoreConfig?.enabled !== false) {
3435
- await this.initializeSharedStore();
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
- var GLOBAL_KEY = "__global__";
3912
- function getDefaultMemoryService() {
3913
- if (!serviceCache.has(GLOBAL_KEY)) {
3914
- serviceCache.set(GLOBAL_KEY, new MemoryService({
3915
- storagePath: "~/.claude-code/memory"
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(import.meta.dir, "../../dist/ui");
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 = Bun.serve({
4392
- hostname: "127.0.0.1",
4757
+ serverInstance = serve({
4758
+ fetch: app.fetch,
4393
4759
  port,
4394
- fetch: app.fetch
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.stop();
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
- if (import.meta.main) {
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
  }