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