claude-memory-layer 1.0.37 → 1.0.38

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.
@@ -10,8 +10,8 @@ const __dirname = dirname(__filename);
10
10
  import * as os5 from "os";
11
11
 
12
12
  // src/core/engine/memory-service-composition.ts
13
- import * as os from "os";
14
- import * as path6 from "path";
13
+ import * as os2 from "os";
14
+ import * as path7 from "path";
15
15
 
16
16
  // src/core/metadata-extractor.ts
17
17
  function createToolObservationEmbedding(toolName, metadata, success) {
@@ -1522,8 +1522,8 @@ function createEndlessMemoryServices(options) {
1522
1522
  }
1523
1523
 
1524
1524
  // src/core/engine/memory-engine-services.ts
1525
- import * as fs5 from "fs";
1526
- import * as path4 from "path";
1525
+ import * as fs6 from "fs";
1526
+ import * as path5 from "path";
1527
1527
 
1528
1528
  // src/extensions/vector/embedder.ts
1529
1529
  var DEFAULT_EMBEDDING_MODEL = "Xenova/multilingual-e5-small";
@@ -2201,14 +2201,39 @@ function makeDedupeKey(content, sessionId) {
2201
2201
  return `${sessionId}:${contentHash}`;
2202
2202
  }
2203
2203
 
2204
+ // src/core/sqlite-event-store.ts
2205
+ import * as nodePath2 from "path";
2206
+
2207
+ // src/core/registry/project-path.ts
2208
+ import * as crypto2 from "crypto";
2209
+ import * as fs3 from "fs";
2210
+ import * as os from "os";
2211
+ import * as path3 from "path";
2212
+ function normalizeProjectPath(projectPath) {
2213
+ const expanded = projectPath.startsWith("~") ? path3.join(os.homedir(), projectPath.slice(1)) : projectPath;
2214
+ try {
2215
+ return fs3.realpathSync(expanded);
2216
+ } catch {
2217
+ return path3.resolve(expanded);
2218
+ }
2219
+ }
2220
+ function hashProjectPath(projectPath) {
2221
+ const normalizedPath = normalizeProjectPath(projectPath);
2222
+ return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
2223
+ }
2224
+ function getProjectStoragePath(projectPath) {
2225
+ const hash = hashProjectPath(projectPath);
2226
+ return path3.join(os.homedir(), ".claude-code", "memory", "projects", hash);
2227
+ }
2228
+
2204
2229
  // src/core/sqlite-wrapper.ts
2205
2230
  import Database from "better-sqlite3";
2206
- import * as fs3 from "fs";
2231
+ import * as fs4 from "fs";
2207
2232
  import * as nodePath from "path";
2208
2233
  function createSQLiteDatabase(path11, options) {
2209
2234
  const dir = nodePath.dirname(path11);
2210
- if (!fs3.existsSync(dir)) {
2211
- fs3.mkdirSync(dir, { recursive: true });
2235
+ if (!fs4.existsSync(dir)) {
2236
+ fs4.mkdirSync(dir, { recursive: true });
2212
2237
  }
2213
2238
  const db = new Database(path11, {
2214
2239
  readonly: options?.readonly ?? false
@@ -2257,8 +2282,8 @@ function toSQLiteTimestamp(date) {
2257
2282
  }
2258
2283
 
2259
2284
  // src/core/markdown-mirror.ts
2260
- import * as fs4 from "fs/promises";
2261
- import * as path3 from "path";
2285
+ import * as fs5 from "fs/promises";
2286
+ import * as path4 from "path";
2262
2287
  var DEFAULT_NAMESPACE = "default";
2263
2288
  var DEFAULT_CATEGORY = "uncategorized";
2264
2289
  function sanitizeSegment2(input, fallback) {
@@ -2287,7 +2312,7 @@ function buildMirrorPath2(rootDir, event) {
2287
2312
  const yyyy = d.getFullYear();
2288
2313
  const mm = String(d.getMonth() + 1).padStart(2, "0");
2289
2314
  const dd = String(d.getDate()).padStart(2, "0");
2290
- return path3.join(rootDir, "memory", namespace, ...categories, `${yyyy}-${mm}-${dd}.md`);
2315
+ return path4.join(rootDir, "memory", namespace, ...categories, `${yyyy}-${mm}-${dd}.md`);
2291
2316
  }
2292
2317
  function formatMirrorEntry(event) {
2293
2318
  const category = Array.isArray(event.metadata?.categoryPath) ? event.metadata.categoryPath.join("/") : String(event.metadata?.category ?? event.eventType);
@@ -2308,8 +2333,8 @@ var MarkdownMirror2 = class {
2308
2333
  }
2309
2334
  async append(event) {
2310
2335
  const outPath = buildMirrorPath2(this.rootDir, event);
2311
- await fs4.mkdir(path3.dirname(outPath), { recursive: true });
2312
- await fs4.appendFile(outPath, formatMirrorEntry(event), "utf8");
2336
+ await fs5.mkdir(path4.dirname(outPath), { recursive: true });
2337
+ await fs5.appendFile(outPath, formatMirrorEntry(event), "utf8");
2313
2338
  return outPath;
2314
2339
  }
2315
2340
  };
@@ -2330,6 +2355,135 @@ function emptyOutboxRecoveryResult() {
2330
2355
  vector: { recoveredProcessing: 0, retriedFailed: 0 }
2331
2356
  };
2332
2357
  }
2358
+ function isRecord(value) {
2359
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2360
+ }
2361
+ function getNestedRecord(root, path11) {
2362
+ let cursor = root;
2363
+ for (const key of path11) {
2364
+ if (!isRecord(cursor))
2365
+ return void 0;
2366
+ cursor = cursor[key];
2367
+ }
2368
+ return isRecord(cursor) ? cursor : void 0;
2369
+ }
2370
+ function getNestedString(root, path11) {
2371
+ let cursor = root;
2372
+ for (const key of path11) {
2373
+ if (!isRecord(cursor))
2374
+ return void 0;
2375
+ cursor = cursor[key];
2376
+ }
2377
+ return typeof cursor === "string" && cursor.length > 0 ? cursor : void 0;
2378
+ }
2379
+ function metadataProjectHash(metadata) {
2380
+ return getNestedString(metadata, ["scope", "project", "hash"]);
2381
+ }
2382
+ function metadataProjectPaths(metadata) {
2383
+ const candidates = [
2384
+ getNestedString(metadata, ["projectPath"]),
2385
+ getNestedString(metadata, ["sourceProjectPath"]),
2386
+ getNestedString(metadata, ["scope", "project", "path"])
2387
+ ];
2388
+ const paths = [];
2389
+ for (const value of candidates) {
2390
+ if (value && !paths.includes(value))
2391
+ paths.push(value);
2392
+ }
2393
+ return paths;
2394
+ }
2395
+ function metadataProjectPath(metadata) {
2396
+ return metadataProjectPaths(metadata)[0];
2397
+ }
2398
+ function isActiveQuarantinedMetadata(metadata) {
2399
+ const quarantine = getNestedRecord(metadata, ["quarantine"]);
2400
+ return quarantine?.status === "active";
2401
+ }
2402
+ function activeQuarantineStatusExpression(column = "metadata") {
2403
+ return `COALESCE(json_extract(CASE WHEN json_valid(${column}) THEN ${column} ELSE '{}' END, '$.quarantine.status'), '')`;
2404
+ }
2405
+ function notActiveQuarantinedSql(column = "metadata") {
2406
+ return `${activeQuarantineStatusExpression(column)} != 'active'`;
2407
+ }
2408
+ function maybeQuarantinePredicate(options, column = "metadata") {
2409
+ return options?.includeQuarantined ? "1=1" : notActiveQuarantinedSql(column);
2410
+ }
2411
+ function safeParseMetadataValue(value) {
2412
+ if (!value)
2413
+ return void 0;
2414
+ if (typeof value === "object")
2415
+ return isRecord(value) ? value : void 0;
2416
+ if (typeof value !== "string")
2417
+ return void 0;
2418
+ try {
2419
+ const parsed = JSON.parse(value);
2420
+ return isRecord(parsed) ? parsed : void 0;
2421
+ } catch {
2422
+ return void 0;
2423
+ }
2424
+ }
2425
+ function isImportedOrLegacyScopedMetadata(metadata) {
2426
+ if (!metadata)
2427
+ return false;
2428
+ return Boolean(
2429
+ metadata.importedFrom || metadata.sourceSessionId || metadata.sourceSessionHash || metadata.hermesSource || metadata.projectPath || metadata.sourceProjectPath || metadata.source === "hermes" || metadata.source === "claude" || metadata.source === "codex"
2430
+ );
2431
+ }
2432
+ function addMetadataTag(metadata, tag) {
2433
+ const current = Array.isArray(metadata.tags) ? metadata.tags.filter((value) => typeof value === "string") : [];
2434
+ if (!current.includes(tag))
2435
+ metadata.tags = [...current, tag];
2436
+ }
2437
+ function buildRepairResult(projectHash, dryRun) {
2438
+ return {
2439
+ dryRun,
2440
+ projectHash,
2441
+ scanned: 0,
2442
+ repaired: 0,
2443
+ quarantined: 0,
2444
+ alreadyScoped: 0,
2445
+ skipped: 0,
2446
+ samples: []
2447
+ };
2448
+ }
2449
+ function normalizeRepoName(value) {
2450
+ return value.replace(/\.git$/i, "").trim().toLowerCase();
2451
+ }
2452
+ function projectBasename(projectPath) {
2453
+ if (!projectPath)
2454
+ return void 0;
2455
+ const trimmed = projectPath.replace(/[\\/]+$/, "");
2456
+ const basename2 = nodePath2.basename(trimmed);
2457
+ return basename2 ? normalizeRepoName(basename2) : void 0;
2458
+ }
2459
+ function isProjectScopeRepairExplanation(content) {
2460
+ const normalized = content.toLowerCase();
2461
+ const hasRepairContext = /project[- ]scope|mis[- ]scoped|quarantine|contamination|legacy|오염|격리|repair/.test(normalized);
2462
+ const hasExplanationContext = /example|detector|trap|not a .*project task|기억|메모리|설명|수정|검증/.test(normalized);
2463
+ return hasRepairContext && hasExplanationContext;
2464
+ }
2465
+ function hasConflictingContentProjectHint(content, projectPath) {
2466
+ const currentName = projectBasename(projectPath);
2467
+ if (!currentName)
2468
+ return false;
2469
+ if (isProjectScopeRepairExplanation(content))
2470
+ return false;
2471
+ const githubRepoPattern = /github\.com[:/]([^/\s`'"#)]+)\/([^/\s`'"#)]+)(?:\.git)?/gi;
2472
+ let githubMatch;
2473
+ while ((githubMatch = githubRepoPattern.exec(content)) !== null) {
2474
+ const repo = normalizeRepoName(githubMatch[2] || "");
2475
+ if (repo && repo !== currentName)
2476
+ return true;
2477
+ }
2478
+ const workspacePathPattern = /\/workspace\/([^/\s`'"#)]+)/gi;
2479
+ let workspaceMatch;
2480
+ while ((workspaceMatch = workspacePathPattern.exec(content)) !== null) {
2481
+ const repo = normalizeRepoName(workspaceMatch[1] || "");
2482
+ if (repo && repo !== currentName)
2483
+ return true;
2484
+ }
2485
+ return false;
2486
+ }
2333
2487
  var SQLiteEventStore = class {
2334
2488
  db;
2335
2489
  initialized = false;
@@ -2828,11 +2982,11 @@ var SQLiteEventStore = class {
2828
2982
  /**
2829
2983
  * Get events by session ID
2830
2984
  */
2831
- async getSessionEvents(sessionId) {
2985
+ async getSessionEvents(sessionId, options) {
2832
2986
  await this.initialize();
2833
2987
  const rows = sqliteAll(
2834
2988
  this.db,
2835
- `SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC`,
2989
+ `SELECT * FROM events WHERE session_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
2836
2990
  [sessionId]
2837
2991
  );
2838
2992
  return rows.map(this.rowToEvent);
@@ -2840,11 +2994,11 @@ var SQLiteEventStore = class {
2840
2994
  /**
2841
2995
  * Get recent events
2842
2996
  */
2843
- async getRecentEvents(limit = 100) {
2997
+ async getRecentEvents(limit = 100, options) {
2844
2998
  await this.initialize();
2845
2999
  const rows = sqliteAll(
2846
3000
  this.db,
2847
- `SELECT * FROM events ORDER BY timestamp DESC LIMIT ?`,
3001
+ `SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp DESC LIMIT ?`,
2848
3002
  [limit]
2849
3003
  );
2850
3004
  return rows.map(this.rowToEvent);
@@ -2852,11 +3006,11 @@ var SQLiteEventStore = class {
2852
3006
  /**
2853
3007
  * Get event by ID
2854
3008
  */
2855
- async getEvent(id) {
3009
+ async getEvent(id, options) {
2856
3010
  await this.initialize();
2857
3011
  const row = sqliteGet(
2858
3012
  this.db,
2859
- `SELECT * FROM events WHERE id = ?`,
3013
+ `SELECT * FROM events WHERE id = ? AND ${maybeQuarantinePredicate(options)}`,
2860
3014
  [id]
2861
3015
  );
2862
3016
  if (!row)
@@ -2866,11 +3020,11 @@ var SQLiteEventStore = class {
2866
3020
  /**
2867
3021
  * Get events since a timestamp (for sync)
2868
3022
  */
2869
- async getEventsSince(timestamp, limit = 1e3) {
3023
+ async getEventsSince(timestamp, limit = 1e3, options) {
2870
3024
  await this.initialize();
2871
3025
  const rows = sqliteAll(
2872
3026
  this.db,
2873
- `SELECT * FROM events WHERE timestamp > ? ORDER BY timestamp ASC LIMIT ?`,
3027
+ `SELECT * FROM events WHERE timestamp > ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ?`,
2874
3028
  [timestamp, limit]
2875
3029
  );
2876
3030
  return rows.map(this.rowToEvent);
@@ -2879,11 +3033,11 @@ var SQLiteEventStore = class {
2879
3033
  * Get events since a SQLite rowid (for robust incremental replication).
2880
3034
  * Rowid is monotonic for append-only tables, independent of client timestamps.
2881
3035
  */
2882
- async getEventsSinceRowid(lastRowid, limit = 1e3) {
3036
+ async getEventsSinceRowid(lastRowid, limit = 1e3, options) {
2883
3037
  await this.initialize();
2884
3038
  const rows = sqliteAll(
2885
3039
  this.db,
2886
- `SELECT rowid as _rowid, * FROM events WHERE rowid > ? ORDER BY rowid ASC LIMIT ?`,
3040
+ `SELECT rowid as _rowid, * FROM events WHERE rowid > ? AND ${maybeQuarantinePredicate(options)} ORDER BY rowid ASC LIMIT ?`,
2887
3041
  [lastRowid, limit]
2888
3042
  );
2889
3043
  return rows.map((row) => ({
@@ -3118,19 +3272,19 @@ var SQLiteEventStore = class {
3118
3272
  /**
3119
3273
  * Count total events
3120
3274
  */
3121
- async countEvents() {
3275
+ async countEvents(options) {
3122
3276
  await this.initialize();
3123
- const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events`);
3277
+ const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events WHERE ${maybeQuarantinePredicate(options)}`);
3124
3278
  return row?.count || 0;
3125
3279
  }
3126
3280
  /**
3127
3281
  * Get events page in timestamp ascending order (stable migration/reindex scans)
3128
3282
  */
3129
- async getEventsPage(limit = 1e3, offset = 0) {
3283
+ async getEventsPage(limit = 1e3, offset = 0, options) {
3130
3284
  await this.initialize();
3131
3285
  const rows = sqliteAll(
3132
3286
  this.db,
3133
- `SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
3287
+ `SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
3134
3288
  [limit, offset]
3135
3289
  );
3136
3290
  return rows.map(this.rowToEvent);
@@ -3204,6 +3358,145 @@ var SQLiteEventStore = class {
3204
3358
  result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
3205
3359
  return result;
3206
3360
  }
3361
+ /**
3362
+ * Repair legacy imported events that predate canonical project scope metadata.
3363
+ *
3364
+ * Same-project legacy rows are tagged with scope.project.hash. Rows that look
3365
+ * imported but cannot be proven to belong to this project are quarantined so
3366
+ * dashboard default reads/search do not surface cross-project contamination.
3367
+ */
3368
+ async repairLegacyProjectScope(options = {}) {
3369
+ await this.initialize();
3370
+ const projectHash = options.projectHash || (options.projectPath ? hashProjectPath(options.projectPath) : void 0);
3371
+ if (!projectHash) {
3372
+ throw new Error("repairLegacyProjectScope requires projectPath or projectHash");
3373
+ }
3374
+ if (options.projectPath && options.projectHash && hashProjectPath(options.projectPath) !== options.projectHash) {
3375
+ throw new Error("repairLegacyProjectScope projectPath and projectHash refer to different project stores");
3376
+ }
3377
+ const dryRun = options.dryRun === true;
3378
+ const nowIso = (options.now || /* @__PURE__ */ new Date()).toISOString();
3379
+ const result = buildRepairResult(projectHash, dryRun);
3380
+ const rows = sqliteAll(
3381
+ this.db,
3382
+ `SELECT e.id, e.content, e.metadata, s.project_path as session_project_path
3383
+ FROM events e
3384
+ LEFT JOIN sessions s ON s.id = e.session_id
3385
+ ORDER BY e.timestamp ASC`,
3386
+ []
3387
+ );
3388
+ const sample = (entry) => {
3389
+ if (result.samples.length < 20)
3390
+ result.samples.push(entry);
3391
+ };
3392
+ for (const row of rows) {
3393
+ result.scanned++;
3394
+ let metadata = {};
3395
+ let metadataParseInvalid = false;
3396
+ if (row.metadata) {
3397
+ const parsed = safeParseMetadataValue(row.metadata);
3398
+ if (parsed) {
3399
+ metadata = parsed;
3400
+ } else {
3401
+ metadataParseInvalid = true;
3402
+ }
3403
+ }
3404
+ if (isActiveQuarantinedMetadata(metadata)) {
3405
+ result.skipped++;
3406
+ continue;
3407
+ }
3408
+ const currentHash = metadataProjectHash(metadata);
3409
+ const explicitPath = metadataProjectPath(metadata);
3410
+ const sessionProjectPath = typeof row.session_project_path === "string" && row.session_project_path.length > 0 ? row.session_project_path : void 0;
3411
+ const candidatePaths = metadataProjectPaths(metadata);
3412
+ if (sessionProjectPath && !candidatePaths.includes(sessionProjectPath)) {
3413
+ candidatePaths.push(sessionProjectPath);
3414
+ }
3415
+ const importedOrLegacy = metadataParseInvalid || isImportedOrLegacyScopedMetadata(metadata) || Boolean(sessionProjectPath);
3416
+ const pathHashes = candidatePaths.map((candidate) => {
3417
+ try {
3418
+ return { path: candidate, hash: hashProjectPath(candidate) };
3419
+ } catch {
3420
+ return { path: candidate, hash: void 0 };
3421
+ }
3422
+ });
3423
+ const matchingPath = pathHashes.find((candidate) => candidate.hash === projectHash);
3424
+ const foreignPath = pathHashes.find((candidate) => candidate.hash && candidate.hash !== projectHash);
3425
+ let action = "skipped";
3426
+ let reason;
3427
+ let observedProjectHash;
3428
+ if (foreignPath) {
3429
+ action = "quarantined";
3430
+ reason = "project-path-mismatch";
3431
+ observedProjectHash = foreignPath.hash;
3432
+ } else if (currentHash === projectHash && importedOrLegacy && hasConflictingContentProjectHint(row.content, options.projectPath)) {
3433
+ action = "quarantined";
3434
+ reason = "content-project-mismatch";
3435
+ } else if (currentHash === projectHash) {
3436
+ result.alreadyScoped++;
3437
+ continue;
3438
+ } else if (currentHash && currentHash !== projectHash) {
3439
+ action = "quarantined";
3440
+ reason = "scope-hash-mismatch";
3441
+ observedProjectHash = currentHash;
3442
+ } else if (matchingPath) {
3443
+ action = "repaired";
3444
+ reason = matchingPath.path === sessionProjectPath && matchingPath.path !== explicitPath ? "session-project-path" : "same-project-path";
3445
+ } else if (candidatePaths.length > 0) {
3446
+ action = "quarantined";
3447
+ reason = "project-path-mismatch";
3448
+ } else if (importedOrLegacy) {
3449
+ action = "quarantined";
3450
+ reason = "missing-project-scope";
3451
+ }
3452
+ if (action === "skipped" || !reason) {
3453
+ result.skipped++;
3454
+ continue;
3455
+ }
3456
+ if (action === "repaired") {
3457
+ const scope = isRecord(metadata.scope) ? { ...metadata.scope } : {};
3458
+ const project = isRecord(scope.project) ? { ...scope.project } : {};
3459
+ project.hash = projectHash;
3460
+ scope.project = project;
3461
+ metadata.scope = scope;
3462
+ metadata.repair = {
3463
+ ...isRecord(metadata.repair) ? metadata.repair : {},
3464
+ legacyProjectScope: {
3465
+ action,
3466
+ reason,
3467
+ repairedAt: nowIso
3468
+ }
3469
+ };
3470
+ addMetadataTag(metadata, `proj:${projectHash}`);
3471
+ result.repaired++;
3472
+ } else {
3473
+ metadata.quarantine = {
3474
+ ...isRecord(metadata.quarantine) ? metadata.quarantine : {},
3475
+ status: "active",
3476
+ category: "project-scope",
3477
+ reason,
3478
+ detectedAt: nowIso,
3479
+ expectedProjectHash: projectHash,
3480
+ ...observedProjectHash ? { observedProjectHash } : {}
3481
+ };
3482
+ metadata.repair = {
3483
+ ...isRecord(metadata.repair) ? metadata.repair : {},
3484
+ legacyProjectScope: {
3485
+ action,
3486
+ reason,
3487
+ repairedAt: nowIso
3488
+ }
3489
+ };
3490
+ addMetadataTag(metadata, "quarantine:project-scope");
3491
+ result.quarantined++;
3492
+ }
3493
+ sample({ eventId: row.id, action, reason });
3494
+ if (!dryRun) {
3495
+ sqliteRun(this.db, `UPDATE events SET metadata = ? WHERE id = ?`, [JSON.stringify(metadata), row.id]);
3496
+ }
3497
+ }
3498
+ return result;
3499
+ }
3207
3500
  /**
3208
3501
  * Get embedding/vector outbox health statistics
3209
3502
  */
@@ -3251,7 +3544,11 @@ var SQLiteEventStore = class {
3251
3544
  await this.initialize();
3252
3545
  const rows = sqliteAll(
3253
3546
  this.db,
3254
- `SELECT level, COUNT(*) as count FROM memory_levels GROUP BY level`
3547
+ `SELECT ml.level, COUNT(*) as count
3548
+ FROM memory_levels ml
3549
+ INNER JOIN events e ON e.id = ml.event_id
3550
+ WHERE ${notActiveQuarantinedSql("e.metadata")}
3551
+ GROUP BY ml.level`
3255
3552
  );
3256
3553
  return rows;
3257
3554
  }
@@ -3267,6 +3564,7 @@ var SQLiteEventStore = class {
3267
3564
  `SELECT e.* FROM events e
3268
3565
  INNER JOIN memory_levels ml ON e.id = ml.event_id
3269
3566
  WHERE ml.level = ?
3567
+ AND ${notActiveQuarantinedSql("e.metadata")}
3270
3568
  ORDER BY e.timestamp DESC
3271
3569
  LIMIT ? OFFSET ?`,
3272
3570
  [level, limit, offset]
@@ -3359,12 +3657,13 @@ var SQLiteEventStore = class {
3359
3657
  /**
3360
3658
  * Get most accessed memories (falls back to recent events if none accessed)
3361
3659
  */
3362
- async getMostAccessed(limit = 10) {
3660
+ async getMostAccessed(limit = 10, options) {
3363
3661
  await this.initialize();
3364
3662
  let rows = sqliteAll(
3365
3663
  this.db,
3366
3664
  `SELECT * FROM events
3367
3665
  WHERE access_count > 0
3666
+ AND ${maybeQuarantinePredicate(options)}
3368
3667
  ORDER BY access_count DESC, last_accessed_at DESC
3369
3668
  LIMIT ?`,
3370
3669
  [limit]
@@ -3373,6 +3672,7 @@ var SQLiteEventStore = class {
3373
3672
  rows = sqliteAll(
3374
3673
  this.db,
3375
3674
  `SELECT * FROM events
3675
+ WHERE ${maybeQuarantinePredicate(options)}
3376
3676
  ORDER BY timestamp DESC
3377
3677
  LIMIT ?`,
3378
3678
  [limit]
@@ -3503,6 +3803,7 @@ var SQLiteEventStore = class {
3503
3803
  FROM memory_helpfulness mh
3504
3804
  JOIN events e ON e.id = mh.event_id
3505
3805
  WHERE mh.measured_at IS NOT NULL
3806
+ AND ${notActiveQuarantinedSql("e.metadata")}
3506
3807
  GROUP BY mh.event_id
3507
3808
  ORDER BY avg_score DESC
3508
3809
  LIMIT ?`,
@@ -3567,6 +3868,7 @@ var SQLiteEventStore = class {
3567
3868
  FROM events_fts fts
3568
3869
  JOIN events e ON e.id = fts.event_id
3569
3870
  WHERE events_fts MATCH ?
3871
+ AND ${notActiveQuarantinedSql("e.metadata")}
3570
3872
  ORDER BY fts.rank
3571
3873
  LIMIT ?`,
3572
3874
  [searchTerms, limit]
@@ -3581,6 +3883,7 @@ var SQLiteEventStore = class {
3581
3883
  this.db,
3582
3884
  `SELECT *, 0 as rank FROM events
3583
3885
  WHERE content LIKE ?
3886
+ AND ${notActiveQuarantinedSql()}
3584
3887
  ORDER BY timestamp DESC
3585
3888
  LIMIT ?`,
3586
3889
  [likePattern, limit]
@@ -3787,6 +4090,7 @@ var SQLiteEventStore = class {
3787
4090
  `SELECT turn_id, MIN(timestamp) as min_ts
3788
4091
  FROM events
3789
4092
  WHERE session_id = ? AND turn_id IS NOT NULL
4093
+ AND ${maybeQuarantinePredicate(options)}
3790
4094
  GROUP BY turn_id
3791
4095
  ORDER BY min_ts DESC
3792
4096
  LIMIT ? OFFSET ?`,
@@ -3794,7 +4098,7 @@ var SQLiteEventStore = class {
3794
4098
  );
3795
4099
  const turns = [];
3796
4100
  for (const turnRow of turnRows) {
3797
- const events = await this.getEventsByTurn(turnRow.turn_id);
4101
+ const events = await this.getEventsByTurn(turnRow.turn_id, options);
3798
4102
  const promptEvent = events.find((e) => e.eventType === "user_prompt");
3799
4103
  const toolEvents = events.filter((e) => e.eventType === "tool_observation");
3800
4104
  const hasResponse = events.some((e) => e.eventType === "agent_response");
@@ -3813,11 +4117,11 @@ var SQLiteEventStore = class {
3813
4117
  /**
3814
4118
  * Get all events for a specific turn_id
3815
4119
  */
3816
- async getEventsByTurn(turnId) {
4120
+ async getEventsByTurn(turnId, options) {
3817
4121
  await this.initialize();
3818
4122
  const rows = sqliteAll(
3819
4123
  this.db,
3820
- `SELECT * FROM events WHERE turn_id = ? ORDER BY timestamp ASC`,
4124
+ `SELECT * FROM events WHERE turn_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
3821
4125
  [turnId]
3822
4126
  );
3823
4127
  return rows.map(this.rowToEvent);
@@ -3825,13 +4129,14 @@ var SQLiteEventStore = class {
3825
4129
  /**
3826
4130
  * Count total turns for a session
3827
4131
  */
3828
- async countSessionTurns(sessionId) {
4132
+ async countSessionTurns(sessionId, options) {
3829
4133
  await this.initialize();
3830
4134
  const row = sqliteGet(
3831
4135
  this.db,
3832
4136
  `SELECT COUNT(DISTINCT turn_id) as count
3833
4137
  FROM events
3834
- WHERE session_id = ? AND turn_id IS NOT NULL`,
4138
+ WHERE session_id = ? AND turn_id IS NOT NULL
4139
+ AND ${maybeQuarantinePredicate(options)}`,
3835
4140
  [sessionId]
3836
4141
  );
3837
4142
  return row?.count || 0;
@@ -3923,7 +4228,7 @@ var SQLiteEventStore = class {
3923
4228
  content: row.content,
3924
4229
  canonicalKey: row.canonical_key,
3925
4230
  dedupeKey: row.dedupe_key,
3926
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0
4231
+ metadata: safeParseMetadataValue(row.metadata)
3927
4232
  };
3928
4233
  if (row.access_count !== void 0) {
3929
4234
  event.access_count = row.access_count;
@@ -4518,6 +4823,10 @@ var MemoryQueryService = class {
4518
4823
  await this.initialize();
4519
4824
  return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
4520
4825
  }
4826
+ async repairLegacyProjectScope(options) {
4827
+ await this.initialize();
4828
+ return this.getMaintenanceStore("repairLegacyProjectScope").repairLegacyProjectScope(options);
4829
+ }
4521
4830
  async getStats() {
4522
4831
  await this.initialize();
4523
4832
  const deps = this.getStatsDeps();
@@ -6140,18 +6449,18 @@ function assertDefaultRetrieverStore(eventStore) {
6140
6449
  function createMemoryEngineServices(options) {
6141
6450
  const factories = options.factories ?? {};
6142
6451
  const storagePath = options.storagePath;
6143
- if (!options.readOnly && !fs5.existsSync(storagePath)) {
6144
- fs5.mkdirSync(storagePath, { recursive: true });
6452
+ if (!options.readOnly && !fs6.existsSync(storagePath)) {
6453
+ fs6.mkdirSync(storagePath, { recursive: true });
6145
6454
  }
6146
6455
  const sqliteStore = (factories.createSQLiteEventStore ?? defaultCreateSQLiteEventStore)(
6147
- path4.join(storagePath, "events.sqlite"),
6456
+ path5.join(storagePath, "events.sqlite"),
6148
6457
  {
6149
6458
  readonly: options.readOnly,
6150
6459
  markdownMirrorRoot: storagePath
6151
6460
  }
6152
6461
  );
6153
6462
  const vectorStore = (factories.createVectorStore ?? defaultCreateVectorStore)(
6154
- path4.join(storagePath, "vectors")
6463
+ path5.join(storagePath, "vectors")
6155
6464
  );
6156
6465
  const embeddingModel = options.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
6157
6466
  const embedder = embeddingModel ? (factories.createEmbedder ?? defaultCreateEmbedder)(embeddingModel) : (factories.getDefaultEmbedder ?? getDefaultEmbedder)();
@@ -6562,8 +6871,8 @@ function createMemoryRuntimeService(deps) {
6562
6871
  }
6563
6872
 
6564
6873
  // src/extensions/shared-memory/shared-memory-services.ts
6565
- import * as fs6 from "fs";
6566
- import * as path5 from "path";
6874
+ import * as fs7 from "fs";
6875
+ import * as path6 from "path";
6567
6876
 
6568
6877
  // src/core/shared-event-store.ts
6569
6878
  var SharedEventStore = class {
@@ -7251,7 +7560,7 @@ var SharedMemoryServices = class {
7251
7560
  this.ensureDirectory(sharedPath, { allowCreate: true });
7252
7561
  const store = await this.openStore(sharedPath);
7253
7562
  this.sharedVectorStore = this.factories.createSharedVectorStore(
7254
- path5.join(sharedPath, "vectors")
7563
+ path6.join(sharedPath, "vectors")
7255
7564
  );
7256
7565
  await this.sharedVectorStore.initialize();
7257
7566
  this.sharedPromoter = this.factories.createSharedPromoter(
@@ -7324,7 +7633,7 @@ var SharedMemoryServices = class {
7324
7633
  async createOpenStorePromise(sharedPath) {
7325
7634
  if (!this.sharedEventStore) {
7326
7635
  const sharedEventStore = this.factories.createSharedEventStore(
7327
- path5.join(sharedPath, "shared.duckdb")
7636
+ path6.join(sharedPath, "shared.duckdb")
7328
7637
  );
7329
7638
  await sharedEventStore.initialize();
7330
7639
  this.sharedEventStore = sharedEventStore;
@@ -7344,9 +7653,9 @@ var SharedMemoryServices = class {
7344
7653
  }
7345
7654
  get factories() {
7346
7655
  return {
7347
- existsSync: this.options.factories?.existsSync ?? fs6.existsSync,
7656
+ existsSync: this.options.factories?.existsSync ?? fs7.existsSync,
7348
7657
  mkdirSync: this.options.factories?.mkdirSync ?? ((targetPath) => {
7349
- fs6.mkdirSync(targetPath, { recursive: true });
7658
+ fs7.mkdirSync(targetPath, { recursive: true });
7350
7659
  }),
7351
7660
  createSharedEventStore: this.options.factories?.createSharedEventStore ?? createSharedEventStore,
7352
7661
  createSharedStore: this.options.factories?.createSharedStore ?? createSharedStore,
@@ -7461,33 +7770,11 @@ function createMemoryServiceComposition(options) {
7461
7770
  }
7462
7771
  function defaultExpandPath(targetPath) {
7463
7772
  if (targetPath.startsWith("~")) {
7464
- return path6.join(os.homedir(), targetPath.slice(1));
7773
+ return path7.join(os2.homedir(), targetPath.slice(1));
7465
7774
  }
7466
7775
  return targetPath;
7467
7776
  }
7468
7777
 
7469
- // src/core/registry/project-path.ts
7470
- import * as crypto2 from "crypto";
7471
- import * as fs7 from "fs";
7472
- import * as os2 from "os";
7473
- import * as path7 from "path";
7474
- function normalizeProjectPath(projectPath) {
7475
- const expanded = projectPath.startsWith("~") ? path7.join(os2.homedir(), projectPath.slice(1)) : projectPath;
7476
- try {
7477
- return fs7.realpathSync(expanded);
7478
- } catch {
7479
- return path7.resolve(expanded);
7480
- }
7481
- }
7482
- function hashProjectPath(projectPath) {
7483
- const normalizedPath = normalizeProjectPath(projectPath);
7484
- return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
7485
- }
7486
- function getProjectStoragePath(projectPath) {
7487
- const hash = hashProjectPath(projectPath);
7488
- return path7.join(os2.homedir(), ".claude-code", "memory", "projects", hash);
7489
- }
7490
-
7491
7778
  // src/core/registry/session-registry.ts
7492
7779
  import * as fs8 from "fs";
7493
7780
  import * as os3 from "os";
@@ -7789,6 +8076,9 @@ var MemoryService = class {
7789
8076
  async recoverStuckOutboxItems(options) {
7790
8077
  return this.queryService.recoverStuckOutboxItems(options);
7791
8078
  }
8079
+ async repairLegacyProjectScope(options) {
8080
+ return this.queryService.repairLegacyProjectScope(options);
8081
+ }
7792
8082
  async getRetrievalTraceStats() {
7793
8083
  return this.retrievalAnalyticsService.getRetrievalTraceStats();
7794
8084
  }