claude-memory-layer 1.0.36 → 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 +776 -126
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +377 -23
- package/dist/core/index.js.map +3 -3
- package/dist/hooks/post-tool-use.js +470 -75
- package/dist/hooks/post-tool-use.js.map +4 -4
- package/dist/hooks/semantic-daemon.js +428 -68
- package/dist/hooks/semantic-daemon.js.map +4 -4
- package/dist/hooks/session-end.js +428 -68
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +428 -68
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +469 -74
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +428 -68
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/index.js +389 -29
- package/dist/index.js.map +3 -3
- package/dist/mcp/index.js +474 -79
- package/dist/mcp/index.js.map +4 -4
- package/dist/server/api/index.js +632 -112
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +632 -112
- package/dist/server/index.js.map +4 -4
- package/dist/services/memory-service.js +428 -68
- package/dist/services/memory-service.js.map +4 -4
- package/dist/ui/assets/js/chat.js +21 -3
- package/package.json +1 -1
|
@@ -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
|
|
20
|
-
import * as
|
|
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
|
|
1532
|
-
import * as
|
|
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
|
|
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 (!
|
|
2217
|
-
|
|
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
|
|
2267
|
-
import * as
|
|
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
|
|
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
|
|
2318
|
-
await
|
|
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
|
};
|
|
@@ -2328,6 +2353,143 @@ function normalizeQueryRewriteKind(value) {
|
|
|
2328
2353
|
return "none";
|
|
2329
2354
|
}
|
|
2330
2355
|
var REWRITTEN_QUERY_REWRITE_KIND_SQL = `LOWER(TRIM(COALESCE(query_rewrite_kind, 'none'))) IN ('follow-up-context', 'intent-rewrite')`;
|
|
2356
|
+
var DEFAULT_OUTBOX_STUCK_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
2357
|
+
var DEFAULT_OUTBOX_MAX_RETRIES = 3;
|
|
2358
|
+
function emptyOutboxRecoveryResult() {
|
|
2359
|
+
return {
|
|
2360
|
+
embedding: { recoveredProcessing: 0, retriedFailed: 0 },
|
|
2361
|
+
vector: { recoveredProcessing: 0, retriedFailed: 0 }
|
|
2362
|
+
};
|
|
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
|
+
}
|
|
2331
2493
|
var SQLiteEventStore = class {
|
|
2332
2494
|
db;
|
|
2333
2495
|
initialized = false;
|
|
@@ -2826,11 +2988,11 @@ var SQLiteEventStore = class {
|
|
|
2826
2988
|
/**
|
|
2827
2989
|
* Get events by session ID
|
|
2828
2990
|
*/
|
|
2829
|
-
async getSessionEvents(sessionId) {
|
|
2991
|
+
async getSessionEvents(sessionId, options) {
|
|
2830
2992
|
await this.initialize();
|
|
2831
2993
|
const rows = sqliteAll(
|
|
2832
2994
|
this.db,
|
|
2833
|
-
`SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC`,
|
|
2995
|
+
`SELECT * FROM events WHERE session_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
|
|
2834
2996
|
[sessionId]
|
|
2835
2997
|
);
|
|
2836
2998
|
return rows.map(this.rowToEvent);
|
|
@@ -2838,11 +3000,11 @@ var SQLiteEventStore = class {
|
|
|
2838
3000
|
/**
|
|
2839
3001
|
* Get recent events
|
|
2840
3002
|
*/
|
|
2841
|
-
async getRecentEvents(limit = 100) {
|
|
3003
|
+
async getRecentEvents(limit = 100, options) {
|
|
2842
3004
|
await this.initialize();
|
|
2843
3005
|
const rows = sqliteAll(
|
|
2844
3006
|
this.db,
|
|
2845
|
-
`SELECT * FROM events ORDER BY timestamp DESC LIMIT ?`,
|
|
3007
|
+
`SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp DESC LIMIT ?`,
|
|
2846
3008
|
[limit]
|
|
2847
3009
|
);
|
|
2848
3010
|
return rows.map(this.rowToEvent);
|
|
@@ -2850,11 +3012,11 @@ var SQLiteEventStore = class {
|
|
|
2850
3012
|
/**
|
|
2851
3013
|
* Get event by ID
|
|
2852
3014
|
*/
|
|
2853
|
-
async getEvent(id) {
|
|
3015
|
+
async getEvent(id, options) {
|
|
2854
3016
|
await this.initialize();
|
|
2855
3017
|
const row = sqliteGet(
|
|
2856
3018
|
this.db,
|
|
2857
|
-
`SELECT * FROM events WHERE id =
|
|
3019
|
+
`SELECT * FROM events WHERE id = ? AND ${maybeQuarantinePredicate(options)}`,
|
|
2858
3020
|
[id]
|
|
2859
3021
|
);
|
|
2860
3022
|
if (!row)
|
|
@@ -2864,11 +3026,11 @@ var SQLiteEventStore = class {
|
|
|
2864
3026
|
/**
|
|
2865
3027
|
* Get events since a timestamp (for sync)
|
|
2866
3028
|
*/
|
|
2867
|
-
async getEventsSince(timestamp, limit = 1e3) {
|
|
3029
|
+
async getEventsSince(timestamp, limit = 1e3, options) {
|
|
2868
3030
|
await this.initialize();
|
|
2869
3031
|
const rows = sqliteAll(
|
|
2870
3032
|
this.db,
|
|
2871
|
-
`SELECT * FROM events WHERE timestamp > ? ORDER BY timestamp ASC LIMIT ?`,
|
|
3033
|
+
`SELECT * FROM events WHERE timestamp > ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ?`,
|
|
2872
3034
|
[timestamp, limit]
|
|
2873
3035
|
);
|
|
2874
3036
|
return rows.map(this.rowToEvent);
|
|
@@ -2877,11 +3039,11 @@ var SQLiteEventStore = class {
|
|
|
2877
3039
|
* Get events since a SQLite rowid (for robust incremental replication).
|
|
2878
3040
|
* Rowid is monotonic for append-only tables, independent of client timestamps.
|
|
2879
3041
|
*/
|
|
2880
|
-
async getEventsSinceRowid(lastRowid, limit = 1e3) {
|
|
3042
|
+
async getEventsSinceRowid(lastRowid, limit = 1e3, options) {
|
|
2881
3043
|
await this.initialize();
|
|
2882
3044
|
const rows = sqliteAll(
|
|
2883
3045
|
this.db,
|
|
2884
|
-
`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 ?`,
|
|
2885
3047
|
[lastRowid, limit]
|
|
2886
3048
|
);
|
|
2887
3049
|
return rows.map((row) => ({
|
|
@@ -3078,7 +3240,9 @@ var SQLiteEventStore = class {
|
|
|
3078
3240
|
const placeholders = ids.map(() => "?").join(",");
|
|
3079
3241
|
sqliteRun(
|
|
3080
3242
|
this.db,
|
|
3081
|
-
`UPDATE embedding_outbox
|
|
3243
|
+
`UPDATE embedding_outbox
|
|
3244
|
+
SET status = 'processing', processed_at = datetime('now'), error_message = NULL
|
|
3245
|
+
WHERE id IN (${placeholders})`,
|
|
3082
3246
|
ids
|
|
3083
3247
|
);
|
|
3084
3248
|
return pending.map((row) => ({
|
|
@@ -3114,19 +3278,19 @@ var SQLiteEventStore = class {
|
|
|
3114
3278
|
/**
|
|
3115
3279
|
* Count total events
|
|
3116
3280
|
*/
|
|
3117
|
-
async countEvents() {
|
|
3281
|
+
async countEvents(options) {
|
|
3118
3282
|
await this.initialize();
|
|
3119
|
-
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)}`);
|
|
3120
3284
|
return row?.count || 0;
|
|
3121
3285
|
}
|
|
3122
3286
|
/**
|
|
3123
3287
|
* Get events page in timestamp ascending order (stable migration/reindex scans)
|
|
3124
3288
|
*/
|
|
3125
|
-
async getEventsPage(limit = 1e3, offset = 0) {
|
|
3289
|
+
async getEventsPage(limit = 1e3, offset = 0, options) {
|
|
3126
3290
|
await this.initialize();
|
|
3127
3291
|
const rows = sqliteAll(
|
|
3128
3292
|
this.db,
|
|
3129
|
-
`SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
3293
|
+
`SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
3130
3294
|
[limit, offset]
|
|
3131
3295
|
);
|
|
3132
3296
|
return rows.map(this.rowToEvent);
|
|
@@ -3148,6 +3312,197 @@ var SQLiteEventStore = class {
|
|
|
3148
3312
|
[error, ...ids]
|
|
3149
3313
|
);
|
|
3150
3314
|
}
|
|
3315
|
+
/**
|
|
3316
|
+
* Recover abandoned outbox work after a worker/process crash.
|
|
3317
|
+
*
|
|
3318
|
+
* Rows in `processing` are claimed work. If the process exits before marking
|
|
3319
|
+
* them done/failed, they otherwise remain invisible to future processing.
|
|
3320
|
+
* Recovery is deliberately age-gated so an active worker is not disturbed.
|
|
3321
|
+
*/
|
|
3322
|
+
async recoverStuckOutboxItems(options = {}) {
|
|
3323
|
+
await this.initialize();
|
|
3324
|
+
const thresholdMs = Number.isFinite(options.stuckThresholdMs) && (options.stuckThresholdMs ?? 0) >= 0 ? options.stuckThresholdMs : DEFAULT_OUTBOX_STUCK_THRESHOLD_MS;
|
|
3325
|
+
const maxRetries = Number.isFinite(options.maxRetries) && (options.maxRetries ?? 0) > 0 ? options.maxRetries : DEFAULT_OUTBOX_MAX_RETRIES;
|
|
3326
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
3327
|
+
const threshold = new Date(now.getTime() - thresholdMs).toISOString();
|
|
3328
|
+
const result = emptyOutboxRecoveryResult();
|
|
3329
|
+
const embeddingRecovered = sqliteRun(
|
|
3330
|
+
this.db,
|
|
3331
|
+
`UPDATE embedding_outbox
|
|
3332
|
+
SET status = 'pending', processed_at = NULL, error_message = NULL
|
|
3333
|
+
WHERE status = 'processing'
|
|
3334
|
+
AND datetime(COALESCE(processed_at, created_at)) < datetime(?)`,
|
|
3335
|
+
[threshold]
|
|
3336
|
+
);
|
|
3337
|
+
result.embedding.recoveredProcessing = Number(embeddingRecovered.changes ?? 0);
|
|
3338
|
+
const embeddingRetried = sqliteRun(
|
|
3339
|
+
this.db,
|
|
3340
|
+
`UPDATE embedding_outbox
|
|
3341
|
+
SET status = 'pending', error_message = NULL
|
|
3342
|
+
WHERE status = 'failed'
|
|
3343
|
+
AND retry_count < ?`,
|
|
3344
|
+
[maxRetries]
|
|
3345
|
+
);
|
|
3346
|
+
result.embedding.retriedFailed = Number(embeddingRetried.changes ?? 0);
|
|
3347
|
+
const vectorRecovered = sqliteRun(
|
|
3348
|
+
this.db,
|
|
3349
|
+
`UPDATE vector_outbox
|
|
3350
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3351
|
+
WHERE status = 'processing'
|
|
3352
|
+
AND datetime(updated_at) < datetime(?)`,
|
|
3353
|
+
[now.toISOString(), threshold]
|
|
3354
|
+
);
|
|
3355
|
+
result.vector.recoveredProcessing = Number(vectorRecovered.changes ?? 0);
|
|
3356
|
+
const vectorRetried = sqliteRun(
|
|
3357
|
+
this.db,
|
|
3358
|
+
`UPDATE vector_outbox
|
|
3359
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3360
|
+
WHERE status = 'failed'
|
|
3361
|
+
AND retry_count < ?`,
|
|
3362
|
+
[now.toISOString(), maxRetries]
|
|
3363
|
+
);
|
|
3364
|
+
result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
|
|
3365
|
+
return result;
|
|
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
|
+
}
|
|
3151
3506
|
/**
|
|
3152
3507
|
* Get embedding/vector outbox health statistics
|
|
3153
3508
|
*/
|
|
@@ -3195,7 +3550,11 @@ var SQLiteEventStore = class {
|
|
|
3195
3550
|
await this.initialize();
|
|
3196
3551
|
const rows = sqliteAll(
|
|
3197
3552
|
this.db,
|
|
3198
|
-
`SELECT level, COUNT(*) as count
|
|
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`
|
|
3199
3558
|
);
|
|
3200
3559
|
return rows;
|
|
3201
3560
|
}
|
|
@@ -3211,6 +3570,7 @@ var SQLiteEventStore = class {
|
|
|
3211
3570
|
`SELECT e.* FROM events e
|
|
3212
3571
|
INNER JOIN memory_levels ml ON e.id = ml.event_id
|
|
3213
3572
|
WHERE ml.level = ?
|
|
3573
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
3214
3574
|
ORDER BY e.timestamp DESC
|
|
3215
3575
|
LIMIT ? OFFSET ?`,
|
|
3216
3576
|
[level, limit, offset]
|
|
@@ -3303,12 +3663,13 @@ var SQLiteEventStore = class {
|
|
|
3303
3663
|
/**
|
|
3304
3664
|
* Get most accessed memories (falls back to recent events if none accessed)
|
|
3305
3665
|
*/
|
|
3306
|
-
async getMostAccessed(limit = 10) {
|
|
3666
|
+
async getMostAccessed(limit = 10, options) {
|
|
3307
3667
|
await this.initialize();
|
|
3308
3668
|
let rows = sqliteAll(
|
|
3309
3669
|
this.db,
|
|
3310
3670
|
`SELECT * FROM events
|
|
3311
3671
|
WHERE access_count > 0
|
|
3672
|
+
AND ${maybeQuarantinePredicate(options)}
|
|
3312
3673
|
ORDER BY access_count DESC, last_accessed_at DESC
|
|
3313
3674
|
LIMIT ?`,
|
|
3314
3675
|
[limit]
|
|
@@ -3317,6 +3678,7 @@ var SQLiteEventStore = class {
|
|
|
3317
3678
|
rows = sqliteAll(
|
|
3318
3679
|
this.db,
|
|
3319
3680
|
`SELECT * FROM events
|
|
3681
|
+
WHERE ${maybeQuarantinePredicate(options)}
|
|
3320
3682
|
ORDER BY timestamp DESC
|
|
3321
3683
|
LIMIT ?`,
|
|
3322
3684
|
[limit]
|
|
@@ -3447,6 +3809,7 @@ var SQLiteEventStore = class {
|
|
|
3447
3809
|
FROM memory_helpfulness mh
|
|
3448
3810
|
JOIN events e ON e.id = mh.event_id
|
|
3449
3811
|
WHERE mh.measured_at IS NOT NULL
|
|
3812
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
3450
3813
|
GROUP BY mh.event_id
|
|
3451
3814
|
ORDER BY avg_score DESC
|
|
3452
3815
|
LIMIT ?`,
|
|
@@ -3511,6 +3874,7 @@ var SQLiteEventStore = class {
|
|
|
3511
3874
|
FROM events_fts fts
|
|
3512
3875
|
JOIN events e ON e.id = fts.event_id
|
|
3513
3876
|
WHERE events_fts MATCH ?
|
|
3877
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
3514
3878
|
ORDER BY fts.rank
|
|
3515
3879
|
LIMIT ?`,
|
|
3516
3880
|
[searchTerms, limit]
|
|
@@ -3525,6 +3889,7 @@ var SQLiteEventStore = class {
|
|
|
3525
3889
|
this.db,
|
|
3526
3890
|
`SELECT *, 0 as rank FROM events
|
|
3527
3891
|
WHERE content LIKE ?
|
|
3892
|
+
AND ${notActiveQuarantinedSql()}
|
|
3528
3893
|
ORDER BY timestamp DESC
|
|
3529
3894
|
LIMIT ?`,
|
|
3530
3895
|
[likePattern, limit]
|
|
@@ -3731,6 +4096,7 @@ var SQLiteEventStore = class {
|
|
|
3731
4096
|
`SELECT turn_id, MIN(timestamp) as min_ts
|
|
3732
4097
|
FROM events
|
|
3733
4098
|
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
4099
|
+
AND ${maybeQuarantinePredicate(options)}
|
|
3734
4100
|
GROUP BY turn_id
|
|
3735
4101
|
ORDER BY min_ts DESC
|
|
3736
4102
|
LIMIT ? OFFSET ?`,
|
|
@@ -3738,7 +4104,7 @@ var SQLiteEventStore = class {
|
|
|
3738
4104
|
);
|
|
3739
4105
|
const turns = [];
|
|
3740
4106
|
for (const turnRow of turnRows) {
|
|
3741
|
-
const events = await this.getEventsByTurn(turnRow.turn_id);
|
|
4107
|
+
const events = await this.getEventsByTurn(turnRow.turn_id, options);
|
|
3742
4108
|
const promptEvent = events.find((e) => e.eventType === "user_prompt");
|
|
3743
4109
|
const toolEvents = events.filter((e) => e.eventType === "tool_observation");
|
|
3744
4110
|
const hasResponse = events.some((e) => e.eventType === "agent_response");
|
|
@@ -3757,11 +4123,11 @@ var SQLiteEventStore = class {
|
|
|
3757
4123
|
/**
|
|
3758
4124
|
* Get all events for a specific turn_id
|
|
3759
4125
|
*/
|
|
3760
|
-
async getEventsByTurn(turnId) {
|
|
4126
|
+
async getEventsByTurn(turnId, options) {
|
|
3761
4127
|
await this.initialize();
|
|
3762
4128
|
const rows = sqliteAll(
|
|
3763
4129
|
this.db,
|
|
3764
|
-
`SELECT * FROM events WHERE turn_id = ? ORDER BY timestamp ASC`,
|
|
4130
|
+
`SELECT * FROM events WHERE turn_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
|
|
3765
4131
|
[turnId]
|
|
3766
4132
|
);
|
|
3767
4133
|
return rows.map(this.rowToEvent);
|
|
@@ -3769,13 +4135,14 @@ var SQLiteEventStore = class {
|
|
|
3769
4135
|
/**
|
|
3770
4136
|
* Count total turns for a session
|
|
3771
4137
|
*/
|
|
3772
|
-
async countSessionTurns(sessionId) {
|
|
4138
|
+
async countSessionTurns(sessionId, options) {
|
|
3773
4139
|
await this.initialize();
|
|
3774
4140
|
const row = sqliteGet(
|
|
3775
4141
|
this.db,
|
|
3776
4142
|
`SELECT COUNT(DISTINCT turn_id) as count
|
|
3777
4143
|
FROM events
|
|
3778
|
-
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
4144
|
+
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
4145
|
+
AND ${maybeQuarantinePredicate(options)}`,
|
|
3779
4146
|
[sessionId]
|
|
3780
4147
|
);
|
|
3781
4148
|
return row?.count || 0;
|
|
@@ -3867,7 +4234,7 @@ var SQLiteEventStore = class {
|
|
|
3867
4234
|
content: row.content,
|
|
3868
4235
|
canonicalKey: row.canonical_key,
|
|
3869
4236
|
dedupeKey: row.dedupe_key,
|
|
3870
|
-
metadata:
|
|
4237
|
+
metadata: safeParseMetadataValue(row.metadata)
|
|
3871
4238
|
};
|
|
3872
4239
|
if (row.access_count !== void 0) {
|
|
3873
4240
|
event.access_count = row.access_count;
|
|
@@ -4019,6 +4386,7 @@ var VectorStore = class {
|
|
|
4019
4386
|
* Get total count of vectors
|
|
4020
4387
|
*/
|
|
4021
4388
|
async count() {
|
|
4389
|
+
await this.initialize();
|
|
4022
4390
|
if (!this.table)
|
|
4023
4391
|
return 0;
|
|
4024
4392
|
const result = await this.table.countRows();
|
|
@@ -4457,6 +4825,14 @@ var MemoryQueryService = class {
|
|
|
4457
4825
|
await this.initialize();
|
|
4458
4826
|
return this.getMaintenanceStore("getOutboxStats").getOutboxStats();
|
|
4459
4827
|
}
|
|
4828
|
+
async recoverStuckOutboxItems(options) {
|
|
4829
|
+
await this.initialize();
|
|
4830
|
+
return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
|
|
4831
|
+
}
|
|
4832
|
+
async repairLegacyProjectScope(options) {
|
|
4833
|
+
await this.initialize();
|
|
4834
|
+
return this.getMaintenanceStore("repairLegacyProjectScope").repairLegacyProjectScope(options);
|
|
4835
|
+
}
|
|
4460
4836
|
async getStats() {
|
|
4461
4837
|
await this.initialize();
|
|
4462
4838
|
const deps = this.getStatsDeps();
|
|
@@ -6079,18 +6455,18 @@ function assertDefaultRetrieverStore(eventStore) {
|
|
|
6079
6455
|
function createMemoryEngineServices(options) {
|
|
6080
6456
|
const factories = options.factories ?? {};
|
|
6081
6457
|
const storagePath = options.storagePath;
|
|
6082
|
-
if (!options.readOnly && !
|
|
6083
|
-
|
|
6458
|
+
if (!options.readOnly && !fs6.existsSync(storagePath)) {
|
|
6459
|
+
fs6.mkdirSync(storagePath, { recursive: true });
|
|
6084
6460
|
}
|
|
6085
6461
|
const sqliteStore = (factories.createSQLiteEventStore ?? defaultCreateSQLiteEventStore)(
|
|
6086
|
-
|
|
6462
|
+
path5.join(storagePath, "events.sqlite"),
|
|
6087
6463
|
{
|
|
6088
6464
|
readonly: options.readOnly,
|
|
6089
6465
|
markdownMirrorRoot: storagePath
|
|
6090
6466
|
}
|
|
6091
6467
|
);
|
|
6092
6468
|
const vectorStore = (factories.createVectorStore ?? defaultCreateVectorStore)(
|
|
6093
|
-
|
|
6469
|
+
path5.join(storagePath, "vectors")
|
|
6094
6470
|
);
|
|
6095
6471
|
const embeddingModel = options.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
6096
6472
|
const embedder = embeddingModel ? (factories.createEmbedder ?? defaultCreateEmbedder)(embeddingModel) : (factories.getDefaultEmbedder ?? getDefaultEmbedder)();
|
|
@@ -6501,8 +6877,8 @@ function createMemoryRuntimeService(deps) {
|
|
|
6501
6877
|
}
|
|
6502
6878
|
|
|
6503
6879
|
// src/extensions/shared-memory/shared-memory-services.ts
|
|
6504
|
-
import * as
|
|
6505
|
-
import * as
|
|
6880
|
+
import * as fs7 from "fs";
|
|
6881
|
+
import * as path6 from "path";
|
|
6506
6882
|
|
|
6507
6883
|
// src/core/shared-event-store.ts
|
|
6508
6884
|
var SharedEventStore = class {
|
|
@@ -7190,7 +7566,7 @@ var SharedMemoryServices = class {
|
|
|
7190
7566
|
this.ensureDirectory(sharedPath, { allowCreate: true });
|
|
7191
7567
|
const store = await this.openStore(sharedPath);
|
|
7192
7568
|
this.sharedVectorStore = this.factories.createSharedVectorStore(
|
|
7193
|
-
|
|
7569
|
+
path6.join(sharedPath, "vectors")
|
|
7194
7570
|
);
|
|
7195
7571
|
await this.sharedVectorStore.initialize();
|
|
7196
7572
|
this.sharedPromoter = this.factories.createSharedPromoter(
|
|
@@ -7263,7 +7639,7 @@ var SharedMemoryServices = class {
|
|
|
7263
7639
|
async createOpenStorePromise(sharedPath) {
|
|
7264
7640
|
if (!this.sharedEventStore) {
|
|
7265
7641
|
const sharedEventStore = this.factories.createSharedEventStore(
|
|
7266
|
-
|
|
7642
|
+
path6.join(sharedPath, "shared.duckdb")
|
|
7267
7643
|
);
|
|
7268
7644
|
await sharedEventStore.initialize();
|
|
7269
7645
|
this.sharedEventStore = sharedEventStore;
|
|
@@ -7283,9 +7659,9 @@ var SharedMemoryServices = class {
|
|
|
7283
7659
|
}
|
|
7284
7660
|
get factories() {
|
|
7285
7661
|
return {
|
|
7286
|
-
existsSync: this.options.factories?.existsSync ??
|
|
7662
|
+
existsSync: this.options.factories?.existsSync ?? fs7.existsSync,
|
|
7287
7663
|
mkdirSync: this.options.factories?.mkdirSync ?? ((targetPath) => {
|
|
7288
|
-
|
|
7664
|
+
fs7.mkdirSync(targetPath, { recursive: true });
|
|
7289
7665
|
}),
|
|
7290
7666
|
createSharedEventStore: this.options.factories?.createSharedEventStore ?? createSharedEventStore,
|
|
7291
7667
|
createSharedStore: this.options.factories?.createSharedStore ?? createSharedStore,
|
|
@@ -7400,33 +7776,11 @@ function createMemoryServiceComposition(options) {
|
|
|
7400
7776
|
}
|
|
7401
7777
|
function defaultExpandPath(targetPath) {
|
|
7402
7778
|
if (targetPath.startsWith("~")) {
|
|
7403
|
-
return
|
|
7779
|
+
return path7.join(os2.homedir(), targetPath.slice(1));
|
|
7404
7780
|
}
|
|
7405
7781
|
return targetPath;
|
|
7406
7782
|
}
|
|
7407
7783
|
|
|
7408
|
-
// src/core/registry/project-path.ts
|
|
7409
|
-
import * as crypto2 from "crypto";
|
|
7410
|
-
import * as fs7 from "fs";
|
|
7411
|
-
import * as os2 from "os";
|
|
7412
|
-
import * as path7 from "path";
|
|
7413
|
-
function normalizeProjectPath(projectPath) {
|
|
7414
|
-
const expanded = projectPath.startsWith("~") ? path7.join(os2.homedir(), projectPath.slice(1)) : projectPath;
|
|
7415
|
-
try {
|
|
7416
|
-
return fs7.realpathSync(expanded);
|
|
7417
|
-
} catch {
|
|
7418
|
-
return path7.resolve(expanded);
|
|
7419
|
-
}
|
|
7420
|
-
}
|
|
7421
|
-
function hashProjectPath(projectPath) {
|
|
7422
|
-
const normalizedPath = normalizeProjectPath(projectPath);
|
|
7423
|
-
return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
|
|
7424
|
-
}
|
|
7425
|
-
function getProjectStoragePath(projectPath) {
|
|
7426
|
-
const hash = hashProjectPath(projectPath);
|
|
7427
|
-
return path7.join(os2.homedir(), ".claude-code", "memory", "projects", hash);
|
|
7428
|
-
}
|
|
7429
|
-
|
|
7430
7784
|
// src/core/registry/session-registry.ts
|
|
7431
7785
|
import * as fs8 from "fs";
|
|
7432
7786
|
import * as os3 from "os";
|
|
@@ -7725,6 +8079,12 @@ var MemoryService = class {
|
|
|
7725
8079
|
async getOutboxStats() {
|
|
7726
8080
|
return this.queryService.getOutboxStats();
|
|
7727
8081
|
}
|
|
8082
|
+
async recoverStuckOutboxItems(options) {
|
|
8083
|
+
return this.queryService.recoverStuckOutboxItems(options);
|
|
8084
|
+
}
|
|
8085
|
+
async repairLegacyProjectScope(options) {
|
|
8086
|
+
return this.queryService.repairLegacyProjectScope(options);
|
|
8087
|
+
}
|
|
7728
8088
|
async getRetrievalTraceStats() {
|
|
7729
8089
|
return this.retrievalAnalyticsService.getRetrievalTraceStats();
|
|
7730
8090
|
}
|