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.
@@ -27,8 +27,8 @@ import { Hono } from "hono";
27
27
  import * as os5 from "os";
28
28
 
29
29
  // src/core/engine/memory-service-composition.ts
30
- import * as os from "os";
31
- import * as path6 from "path";
30
+ import * as os2 from "os";
31
+ import * as path7 from "path";
32
32
 
33
33
  // src/core/metadata-extractor.ts
34
34
  function createToolObservationEmbedding(toolName, metadata, success) {
@@ -1539,8 +1539,8 @@ function createEndlessMemoryServices(options) {
1539
1539
  }
1540
1540
 
1541
1541
  // src/core/engine/memory-engine-services.ts
1542
- import * as fs5 from "fs";
1543
- import * as path4 from "path";
1542
+ import * as fs6 from "fs";
1543
+ import * as path5 from "path";
1544
1544
 
1545
1545
  // src/extensions/vector/embedder.ts
1546
1546
  var DEFAULT_EMBEDDING_MODEL = "Xenova/multilingual-e5-small";
@@ -2218,14 +2218,43 @@ function makeDedupeKey(content, sessionId) {
2218
2218
  return `${sessionId}:${contentHash}`;
2219
2219
  }
2220
2220
 
2221
+ // src/core/sqlite-event-store.ts
2222
+ import * as nodePath2 from "path";
2223
+
2224
+ // src/core/registry/project-path.ts
2225
+ import * as crypto2 from "crypto";
2226
+ import * as fs3 from "fs";
2227
+ import * as os from "os";
2228
+ import * as path3 from "path";
2229
+ function normalizeProjectPath(projectPath) {
2230
+ const expanded = projectPath.startsWith("~") ? path3.join(os.homedir(), projectPath.slice(1)) : projectPath;
2231
+ try {
2232
+ return fs3.realpathSync(expanded);
2233
+ } catch {
2234
+ return path3.resolve(expanded);
2235
+ }
2236
+ }
2237
+ function hashProjectPath(projectPath) {
2238
+ const normalizedPath = normalizeProjectPath(projectPath);
2239
+ return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
2240
+ }
2241
+ function getProjectStoragePath(projectPath) {
2242
+ const hash = hashProjectPath(projectPath);
2243
+ return path3.join(os.homedir(), ".claude-code", "memory", "projects", hash);
2244
+ }
2245
+ function resolveProjectStoragePath(projectOrHash) {
2246
+ const isHash = /^[a-f0-9]{8}$/.test(projectOrHash);
2247
+ return isHash ? path3.join(os.homedir(), ".claude-code", "memory", "projects", projectOrHash) : getProjectStoragePath(projectOrHash);
2248
+ }
2249
+
2221
2250
  // src/core/sqlite-wrapper.ts
2222
2251
  import Database from "better-sqlite3";
2223
- import * as fs3 from "fs";
2252
+ import * as fs4 from "fs";
2224
2253
  import * as nodePath from "path";
2225
2254
  function createSQLiteDatabase(path14, options) {
2226
2255
  const dir = nodePath.dirname(path14);
2227
- if (!fs3.existsSync(dir)) {
2228
- fs3.mkdirSync(dir, { recursive: true });
2256
+ if (!fs4.existsSync(dir)) {
2257
+ fs4.mkdirSync(dir, { recursive: true });
2229
2258
  }
2230
2259
  const db = new Database(path14, {
2231
2260
  readonly: options?.readonly ?? false
@@ -2274,8 +2303,8 @@ function toSQLiteTimestamp(date) {
2274
2303
  }
2275
2304
 
2276
2305
  // src/core/markdown-mirror.ts
2277
- import * as fs4 from "fs/promises";
2278
- import * as path3 from "path";
2306
+ import * as fs5 from "fs/promises";
2307
+ import * as path4 from "path";
2279
2308
  var DEFAULT_NAMESPACE = "default";
2280
2309
  var DEFAULT_CATEGORY = "uncategorized";
2281
2310
  function sanitizeSegment2(input, fallback) {
@@ -2304,7 +2333,7 @@ function buildMirrorPath2(rootDir, event) {
2304
2333
  const yyyy = d.getFullYear();
2305
2334
  const mm = String(d.getMonth() + 1).padStart(2, "0");
2306
2335
  const dd = String(d.getDate()).padStart(2, "0");
2307
- return path3.join(rootDir, "memory", namespace, ...categories, `${yyyy}-${mm}-${dd}.md`);
2336
+ return path4.join(rootDir, "memory", namespace, ...categories, `${yyyy}-${mm}-${dd}.md`);
2308
2337
  }
2309
2338
  function formatMirrorEntry(event) {
2310
2339
  const category = Array.isArray(event.metadata?.categoryPath) ? event.metadata.categoryPath.join("/") : String(event.metadata?.category ?? event.eventType);
@@ -2325,8 +2354,8 @@ var MarkdownMirror2 = class {
2325
2354
  }
2326
2355
  async append(event) {
2327
2356
  const outPath = buildMirrorPath2(this.rootDir, event);
2328
- await fs4.mkdir(path3.dirname(outPath), { recursive: true });
2329
- await fs4.appendFile(outPath, formatMirrorEntry(event), "utf8");
2357
+ await fs5.mkdir(path4.dirname(outPath), { recursive: true });
2358
+ await fs5.appendFile(outPath, formatMirrorEntry(event), "utf8");
2330
2359
  return outPath;
2331
2360
  }
2332
2361
  };
@@ -2347,6 +2376,135 @@ function emptyOutboxRecoveryResult() {
2347
2376
  vector: { recoveredProcessing: 0, retriedFailed: 0 }
2348
2377
  };
2349
2378
  }
2379
+ function isRecord(value) {
2380
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2381
+ }
2382
+ function getNestedRecord(root, path14) {
2383
+ let cursor = root;
2384
+ for (const key of path14) {
2385
+ if (!isRecord(cursor))
2386
+ return void 0;
2387
+ cursor = cursor[key];
2388
+ }
2389
+ return isRecord(cursor) ? cursor : void 0;
2390
+ }
2391
+ function getNestedString(root, path14) {
2392
+ let cursor = root;
2393
+ for (const key of path14) {
2394
+ if (!isRecord(cursor))
2395
+ return void 0;
2396
+ cursor = cursor[key];
2397
+ }
2398
+ return typeof cursor === "string" && cursor.length > 0 ? cursor : void 0;
2399
+ }
2400
+ function metadataProjectHash(metadata) {
2401
+ return getNestedString(metadata, ["scope", "project", "hash"]);
2402
+ }
2403
+ function metadataProjectPaths(metadata) {
2404
+ const candidates = [
2405
+ getNestedString(metadata, ["projectPath"]),
2406
+ getNestedString(metadata, ["sourceProjectPath"]),
2407
+ getNestedString(metadata, ["scope", "project", "path"])
2408
+ ];
2409
+ const paths = [];
2410
+ for (const value of candidates) {
2411
+ if (value && !paths.includes(value))
2412
+ paths.push(value);
2413
+ }
2414
+ return paths;
2415
+ }
2416
+ function metadataProjectPath(metadata) {
2417
+ return metadataProjectPaths(metadata)[0];
2418
+ }
2419
+ function isActiveQuarantinedMetadata(metadata) {
2420
+ const quarantine = getNestedRecord(metadata, ["quarantine"]);
2421
+ return quarantine?.status === "active";
2422
+ }
2423
+ function activeQuarantineStatusExpression(column = "metadata") {
2424
+ return `COALESCE(json_extract(CASE WHEN json_valid(${column}) THEN ${column} ELSE '{}' END, '$.quarantine.status'), '')`;
2425
+ }
2426
+ function notActiveQuarantinedSql(column = "metadata") {
2427
+ return `${activeQuarantineStatusExpression(column)} != 'active'`;
2428
+ }
2429
+ function maybeQuarantinePredicate(options, column = "metadata") {
2430
+ return options?.includeQuarantined ? "1=1" : notActiveQuarantinedSql(column);
2431
+ }
2432
+ function safeParseMetadataValue(value) {
2433
+ if (!value)
2434
+ return void 0;
2435
+ if (typeof value === "object")
2436
+ return isRecord(value) ? value : void 0;
2437
+ if (typeof value !== "string")
2438
+ return void 0;
2439
+ try {
2440
+ const parsed = JSON.parse(value);
2441
+ return isRecord(parsed) ? parsed : void 0;
2442
+ } catch {
2443
+ return void 0;
2444
+ }
2445
+ }
2446
+ function isImportedOrLegacyScopedMetadata(metadata) {
2447
+ if (!metadata)
2448
+ return false;
2449
+ return Boolean(
2450
+ metadata.importedFrom || metadata.sourceSessionId || metadata.sourceSessionHash || metadata.hermesSource || metadata.projectPath || metadata.sourceProjectPath || metadata.source === "hermes" || metadata.source === "claude" || metadata.source === "codex"
2451
+ );
2452
+ }
2453
+ function addMetadataTag(metadata, tag) {
2454
+ const current = Array.isArray(metadata.tags) ? metadata.tags.filter((value) => typeof value === "string") : [];
2455
+ if (!current.includes(tag))
2456
+ metadata.tags = [...current, tag];
2457
+ }
2458
+ function buildRepairResult(projectHash, dryRun) {
2459
+ return {
2460
+ dryRun,
2461
+ projectHash,
2462
+ scanned: 0,
2463
+ repaired: 0,
2464
+ quarantined: 0,
2465
+ alreadyScoped: 0,
2466
+ skipped: 0,
2467
+ samples: []
2468
+ };
2469
+ }
2470
+ function normalizeRepoName(value) {
2471
+ return value.replace(/\.git$/i, "").trim().toLowerCase();
2472
+ }
2473
+ function projectBasename(projectPath) {
2474
+ if (!projectPath)
2475
+ return void 0;
2476
+ const trimmed = projectPath.replace(/[\\/]+$/, "");
2477
+ const basename3 = nodePath2.basename(trimmed);
2478
+ return basename3 ? normalizeRepoName(basename3) : void 0;
2479
+ }
2480
+ function isProjectScopeRepairExplanation(content) {
2481
+ const normalized = content.toLowerCase();
2482
+ const hasRepairContext = /project[- ]scope|mis[- ]scoped|quarantine|contamination|legacy|오염|격리|repair/.test(normalized);
2483
+ const hasExplanationContext = /example|detector|trap|not a .*project task|기억|메모리|설명|수정|검증/.test(normalized);
2484
+ return hasRepairContext && hasExplanationContext;
2485
+ }
2486
+ function hasConflictingContentProjectHint(content, projectPath) {
2487
+ const currentName = projectBasename(projectPath);
2488
+ if (!currentName)
2489
+ return false;
2490
+ if (isProjectScopeRepairExplanation(content))
2491
+ return false;
2492
+ const githubRepoPattern = /github\.com[:/]([^/\s`'"#)]+)\/([^/\s`'"#)]+)(?:\.git)?/gi;
2493
+ let githubMatch;
2494
+ while ((githubMatch = githubRepoPattern.exec(content)) !== null) {
2495
+ const repo = normalizeRepoName(githubMatch[2] || "");
2496
+ if (repo && repo !== currentName)
2497
+ return true;
2498
+ }
2499
+ const workspacePathPattern = /\/workspace\/([^/\s`'"#)]+)/gi;
2500
+ let workspaceMatch;
2501
+ while ((workspaceMatch = workspacePathPattern.exec(content)) !== null) {
2502
+ const repo = normalizeRepoName(workspaceMatch[1] || "");
2503
+ if (repo && repo !== currentName)
2504
+ return true;
2505
+ }
2506
+ return false;
2507
+ }
2350
2508
  var SQLiteEventStore = class {
2351
2509
  db;
2352
2510
  initialized = false;
@@ -2845,11 +3003,11 @@ var SQLiteEventStore = class {
2845
3003
  /**
2846
3004
  * Get events by session ID
2847
3005
  */
2848
- async getSessionEvents(sessionId) {
3006
+ async getSessionEvents(sessionId, options) {
2849
3007
  await this.initialize();
2850
3008
  const rows = sqliteAll(
2851
3009
  this.db,
2852
- `SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC`,
3010
+ `SELECT * FROM events WHERE session_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
2853
3011
  [sessionId]
2854
3012
  );
2855
3013
  return rows.map(this.rowToEvent);
@@ -2857,11 +3015,11 @@ var SQLiteEventStore = class {
2857
3015
  /**
2858
3016
  * Get recent events
2859
3017
  */
2860
- async getRecentEvents(limit = 100) {
3018
+ async getRecentEvents(limit = 100, options) {
2861
3019
  await this.initialize();
2862
3020
  const rows = sqliteAll(
2863
3021
  this.db,
2864
- `SELECT * FROM events ORDER BY timestamp DESC LIMIT ?`,
3022
+ `SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp DESC LIMIT ?`,
2865
3023
  [limit]
2866
3024
  );
2867
3025
  return rows.map(this.rowToEvent);
@@ -2869,11 +3027,11 @@ var SQLiteEventStore = class {
2869
3027
  /**
2870
3028
  * Get event by ID
2871
3029
  */
2872
- async getEvent(id) {
3030
+ async getEvent(id, options) {
2873
3031
  await this.initialize();
2874
3032
  const row = sqliteGet(
2875
3033
  this.db,
2876
- `SELECT * FROM events WHERE id = ?`,
3034
+ `SELECT * FROM events WHERE id = ? AND ${maybeQuarantinePredicate(options)}`,
2877
3035
  [id]
2878
3036
  );
2879
3037
  if (!row)
@@ -2883,11 +3041,11 @@ var SQLiteEventStore = class {
2883
3041
  /**
2884
3042
  * Get events since a timestamp (for sync)
2885
3043
  */
2886
- async getEventsSince(timestamp, limit = 1e3) {
3044
+ async getEventsSince(timestamp, limit = 1e3, options) {
2887
3045
  await this.initialize();
2888
3046
  const rows = sqliteAll(
2889
3047
  this.db,
2890
- `SELECT * FROM events WHERE timestamp > ? ORDER BY timestamp ASC LIMIT ?`,
3048
+ `SELECT * FROM events WHERE timestamp > ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ?`,
2891
3049
  [timestamp, limit]
2892
3050
  );
2893
3051
  return rows.map(this.rowToEvent);
@@ -2896,11 +3054,11 @@ var SQLiteEventStore = class {
2896
3054
  * Get events since a SQLite rowid (for robust incremental replication).
2897
3055
  * Rowid is monotonic for append-only tables, independent of client timestamps.
2898
3056
  */
2899
- async getEventsSinceRowid(lastRowid, limit = 1e3) {
3057
+ async getEventsSinceRowid(lastRowid, limit = 1e3, options) {
2900
3058
  await this.initialize();
2901
3059
  const rows = sqliteAll(
2902
3060
  this.db,
2903
- `SELECT rowid as _rowid, * FROM events WHERE rowid > ? ORDER BY rowid ASC LIMIT ?`,
3061
+ `SELECT rowid as _rowid, * FROM events WHERE rowid > ? AND ${maybeQuarantinePredicate(options)} ORDER BY rowid ASC LIMIT ?`,
2904
3062
  [lastRowid, limit]
2905
3063
  );
2906
3064
  return rows.map((row) => ({
@@ -3135,19 +3293,19 @@ var SQLiteEventStore = class {
3135
3293
  /**
3136
3294
  * Count total events
3137
3295
  */
3138
- async countEvents() {
3296
+ async countEvents(options) {
3139
3297
  await this.initialize();
3140
- const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events`);
3298
+ const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events WHERE ${maybeQuarantinePredicate(options)}`);
3141
3299
  return row?.count || 0;
3142
3300
  }
3143
3301
  /**
3144
3302
  * Get events page in timestamp ascending order (stable migration/reindex scans)
3145
3303
  */
3146
- async getEventsPage(limit = 1e3, offset = 0) {
3304
+ async getEventsPage(limit = 1e3, offset = 0, options) {
3147
3305
  await this.initialize();
3148
3306
  const rows = sqliteAll(
3149
3307
  this.db,
3150
- `SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
3308
+ `SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
3151
3309
  [limit, offset]
3152
3310
  );
3153
3311
  return rows.map(this.rowToEvent);
@@ -3221,6 +3379,145 @@ var SQLiteEventStore = class {
3221
3379
  result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
3222
3380
  return result;
3223
3381
  }
3382
+ /**
3383
+ * Repair legacy imported events that predate canonical project scope metadata.
3384
+ *
3385
+ * Same-project legacy rows are tagged with scope.project.hash. Rows that look
3386
+ * imported but cannot be proven to belong to this project are quarantined so
3387
+ * dashboard default reads/search do not surface cross-project contamination.
3388
+ */
3389
+ async repairLegacyProjectScope(options = {}) {
3390
+ await this.initialize();
3391
+ const projectHash = options.projectHash || (options.projectPath ? hashProjectPath(options.projectPath) : void 0);
3392
+ if (!projectHash) {
3393
+ throw new Error("repairLegacyProjectScope requires projectPath or projectHash");
3394
+ }
3395
+ if (options.projectPath && options.projectHash && hashProjectPath(options.projectPath) !== options.projectHash) {
3396
+ throw new Error("repairLegacyProjectScope projectPath and projectHash refer to different project stores");
3397
+ }
3398
+ const dryRun = options.dryRun === true;
3399
+ const nowIso = (options.now || /* @__PURE__ */ new Date()).toISOString();
3400
+ const result = buildRepairResult(projectHash, dryRun);
3401
+ const rows = sqliteAll(
3402
+ this.db,
3403
+ `SELECT e.id, e.content, e.metadata, s.project_path as session_project_path
3404
+ FROM events e
3405
+ LEFT JOIN sessions s ON s.id = e.session_id
3406
+ ORDER BY e.timestamp ASC`,
3407
+ []
3408
+ );
3409
+ const sample = (entry) => {
3410
+ if (result.samples.length < 20)
3411
+ result.samples.push(entry);
3412
+ };
3413
+ for (const row of rows) {
3414
+ result.scanned++;
3415
+ let metadata = {};
3416
+ let metadataParseInvalid = false;
3417
+ if (row.metadata) {
3418
+ const parsed = safeParseMetadataValue(row.metadata);
3419
+ if (parsed) {
3420
+ metadata = parsed;
3421
+ } else {
3422
+ metadataParseInvalid = true;
3423
+ }
3424
+ }
3425
+ if (isActiveQuarantinedMetadata(metadata)) {
3426
+ result.skipped++;
3427
+ continue;
3428
+ }
3429
+ const currentHash = metadataProjectHash(metadata);
3430
+ const explicitPath = metadataProjectPath(metadata);
3431
+ const sessionProjectPath = typeof row.session_project_path === "string" && row.session_project_path.length > 0 ? row.session_project_path : void 0;
3432
+ const candidatePaths = metadataProjectPaths(metadata);
3433
+ if (sessionProjectPath && !candidatePaths.includes(sessionProjectPath)) {
3434
+ candidatePaths.push(sessionProjectPath);
3435
+ }
3436
+ const importedOrLegacy = metadataParseInvalid || isImportedOrLegacyScopedMetadata(metadata) || Boolean(sessionProjectPath);
3437
+ const pathHashes = candidatePaths.map((candidate) => {
3438
+ try {
3439
+ return { path: candidate, hash: hashProjectPath(candidate) };
3440
+ } catch {
3441
+ return { path: candidate, hash: void 0 };
3442
+ }
3443
+ });
3444
+ const matchingPath = pathHashes.find((candidate) => candidate.hash === projectHash);
3445
+ const foreignPath = pathHashes.find((candidate) => candidate.hash && candidate.hash !== projectHash);
3446
+ let action = "skipped";
3447
+ let reason;
3448
+ let observedProjectHash;
3449
+ if (foreignPath) {
3450
+ action = "quarantined";
3451
+ reason = "project-path-mismatch";
3452
+ observedProjectHash = foreignPath.hash;
3453
+ } else if (currentHash === projectHash && importedOrLegacy && hasConflictingContentProjectHint(row.content, options.projectPath)) {
3454
+ action = "quarantined";
3455
+ reason = "content-project-mismatch";
3456
+ } else if (currentHash === projectHash) {
3457
+ result.alreadyScoped++;
3458
+ continue;
3459
+ } else if (currentHash && currentHash !== projectHash) {
3460
+ action = "quarantined";
3461
+ reason = "scope-hash-mismatch";
3462
+ observedProjectHash = currentHash;
3463
+ } else if (matchingPath) {
3464
+ action = "repaired";
3465
+ reason = matchingPath.path === sessionProjectPath && matchingPath.path !== explicitPath ? "session-project-path" : "same-project-path";
3466
+ } else if (candidatePaths.length > 0) {
3467
+ action = "quarantined";
3468
+ reason = "project-path-mismatch";
3469
+ } else if (importedOrLegacy) {
3470
+ action = "quarantined";
3471
+ reason = "missing-project-scope";
3472
+ }
3473
+ if (action === "skipped" || !reason) {
3474
+ result.skipped++;
3475
+ continue;
3476
+ }
3477
+ if (action === "repaired") {
3478
+ const scope = isRecord(metadata.scope) ? { ...metadata.scope } : {};
3479
+ const project = isRecord(scope.project) ? { ...scope.project } : {};
3480
+ project.hash = projectHash;
3481
+ scope.project = project;
3482
+ metadata.scope = scope;
3483
+ metadata.repair = {
3484
+ ...isRecord(metadata.repair) ? metadata.repair : {},
3485
+ legacyProjectScope: {
3486
+ action,
3487
+ reason,
3488
+ repairedAt: nowIso
3489
+ }
3490
+ };
3491
+ addMetadataTag(metadata, `proj:${projectHash}`);
3492
+ result.repaired++;
3493
+ } else {
3494
+ metadata.quarantine = {
3495
+ ...isRecord(metadata.quarantine) ? metadata.quarantine : {},
3496
+ status: "active",
3497
+ category: "project-scope",
3498
+ reason,
3499
+ detectedAt: nowIso,
3500
+ expectedProjectHash: projectHash,
3501
+ ...observedProjectHash ? { observedProjectHash } : {}
3502
+ };
3503
+ metadata.repair = {
3504
+ ...isRecord(metadata.repair) ? metadata.repair : {},
3505
+ legacyProjectScope: {
3506
+ action,
3507
+ reason,
3508
+ repairedAt: nowIso
3509
+ }
3510
+ };
3511
+ addMetadataTag(metadata, "quarantine:project-scope");
3512
+ result.quarantined++;
3513
+ }
3514
+ sample({ eventId: row.id, action, reason });
3515
+ if (!dryRun) {
3516
+ sqliteRun(this.db, `UPDATE events SET metadata = ? WHERE id = ?`, [JSON.stringify(metadata), row.id]);
3517
+ }
3518
+ }
3519
+ return result;
3520
+ }
3224
3521
  /**
3225
3522
  * Get embedding/vector outbox health statistics
3226
3523
  */
@@ -3268,7 +3565,11 @@ var SQLiteEventStore = class {
3268
3565
  await this.initialize();
3269
3566
  const rows = sqliteAll(
3270
3567
  this.db,
3271
- `SELECT level, COUNT(*) as count FROM memory_levels GROUP BY level`
3568
+ `SELECT ml.level, COUNT(*) as count
3569
+ FROM memory_levels ml
3570
+ INNER JOIN events e ON e.id = ml.event_id
3571
+ WHERE ${notActiveQuarantinedSql("e.metadata")}
3572
+ GROUP BY ml.level`
3272
3573
  );
3273
3574
  return rows;
3274
3575
  }
@@ -3284,6 +3585,7 @@ var SQLiteEventStore = class {
3284
3585
  `SELECT e.* FROM events e
3285
3586
  INNER JOIN memory_levels ml ON e.id = ml.event_id
3286
3587
  WHERE ml.level = ?
3588
+ AND ${notActiveQuarantinedSql("e.metadata")}
3287
3589
  ORDER BY e.timestamp DESC
3288
3590
  LIMIT ? OFFSET ?`,
3289
3591
  [level, limit, offset]
@@ -3376,12 +3678,13 @@ var SQLiteEventStore = class {
3376
3678
  /**
3377
3679
  * Get most accessed memories (falls back to recent events if none accessed)
3378
3680
  */
3379
- async getMostAccessed(limit = 10) {
3681
+ async getMostAccessed(limit = 10, options) {
3380
3682
  await this.initialize();
3381
3683
  let rows = sqliteAll(
3382
3684
  this.db,
3383
3685
  `SELECT * FROM events
3384
3686
  WHERE access_count > 0
3687
+ AND ${maybeQuarantinePredicate(options)}
3385
3688
  ORDER BY access_count DESC, last_accessed_at DESC
3386
3689
  LIMIT ?`,
3387
3690
  [limit]
@@ -3390,6 +3693,7 @@ var SQLiteEventStore = class {
3390
3693
  rows = sqliteAll(
3391
3694
  this.db,
3392
3695
  `SELECT * FROM events
3696
+ WHERE ${maybeQuarantinePredicate(options)}
3393
3697
  ORDER BY timestamp DESC
3394
3698
  LIMIT ?`,
3395
3699
  [limit]
@@ -3520,6 +3824,7 @@ var SQLiteEventStore = class {
3520
3824
  FROM memory_helpfulness mh
3521
3825
  JOIN events e ON e.id = mh.event_id
3522
3826
  WHERE mh.measured_at IS NOT NULL
3827
+ AND ${notActiveQuarantinedSql("e.metadata")}
3523
3828
  GROUP BY mh.event_id
3524
3829
  ORDER BY avg_score DESC
3525
3830
  LIMIT ?`,
@@ -3584,6 +3889,7 @@ var SQLiteEventStore = class {
3584
3889
  FROM events_fts fts
3585
3890
  JOIN events e ON e.id = fts.event_id
3586
3891
  WHERE events_fts MATCH ?
3892
+ AND ${notActiveQuarantinedSql("e.metadata")}
3587
3893
  ORDER BY fts.rank
3588
3894
  LIMIT ?`,
3589
3895
  [searchTerms, limit]
@@ -3598,6 +3904,7 @@ var SQLiteEventStore = class {
3598
3904
  this.db,
3599
3905
  `SELECT *, 0 as rank FROM events
3600
3906
  WHERE content LIKE ?
3907
+ AND ${notActiveQuarantinedSql()}
3601
3908
  ORDER BY timestamp DESC
3602
3909
  LIMIT ?`,
3603
3910
  [likePattern, limit]
@@ -3804,6 +4111,7 @@ var SQLiteEventStore = class {
3804
4111
  `SELECT turn_id, MIN(timestamp) as min_ts
3805
4112
  FROM events
3806
4113
  WHERE session_id = ? AND turn_id IS NOT NULL
4114
+ AND ${maybeQuarantinePredicate(options)}
3807
4115
  GROUP BY turn_id
3808
4116
  ORDER BY min_ts DESC
3809
4117
  LIMIT ? OFFSET ?`,
@@ -3811,7 +4119,7 @@ var SQLiteEventStore = class {
3811
4119
  );
3812
4120
  const turns = [];
3813
4121
  for (const turnRow of turnRows) {
3814
- const events = await this.getEventsByTurn(turnRow.turn_id);
4122
+ const events = await this.getEventsByTurn(turnRow.turn_id, options);
3815
4123
  const promptEvent = events.find((e) => e.eventType === "user_prompt");
3816
4124
  const toolEvents = events.filter((e) => e.eventType === "tool_observation");
3817
4125
  const hasResponse = events.some((e) => e.eventType === "agent_response");
@@ -3830,11 +4138,11 @@ var SQLiteEventStore = class {
3830
4138
  /**
3831
4139
  * Get all events for a specific turn_id
3832
4140
  */
3833
- async getEventsByTurn(turnId) {
4141
+ async getEventsByTurn(turnId, options) {
3834
4142
  await this.initialize();
3835
4143
  const rows = sqliteAll(
3836
4144
  this.db,
3837
- `SELECT * FROM events WHERE turn_id = ? ORDER BY timestamp ASC`,
4145
+ `SELECT * FROM events WHERE turn_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
3838
4146
  [turnId]
3839
4147
  );
3840
4148
  return rows.map(this.rowToEvent);
@@ -3842,13 +4150,14 @@ var SQLiteEventStore = class {
3842
4150
  /**
3843
4151
  * Count total turns for a session
3844
4152
  */
3845
- async countSessionTurns(sessionId) {
4153
+ async countSessionTurns(sessionId, options) {
3846
4154
  await this.initialize();
3847
4155
  const row = sqliteGet(
3848
4156
  this.db,
3849
4157
  `SELECT COUNT(DISTINCT turn_id) as count
3850
4158
  FROM events
3851
- WHERE session_id = ? AND turn_id IS NOT NULL`,
4159
+ WHERE session_id = ? AND turn_id IS NOT NULL
4160
+ AND ${maybeQuarantinePredicate(options)}`,
3852
4161
  [sessionId]
3853
4162
  );
3854
4163
  return row?.count || 0;
@@ -3940,7 +4249,7 @@ var SQLiteEventStore = class {
3940
4249
  content: row.content,
3941
4250
  canonicalKey: row.canonical_key,
3942
4251
  dedupeKey: row.dedupe_key,
3943
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0
4252
+ metadata: safeParseMetadataValue(row.metadata)
3944
4253
  };
3945
4254
  if (row.access_count !== void 0) {
3946
4255
  event.access_count = row.access_count;
@@ -4535,6 +4844,10 @@ var MemoryQueryService = class {
4535
4844
  await this.initialize();
4536
4845
  return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
4537
4846
  }
4847
+ async repairLegacyProjectScope(options) {
4848
+ await this.initialize();
4849
+ return this.getMaintenanceStore("repairLegacyProjectScope").repairLegacyProjectScope(options);
4850
+ }
4538
4851
  async getStats() {
4539
4852
  await this.initialize();
4540
4853
  const deps = this.getStatsDeps();
@@ -6157,18 +6470,18 @@ function assertDefaultRetrieverStore(eventStore) {
6157
6470
  function createMemoryEngineServices(options) {
6158
6471
  const factories = options.factories ?? {};
6159
6472
  const storagePath = options.storagePath;
6160
- if (!options.readOnly && !fs5.existsSync(storagePath)) {
6161
- fs5.mkdirSync(storagePath, { recursive: true });
6473
+ if (!options.readOnly && !fs6.existsSync(storagePath)) {
6474
+ fs6.mkdirSync(storagePath, { recursive: true });
6162
6475
  }
6163
6476
  const sqliteStore = (factories.createSQLiteEventStore ?? defaultCreateSQLiteEventStore)(
6164
- path4.join(storagePath, "events.sqlite"),
6477
+ path5.join(storagePath, "events.sqlite"),
6165
6478
  {
6166
6479
  readonly: options.readOnly,
6167
6480
  markdownMirrorRoot: storagePath
6168
6481
  }
6169
6482
  );
6170
6483
  const vectorStore = (factories.createVectorStore ?? defaultCreateVectorStore)(
6171
- path4.join(storagePath, "vectors")
6484
+ path5.join(storagePath, "vectors")
6172
6485
  );
6173
6486
  const embeddingModel = options.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
6174
6487
  const embedder = embeddingModel ? (factories.createEmbedder ?? defaultCreateEmbedder)(embeddingModel) : (factories.getDefaultEmbedder ?? getDefaultEmbedder)();
@@ -6579,8 +6892,8 @@ function createMemoryRuntimeService(deps) {
6579
6892
  }
6580
6893
 
6581
6894
  // src/extensions/shared-memory/shared-memory-services.ts
6582
- import * as fs6 from "fs";
6583
- import * as path5 from "path";
6895
+ import * as fs7 from "fs";
6896
+ import * as path6 from "path";
6584
6897
 
6585
6898
  // src/core/shared-event-store.ts
6586
6899
  var SharedEventStore = class {
@@ -7268,7 +7581,7 @@ var SharedMemoryServices = class {
7268
7581
  this.ensureDirectory(sharedPath, { allowCreate: true });
7269
7582
  const store = await this.openStore(sharedPath);
7270
7583
  this.sharedVectorStore = this.factories.createSharedVectorStore(
7271
- path5.join(sharedPath, "vectors")
7584
+ path6.join(sharedPath, "vectors")
7272
7585
  );
7273
7586
  await this.sharedVectorStore.initialize();
7274
7587
  this.sharedPromoter = this.factories.createSharedPromoter(
@@ -7341,7 +7654,7 @@ var SharedMemoryServices = class {
7341
7654
  async createOpenStorePromise(sharedPath) {
7342
7655
  if (!this.sharedEventStore) {
7343
7656
  const sharedEventStore = this.factories.createSharedEventStore(
7344
- path5.join(sharedPath, "shared.duckdb")
7657
+ path6.join(sharedPath, "shared.duckdb")
7345
7658
  );
7346
7659
  await sharedEventStore.initialize();
7347
7660
  this.sharedEventStore = sharedEventStore;
@@ -7361,9 +7674,9 @@ var SharedMemoryServices = class {
7361
7674
  }
7362
7675
  get factories() {
7363
7676
  return {
7364
- existsSync: this.options.factories?.existsSync ?? fs6.existsSync,
7677
+ existsSync: this.options.factories?.existsSync ?? fs7.existsSync,
7365
7678
  mkdirSync: this.options.factories?.mkdirSync ?? ((targetPath) => {
7366
- fs6.mkdirSync(targetPath, { recursive: true });
7679
+ fs7.mkdirSync(targetPath, { recursive: true });
7367
7680
  }),
7368
7681
  createSharedEventStore: this.options.factories?.createSharedEventStore ?? createSharedEventStore,
7369
7682
  createSharedStore: this.options.factories?.createSharedStore ?? createSharedStore,
@@ -7478,37 +7791,11 @@ function createMemoryServiceComposition(options) {
7478
7791
  }
7479
7792
  function defaultExpandPath(targetPath) {
7480
7793
  if (targetPath.startsWith("~")) {
7481
- return path6.join(os.homedir(), targetPath.slice(1));
7794
+ return path7.join(os2.homedir(), targetPath.slice(1));
7482
7795
  }
7483
7796
  return targetPath;
7484
7797
  }
7485
7798
 
7486
- // src/core/registry/project-path.ts
7487
- import * as crypto2 from "crypto";
7488
- import * as fs7 from "fs";
7489
- import * as os2 from "os";
7490
- import * as path7 from "path";
7491
- function normalizeProjectPath(projectPath) {
7492
- const expanded = projectPath.startsWith("~") ? path7.join(os2.homedir(), projectPath.slice(1)) : projectPath;
7493
- try {
7494
- return fs7.realpathSync(expanded);
7495
- } catch {
7496
- return path7.resolve(expanded);
7497
- }
7498
- }
7499
- function hashProjectPath(projectPath) {
7500
- const normalizedPath = normalizeProjectPath(projectPath);
7501
- return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
7502
- }
7503
- function getProjectStoragePath(projectPath) {
7504
- const hash = hashProjectPath(projectPath);
7505
- return path7.join(os2.homedir(), ".claude-code", "memory", "projects", hash);
7506
- }
7507
- function resolveProjectStoragePath(projectOrHash) {
7508
- const isHash = /^[a-f0-9]{8}$/.test(projectOrHash);
7509
- return isHash ? path7.join(os2.homedir(), ".claude-code", "memory", "projects", projectOrHash) : getProjectStoragePath(projectOrHash);
7510
- }
7511
-
7512
7799
  // src/core/registry/session-registry.ts
7513
7800
  import * as fs8 from "fs";
7514
7801
  import * as os3 from "os";
@@ -7810,6 +8097,9 @@ var MemoryService = class {
7810
8097
  async recoverStuckOutboxItems(options) {
7811
8098
  return this.queryService.recoverStuckOutboxItems(options);
7812
8099
  }
8100
+ async repairLegacyProjectScope(options) {
8101
+ return this.queryService.repairLegacyProjectScope(options);
8102
+ }
7813
8103
  async getRetrievalTraceStats() {
7814
8104
  return this.retrievalAnalyticsService.getRetrievalTraceStats();
7815
8105
  }