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