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.
@@ -15,8 +15,8 @@ import { Hono } from "hono";
15
15
  import * as os5 from "os";
16
16
 
17
17
  // src/core/engine/memory-service-composition.ts
18
- import * as os from "os";
19
- import * as path6 from "path";
18
+ import * as os2 from "os";
19
+ import * as path7 from "path";
20
20
 
21
21
  // src/core/metadata-extractor.ts
22
22
  function createToolObservationEmbedding(toolName, metadata, success) {
@@ -1527,8 +1527,8 @@ function createEndlessMemoryServices(options) {
1527
1527
  }
1528
1528
 
1529
1529
  // src/core/engine/memory-engine-services.ts
1530
- import * as fs5 from "fs";
1531
- import * as path4 from "path";
1530
+ import * as fs6 from "fs";
1531
+ import * as path5 from "path";
1532
1532
 
1533
1533
  // src/extensions/vector/embedder.ts
1534
1534
  var DEFAULT_EMBEDDING_MODEL = "Xenova/multilingual-e5-small";
@@ -2206,14 +2206,43 @@ function makeDedupeKey(content, sessionId) {
2206
2206
  return `${sessionId}:${contentHash}`;
2207
2207
  }
2208
2208
 
2209
+ // src/core/sqlite-event-store.ts
2210
+ import * as nodePath2 from "path";
2211
+
2212
+ // src/core/registry/project-path.ts
2213
+ import * as crypto2 from "crypto";
2214
+ import * as fs3 from "fs";
2215
+ import * as os from "os";
2216
+ import * as path3 from "path";
2217
+ function normalizeProjectPath(projectPath) {
2218
+ const expanded = projectPath.startsWith("~") ? path3.join(os.homedir(), projectPath.slice(1)) : projectPath;
2219
+ try {
2220
+ return fs3.realpathSync(expanded);
2221
+ } catch {
2222
+ return path3.resolve(expanded);
2223
+ }
2224
+ }
2225
+ function hashProjectPath(projectPath) {
2226
+ const normalizedPath = normalizeProjectPath(projectPath);
2227
+ return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
2228
+ }
2229
+ function getProjectStoragePath(projectPath) {
2230
+ const hash = hashProjectPath(projectPath);
2231
+ return path3.join(os.homedir(), ".claude-code", "memory", "projects", hash);
2232
+ }
2233
+ function resolveProjectStoragePath(projectOrHash) {
2234
+ const isHash = /^[a-f0-9]{8}$/.test(projectOrHash);
2235
+ return isHash ? path3.join(os.homedir(), ".claude-code", "memory", "projects", projectOrHash) : getProjectStoragePath(projectOrHash);
2236
+ }
2237
+
2209
2238
  // src/core/sqlite-wrapper.ts
2210
2239
  import Database from "better-sqlite3";
2211
- import * as fs3 from "fs";
2240
+ import * as fs4 from "fs";
2212
2241
  import * as nodePath from "path";
2213
2242
  function createSQLiteDatabase(path13, options) {
2214
2243
  const dir = nodePath.dirname(path13);
2215
- if (!fs3.existsSync(dir)) {
2216
- fs3.mkdirSync(dir, { recursive: true });
2244
+ if (!fs4.existsSync(dir)) {
2245
+ fs4.mkdirSync(dir, { recursive: true });
2217
2246
  }
2218
2247
  const db = new Database(path13, {
2219
2248
  readonly: options?.readonly ?? false
@@ -2262,8 +2291,8 @@ function toSQLiteTimestamp(date) {
2262
2291
  }
2263
2292
 
2264
2293
  // src/core/markdown-mirror.ts
2265
- import * as fs4 from "fs/promises";
2266
- import * as path3 from "path";
2294
+ import * as fs5 from "fs/promises";
2295
+ import * as path4 from "path";
2267
2296
  var DEFAULT_NAMESPACE = "default";
2268
2297
  var DEFAULT_CATEGORY = "uncategorized";
2269
2298
  function sanitizeSegment2(input, fallback) {
@@ -2292,7 +2321,7 @@ function buildMirrorPath2(rootDir, event) {
2292
2321
  const yyyy = d.getFullYear();
2293
2322
  const mm = String(d.getMonth() + 1).padStart(2, "0");
2294
2323
  const dd = String(d.getDate()).padStart(2, "0");
2295
- return path3.join(rootDir, "memory", namespace, ...categories, `${yyyy}-${mm}-${dd}.md`);
2324
+ return path4.join(rootDir, "memory", namespace, ...categories, `${yyyy}-${mm}-${dd}.md`);
2296
2325
  }
2297
2326
  function formatMirrorEntry(event) {
2298
2327
  const category = Array.isArray(event.metadata?.categoryPath) ? event.metadata.categoryPath.join("/") : String(event.metadata?.category ?? event.eventType);
@@ -2313,8 +2342,8 @@ var MarkdownMirror2 = class {
2313
2342
  }
2314
2343
  async append(event) {
2315
2344
  const outPath = buildMirrorPath2(this.rootDir, event);
2316
- await fs4.mkdir(path3.dirname(outPath), { recursive: true });
2317
- await fs4.appendFile(outPath, formatMirrorEntry(event), "utf8");
2345
+ await fs5.mkdir(path4.dirname(outPath), { recursive: true });
2346
+ await fs5.appendFile(outPath, formatMirrorEntry(event), "utf8");
2318
2347
  return outPath;
2319
2348
  }
2320
2349
  };
@@ -2335,6 +2364,135 @@ function emptyOutboxRecoveryResult() {
2335
2364
  vector: { recoveredProcessing: 0, retriedFailed: 0 }
2336
2365
  };
2337
2366
  }
2367
+ function isRecord(value) {
2368
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2369
+ }
2370
+ function getNestedRecord(root, path13) {
2371
+ let cursor = root;
2372
+ for (const key of path13) {
2373
+ if (!isRecord(cursor))
2374
+ return void 0;
2375
+ cursor = cursor[key];
2376
+ }
2377
+ return isRecord(cursor) ? cursor : void 0;
2378
+ }
2379
+ function getNestedString(root, path13) {
2380
+ let cursor = root;
2381
+ for (const key of path13) {
2382
+ if (!isRecord(cursor))
2383
+ return void 0;
2384
+ cursor = cursor[key];
2385
+ }
2386
+ return typeof cursor === "string" && cursor.length > 0 ? cursor : void 0;
2387
+ }
2388
+ function metadataProjectHash(metadata) {
2389
+ return getNestedString(metadata, ["scope", "project", "hash"]);
2390
+ }
2391
+ function metadataProjectPaths(metadata) {
2392
+ const candidates = [
2393
+ getNestedString(metadata, ["projectPath"]),
2394
+ getNestedString(metadata, ["sourceProjectPath"]),
2395
+ getNestedString(metadata, ["scope", "project", "path"])
2396
+ ];
2397
+ const paths = [];
2398
+ for (const value of candidates) {
2399
+ if (value && !paths.includes(value))
2400
+ paths.push(value);
2401
+ }
2402
+ return paths;
2403
+ }
2404
+ function metadataProjectPath(metadata) {
2405
+ return metadataProjectPaths(metadata)[0];
2406
+ }
2407
+ function isActiveQuarantinedMetadata(metadata) {
2408
+ const quarantine = getNestedRecord(metadata, ["quarantine"]);
2409
+ return quarantine?.status === "active";
2410
+ }
2411
+ function activeQuarantineStatusExpression(column = "metadata") {
2412
+ return `COALESCE(json_extract(CASE WHEN json_valid(${column}) THEN ${column} ELSE '{}' END, '$.quarantine.status'), '')`;
2413
+ }
2414
+ function notActiveQuarantinedSql(column = "metadata") {
2415
+ return `${activeQuarantineStatusExpression(column)} != 'active'`;
2416
+ }
2417
+ function maybeQuarantinePredicate(options, column = "metadata") {
2418
+ return options?.includeQuarantined ? "1=1" : notActiveQuarantinedSql(column);
2419
+ }
2420
+ function safeParseMetadataValue(value) {
2421
+ if (!value)
2422
+ return void 0;
2423
+ if (typeof value === "object")
2424
+ return isRecord(value) ? value : void 0;
2425
+ if (typeof value !== "string")
2426
+ return void 0;
2427
+ try {
2428
+ const parsed = JSON.parse(value);
2429
+ return isRecord(parsed) ? parsed : void 0;
2430
+ } catch {
2431
+ return void 0;
2432
+ }
2433
+ }
2434
+ function isImportedOrLegacyScopedMetadata(metadata) {
2435
+ if (!metadata)
2436
+ return false;
2437
+ return Boolean(
2438
+ metadata.importedFrom || metadata.sourceSessionId || metadata.sourceSessionHash || metadata.hermesSource || metadata.projectPath || metadata.sourceProjectPath || metadata.source === "hermes" || metadata.source === "claude" || metadata.source === "codex"
2439
+ );
2440
+ }
2441
+ function addMetadataTag(metadata, tag) {
2442
+ const current = Array.isArray(metadata.tags) ? metadata.tags.filter((value) => typeof value === "string") : [];
2443
+ if (!current.includes(tag))
2444
+ metadata.tags = [...current, tag];
2445
+ }
2446
+ function buildRepairResult(projectHash, dryRun) {
2447
+ return {
2448
+ dryRun,
2449
+ projectHash,
2450
+ scanned: 0,
2451
+ repaired: 0,
2452
+ quarantined: 0,
2453
+ alreadyScoped: 0,
2454
+ skipped: 0,
2455
+ samples: []
2456
+ };
2457
+ }
2458
+ function normalizeRepoName(value) {
2459
+ return value.replace(/\.git$/i, "").trim().toLowerCase();
2460
+ }
2461
+ function projectBasename(projectPath) {
2462
+ if (!projectPath)
2463
+ return void 0;
2464
+ const trimmed = projectPath.replace(/[\\/]+$/, "");
2465
+ const basename3 = nodePath2.basename(trimmed);
2466
+ return basename3 ? normalizeRepoName(basename3) : void 0;
2467
+ }
2468
+ function isProjectScopeRepairExplanation(content) {
2469
+ const normalized = content.toLowerCase();
2470
+ const hasRepairContext = /project[- ]scope|mis[- ]scoped|quarantine|contamination|legacy|오염|격리|repair/.test(normalized);
2471
+ const hasExplanationContext = /example|detector|trap|not a .*project task|기억|메모리|설명|수정|검증/.test(normalized);
2472
+ return hasRepairContext && hasExplanationContext;
2473
+ }
2474
+ function hasConflictingContentProjectHint(content, projectPath) {
2475
+ const currentName = projectBasename(projectPath);
2476
+ if (!currentName)
2477
+ return false;
2478
+ if (isProjectScopeRepairExplanation(content))
2479
+ return false;
2480
+ const githubRepoPattern = /github\.com[:/]([^/\s`'"#)]+)\/([^/\s`'"#)]+)(?:\.git)?/gi;
2481
+ let githubMatch;
2482
+ while ((githubMatch = githubRepoPattern.exec(content)) !== null) {
2483
+ const repo = normalizeRepoName(githubMatch[2] || "");
2484
+ if (repo && repo !== currentName)
2485
+ return true;
2486
+ }
2487
+ const workspacePathPattern = /\/workspace\/([^/\s`'"#)]+)/gi;
2488
+ let workspaceMatch;
2489
+ while ((workspaceMatch = workspacePathPattern.exec(content)) !== null) {
2490
+ const repo = normalizeRepoName(workspaceMatch[1] || "");
2491
+ if (repo && repo !== currentName)
2492
+ return true;
2493
+ }
2494
+ return false;
2495
+ }
2338
2496
  var SQLiteEventStore = class {
2339
2497
  db;
2340
2498
  initialized = false;
@@ -2833,11 +2991,11 @@ var SQLiteEventStore = class {
2833
2991
  /**
2834
2992
  * Get events by session ID
2835
2993
  */
2836
- async getSessionEvents(sessionId) {
2994
+ async getSessionEvents(sessionId, options) {
2837
2995
  await this.initialize();
2838
2996
  const rows = sqliteAll(
2839
2997
  this.db,
2840
- `SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC`,
2998
+ `SELECT * FROM events WHERE session_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
2841
2999
  [sessionId]
2842
3000
  );
2843
3001
  return rows.map(this.rowToEvent);
@@ -2845,11 +3003,11 @@ var SQLiteEventStore = class {
2845
3003
  /**
2846
3004
  * Get recent events
2847
3005
  */
2848
- async getRecentEvents(limit = 100) {
3006
+ async getRecentEvents(limit = 100, options) {
2849
3007
  await this.initialize();
2850
3008
  const rows = sqliteAll(
2851
3009
  this.db,
2852
- `SELECT * FROM events ORDER BY timestamp DESC LIMIT ?`,
3010
+ `SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp DESC LIMIT ?`,
2853
3011
  [limit]
2854
3012
  );
2855
3013
  return rows.map(this.rowToEvent);
@@ -2857,11 +3015,11 @@ var SQLiteEventStore = class {
2857
3015
  /**
2858
3016
  * Get event by ID
2859
3017
  */
2860
- async getEvent(id) {
3018
+ async getEvent(id, options) {
2861
3019
  await this.initialize();
2862
3020
  const row = sqliteGet(
2863
3021
  this.db,
2864
- `SELECT * FROM events WHERE id = ?`,
3022
+ `SELECT * FROM events WHERE id = ? AND ${maybeQuarantinePredicate(options)}`,
2865
3023
  [id]
2866
3024
  );
2867
3025
  if (!row)
@@ -2871,11 +3029,11 @@ var SQLiteEventStore = class {
2871
3029
  /**
2872
3030
  * Get events since a timestamp (for sync)
2873
3031
  */
2874
- async getEventsSince(timestamp, limit = 1e3) {
3032
+ async getEventsSince(timestamp, limit = 1e3, options) {
2875
3033
  await this.initialize();
2876
3034
  const rows = sqliteAll(
2877
3035
  this.db,
2878
- `SELECT * FROM events WHERE timestamp > ? ORDER BY timestamp ASC LIMIT ?`,
3036
+ `SELECT * FROM events WHERE timestamp > ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ?`,
2879
3037
  [timestamp, limit]
2880
3038
  );
2881
3039
  return rows.map(this.rowToEvent);
@@ -2884,11 +3042,11 @@ var SQLiteEventStore = class {
2884
3042
  * Get events since a SQLite rowid (for robust incremental replication).
2885
3043
  * Rowid is monotonic for append-only tables, independent of client timestamps.
2886
3044
  */
2887
- async getEventsSinceRowid(lastRowid, limit = 1e3) {
3045
+ async getEventsSinceRowid(lastRowid, limit = 1e3, options) {
2888
3046
  await this.initialize();
2889
3047
  const rows = sqliteAll(
2890
3048
  this.db,
2891
- `SELECT rowid as _rowid, * FROM events WHERE rowid > ? ORDER BY rowid ASC LIMIT ?`,
3049
+ `SELECT rowid as _rowid, * FROM events WHERE rowid > ? AND ${maybeQuarantinePredicate(options)} ORDER BY rowid ASC LIMIT ?`,
2892
3050
  [lastRowid, limit]
2893
3051
  );
2894
3052
  return rows.map((row) => ({
@@ -3123,19 +3281,19 @@ var SQLiteEventStore = class {
3123
3281
  /**
3124
3282
  * Count total events
3125
3283
  */
3126
- async countEvents() {
3284
+ async countEvents(options) {
3127
3285
  await this.initialize();
3128
- const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events`);
3286
+ const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events WHERE ${maybeQuarantinePredicate(options)}`);
3129
3287
  return row?.count || 0;
3130
3288
  }
3131
3289
  /**
3132
3290
  * Get events page in timestamp ascending order (stable migration/reindex scans)
3133
3291
  */
3134
- async getEventsPage(limit = 1e3, offset = 0) {
3292
+ async getEventsPage(limit = 1e3, offset = 0, options) {
3135
3293
  await this.initialize();
3136
3294
  const rows = sqliteAll(
3137
3295
  this.db,
3138
- `SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
3296
+ `SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
3139
3297
  [limit, offset]
3140
3298
  );
3141
3299
  return rows.map(this.rowToEvent);
@@ -3209,6 +3367,145 @@ var SQLiteEventStore = class {
3209
3367
  result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
3210
3368
  return result;
3211
3369
  }
3370
+ /**
3371
+ * Repair legacy imported events that predate canonical project scope metadata.
3372
+ *
3373
+ * Same-project legacy rows are tagged with scope.project.hash. Rows that look
3374
+ * imported but cannot be proven to belong to this project are quarantined so
3375
+ * dashboard default reads/search do not surface cross-project contamination.
3376
+ */
3377
+ async repairLegacyProjectScope(options = {}) {
3378
+ await this.initialize();
3379
+ const projectHash = options.projectHash || (options.projectPath ? hashProjectPath(options.projectPath) : void 0);
3380
+ if (!projectHash) {
3381
+ throw new Error("repairLegacyProjectScope requires projectPath or projectHash");
3382
+ }
3383
+ if (options.projectPath && options.projectHash && hashProjectPath(options.projectPath) !== options.projectHash) {
3384
+ throw new Error("repairLegacyProjectScope projectPath and projectHash refer to different project stores");
3385
+ }
3386
+ const dryRun = options.dryRun === true;
3387
+ const nowIso = (options.now || /* @__PURE__ */ new Date()).toISOString();
3388
+ const result = buildRepairResult(projectHash, dryRun);
3389
+ const rows = sqliteAll(
3390
+ this.db,
3391
+ `SELECT e.id, e.content, e.metadata, s.project_path as session_project_path
3392
+ FROM events e
3393
+ LEFT JOIN sessions s ON s.id = e.session_id
3394
+ ORDER BY e.timestamp ASC`,
3395
+ []
3396
+ );
3397
+ const sample = (entry) => {
3398
+ if (result.samples.length < 20)
3399
+ result.samples.push(entry);
3400
+ };
3401
+ for (const row of rows) {
3402
+ result.scanned++;
3403
+ let metadata = {};
3404
+ let metadataParseInvalid = false;
3405
+ if (row.metadata) {
3406
+ const parsed = safeParseMetadataValue(row.metadata);
3407
+ if (parsed) {
3408
+ metadata = parsed;
3409
+ } else {
3410
+ metadataParseInvalid = true;
3411
+ }
3412
+ }
3413
+ if (isActiveQuarantinedMetadata(metadata)) {
3414
+ result.skipped++;
3415
+ continue;
3416
+ }
3417
+ const currentHash = metadataProjectHash(metadata);
3418
+ const explicitPath = metadataProjectPath(metadata);
3419
+ const sessionProjectPath = typeof row.session_project_path === "string" && row.session_project_path.length > 0 ? row.session_project_path : void 0;
3420
+ const candidatePaths = metadataProjectPaths(metadata);
3421
+ if (sessionProjectPath && !candidatePaths.includes(sessionProjectPath)) {
3422
+ candidatePaths.push(sessionProjectPath);
3423
+ }
3424
+ const importedOrLegacy = metadataParseInvalid || isImportedOrLegacyScopedMetadata(metadata) || Boolean(sessionProjectPath);
3425
+ const pathHashes = candidatePaths.map((candidate) => {
3426
+ try {
3427
+ return { path: candidate, hash: hashProjectPath(candidate) };
3428
+ } catch {
3429
+ return { path: candidate, hash: void 0 };
3430
+ }
3431
+ });
3432
+ const matchingPath = pathHashes.find((candidate) => candidate.hash === projectHash);
3433
+ const foreignPath = pathHashes.find((candidate) => candidate.hash && candidate.hash !== projectHash);
3434
+ let action = "skipped";
3435
+ let reason;
3436
+ let observedProjectHash;
3437
+ if (foreignPath) {
3438
+ action = "quarantined";
3439
+ reason = "project-path-mismatch";
3440
+ observedProjectHash = foreignPath.hash;
3441
+ } else if (currentHash === projectHash && importedOrLegacy && hasConflictingContentProjectHint(row.content, options.projectPath)) {
3442
+ action = "quarantined";
3443
+ reason = "content-project-mismatch";
3444
+ } else if (currentHash === projectHash) {
3445
+ result.alreadyScoped++;
3446
+ continue;
3447
+ } else if (currentHash && currentHash !== projectHash) {
3448
+ action = "quarantined";
3449
+ reason = "scope-hash-mismatch";
3450
+ observedProjectHash = currentHash;
3451
+ } else if (matchingPath) {
3452
+ action = "repaired";
3453
+ reason = matchingPath.path === sessionProjectPath && matchingPath.path !== explicitPath ? "session-project-path" : "same-project-path";
3454
+ } else if (candidatePaths.length > 0) {
3455
+ action = "quarantined";
3456
+ reason = "project-path-mismatch";
3457
+ } else if (importedOrLegacy) {
3458
+ action = "quarantined";
3459
+ reason = "missing-project-scope";
3460
+ }
3461
+ if (action === "skipped" || !reason) {
3462
+ result.skipped++;
3463
+ continue;
3464
+ }
3465
+ if (action === "repaired") {
3466
+ const scope = isRecord(metadata.scope) ? { ...metadata.scope } : {};
3467
+ const project = isRecord(scope.project) ? { ...scope.project } : {};
3468
+ project.hash = projectHash;
3469
+ scope.project = project;
3470
+ metadata.scope = scope;
3471
+ metadata.repair = {
3472
+ ...isRecord(metadata.repair) ? metadata.repair : {},
3473
+ legacyProjectScope: {
3474
+ action,
3475
+ reason,
3476
+ repairedAt: nowIso
3477
+ }
3478
+ };
3479
+ addMetadataTag(metadata, `proj:${projectHash}`);
3480
+ result.repaired++;
3481
+ } else {
3482
+ metadata.quarantine = {
3483
+ ...isRecord(metadata.quarantine) ? metadata.quarantine : {},
3484
+ status: "active",
3485
+ category: "project-scope",
3486
+ reason,
3487
+ detectedAt: nowIso,
3488
+ expectedProjectHash: projectHash,
3489
+ ...observedProjectHash ? { observedProjectHash } : {}
3490
+ };
3491
+ metadata.repair = {
3492
+ ...isRecord(metadata.repair) ? metadata.repair : {},
3493
+ legacyProjectScope: {
3494
+ action,
3495
+ reason,
3496
+ repairedAt: nowIso
3497
+ }
3498
+ };
3499
+ addMetadataTag(metadata, "quarantine:project-scope");
3500
+ result.quarantined++;
3501
+ }
3502
+ sample({ eventId: row.id, action, reason });
3503
+ if (!dryRun) {
3504
+ sqliteRun(this.db, `UPDATE events SET metadata = ? WHERE id = ?`, [JSON.stringify(metadata), row.id]);
3505
+ }
3506
+ }
3507
+ return result;
3508
+ }
3212
3509
  /**
3213
3510
  * Get embedding/vector outbox health statistics
3214
3511
  */
@@ -3256,7 +3553,11 @@ var SQLiteEventStore = class {
3256
3553
  await this.initialize();
3257
3554
  const rows = sqliteAll(
3258
3555
  this.db,
3259
- `SELECT level, COUNT(*) as count FROM memory_levels GROUP BY level`
3556
+ `SELECT ml.level, COUNT(*) as count
3557
+ FROM memory_levels ml
3558
+ INNER JOIN events e ON e.id = ml.event_id
3559
+ WHERE ${notActiveQuarantinedSql("e.metadata")}
3560
+ GROUP BY ml.level`
3260
3561
  );
3261
3562
  return rows;
3262
3563
  }
@@ -3272,6 +3573,7 @@ var SQLiteEventStore = class {
3272
3573
  `SELECT e.* FROM events e
3273
3574
  INNER JOIN memory_levels ml ON e.id = ml.event_id
3274
3575
  WHERE ml.level = ?
3576
+ AND ${notActiveQuarantinedSql("e.metadata")}
3275
3577
  ORDER BY e.timestamp DESC
3276
3578
  LIMIT ? OFFSET ?`,
3277
3579
  [level, limit, offset]
@@ -3364,12 +3666,13 @@ var SQLiteEventStore = class {
3364
3666
  /**
3365
3667
  * Get most accessed memories (falls back to recent events if none accessed)
3366
3668
  */
3367
- async getMostAccessed(limit = 10) {
3669
+ async getMostAccessed(limit = 10, options) {
3368
3670
  await this.initialize();
3369
3671
  let rows = sqliteAll(
3370
3672
  this.db,
3371
3673
  `SELECT * FROM events
3372
3674
  WHERE access_count > 0
3675
+ AND ${maybeQuarantinePredicate(options)}
3373
3676
  ORDER BY access_count DESC, last_accessed_at DESC
3374
3677
  LIMIT ?`,
3375
3678
  [limit]
@@ -3378,6 +3681,7 @@ var SQLiteEventStore = class {
3378
3681
  rows = sqliteAll(
3379
3682
  this.db,
3380
3683
  `SELECT * FROM events
3684
+ WHERE ${maybeQuarantinePredicate(options)}
3381
3685
  ORDER BY timestamp DESC
3382
3686
  LIMIT ?`,
3383
3687
  [limit]
@@ -3508,6 +3812,7 @@ var SQLiteEventStore = class {
3508
3812
  FROM memory_helpfulness mh
3509
3813
  JOIN events e ON e.id = mh.event_id
3510
3814
  WHERE mh.measured_at IS NOT NULL
3815
+ AND ${notActiveQuarantinedSql("e.metadata")}
3511
3816
  GROUP BY mh.event_id
3512
3817
  ORDER BY avg_score DESC
3513
3818
  LIMIT ?`,
@@ -3572,6 +3877,7 @@ var SQLiteEventStore = class {
3572
3877
  FROM events_fts fts
3573
3878
  JOIN events e ON e.id = fts.event_id
3574
3879
  WHERE events_fts MATCH ?
3880
+ AND ${notActiveQuarantinedSql("e.metadata")}
3575
3881
  ORDER BY fts.rank
3576
3882
  LIMIT ?`,
3577
3883
  [searchTerms, limit]
@@ -3586,6 +3892,7 @@ var SQLiteEventStore = class {
3586
3892
  this.db,
3587
3893
  `SELECT *, 0 as rank FROM events
3588
3894
  WHERE content LIKE ?
3895
+ AND ${notActiveQuarantinedSql()}
3589
3896
  ORDER BY timestamp DESC
3590
3897
  LIMIT ?`,
3591
3898
  [likePattern, limit]
@@ -3792,6 +4099,7 @@ var SQLiteEventStore = class {
3792
4099
  `SELECT turn_id, MIN(timestamp) as min_ts
3793
4100
  FROM events
3794
4101
  WHERE session_id = ? AND turn_id IS NOT NULL
4102
+ AND ${maybeQuarantinePredicate(options)}
3795
4103
  GROUP BY turn_id
3796
4104
  ORDER BY min_ts DESC
3797
4105
  LIMIT ? OFFSET ?`,
@@ -3799,7 +4107,7 @@ var SQLiteEventStore = class {
3799
4107
  );
3800
4108
  const turns = [];
3801
4109
  for (const turnRow of turnRows) {
3802
- const events = await this.getEventsByTurn(turnRow.turn_id);
4110
+ const events = await this.getEventsByTurn(turnRow.turn_id, options);
3803
4111
  const promptEvent = events.find((e) => e.eventType === "user_prompt");
3804
4112
  const toolEvents = events.filter((e) => e.eventType === "tool_observation");
3805
4113
  const hasResponse = events.some((e) => e.eventType === "agent_response");
@@ -3818,11 +4126,11 @@ var SQLiteEventStore = class {
3818
4126
  /**
3819
4127
  * Get all events for a specific turn_id
3820
4128
  */
3821
- async getEventsByTurn(turnId) {
4129
+ async getEventsByTurn(turnId, options) {
3822
4130
  await this.initialize();
3823
4131
  const rows = sqliteAll(
3824
4132
  this.db,
3825
- `SELECT * FROM events WHERE turn_id = ? ORDER BY timestamp ASC`,
4133
+ `SELECT * FROM events WHERE turn_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
3826
4134
  [turnId]
3827
4135
  );
3828
4136
  return rows.map(this.rowToEvent);
@@ -3830,13 +4138,14 @@ var SQLiteEventStore = class {
3830
4138
  /**
3831
4139
  * Count total turns for a session
3832
4140
  */
3833
- async countSessionTurns(sessionId) {
4141
+ async countSessionTurns(sessionId, options) {
3834
4142
  await this.initialize();
3835
4143
  const row = sqliteGet(
3836
4144
  this.db,
3837
4145
  `SELECT COUNT(DISTINCT turn_id) as count
3838
4146
  FROM events
3839
- WHERE session_id = ? AND turn_id IS NOT NULL`,
4147
+ WHERE session_id = ? AND turn_id IS NOT NULL
4148
+ AND ${maybeQuarantinePredicate(options)}`,
3840
4149
  [sessionId]
3841
4150
  );
3842
4151
  return row?.count || 0;
@@ -3928,7 +4237,7 @@ var SQLiteEventStore = class {
3928
4237
  content: row.content,
3929
4238
  canonicalKey: row.canonical_key,
3930
4239
  dedupeKey: row.dedupe_key,
3931
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0
4240
+ metadata: safeParseMetadataValue(row.metadata)
3932
4241
  };
3933
4242
  if (row.access_count !== void 0) {
3934
4243
  event.access_count = row.access_count;
@@ -4523,6 +4832,10 @@ var MemoryQueryService = class {
4523
4832
  await this.initialize();
4524
4833
  return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
4525
4834
  }
4835
+ async repairLegacyProjectScope(options) {
4836
+ await this.initialize();
4837
+ return this.getMaintenanceStore("repairLegacyProjectScope").repairLegacyProjectScope(options);
4838
+ }
4526
4839
  async getStats() {
4527
4840
  await this.initialize();
4528
4841
  const deps = this.getStatsDeps();
@@ -6145,18 +6458,18 @@ function assertDefaultRetrieverStore(eventStore) {
6145
6458
  function createMemoryEngineServices(options) {
6146
6459
  const factories = options.factories ?? {};
6147
6460
  const storagePath = options.storagePath;
6148
- if (!options.readOnly && !fs5.existsSync(storagePath)) {
6149
- fs5.mkdirSync(storagePath, { recursive: true });
6461
+ if (!options.readOnly && !fs6.existsSync(storagePath)) {
6462
+ fs6.mkdirSync(storagePath, { recursive: true });
6150
6463
  }
6151
6464
  const sqliteStore = (factories.createSQLiteEventStore ?? defaultCreateSQLiteEventStore)(
6152
- path4.join(storagePath, "events.sqlite"),
6465
+ path5.join(storagePath, "events.sqlite"),
6153
6466
  {
6154
6467
  readonly: options.readOnly,
6155
6468
  markdownMirrorRoot: storagePath
6156
6469
  }
6157
6470
  );
6158
6471
  const vectorStore = (factories.createVectorStore ?? defaultCreateVectorStore)(
6159
- path4.join(storagePath, "vectors")
6472
+ path5.join(storagePath, "vectors")
6160
6473
  );
6161
6474
  const embeddingModel = options.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
6162
6475
  const embedder = embeddingModel ? (factories.createEmbedder ?? defaultCreateEmbedder)(embeddingModel) : (factories.getDefaultEmbedder ?? getDefaultEmbedder)();
@@ -6567,8 +6880,8 @@ function createMemoryRuntimeService(deps) {
6567
6880
  }
6568
6881
 
6569
6882
  // src/extensions/shared-memory/shared-memory-services.ts
6570
- import * as fs6 from "fs";
6571
- import * as path5 from "path";
6883
+ import * as fs7 from "fs";
6884
+ import * as path6 from "path";
6572
6885
 
6573
6886
  // src/core/shared-event-store.ts
6574
6887
  var SharedEventStore = class {
@@ -7256,7 +7569,7 @@ var SharedMemoryServices = class {
7256
7569
  this.ensureDirectory(sharedPath, { allowCreate: true });
7257
7570
  const store = await this.openStore(sharedPath);
7258
7571
  this.sharedVectorStore = this.factories.createSharedVectorStore(
7259
- path5.join(sharedPath, "vectors")
7572
+ path6.join(sharedPath, "vectors")
7260
7573
  );
7261
7574
  await this.sharedVectorStore.initialize();
7262
7575
  this.sharedPromoter = this.factories.createSharedPromoter(
@@ -7329,7 +7642,7 @@ var SharedMemoryServices = class {
7329
7642
  async createOpenStorePromise(sharedPath) {
7330
7643
  if (!this.sharedEventStore) {
7331
7644
  const sharedEventStore = this.factories.createSharedEventStore(
7332
- path5.join(sharedPath, "shared.duckdb")
7645
+ path6.join(sharedPath, "shared.duckdb")
7333
7646
  );
7334
7647
  await sharedEventStore.initialize();
7335
7648
  this.sharedEventStore = sharedEventStore;
@@ -7349,9 +7662,9 @@ var SharedMemoryServices = class {
7349
7662
  }
7350
7663
  get factories() {
7351
7664
  return {
7352
- existsSync: this.options.factories?.existsSync ?? fs6.existsSync,
7665
+ existsSync: this.options.factories?.existsSync ?? fs7.existsSync,
7353
7666
  mkdirSync: this.options.factories?.mkdirSync ?? ((targetPath) => {
7354
- fs6.mkdirSync(targetPath, { recursive: true });
7667
+ fs7.mkdirSync(targetPath, { recursive: true });
7355
7668
  }),
7356
7669
  createSharedEventStore: this.options.factories?.createSharedEventStore ?? createSharedEventStore,
7357
7670
  createSharedStore: this.options.factories?.createSharedStore ?? createSharedStore,
@@ -7466,37 +7779,11 @@ function createMemoryServiceComposition(options) {
7466
7779
  }
7467
7780
  function defaultExpandPath(targetPath) {
7468
7781
  if (targetPath.startsWith("~")) {
7469
- return path6.join(os.homedir(), targetPath.slice(1));
7782
+ return path7.join(os2.homedir(), targetPath.slice(1));
7470
7783
  }
7471
7784
  return targetPath;
7472
7785
  }
7473
7786
 
7474
- // src/core/registry/project-path.ts
7475
- import * as crypto2 from "crypto";
7476
- import * as fs7 from "fs";
7477
- import * as os2 from "os";
7478
- import * as path7 from "path";
7479
- function normalizeProjectPath(projectPath) {
7480
- const expanded = projectPath.startsWith("~") ? path7.join(os2.homedir(), projectPath.slice(1)) : projectPath;
7481
- try {
7482
- return fs7.realpathSync(expanded);
7483
- } catch {
7484
- return path7.resolve(expanded);
7485
- }
7486
- }
7487
- function hashProjectPath(projectPath) {
7488
- const normalizedPath = normalizeProjectPath(projectPath);
7489
- return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
7490
- }
7491
- function getProjectStoragePath(projectPath) {
7492
- const hash = hashProjectPath(projectPath);
7493
- return path7.join(os2.homedir(), ".claude-code", "memory", "projects", hash);
7494
- }
7495
- function resolveProjectStoragePath(projectOrHash) {
7496
- const isHash = /^[a-f0-9]{8}$/.test(projectOrHash);
7497
- return isHash ? path7.join(os2.homedir(), ".claude-code", "memory", "projects", projectOrHash) : getProjectStoragePath(projectOrHash);
7498
- }
7499
-
7500
7787
  // src/core/registry/session-registry.ts
7501
7788
  import * as fs8 from "fs";
7502
7789
  import * as os3 from "os";
@@ -7798,6 +8085,9 @@ var MemoryService = class {
7798
8085
  async recoverStuckOutboxItems(options) {
7799
8086
  return this.queryService.recoverStuckOutboxItems(options);
7800
8087
  }
8088
+ async repairLegacyProjectScope(options) {
8089
+ return this.queryService.repairLegacyProjectScope(options);
8090
+ }
7801
8091
  async getRetrievalTraceStats() {
7802
8092
  return this.retrievalAnalyticsService.getRetrievalTraceStats();
7803
8093
  }