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.
- package/dist/cli/index.js +497 -84
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +309 -22
- package/dist/core/index.js.map +3 -3
- package/dist/hooks/post-tool-use.js +399 -74
- package/dist/hooks/post-tool-use.js.map +4 -4
- package/dist/hooks/semantic-daemon.js +357 -67
- package/dist/hooks/semantic-daemon.js.map +4 -4
- package/dist/hooks/session-end.js +357 -67
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +357 -67
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +398 -73
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +357 -67
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/index.js +318 -28
- package/dist/index.js.map +3 -3
- package/dist/mcp/index.js +403 -78
- package/dist/mcp/index.js.map +4 -4
- package/dist/server/api/index.js +361 -71
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +361 -71
- package/dist/server/index.js.map +4 -4
- package/dist/services/memory-service.js +357 -67
- package/dist/services/memory-service.js.map +4 -4
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -17,8 +17,8 @@ import * as os11 from "os";
|
|
|
17
17
|
import * as os5 from "os";
|
|
18
18
|
|
|
19
19
|
// src/core/engine/memory-service-composition.ts
|
|
20
|
-
import * as
|
|
21
|
-
import * as
|
|
20
|
+
import * as os2 from "os";
|
|
21
|
+
import * as path7 from "path";
|
|
22
22
|
|
|
23
23
|
// src/core/metadata-extractor.ts
|
|
24
24
|
function createToolObservationEmbedding(toolName, metadata, success) {
|
|
@@ -1529,8 +1529,8 @@ function createEndlessMemoryServices(options) {
|
|
|
1529
1529
|
}
|
|
1530
1530
|
|
|
1531
1531
|
// src/core/engine/memory-engine-services.ts
|
|
1532
|
-
import * as
|
|
1533
|
-
import * as
|
|
1532
|
+
import * as fs6 from "fs";
|
|
1533
|
+
import * as path5 from "path";
|
|
1534
1534
|
|
|
1535
1535
|
// src/extensions/vector/embedder.ts
|
|
1536
1536
|
var DEFAULT_EMBEDDING_MODEL = "Xenova/multilingual-e5-small";
|
|
@@ -2208,14 +2208,43 @@ function makeDedupeKey(content, sessionId) {
|
|
|
2208
2208
|
return `${sessionId}:${contentHash}`;
|
|
2209
2209
|
}
|
|
2210
2210
|
|
|
2211
|
+
// src/core/sqlite-event-store.ts
|
|
2212
|
+
import * as nodePath2 from "path";
|
|
2213
|
+
|
|
2214
|
+
// src/core/registry/project-path.ts
|
|
2215
|
+
import * as crypto2 from "crypto";
|
|
2216
|
+
import * as fs3 from "fs";
|
|
2217
|
+
import * as os from "os";
|
|
2218
|
+
import * as path3 from "path";
|
|
2219
|
+
function normalizeProjectPath(projectPath) {
|
|
2220
|
+
const expanded = projectPath.startsWith("~") ? path3.join(os.homedir(), projectPath.slice(1)) : projectPath;
|
|
2221
|
+
try {
|
|
2222
|
+
return fs3.realpathSync(expanded);
|
|
2223
|
+
} catch {
|
|
2224
|
+
return path3.resolve(expanded);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
function hashProjectPath(projectPath) {
|
|
2228
|
+
const normalizedPath = normalizeProjectPath(projectPath);
|
|
2229
|
+
return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
|
|
2230
|
+
}
|
|
2231
|
+
function getProjectStoragePath(projectPath) {
|
|
2232
|
+
const hash = hashProjectPath(projectPath);
|
|
2233
|
+
return path3.join(os.homedir(), ".claude-code", "memory", "projects", hash);
|
|
2234
|
+
}
|
|
2235
|
+
function resolveProjectStoragePath(projectOrHash) {
|
|
2236
|
+
const isHash = /^[a-f0-9]{8}$/.test(projectOrHash);
|
|
2237
|
+
return isHash ? path3.join(os.homedir(), ".claude-code", "memory", "projects", projectOrHash) : getProjectStoragePath(projectOrHash);
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2211
2240
|
// src/core/sqlite-wrapper.ts
|
|
2212
2241
|
import Database from "better-sqlite3";
|
|
2213
|
-
import * as
|
|
2242
|
+
import * as fs4 from "fs";
|
|
2214
2243
|
import * as nodePath from "path";
|
|
2215
2244
|
function createSQLiteDatabase(path22, options) {
|
|
2216
2245
|
const dir = nodePath.dirname(path22);
|
|
2217
|
-
if (!
|
|
2218
|
-
|
|
2246
|
+
if (!fs4.existsSync(dir)) {
|
|
2247
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
2219
2248
|
}
|
|
2220
2249
|
const db = new Database(path22, {
|
|
2221
2250
|
readonly: options?.readonly ?? false
|
|
@@ -2264,8 +2293,8 @@ function toSQLiteTimestamp(date) {
|
|
|
2264
2293
|
}
|
|
2265
2294
|
|
|
2266
2295
|
// src/core/markdown-mirror.ts
|
|
2267
|
-
import * as
|
|
2268
|
-
import * as
|
|
2296
|
+
import * as fs5 from "fs/promises";
|
|
2297
|
+
import * as path4 from "path";
|
|
2269
2298
|
var DEFAULT_NAMESPACE = "default";
|
|
2270
2299
|
var DEFAULT_CATEGORY = "uncategorized";
|
|
2271
2300
|
function sanitizeSegment2(input, fallback) {
|
|
@@ -2294,7 +2323,7 @@ function buildMirrorPath2(rootDir, event) {
|
|
|
2294
2323
|
const yyyy = d.getFullYear();
|
|
2295
2324
|
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
2296
2325
|
const dd = String(d.getDate()).padStart(2, "0");
|
|
2297
|
-
return
|
|
2326
|
+
return path4.join(rootDir, "memory", namespace, ...categories, `${yyyy}-${mm}-${dd}.md`);
|
|
2298
2327
|
}
|
|
2299
2328
|
function formatMirrorEntry(event) {
|
|
2300
2329
|
const category = Array.isArray(event.metadata?.categoryPath) ? event.metadata.categoryPath.join("/") : String(event.metadata?.category ?? event.eventType);
|
|
@@ -2315,8 +2344,8 @@ var MarkdownMirror2 = class {
|
|
|
2315
2344
|
}
|
|
2316
2345
|
async append(event) {
|
|
2317
2346
|
const outPath = buildMirrorPath2(this.rootDir, event);
|
|
2318
|
-
await
|
|
2319
|
-
await
|
|
2347
|
+
await fs5.mkdir(path4.dirname(outPath), { recursive: true });
|
|
2348
|
+
await fs5.appendFile(outPath, formatMirrorEntry(event), "utf8");
|
|
2320
2349
|
return outPath;
|
|
2321
2350
|
}
|
|
2322
2351
|
};
|
|
@@ -2337,6 +2366,135 @@ function emptyOutboxRecoveryResult() {
|
|
|
2337
2366
|
vector: { recoveredProcessing: 0, retriedFailed: 0 }
|
|
2338
2367
|
};
|
|
2339
2368
|
}
|
|
2369
|
+
function isRecord(value) {
|
|
2370
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2371
|
+
}
|
|
2372
|
+
function getNestedRecord(root, path22) {
|
|
2373
|
+
let cursor = root;
|
|
2374
|
+
for (const key of path22) {
|
|
2375
|
+
if (!isRecord(cursor))
|
|
2376
|
+
return void 0;
|
|
2377
|
+
cursor = cursor[key];
|
|
2378
|
+
}
|
|
2379
|
+
return isRecord(cursor) ? cursor : void 0;
|
|
2380
|
+
}
|
|
2381
|
+
function getNestedString(root, path22) {
|
|
2382
|
+
let cursor = root;
|
|
2383
|
+
for (const key of path22) {
|
|
2384
|
+
if (!isRecord(cursor))
|
|
2385
|
+
return void 0;
|
|
2386
|
+
cursor = cursor[key];
|
|
2387
|
+
}
|
|
2388
|
+
return typeof cursor === "string" && cursor.length > 0 ? cursor : void 0;
|
|
2389
|
+
}
|
|
2390
|
+
function metadataProjectHash(metadata) {
|
|
2391
|
+
return getNestedString(metadata, ["scope", "project", "hash"]);
|
|
2392
|
+
}
|
|
2393
|
+
function metadataProjectPaths(metadata) {
|
|
2394
|
+
const candidates = [
|
|
2395
|
+
getNestedString(metadata, ["projectPath"]),
|
|
2396
|
+
getNestedString(metadata, ["sourceProjectPath"]),
|
|
2397
|
+
getNestedString(metadata, ["scope", "project", "path"])
|
|
2398
|
+
];
|
|
2399
|
+
const paths = [];
|
|
2400
|
+
for (const value of candidates) {
|
|
2401
|
+
if (value && !paths.includes(value))
|
|
2402
|
+
paths.push(value);
|
|
2403
|
+
}
|
|
2404
|
+
return paths;
|
|
2405
|
+
}
|
|
2406
|
+
function metadataProjectPath(metadata) {
|
|
2407
|
+
return metadataProjectPaths(metadata)[0];
|
|
2408
|
+
}
|
|
2409
|
+
function isActiveQuarantinedMetadata(metadata) {
|
|
2410
|
+
const quarantine = getNestedRecord(metadata, ["quarantine"]);
|
|
2411
|
+
return quarantine?.status === "active";
|
|
2412
|
+
}
|
|
2413
|
+
function activeQuarantineStatusExpression(column = "metadata") {
|
|
2414
|
+
return `COALESCE(json_extract(CASE WHEN json_valid(${column}) THEN ${column} ELSE '{}' END, '$.quarantine.status'), '')`;
|
|
2415
|
+
}
|
|
2416
|
+
function notActiveQuarantinedSql(column = "metadata") {
|
|
2417
|
+
return `${activeQuarantineStatusExpression(column)} != 'active'`;
|
|
2418
|
+
}
|
|
2419
|
+
function maybeQuarantinePredicate(options, column = "metadata") {
|
|
2420
|
+
return options?.includeQuarantined ? "1=1" : notActiveQuarantinedSql(column);
|
|
2421
|
+
}
|
|
2422
|
+
function safeParseMetadataValue(value) {
|
|
2423
|
+
if (!value)
|
|
2424
|
+
return void 0;
|
|
2425
|
+
if (typeof value === "object")
|
|
2426
|
+
return isRecord(value) ? value : void 0;
|
|
2427
|
+
if (typeof value !== "string")
|
|
2428
|
+
return void 0;
|
|
2429
|
+
try {
|
|
2430
|
+
const parsed = JSON.parse(value);
|
|
2431
|
+
return isRecord(parsed) ? parsed : void 0;
|
|
2432
|
+
} catch {
|
|
2433
|
+
return void 0;
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
function isImportedOrLegacyScopedMetadata(metadata) {
|
|
2437
|
+
if (!metadata)
|
|
2438
|
+
return false;
|
|
2439
|
+
return Boolean(
|
|
2440
|
+
metadata.importedFrom || metadata.sourceSessionId || metadata.sourceSessionHash || metadata.hermesSource || metadata.projectPath || metadata.sourceProjectPath || metadata.source === "hermes" || metadata.source === "claude" || metadata.source === "codex"
|
|
2441
|
+
);
|
|
2442
|
+
}
|
|
2443
|
+
function addMetadataTag(metadata, tag) {
|
|
2444
|
+
const current = Array.isArray(metadata.tags) ? metadata.tags.filter((value) => typeof value === "string") : [];
|
|
2445
|
+
if (!current.includes(tag))
|
|
2446
|
+
metadata.tags = [...current, tag];
|
|
2447
|
+
}
|
|
2448
|
+
function buildRepairResult(projectHash, dryRun) {
|
|
2449
|
+
return {
|
|
2450
|
+
dryRun,
|
|
2451
|
+
projectHash,
|
|
2452
|
+
scanned: 0,
|
|
2453
|
+
repaired: 0,
|
|
2454
|
+
quarantined: 0,
|
|
2455
|
+
alreadyScoped: 0,
|
|
2456
|
+
skipped: 0,
|
|
2457
|
+
samples: []
|
|
2458
|
+
};
|
|
2459
|
+
}
|
|
2460
|
+
function normalizeRepoName(value) {
|
|
2461
|
+
return value.replace(/\.git$/i, "").trim().toLowerCase();
|
|
2462
|
+
}
|
|
2463
|
+
function projectBasename(projectPath) {
|
|
2464
|
+
if (!projectPath)
|
|
2465
|
+
return void 0;
|
|
2466
|
+
const trimmed = projectPath.replace(/[\\/]+$/, "");
|
|
2467
|
+
const basename7 = nodePath2.basename(trimmed);
|
|
2468
|
+
return basename7 ? normalizeRepoName(basename7) : void 0;
|
|
2469
|
+
}
|
|
2470
|
+
function isProjectScopeRepairExplanation(content) {
|
|
2471
|
+
const normalized = content.toLowerCase();
|
|
2472
|
+
const hasRepairContext = /project[- ]scope|mis[- ]scoped|quarantine|contamination|legacy|오염|격리|repair/.test(normalized);
|
|
2473
|
+
const hasExplanationContext = /example|detector|trap|not a .*project task|기억|메모리|설명|수정|검증/.test(normalized);
|
|
2474
|
+
return hasRepairContext && hasExplanationContext;
|
|
2475
|
+
}
|
|
2476
|
+
function hasConflictingContentProjectHint(content, projectPath) {
|
|
2477
|
+
const currentName = projectBasename(projectPath);
|
|
2478
|
+
if (!currentName)
|
|
2479
|
+
return false;
|
|
2480
|
+
if (isProjectScopeRepairExplanation(content))
|
|
2481
|
+
return false;
|
|
2482
|
+
const githubRepoPattern = /github\.com[:/]([^/\s`'"#)]+)\/([^/\s`'"#)]+)(?:\.git)?/gi;
|
|
2483
|
+
let githubMatch;
|
|
2484
|
+
while ((githubMatch = githubRepoPattern.exec(content)) !== null) {
|
|
2485
|
+
const repo = normalizeRepoName(githubMatch[2] || "");
|
|
2486
|
+
if (repo && repo !== currentName)
|
|
2487
|
+
return true;
|
|
2488
|
+
}
|
|
2489
|
+
const workspacePathPattern = /\/workspace\/([^/\s`'"#)]+)/gi;
|
|
2490
|
+
let workspaceMatch;
|
|
2491
|
+
while ((workspaceMatch = workspacePathPattern.exec(content)) !== null) {
|
|
2492
|
+
const repo = normalizeRepoName(workspaceMatch[1] || "");
|
|
2493
|
+
if (repo && repo !== currentName)
|
|
2494
|
+
return true;
|
|
2495
|
+
}
|
|
2496
|
+
return false;
|
|
2497
|
+
}
|
|
2340
2498
|
var SQLiteEventStore = class {
|
|
2341
2499
|
db;
|
|
2342
2500
|
initialized = false;
|
|
@@ -2835,11 +2993,11 @@ var SQLiteEventStore = class {
|
|
|
2835
2993
|
/**
|
|
2836
2994
|
* Get events by session ID
|
|
2837
2995
|
*/
|
|
2838
|
-
async getSessionEvents(sessionId) {
|
|
2996
|
+
async getSessionEvents(sessionId, options) {
|
|
2839
2997
|
await this.initialize();
|
|
2840
2998
|
const rows = sqliteAll(
|
|
2841
2999
|
this.db,
|
|
2842
|
-
`SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC`,
|
|
3000
|
+
`SELECT * FROM events WHERE session_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
|
|
2843
3001
|
[sessionId]
|
|
2844
3002
|
);
|
|
2845
3003
|
return rows.map(this.rowToEvent);
|
|
@@ -2847,11 +3005,11 @@ var SQLiteEventStore = class {
|
|
|
2847
3005
|
/**
|
|
2848
3006
|
* Get recent events
|
|
2849
3007
|
*/
|
|
2850
|
-
async getRecentEvents(limit = 100) {
|
|
3008
|
+
async getRecentEvents(limit = 100, options) {
|
|
2851
3009
|
await this.initialize();
|
|
2852
3010
|
const rows = sqliteAll(
|
|
2853
3011
|
this.db,
|
|
2854
|
-
`SELECT * FROM events ORDER BY timestamp DESC LIMIT ?`,
|
|
3012
|
+
`SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp DESC LIMIT ?`,
|
|
2855
3013
|
[limit]
|
|
2856
3014
|
);
|
|
2857
3015
|
return rows.map(this.rowToEvent);
|
|
@@ -2859,11 +3017,11 @@ var SQLiteEventStore = class {
|
|
|
2859
3017
|
/**
|
|
2860
3018
|
* Get event by ID
|
|
2861
3019
|
*/
|
|
2862
|
-
async getEvent(id) {
|
|
3020
|
+
async getEvent(id, options) {
|
|
2863
3021
|
await this.initialize();
|
|
2864
3022
|
const row = sqliteGet(
|
|
2865
3023
|
this.db,
|
|
2866
|
-
`SELECT * FROM events WHERE id =
|
|
3024
|
+
`SELECT * FROM events WHERE id = ? AND ${maybeQuarantinePredicate(options)}`,
|
|
2867
3025
|
[id]
|
|
2868
3026
|
);
|
|
2869
3027
|
if (!row)
|
|
@@ -2873,11 +3031,11 @@ var SQLiteEventStore = class {
|
|
|
2873
3031
|
/**
|
|
2874
3032
|
* Get events since a timestamp (for sync)
|
|
2875
3033
|
*/
|
|
2876
|
-
async getEventsSince(timestamp, limit = 1e3) {
|
|
3034
|
+
async getEventsSince(timestamp, limit = 1e3, options) {
|
|
2877
3035
|
await this.initialize();
|
|
2878
3036
|
const rows = sqliteAll(
|
|
2879
3037
|
this.db,
|
|
2880
|
-
`SELECT * FROM events WHERE timestamp > ? ORDER BY timestamp ASC LIMIT ?`,
|
|
3038
|
+
`SELECT * FROM events WHERE timestamp > ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ?`,
|
|
2881
3039
|
[timestamp, limit]
|
|
2882
3040
|
);
|
|
2883
3041
|
return rows.map(this.rowToEvent);
|
|
@@ -2886,11 +3044,11 @@ var SQLiteEventStore = class {
|
|
|
2886
3044
|
* Get events since a SQLite rowid (for robust incremental replication).
|
|
2887
3045
|
* Rowid is monotonic for append-only tables, independent of client timestamps.
|
|
2888
3046
|
*/
|
|
2889
|
-
async getEventsSinceRowid(lastRowid, limit = 1e3) {
|
|
3047
|
+
async getEventsSinceRowid(lastRowid, limit = 1e3, options) {
|
|
2890
3048
|
await this.initialize();
|
|
2891
3049
|
const rows = sqliteAll(
|
|
2892
3050
|
this.db,
|
|
2893
|
-
`SELECT rowid as _rowid, * FROM events WHERE rowid > ? ORDER BY rowid ASC LIMIT ?`,
|
|
3051
|
+
`SELECT rowid as _rowid, * FROM events WHERE rowid > ? AND ${maybeQuarantinePredicate(options)} ORDER BY rowid ASC LIMIT ?`,
|
|
2894
3052
|
[lastRowid, limit]
|
|
2895
3053
|
);
|
|
2896
3054
|
return rows.map((row) => ({
|
|
@@ -3125,19 +3283,19 @@ var SQLiteEventStore = class {
|
|
|
3125
3283
|
/**
|
|
3126
3284
|
* Count total events
|
|
3127
3285
|
*/
|
|
3128
|
-
async countEvents() {
|
|
3286
|
+
async countEvents(options) {
|
|
3129
3287
|
await this.initialize();
|
|
3130
|
-
const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events`);
|
|
3288
|
+
const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events WHERE ${maybeQuarantinePredicate(options)}`);
|
|
3131
3289
|
return row?.count || 0;
|
|
3132
3290
|
}
|
|
3133
3291
|
/**
|
|
3134
3292
|
* Get events page in timestamp ascending order (stable migration/reindex scans)
|
|
3135
3293
|
*/
|
|
3136
|
-
async getEventsPage(limit = 1e3, offset = 0) {
|
|
3294
|
+
async getEventsPage(limit = 1e3, offset = 0, options) {
|
|
3137
3295
|
await this.initialize();
|
|
3138
3296
|
const rows = sqliteAll(
|
|
3139
3297
|
this.db,
|
|
3140
|
-
`SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
3298
|
+
`SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
3141
3299
|
[limit, offset]
|
|
3142
3300
|
);
|
|
3143
3301
|
return rows.map(this.rowToEvent);
|
|
@@ -3211,6 +3369,145 @@ var SQLiteEventStore = class {
|
|
|
3211
3369
|
result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
|
|
3212
3370
|
return result;
|
|
3213
3371
|
}
|
|
3372
|
+
/**
|
|
3373
|
+
* Repair legacy imported events that predate canonical project scope metadata.
|
|
3374
|
+
*
|
|
3375
|
+
* Same-project legacy rows are tagged with scope.project.hash. Rows that look
|
|
3376
|
+
* imported but cannot be proven to belong to this project are quarantined so
|
|
3377
|
+
* dashboard default reads/search do not surface cross-project contamination.
|
|
3378
|
+
*/
|
|
3379
|
+
async repairLegacyProjectScope(options = {}) {
|
|
3380
|
+
await this.initialize();
|
|
3381
|
+
const projectHash = options.projectHash || (options.projectPath ? hashProjectPath(options.projectPath) : void 0);
|
|
3382
|
+
if (!projectHash) {
|
|
3383
|
+
throw new Error("repairLegacyProjectScope requires projectPath or projectHash");
|
|
3384
|
+
}
|
|
3385
|
+
if (options.projectPath && options.projectHash && hashProjectPath(options.projectPath) !== options.projectHash) {
|
|
3386
|
+
throw new Error("repairLegacyProjectScope projectPath and projectHash refer to different project stores");
|
|
3387
|
+
}
|
|
3388
|
+
const dryRun = options.dryRun === true;
|
|
3389
|
+
const nowIso = (options.now || /* @__PURE__ */ new Date()).toISOString();
|
|
3390
|
+
const result = buildRepairResult(projectHash, dryRun);
|
|
3391
|
+
const rows = sqliteAll(
|
|
3392
|
+
this.db,
|
|
3393
|
+
`SELECT e.id, e.content, e.metadata, s.project_path as session_project_path
|
|
3394
|
+
FROM events e
|
|
3395
|
+
LEFT JOIN sessions s ON s.id = e.session_id
|
|
3396
|
+
ORDER BY e.timestamp ASC`,
|
|
3397
|
+
[]
|
|
3398
|
+
);
|
|
3399
|
+
const sample = (entry) => {
|
|
3400
|
+
if (result.samples.length < 20)
|
|
3401
|
+
result.samples.push(entry);
|
|
3402
|
+
};
|
|
3403
|
+
for (const row of rows) {
|
|
3404
|
+
result.scanned++;
|
|
3405
|
+
let metadata = {};
|
|
3406
|
+
let metadataParseInvalid = false;
|
|
3407
|
+
if (row.metadata) {
|
|
3408
|
+
const parsed = safeParseMetadataValue(row.metadata);
|
|
3409
|
+
if (parsed) {
|
|
3410
|
+
metadata = parsed;
|
|
3411
|
+
} else {
|
|
3412
|
+
metadataParseInvalid = true;
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
if (isActiveQuarantinedMetadata(metadata)) {
|
|
3416
|
+
result.skipped++;
|
|
3417
|
+
continue;
|
|
3418
|
+
}
|
|
3419
|
+
const currentHash = metadataProjectHash(metadata);
|
|
3420
|
+
const explicitPath = metadataProjectPath(metadata);
|
|
3421
|
+
const sessionProjectPath = typeof row.session_project_path === "string" && row.session_project_path.length > 0 ? row.session_project_path : void 0;
|
|
3422
|
+
const candidatePaths = metadataProjectPaths(metadata);
|
|
3423
|
+
if (sessionProjectPath && !candidatePaths.includes(sessionProjectPath)) {
|
|
3424
|
+
candidatePaths.push(sessionProjectPath);
|
|
3425
|
+
}
|
|
3426
|
+
const importedOrLegacy = metadataParseInvalid || isImportedOrLegacyScopedMetadata(metadata) || Boolean(sessionProjectPath);
|
|
3427
|
+
const pathHashes = candidatePaths.map((candidate) => {
|
|
3428
|
+
try {
|
|
3429
|
+
return { path: candidate, hash: hashProjectPath(candidate) };
|
|
3430
|
+
} catch {
|
|
3431
|
+
return { path: candidate, hash: void 0 };
|
|
3432
|
+
}
|
|
3433
|
+
});
|
|
3434
|
+
const matchingPath = pathHashes.find((candidate) => candidate.hash === projectHash);
|
|
3435
|
+
const foreignPath = pathHashes.find((candidate) => candidate.hash && candidate.hash !== projectHash);
|
|
3436
|
+
let action = "skipped";
|
|
3437
|
+
let reason;
|
|
3438
|
+
let observedProjectHash;
|
|
3439
|
+
if (foreignPath) {
|
|
3440
|
+
action = "quarantined";
|
|
3441
|
+
reason = "project-path-mismatch";
|
|
3442
|
+
observedProjectHash = foreignPath.hash;
|
|
3443
|
+
} else if (currentHash === projectHash && importedOrLegacy && hasConflictingContentProjectHint(row.content, options.projectPath)) {
|
|
3444
|
+
action = "quarantined";
|
|
3445
|
+
reason = "content-project-mismatch";
|
|
3446
|
+
} else if (currentHash === projectHash) {
|
|
3447
|
+
result.alreadyScoped++;
|
|
3448
|
+
continue;
|
|
3449
|
+
} else if (currentHash && currentHash !== projectHash) {
|
|
3450
|
+
action = "quarantined";
|
|
3451
|
+
reason = "scope-hash-mismatch";
|
|
3452
|
+
observedProjectHash = currentHash;
|
|
3453
|
+
} else if (matchingPath) {
|
|
3454
|
+
action = "repaired";
|
|
3455
|
+
reason = matchingPath.path === sessionProjectPath && matchingPath.path !== explicitPath ? "session-project-path" : "same-project-path";
|
|
3456
|
+
} else if (candidatePaths.length > 0) {
|
|
3457
|
+
action = "quarantined";
|
|
3458
|
+
reason = "project-path-mismatch";
|
|
3459
|
+
} else if (importedOrLegacy) {
|
|
3460
|
+
action = "quarantined";
|
|
3461
|
+
reason = "missing-project-scope";
|
|
3462
|
+
}
|
|
3463
|
+
if (action === "skipped" || !reason) {
|
|
3464
|
+
result.skipped++;
|
|
3465
|
+
continue;
|
|
3466
|
+
}
|
|
3467
|
+
if (action === "repaired") {
|
|
3468
|
+
const scope = isRecord(metadata.scope) ? { ...metadata.scope } : {};
|
|
3469
|
+
const project = isRecord(scope.project) ? { ...scope.project } : {};
|
|
3470
|
+
project.hash = projectHash;
|
|
3471
|
+
scope.project = project;
|
|
3472
|
+
metadata.scope = scope;
|
|
3473
|
+
metadata.repair = {
|
|
3474
|
+
...isRecord(metadata.repair) ? metadata.repair : {},
|
|
3475
|
+
legacyProjectScope: {
|
|
3476
|
+
action,
|
|
3477
|
+
reason,
|
|
3478
|
+
repairedAt: nowIso
|
|
3479
|
+
}
|
|
3480
|
+
};
|
|
3481
|
+
addMetadataTag(metadata, `proj:${projectHash}`);
|
|
3482
|
+
result.repaired++;
|
|
3483
|
+
} else {
|
|
3484
|
+
metadata.quarantine = {
|
|
3485
|
+
...isRecord(metadata.quarantine) ? metadata.quarantine : {},
|
|
3486
|
+
status: "active",
|
|
3487
|
+
category: "project-scope",
|
|
3488
|
+
reason,
|
|
3489
|
+
detectedAt: nowIso,
|
|
3490
|
+
expectedProjectHash: projectHash,
|
|
3491
|
+
...observedProjectHash ? { observedProjectHash } : {}
|
|
3492
|
+
};
|
|
3493
|
+
metadata.repair = {
|
|
3494
|
+
...isRecord(metadata.repair) ? metadata.repair : {},
|
|
3495
|
+
legacyProjectScope: {
|
|
3496
|
+
action,
|
|
3497
|
+
reason,
|
|
3498
|
+
repairedAt: nowIso
|
|
3499
|
+
}
|
|
3500
|
+
};
|
|
3501
|
+
addMetadataTag(metadata, "quarantine:project-scope");
|
|
3502
|
+
result.quarantined++;
|
|
3503
|
+
}
|
|
3504
|
+
sample({ eventId: row.id, action, reason });
|
|
3505
|
+
if (!dryRun) {
|
|
3506
|
+
sqliteRun(this.db, `UPDATE events SET metadata = ? WHERE id = ?`, [JSON.stringify(metadata), row.id]);
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
return result;
|
|
3510
|
+
}
|
|
3214
3511
|
/**
|
|
3215
3512
|
* Get embedding/vector outbox health statistics
|
|
3216
3513
|
*/
|
|
@@ -3258,7 +3555,11 @@ var SQLiteEventStore = class {
|
|
|
3258
3555
|
await this.initialize();
|
|
3259
3556
|
const rows = sqliteAll(
|
|
3260
3557
|
this.db,
|
|
3261
|
-
`SELECT level, COUNT(*) as count
|
|
3558
|
+
`SELECT ml.level, COUNT(*) as count
|
|
3559
|
+
FROM memory_levels ml
|
|
3560
|
+
INNER JOIN events e ON e.id = ml.event_id
|
|
3561
|
+
WHERE ${notActiveQuarantinedSql("e.metadata")}
|
|
3562
|
+
GROUP BY ml.level`
|
|
3262
3563
|
);
|
|
3263
3564
|
return rows;
|
|
3264
3565
|
}
|
|
@@ -3274,6 +3575,7 @@ var SQLiteEventStore = class {
|
|
|
3274
3575
|
`SELECT e.* FROM events e
|
|
3275
3576
|
INNER JOIN memory_levels ml ON e.id = ml.event_id
|
|
3276
3577
|
WHERE ml.level = ?
|
|
3578
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
3277
3579
|
ORDER BY e.timestamp DESC
|
|
3278
3580
|
LIMIT ? OFFSET ?`,
|
|
3279
3581
|
[level, limit, offset]
|
|
@@ -3366,12 +3668,13 @@ var SQLiteEventStore = class {
|
|
|
3366
3668
|
/**
|
|
3367
3669
|
* Get most accessed memories (falls back to recent events if none accessed)
|
|
3368
3670
|
*/
|
|
3369
|
-
async getMostAccessed(limit = 10) {
|
|
3671
|
+
async getMostAccessed(limit = 10, options) {
|
|
3370
3672
|
await this.initialize();
|
|
3371
3673
|
let rows = sqliteAll(
|
|
3372
3674
|
this.db,
|
|
3373
3675
|
`SELECT * FROM events
|
|
3374
3676
|
WHERE access_count > 0
|
|
3677
|
+
AND ${maybeQuarantinePredicate(options)}
|
|
3375
3678
|
ORDER BY access_count DESC, last_accessed_at DESC
|
|
3376
3679
|
LIMIT ?`,
|
|
3377
3680
|
[limit]
|
|
@@ -3380,6 +3683,7 @@ var SQLiteEventStore = class {
|
|
|
3380
3683
|
rows = sqliteAll(
|
|
3381
3684
|
this.db,
|
|
3382
3685
|
`SELECT * FROM events
|
|
3686
|
+
WHERE ${maybeQuarantinePredicate(options)}
|
|
3383
3687
|
ORDER BY timestamp DESC
|
|
3384
3688
|
LIMIT ?`,
|
|
3385
3689
|
[limit]
|
|
@@ -3510,6 +3814,7 @@ var SQLiteEventStore = class {
|
|
|
3510
3814
|
FROM memory_helpfulness mh
|
|
3511
3815
|
JOIN events e ON e.id = mh.event_id
|
|
3512
3816
|
WHERE mh.measured_at IS NOT NULL
|
|
3817
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
3513
3818
|
GROUP BY mh.event_id
|
|
3514
3819
|
ORDER BY avg_score DESC
|
|
3515
3820
|
LIMIT ?`,
|
|
@@ -3574,6 +3879,7 @@ var SQLiteEventStore = class {
|
|
|
3574
3879
|
FROM events_fts fts
|
|
3575
3880
|
JOIN events e ON e.id = fts.event_id
|
|
3576
3881
|
WHERE events_fts MATCH ?
|
|
3882
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
3577
3883
|
ORDER BY fts.rank
|
|
3578
3884
|
LIMIT ?`,
|
|
3579
3885
|
[searchTerms, limit]
|
|
@@ -3588,6 +3894,7 @@ var SQLiteEventStore = class {
|
|
|
3588
3894
|
this.db,
|
|
3589
3895
|
`SELECT *, 0 as rank FROM events
|
|
3590
3896
|
WHERE content LIKE ?
|
|
3897
|
+
AND ${notActiveQuarantinedSql()}
|
|
3591
3898
|
ORDER BY timestamp DESC
|
|
3592
3899
|
LIMIT ?`,
|
|
3593
3900
|
[likePattern, limit]
|
|
@@ -3794,6 +4101,7 @@ var SQLiteEventStore = class {
|
|
|
3794
4101
|
`SELECT turn_id, MIN(timestamp) as min_ts
|
|
3795
4102
|
FROM events
|
|
3796
4103
|
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
4104
|
+
AND ${maybeQuarantinePredicate(options)}
|
|
3797
4105
|
GROUP BY turn_id
|
|
3798
4106
|
ORDER BY min_ts DESC
|
|
3799
4107
|
LIMIT ? OFFSET ?`,
|
|
@@ -3801,7 +4109,7 @@ var SQLiteEventStore = class {
|
|
|
3801
4109
|
);
|
|
3802
4110
|
const turns = [];
|
|
3803
4111
|
for (const turnRow of turnRows) {
|
|
3804
|
-
const events = await this.getEventsByTurn(turnRow.turn_id);
|
|
4112
|
+
const events = await this.getEventsByTurn(turnRow.turn_id, options);
|
|
3805
4113
|
const promptEvent = events.find((e) => e.eventType === "user_prompt");
|
|
3806
4114
|
const toolEvents = events.filter((e) => e.eventType === "tool_observation");
|
|
3807
4115
|
const hasResponse = events.some((e) => e.eventType === "agent_response");
|
|
@@ -3820,11 +4128,11 @@ var SQLiteEventStore = class {
|
|
|
3820
4128
|
/**
|
|
3821
4129
|
* Get all events for a specific turn_id
|
|
3822
4130
|
*/
|
|
3823
|
-
async getEventsByTurn(turnId) {
|
|
4131
|
+
async getEventsByTurn(turnId, options) {
|
|
3824
4132
|
await this.initialize();
|
|
3825
4133
|
const rows = sqliteAll(
|
|
3826
4134
|
this.db,
|
|
3827
|
-
`SELECT * FROM events WHERE turn_id = ? ORDER BY timestamp ASC`,
|
|
4135
|
+
`SELECT * FROM events WHERE turn_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
|
|
3828
4136
|
[turnId]
|
|
3829
4137
|
);
|
|
3830
4138
|
return rows.map(this.rowToEvent);
|
|
@@ -3832,13 +4140,14 @@ var SQLiteEventStore = class {
|
|
|
3832
4140
|
/**
|
|
3833
4141
|
* Count total turns for a session
|
|
3834
4142
|
*/
|
|
3835
|
-
async countSessionTurns(sessionId) {
|
|
4143
|
+
async countSessionTurns(sessionId, options) {
|
|
3836
4144
|
await this.initialize();
|
|
3837
4145
|
const row = sqliteGet(
|
|
3838
4146
|
this.db,
|
|
3839
4147
|
`SELECT COUNT(DISTINCT turn_id) as count
|
|
3840
4148
|
FROM events
|
|
3841
|
-
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
4149
|
+
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
4150
|
+
AND ${maybeQuarantinePredicate(options)}`,
|
|
3842
4151
|
[sessionId]
|
|
3843
4152
|
);
|
|
3844
4153
|
return row?.count || 0;
|
|
@@ -3930,7 +4239,7 @@ var SQLiteEventStore = class {
|
|
|
3930
4239
|
content: row.content,
|
|
3931
4240
|
canonicalKey: row.canonical_key,
|
|
3932
4241
|
dedupeKey: row.dedupe_key,
|
|
3933
|
-
metadata:
|
|
4242
|
+
metadata: safeParseMetadataValue(row.metadata)
|
|
3934
4243
|
};
|
|
3935
4244
|
if (row.access_count !== void 0) {
|
|
3936
4245
|
event.access_count = row.access_count;
|
|
@@ -4525,6 +4834,10 @@ var MemoryQueryService = class {
|
|
|
4525
4834
|
await this.initialize();
|
|
4526
4835
|
return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
|
|
4527
4836
|
}
|
|
4837
|
+
async repairLegacyProjectScope(options) {
|
|
4838
|
+
await this.initialize();
|
|
4839
|
+
return this.getMaintenanceStore("repairLegacyProjectScope").repairLegacyProjectScope(options);
|
|
4840
|
+
}
|
|
4528
4841
|
async getStats() {
|
|
4529
4842
|
await this.initialize();
|
|
4530
4843
|
const deps = this.getStatsDeps();
|
|
@@ -6147,18 +6460,18 @@ function assertDefaultRetrieverStore(eventStore) {
|
|
|
6147
6460
|
function createMemoryEngineServices(options) {
|
|
6148
6461
|
const factories = options.factories ?? {};
|
|
6149
6462
|
const storagePath = options.storagePath;
|
|
6150
|
-
if (!options.readOnly && !
|
|
6151
|
-
|
|
6463
|
+
if (!options.readOnly && !fs6.existsSync(storagePath)) {
|
|
6464
|
+
fs6.mkdirSync(storagePath, { recursive: true });
|
|
6152
6465
|
}
|
|
6153
6466
|
const sqliteStore = (factories.createSQLiteEventStore ?? defaultCreateSQLiteEventStore)(
|
|
6154
|
-
|
|
6467
|
+
path5.join(storagePath, "events.sqlite"),
|
|
6155
6468
|
{
|
|
6156
6469
|
readonly: options.readOnly,
|
|
6157
6470
|
markdownMirrorRoot: storagePath
|
|
6158
6471
|
}
|
|
6159
6472
|
);
|
|
6160
6473
|
const vectorStore = (factories.createVectorStore ?? defaultCreateVectorStore)(
|
|
6161
|
-
|
|
6474
|
+
path5.join(storagePath, "vectors")
|
|
6162
6475
|
);
|
|
6163
6476
|
const embeddingModel = options.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
6164
6477
|
const embedder = embeddingModel ? (factories.createEmbedder ?? defaultCreateEmbedder)(embeddingModel) : (factories.getDefaultEmbedder ?? getDefaultEmbedder)();
|
|
@@ -6569,8 +6882,8 @@ function createMemoryRuntimeService(deps) {
|
|
|
6569
6882
|
}
|
|
6570
6883
|
|
|
6571
6884
|
// src/extensions/shared-memory/shared-memory-services.ts
|
|
6572
|
-
import * as
|
|
6573
|
-
import * as
|
|
6885
|
+
import * as fs7 from "fs";
|
|
6886
|
+
import * as path6 from "path";
|
|
6574
6887
|
|
|
6575
6888
|
// src/core/shared-event-store.ts
|
|
6576
6889
|
var SharedEventStore = class {
|
|
@@ -7258,7 +7571,7 @@ var SharedMemoryServices = class {
|
|
|
7258
7571
|
this.ensureDirectory(sharedPath, { allowCreate: true });
|
|
7259
7572
|
const store = await this.openStore(sharedPath);
|
|
7260
7573
|
this.sharedVectorStore = this.factories.createSharedVectorStore(
|
|
7261
|
-
|
|
7574
|
+
path6.join(sharedPath, "vectors")
|
|
7262
7575
|
);
|
|
7263
7576
|
await this.sharedVectorStore.initialize();
|
|
7264
7577
|
this.sharedPromoter = this.factories.createSharedPromoter(
|
|
@@ -7331,7 +7644,7 @@ var SharedMemoryServices = class {
|
|
|
7331
7644
|
async createOpenStorePromise(sharedPath) {
|
|
7332
7645
|
if (!this.sharedEventStore) {
|
|
7333
7646
|
const sharedEventStore = this.factories.createSharedEventStore(
|
|
7334
|
-
|
|
7647
|
+
path6.join(sharedPath, "shared.duckdb")
|
|
7335
7648
|
);
|
|
7336
7649
|
await sharedEventStore.initialize();
|
|
7337
7650
|
this.sharedEventStore = sharedEventStore;
|
|
@@ -7351,9 +7664,9 @@ var SharedMemoryServices = class {
|
|
|
7351
7664
|
}
|
|
7352
7665
|
get factories() {
|
|
7353
7666
|
return {
|
|
7354
|
-
existsSync: this.options.factories?.existsSync ??
|
|
7667
|
+
existsSync: this.options.factories?.existsSync ?? fs7.existsSync,
|
|
7355
7668
|
mkdirSync: this.options.factories?.mkdirSync ?? ((targetPath) => {
|
|
7356
|
-
|
|
7669
|
+
fs7.mkdirSync(targetPath, { recursive: true });
|
|
7357
7670
|
}),
|
|
7358
7671
|
createSharedEventStore: this.options.factories?.createSharedEventStore ?? createSharedEventStore,
|
|
7359
7672
|
createSharedStore: this.options.factories?.createSharedStore ?? createSharedStore,
|
|
@@ -7468,37 +7781,11 @@ function createMemoryServiceComposition(options) {
|
|
|
7468
7781
|
}
|
|
7469
7782
|
function defaultExpandPath(targetPath) {
|
|
7470
7783
|
if (targetPath.startsWith("~")) {
|
|
7471
|
-
return
|
|
7784
|
+
return path7.join(os2.homedir(), targetPath.slice(1));
|
|
7472
7785
|
}
|
|
7473
7786
|
return targetPath;
|
|
7474
7787
|
}
|
|
7475
7788
|
|
|
7476
|
-
// src/core/registry/project-path.ts
|
|
7477
|
-
import * as crypto2 from "crypto";
|
|
7478
|
-
import * as fs7 from "fs";
|
|
7479
|
-
import * as os2 from "os";
|
|
7480
|
-
import * as path7 from "path";
|
|
7481
|
-
function normalizeProjectPath(projectPath) {
|
|
7482
|
-
const expanded = projectPath.startsWith("~") ? path7.join(os2.homedir(), projectPath.slice(1)) : projectPath;
|
|
7483
|
-
try {
|
|
7484
|
-
return fs7.realpathSync(expanded);
|
|
7485
|
-
} catch {
|
|
7486
|
-
return path7.resolve(expanded);
|
|
7487
|
-
}
|
|
7488
|
-
}
|
|
7489
|
-
function hashProjectPath(projectPath) {
|
|
7490
|
-
const normalizedPath = normalizeProjectPath(projectPath);
|
|
7491
|
-
return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
|
|
7492
|
-
}
|
|
7493
|
-
function getProjectStoragePath(projectPath) {
|
|
7494
|
-
const hash = hashProjectPath(projectPath);
|
|
7495
|
-
return path7.join(os2.homedir(), ".claude-code", "memory", "projects", hash);
|
|
7496
|
-
}
|
|
7497
|
-
function resolveProjectStoragePath(projectOrHash) {
|
|
7498
|
-
const isHash = /^[a-f0-9]{8}$/.test(projectOrHash);
|
|
7499
|
-
return isHash ? path7.join(os2.homedir(), ".claude-code", "memory", "projects", projectOrHash) : getProjectStoragePath(projectOrHash);
|
|
7500
|
-
}
|
|
7501
|
-
|
|
7502
7789
|
// src/core/registry/session-registry.ts
|
|
7503
7790
|
import * as fs8 from "fs";
|
|
7504
7791
|
import * as os3 from "os";
|
|
@@ -7825,6 +8112,9 @@ var MemoryService = class {
|
|
|
7825
8112
|
async recoverStuckOutboxItems(options) {
|
|
7826
8113
|
return this.queryService.recoverStuckOutboxItems(options);
|
|
7827
8114
|
}
|
|
8115
|
+
async repairLegacyProjectScope(options) {
|
|
8116
|
+
return this.queryService.repairLegacyProjectScope(options);
|
|
8117
|
+
}
|
|
7828
8118
|
async getRetrievalTraceStats() {
|
|
7829
8119
|
return this.retrievalAnalyticsService.getRetrievalTraceStats();
|
|
7830
8120
|
}
|
|
@@ -8554,7 +8844,7 @@ import * as os7 from "os";
|
|
|
8554
8844
|
import * as readline2 from "readline";
|
|
8555
8845
|
import { createHash as createHash3, randomUUID as randomUUID9 } from "crypto";
|
|
8556
8846
|
var CODEX_VALIDATION_DEFAULT_MAX_CONTENT_CHARS = 1e4;
|
|
8557
|
-
function
|
|
8847
|
+
function isRecord2(value) {
|
|
8558
8848
|
return typeof value === "object" && value !== null;
|
|
8559
8849
|
}
|
|
8560
8850
|
function normalizeMaybeRealpath(p) {
|
|
@@ -8571,7 +8861,7 @@ function extractCodexContentText(content, maxContentChars = CODEX_VALIDATION_DEF
|
|
|
8571
8861
|
texts.push(content);
|
|
8572
8862
|
} else if (Array.isArray(content)) {
|
|
8573
8863
|
for (const block of content) {
|
|
8574
|
-
if (!
|
|
8864
|
+
if (!isRecord2(block))
|
|
8575
8865
|
continue;
|
|
8576
8866
|
const b = block;
|
|
8577
8867
|
const t = typeof b.type === "string" ? b.type : "";
|
|
@@ -8659,7 +8949,7 @@ async function readCodexSessionMeta(filePath) {
|
|
|
8659
8949
|
const obj = JSON.parse(line);
|
|
8660
8950
|
if (obj.type !== "session_meta")
|
|
8661
8951
|
continue;
|
|
8662
|
-
if (!
|
|
8952
|
+
if (!isRecord2(obj.payload))
|
|
8663
8953
|
break;
|
|
8664
8954
|
const payload = obj.payload;
|
|
8665
8955
|
const sessionId = typeof payload.id === "string" ? payload.id : null;
|
|
@@ -8797,7 +9087,7 @@ async function normalizeCodexSessionFile(filePath, options = {}) {
|
|
|
8797
9087
|
}
|
|
8798
9088
|
if (entry.type === "session_meta")
|
|
8799
9089
|
continue;
|
|
8800
|
-
if (entry.type !== "response_item" || !
|
|
9090
|
+
if (entry.type !== "response_item" || !isRecord2(entry.payload)) {
|
|
8801
9091
|
summary.skippedUnsupportedRecords += 1;
|
|
8802
9092
|
continue;
|
|
8803
9093
|
}
|
|
@@ -8952,7 +9242,7 @@ var CodexSessionHistoryImporter = class {
|
|
|
8952
9242
|
const obj = JSON.parse(line);
|
|
8953
9243
|
if (obj.type !== "session_meta")
|
|
8954
9244
|
continue;
|
|
8955
|
-
if (!
|
|
9245
|
+
if (!isRecord2(obj.payload))
|
|
8956
9246
|
break;
|
|
8957
9247
|
const payload = obj.payload;
|
|
8958
9248
|
const sessionId = typeof payload.id === "string" ? payload.id : null;
|
|
@@ -9168,7 +9458,7 @@ var CodexSessionHistoryImporter = class {
|
|
|
9168
9458
|
try {
|
|
9169
9459
|
const entry = JSON.parse(line);
|
|
9170
9460
|
result.totalMessages++;
|
|
9171
|
-
if (entry.type === "response_item" &&
|
|
9461
|
+
if (entry.type === "response_item" && isRecord2(entry.payload)) {
|
|
9172
9462
|
const payload = entry.payload;
|
|
9173
9463
|
if (payload.type !== "message")
|
|
9174
9464
|
continue;
|
|
@@ -9360,10 +9650,10 @@ var SENSITIVE_PATTERNS = [
|
|
|
9360
9650
|
// Redact the whole URI so usernames, credentials, hosts, paths, and query
|
|
9361
9651
|
// params do not leak either.
|
|
9362
9652
|
/\b[a-z][a-z0-9+.-]*:\/\/[^\s'"`<>/@]+@[^\s'"`<>]+/gi,
|
|
9363
|
-
/password\s*[:=]\s*['"]?[^\s'"]+/gi,
|
|
9364
|
-
/api[_
|
|
9365
|
-
/secret\s*[:=]\s*['"]?[^\s'"]+/gi,
|
|
9366
|
-
/token\s*[:=]\s*['"]?[^\s'"]+/gi,
|
|
9653
|
+
/(?:[\w.-]+[-_])?password\s*[:=]\s*(?!\[REDACTED\])['"]?[^\s'"]+/gi,
|
|
9654
|
+
/(?:[\w.-]+[-_])?api[-_]?key\s*[:=]\s*(?!\[REDACTED\])['"]?[^\s'"]+/gi,
|
|
9655
|
+
/(?:[\w.-]+[-_])?secret\s*[:=]\s*(?!\[REDACTED\])['"]?[^\s'"]+/gi,
|
|
9656
|
+
/(?:[\w.-]+[-_])?token\s*[:=]\s*(?!\[REDACTED\])['"]?[^\s'"]+/gi,
|
|
9367
9657
|
/bearer\s+[a-zA-Z0-9\-_.]+/gi,
|
|
9368
9658
|
/AWS[_-]?ACCESS[_-]?KEY[_-]?ID\s*[:=]\s*['"]?[A-Z0-9]+/gi,
|
|
9369
9659
|
/AWS[_-]?SECRET[_-]?ACCESS[_-]?KEY\s*[:=]\s*['"]?[^\s'"]+/gi,
|
|
@@ -9373,6 +9663,35 @@ var SENSITIVE_PATTERNS = [
|
|
|
9373
9663
|
/sk-[a-zA-Z0-9]{48}/g
|
|
9374
9664
|
// OpenAI API Key
|
|
9375
9665
|
];
|
|
9666
|
+
var CLI_SECRET_OPTION_PATTERNS = [
|
|
9667
|
+
/(--(?:[a-z0-9]+[-_])*(?:password|secret|api[-_]?key|token|bearer)(?:[-_][a-z0-9]+)*(?:\s+|=))(?:"[^"]*"|'[^']*'|[^\s'"`<>]+)/gi
|
|
9668
|
+
];
|
|
9669
|
+
var URL_FOLLOWING_SECRET_PATTERN = /((?:https?:\/\/[^\s'"`<>]+)\s*\r?\n\s*)([A-Za-z0-9!@#$%^&*._+\-~:=/]{6,})(?=\s*(?:\r?\n|$))/gi;
|
|
9670
|
+
function looksLikePastedSecret(value) {
|
|
9671
|
+
return value.length >= 8 && /(?:\d|[^A-Za-z0-9])/.test(value);
|
|
9672
|
+
}
|
|
9673
|
+
function maskUrlFollowingSecret(value) {
|
|
9674
|
+
let count = 0;
|
|
9675
|
+
const content = value.replace(URL_FOLLOWING_SECRET_PATTERN, (_match, prefix, secret) => {
|
|
9676
|
+
if (!looksLikePastedSecret(secret))
|
|
9677
|
+
return `${prefix}${secret}`;
|
|
9678
|
+
count++;
|
|
9679
|
+
return `${prefix}[REDACTED]`;
|
|
9680
|
+
});
|
|
9681
|
+
return { content, count };
|
|
9682
|
+
}
|
|
9683
|
+
function maskCliSecretOptions(value) {
|
|
9684
|
+
let count = 0;
|
|
9685
|
+
let filtered = value;
|
|
9686
|
+
for (const pattern of CLI_SECRET_OPTION_PATTERNS) {
|
|
9687
|
+
pattern.lastIndex = 0;
|
|
9688
|
+
filtered = filtered.replace(pattern, (_match, prefix) => {
|
|
9689
|
+
count++;
|
|
9690
|
+
return `${prefix}[REDACTED]`;
|
|
9691
|
+
});
|
|
9692
|
+
}
|
|
9693
|
+
return { content: filtered, count };
|
|
9694
|
+
}
|
|
9376
9695
|
function applyPrivacyFilter(content, config) {
|
|
9377
9696
|
let filtered = content;
|
|
9378
9697
|
let privateTagCount = 0;
|
|
@@ -9386,6 +9705,12 @@ function applyPrivacyFilter(content, config) {
|
|
|
9386
9705
|
filtered = tagResult.filtered;
|
|
9387
9706
|
privateTagCount = tagResult.stats.count;
|
|
9388
9707
|
}
|
|
9708
|
+
const cliResult = maskCliSecretOptions(filtered);
|
|
9709
|
+
filtered = cliResult.content;
|
|
9710
|
+
patternMatchCount += cliResult.count;
|
|
9711
|
+
const urlSecretResult = maskUrlFollowingSecret(filtered);
|
|
9712
|
+
filtered = urlSecretResult.content;
|
|
9713
|
+
patternMatchCount += urlSecretResult.count;
|
|
9389
9714
|
for (const pattern of SENSITIVE_PATTERNS) {
|
|
9390
9715
|
pattern.lastIndex = 0;
|
|
9391
9716
|
const matches = filtered.match(pattern);
|
|
@@ -9397,13 +9722,13 @@ function applyPrivacyFilter(content, config) {
|
|
|
9397
9722
|
for (const patternStr of config.excludePatterns || []) {
|
|
9398
9723
|
try {
|
|
9399
9724
|
const regex = new RegExp(
|
|
9400
|
-
`(${patternStr})\\s*[:=]\\s*['"]?[^\\s'"]+`,
|
|
9725
|
+
`(^|[^\\w-])(${patternStr})\\s*[:=]\\s*['"]?[^\\s'"]+`,
|
|
9401
9726
|
"gi"
|
|
9402
9727
|
);
|
|
9403
9728
|
const matches = filtered.match(regex);
|
|
9404
9729
|
if (matches) {
|
|
9405
9730
|
patternMatchCount += matches.length;
|
|
9406
|
-
filtered = filtered.replace(regex, "[REDACTED]");
|
|
9731
|
+
filtered = filtered.replace(regex, "$1[REDACTED]");
|
|
9407
9732
|
}
|
|
9408
9733
|
} catch {
|
|
9409
9734
|
}
|
|
@@ -13632,6 +13957,54 @@ function resolveDashboardCommandOptions(options) {
|
|
|
13632
13957
|
};
|
|
13633
13958
|
}
|
|
13634
13959
|
|
|
13960
|
+
// src/apps/cli/repair-command.ts
|
|
13961
|
+
function resolveLegacyProjectScopeRepairOptions(options) {
|
|
13962
|
+
if (options.project !== void 0 && options.project.trim().length === 0) {
|
|
13963
|
+
throw new Error("legacy project-scope repair --project must not be empty");
|
|
13964
|
+
}
|
|
13965
|
+
if (options.projectHash !== void 0 && options.projectHash.trim().length === 0) {
|
|
13966
|
+
throw new Error("legacy project-scope repair --project-hash must not be empty");
|
|
13967
|
+
}
|
|
13968
|
+
const projectPath = typeof options.project === "string" && options.project.length > 0 ? options.project : void 0;
|
|
13969
|
+
const projectHash = typeof options.projectHash === "string" && options.projectHash.length > 0 ? options.projectHash : void 0;
|
|
13970
|
+
if (!projectPath && !projectHash) {
|
|
13971
|
+
throw new Error("legacy project-scope repair requires --project or --project-hash");
|
|
13972
|
+
}
|
|
13973
|
+
if (projectHash && !/^[a-f0-9]{8}$/.test(projectHash)) {
|
|
13974
|
+
throw new Error("legacy project-scope repair --project-hash must be an 8-character lowercase hex hash");
|
|
13975
|
+
}
|
|
13976
|
+
if (projectPath && projectHash && hashProjectPath(projectPath) !== projectHash) {
|
|
13977
|
+
throw new Error("legacy project-scope repair --project and --project-hash refer to different project stores");
|
|
13978
|
+
}
|
|
13979
|
+
return {
|
|
13980
|
+
projectPath,
|
|
13981
|
+
projectHash,
|
|
13982
|
+
dryRun: options.apply !== true
|
|
13983
|
+
};
|
|
13984
|
+
}
|
|
13985
|
+
function formatLegacyProjectScopeRepairResult(result) {
|
|
13986
|
+
const lines = [
|
|
13987
|
+
"Legacy project-scope repair",
|
|
13988
|
+
`Mode: ${result.dryRun ? "dry-run" : "apply"}`,
|
|
13989
|
+
`Project: ${result.projectHash}`,
|
|
13990
|
+
`Scanned: ${result.scanned}`,
|
|
13991
|
+
`Already scoped: ${result.alreadyScoped}`,
|
|
13992
|
+
`Repaired: ${result.repaired}`,
|
|
13993
|
+
`Quarantined: ${result.quarantined}`,
|
|
13994
|
+
`Skipped: ${result.skipped}`
|
|
13995
|
+
];
|
|
13996
|
+
if (result.samples.length > 0) {
|
|
13997
|
+
lines.push("Samples:");
|
|
13998
|
+
for (const sample of result.samples) {
|
|
13999
|
+
lines.push(`- ${sample.eventId} ${sample.action} ${sample.reason}`);
|
|
14000
|
+
}
|
|
14001
|
+
}
|
|
14002
|
+
if (result.dryRun) {
|
|
14003
|
+
lines.push("Dry-run only. Re-run with --apply to mutate event metadata.");
|
|
14004
|
+
}
|
|
14005
|
+
return lines.join("\n");
|
|
14006
|
+
}
|
|
14007
|
+
|
|
13635
14008
|
// src/core/external-market-context.ts
|
|
13636
14009
|
var MAX_RENDERED_ITEMS = 8;
|
|
13637
14010
|
var MAX_FRED_SERIES = 10;
|
|
@@ -14242,7 +14615,7 @@ async function runMarketContextCommand(options) {
|
|
|
14242
14615
|
}
|
|
14243
14616
|
}
|
|
14244
14617
|
var program = new Command();
|
|
14245
|
-
program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.
|
|
14618
|
+
program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.38");
|
|
14246
14619
|
program.command("market-context").description("Fetch read-only DART/FRED/Finnhub context with structured MarketContextSnapshot bull/bear/risk/catalyst analysis").option("--company <name>", "Company name for DART fallback search and report subject").option("--dart-corp-code <code>", "Exact DART corp_code for issuer-specific filings").option("--symbol <ticker>", "Listed ticker for Finnhub company profile").option("--providers <list>", "Comma-separated providers: dart,fred,finnhub").option("--fred-series <list>", "Comma-separated FRED series IDs").option("--json", "Print structured JSON including analysis.marketSnapshot").option("--no-snapshot", "Disable MarketContextSnapshot and DART company snapshot analysis").action(async (options) => {
|
|
14247
14620
|
try {
|
|
14248
14621
|
await runMarketContextCommand(options);
|
|
@@ -14498,6 +14871,46 @@ program.command("process").description("Process pending embeddings").option("-p,
|
|
|
14498
14871
|
process.exit(1);
|
|
14499
14872
|
}
|
|
14500
14873
|
});
|
|
14874
|
+
var repairCommand = program.command("repair").description("Repair or quarantine legacy memory metadata");
|
|
14875
|
+
repairCommand.command("legacy-project-scope").description("Dry-run or apply project-scope repair/quarantine for legacy imported events").option("-p, --project <path>", "Project path (defaults to cwd unless --project-hash is used)").option("--project-hash <hash>", "Project storage hash for hash-only repair flows").option("--apply", "Apply metadata changes (default is dry-run)").action(async (options) => {
|
|
14876
|
+
try {
|
|
14877
|
+
const projectPath = options.project !== void 0 ? options.project : !options.projectHash ? process.cwd() : void 0;
|
|
14878
|
+
const repairOptions = resolveLegacyProjectScopeRepairOptions({
|
|
14879
|
+
project: projectPath,
|
|
14880
|
+
projectHash: options.projectHash,
|
|
14881
|
+
apply: options.apply
|
|
14882
|
+
});
|
|
14883
|
+
const storagePath = projectPath ? getProjectStoragePath(projectPath) : resolveProjectStoragePath(repairOptions.projectHash);
|
|
14884
|
+
const dbPath = path21.join(storagePath, "events.sqlite");
|
|
14885
|
+
if (repairOptions.dryRun && !fs18.existsSync(dbPath)) {
|
|
14886
|
+
const projectHash = repairOptions.projectHash || hashProjectPath(repairOptions.projectPath);
|
|
14887
|
+
console.log(formatLegacyProjectScopeRepairResult({
|
|
14888
|
+
dryRun: true,
|
|
14889
|
+
projectHash,
|
|
14890
|
+
scanned: 0,
|
|
14891
|
+
repaired: 0,
|
|
14892
|
+
quarantined: 0,
|
|
14893
|
+
alreadyScoped: 0,
|
|
14894
|
+
skipped: 0,
|
|
14895
|
+
samples: []
|
|
14896
|
+
}));
|
|
14897
|
+
return;
|
|
14898
|
+
}
|
|
14899
|
+
const store = new SQLiteEventStore(dbPath, {
|
|
14900
|
+
readonly: repairOptions.dryRun
|
|
14901
|
+
});
|
|
14902
|
+
try {
|
|
14903
|
+
const result = await store.repairLegacyProjectScope(repairOptions);
|
|
14904
|
+
console.log(formatLegacyProjectScopeRepairResult(result));
|
|
14905
|
+
} finally {
|
|
14906
|
+
await store.close().catch(() => void 0);
|
|
14907
|
+
}
|
|
14908
|
+
} catch (error) {
|
|
14909
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14910
|
+
console.error(`Repair failed: ${message}`);
|
|
14911
|
+
process.exit(1);
|
|
14912
|
+
}
|
|
14913
|
+
});
|
|
14501
14914
|
program.command("mongo-sync").description("Sync events with MongoDB for multi-server collaboration (optional)").option("-p, --project <path>", "Project path (defaults to cwd)").option("--mongo-uri <uri>", "MongoDB connection URI (env: CLAUDE_MEMORY_MONGO_URI)").option("--mongo-db <name>", "MongoDB database name (env: CLAUDE_MEMORY_MONGO_DB)").option("--mongo-project <key>", "Remote project key (env: CLAUDE_MEMORY_MONGO_PROJECT, default: basename(projectPath))").option("--direction <dir>", "push|pull|both", "both").option("--batch-size <n>", "Batch size", "500").option("--interval <ms>", "Watch interval ms", "30000").option("--watch", "Run continuously").action(async (options) => {
|
|
14502
14915
|
const projectPath = options.project || process.cwd();
|
|
14503
14916
|
const mongoUri = options.mongoUri || process.env.CLAUDE_MEMORY_MONGO_URI;
|