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
|
@@ -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
|
|
13
|
-
import * as
|
|
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
|
|
1525
|
-
import * as
|
|
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
|
|
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 (!
|
|
2210
|
-
|
|
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
|
|
2260
|
-
import * as
|
|
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
|
|
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
|
|
2311
|
-
await
|
|
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
|
};
|
|
@@ -2321,6 +2346,143 @@ function normalizeQueryRewriteKind(value) {
|
|
|
2321
2346
|
return "none";
|
|
2322
2347
|
}
|
|
2323
2348
|
var REWRITTEN_QUERY_REWRITE_KIND_SQL = `LOWER(TRIM(COALESCE(query_rewrite_kind, 'none'))) IN ('follow-up-context', 'intent-rewrite')`;
|
|
2349
|
+
var DEFAULT_OUTBOX_STUCK_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
2350
|
+
var DEFAULT_OUTBOX_MAX_RETRIES = 3;
|
|
2351
|
+
function emptyOutboxRecoveryResult() {
|
|
2352
|
+
return {
|
|
2353
|
+
embedding: { recoveredProcessing: 0, retriedFailed: 0 },
|
|
2354
|
+
vector: { recoveredProcessing: 0, retriedFailed: 0 }
|
|
2355
|
+
};
|
|
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
|
+
}
|
|
2324
2486
|
var SQLiteEventStore = class {
|
|
2325
2487
|
db;
|
|
2326
2488
|
initialized = false;
|
|
@@ -2819,11 +2981,11 @@ var SQLiteEventStore = class {
|
|
|
2819
2981
|
/**
|
|
2820
2982
|
* Get events by session ID
|
|
2821
2983
|
*/
|
|
2822
|
-
async getSessionEvents(sessionId) {
|
|
2984
|
+
async getSessionEvents(sessionId, options) {
|
|
2823
2985
|
await this.initialize();
|
|
2824
2986
|
const rows = sqliteAll(
|
|
2825
2987
|
this.db,
|
|
2826
|
-
`SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC`,
|
|
2988
|
+
`SELECT * FROM events WHERE session_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
|
|
2827
2989
|
[sessionId]
|
|
2828
2990
|
);
|
|
2829
2991
|
return rows.map(this.rowToEvent);
|
|
@@ -2831,11 +2993,11 @@ var SQLiteEventStore = class {
|
|
|
2831
2993
|
/**
|
|
2832
2994
|
* Get recent events
|
|
2833
2995
|
*/
|
|
2834
|
-
async getRecentEvents(limit = 100) {
|
|
2996
|
+
async getRecentEvents(limit = 100, options) {
|
|
2835
2997
|
await this.initialize();
|
|
2836
2998
|
const rows = sqliteAll(
|
|
2837
2999
|
this.db,
|
|
2838
|
-
`SELECT * FROM events ORDER BY timestamp DESC LIMIT ?`,
|
|
3000
|
+
`SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp DESC LIMIT ?`,
|
|
2839
3001
|
[limit]
|
|
2840
3002
|
);
|
|
2841
3003
|
return rows.map(this.rowToEvent);
|
|
@@ -2843,11 +3005,11 @@ var SQLiteEventStore = class {
|
|
|
2843
3005
|
/**
|
|
2844
3006
|
* Get event by ID
|
|
2845
3007
|
*/
|
|
2846
|
-
async getEvent(id) {
|
|
3008
|
+
async getEvent(id, options) {
|
|
2847
3009
|
await this.initialize();
|
|
2848
3010
|
const row = sqliteGet(
|
|
2849
3011
|
this.db,
|
|
2850
|
-
`SELECT * FROM events WHERE id =
|
|
3012
|
+
`SELECT * FROM events WHERE id = ? AND ${maybeQuarantinePredicate(options)}`,
|
|
2851
3013
|
[id]
|
|
2852
3014
|
);
|
|
2853
3015
|
if (!row)
|
|
@@ -2857,11 +3019,11 @@ var SQLiteEventStore = class {
|
|
|
2857
3019
|
/**
|
|
2858
3020
|
* Get events since a timestamp (for sync)
|
|
2859
3021
|
*/
|
|
2860
|
-
async getEventsSince(timestamp, limit = 1e3) {
|
|
3022
|
+
async getEventsSince(timestamp, limit = 1e3, options) {
|
|
2861
3023
|
await this.initialize();
|
|
2862
3024
|
const rows = sqliteAll(
|
|
2863
3025
|
this.db,
|
|
2864
|
-
`SELECT * FROM events WHERE timestamp > ? ORDER BY timestamp ASC LIMIT ?`,
|
|
3026
|
+
`SELECT * FROM events WHERE timestamp > ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ?`,
|
|
2865
3027
|
[timestamp, limit]
|
|
2866
3028
|
);
|
|
2867
3029
|
return rows.map(this.rowToEvent);
|
|
@@ -2870,11 +3032,11 @@ var SQLiteEventStore = class {
|
|
|
2870
3032
|
* Get events since a SQLite rowid (for robust incremental replication).
|
|
2871
3033
|
* Rowid is monotonic for append-only tables, independent of client timestamps.
|
|
2872
3034
|
*/
|
|
2873
|
-
async getEventsSinceRowid(lastRowid, limit = 1e3) {
|
|
3035
|
+
async getEventsSinceRowid(lastRowid, limit = 1e3, options) {
|
|
2874
3036
|
await this.initialize();
|
|
2875
3037
|
const rows = sqliteAll(
|
|
2876
3038
|
this.db,
|
|
2877
|
-
`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 ?`,
|
|
2878
3040
|
[lastRowid, limit]
|
|
2879
3041
|
);
|
|
2880
3042
|
return rows.map((row) => ({
|
|
@@ -3071,7 +3233,9 @@ var SQLiteEventStore = class {
|
|
|
3071
3233
|
const placeholders = ids.map(() => "?").join(",");
|
|
3072
3234
|
sqliteRun(
|
|
3073
3235
|
this.db,
|
|
3074
|
-
`UPDATE embedding_outbox
|
|
3236
|
+
`UPDATE embedding_outbox
|
|
3237
|
+
SET status = 'processing', processed_at = datetime('now'), error_message = NULL
|
|
3238
|
+
WHERE id IN (${placeholders})`,
|
|
3075
3239
|
ids
|
|
3076
3240
|
);
|
|
3077
3241
|
return pending.map((row) => ({
|
|
@@ -3107,19 +3271,19 @@ var SQLiteEventStore = class {
|
|
|
3107
3271
|
/**
|
|
3108
3272
|
* Count total events
|
|
3109
3273
|
*/
|
|
3110
|
-
async countEvents() {
|
|
3274
|
+
async countEvents(options) {
|
|
3111
3275
|
await this.initialize();
|
|
3112
|
-
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)}`);
|
|
3113
3277
|
return row?.count || 0;
|
|
3114
3278
|
}
|
|
3115
3279
|
/**
|
|
3116
3280
|
* Get events page in timestamp ascending order (stable migration/reindex scans)
|
|
3117
3281
|
*/
|
|
3118
|
-
async getEventsPage(limit = 1e3, offset = 0) {
|
|
3282
|
+
async getEventsPage(limit = 1e3, offset = 0, options) {
|
|
3119
3283
|
await this.initialize();
|
|
3120
3284
|
const rows = sqliteAll(
|
|
3121
3285
|
this.db,
|
|
3122
|
-
`SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
3286
|
+
`SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
3123
3287
|
[limit, offset]
|
|
3124
3288
|
);
|
|
3125
3289
|
return rows.map(this.rowToEvent);
|
|
@@ -3141,6 +3305,197 @@ var SQLiteEventStore = class {
|
|
|
3141
3305
|
[error, ...ids]
|
|
3142
3306
|
);
|
|
3143
3307
|
}
|
|
3308
|
+
/**
|
|
3309
|
+
* Recover abandoned outbox work after a worker/process crash.
|
|
3310
|
+
*
|
|
3311
|
+
* Rows in `processing` are claimed work. If the process exits before marking
|
|
3312
|
+
* them done/failed, they otherwise remain invisible to future processing.
|
|
3313
|
+
* Recovery is deliberately age-gated so an active worker is not disturbed.
|
|
3314
|
+
*/
|
|
3315
|
+
async recoverStuckOutboxItems(options = {}) {
|
|
3316
|
+
await this.initialize();
|
|
3317
|
+
const thresholdMs = Number.isFinite(options.stuckThresholdMs) && (options.stuckThresholdMs ?? 0) >= 0 ? options.stuckThresholdMs : DEFAULT_OUTBOX_STUCK_THRESHOLD_MS;
|
|
3318
|
+
const maxRetries = Number.isFinite(options.maxRetries) && (options.maxRetries ?? 0) > 0 ? options.maxRetries : DEFAULT_OUTBOX_MAX_RETRIES;
|
|
3319
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
3320
|
+
const threshold = new Date(now.getTime() - thresholdMs).toISOString();
|
|
3321
|
+
const result = emptyOutboxRecoveryResult();
|
|
3322
|
+
const embeddingRecovered = sqliteRun(
|
|
3323
|
+
this.db,
|
|
3324
|
+
`UPDATE embedding_outbox
|
|
3325
|
+
SET status = 'pending', processed_at = NULL, error_message = NULL
|
|
3326
|
+
WHERE status = 'processing'
|
|
3327
|
+
AND datetime(COALESCE(processed_at, created_at)) < datetime(?)`,
|
|
3328
|
+
[threshold]
|
|
3329
|
+
);
|
|
3330
|
+
result.embedding.recoveredProcessing = Number(embeddingRecovered.changes ?? 0);
|
|
3331
|
+
const embeddingRetried = sqliteRun(
|
|
3332
|
+
this.db,
|
|
3333
|
+
`UPDATE embedding_outbox
|
|
3334
|
+
SET status = 'pending', error_message = NULL
|
|
3335
|
+
WHERE status = 'failed'
|
|
3336
|
+
AND retry_count < ?`,
|
|
3337
|
+
[maxRetries]
|
|
3338
|
+
);
|
|
3339
|
+
result.embedding.retriedFailed = Number(embeddingRetried.changes ?? 0);
|
|
3340
|
+
const vectorRecovered = sqliteRun(
|
|
3341
|
+
this.db,
|
|
3342
|
+
`UPDATE vector_outbox
|
|
3343
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3344
|
+
WHERE status = 'processing'
|
|
3345
|
+
AND datetime(updated_at) < datetime(?)`,
|
|
3346
|
+
[now.toISOString(), threshold]
|
|
3347
|
+
);
|
|
3348
|
+
result.vector.recoveredProcessing = Number(vectorRecovered.changes ?? 0);
|
|
3349
|
+
const vectorRetried = sqliteRun(
|
|
3350
|
+
this.db,
|
|
3351
|
+
`UPDATE vector_outbox
|
|
3352
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3353
|
+
WHERE status = 'failed'
|
|
3354
|
+
AND retry_count < ?`,
|
|
3355
|
+
[now.toISOString(), maxRetries]
|
|
3356
|
+
);
|
|
3357
|
+
result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
|
|
3358
|
+
return result;
|
|
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
|
+
}
|
|
3144
3499
|
/**
|
|
3145
3500
|
* Get embedding/vector outbox health statistics
|
|
3146
3501
|
*/
|
|
@@ -3188,7 +3543,11 @@ var SQLiteEventStore = class {
|
|
|
3188
3543
|
await this.initialize();
|
|
3189
3544
|
const rows = sqliteAll(
|
|
3190
3545
|
this.db,
|
|
3191
|
-
`SELECT level, COUNT(*) as count
|
|
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`
|
|
3192
3551
|
);
|
|
3193
3552
|
return rows;
|
|
3194
3553
|
}
|
|
@@ -3204,6 +3563,7 @@ var SQLiteEventStore = class {
|
|
|
3204
3563
|
`SELECT e.* FROM events e
|
|
3205
3564
|
INNER JOIN memory_levels ml ON e.id = ml.event_id
|
|
3206
3565
|
WHERE ml.level = ?
|
|
3566
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
3207
3567
|
ORDER BY e.timestamp DESC
|
|
3208
3568
|
LIMIT ? OFFSET ?`,
|
|
3209
3569
|
[level, limit, offset]
|
|
@@ -3296,12 +3656,13 @@ var SQLiteEventStore = class {
|
|
|
3296
3656
|
/**
|
|
3297
3657
|
* Get most accessed memories (falls back to recent events if none accessed)
|
|
3298
3658
|
*/
|
|
3299
|
-
async getMostAccessed(limit = 10) {
|
|
3659
|
+
async getMostAccessed(limit = 10, options) {
|
|
3300
3660
|
await this.initialize();
|
|
3301
3661
|
let rows = sqliteAll(
|
|
3302
3662
|
this.db,
|
|
3303
3663
|
`SELECT * FROM events
|
|
3304
3664
|
WHERE access_count > 0
|
|
3665
|
+
AND ${maybeQuarantinePredicate(options)}
|
|
3305
3666
|
ORDER BY access_count DESC, last_accessed_at DESC
|
|
3306
3667
|
LIMIT ?`,
|
|
3307
3668
|
[limit]
|
|
@@ -3310,6 +3671,7 @@ var SQLiteEventStore = class {
|
|
|
3310
3671
|
rows = sqliteAll(
|
|
3311
3672
|
this.db,
|
|
3312
3673
|
`SELECT * FROM events
|
|
3674
|
+
WHERE ${maybeQuarantinePredicate(options)}
|
|
3313
3675
|
ORDER BY timestamp DESC
|
|
3314
3676
|
LIMIT ?`,
|
|
3315
3677
|
[limit]
|
|
@@ -3440,6 +3802,7 @@ var SQLiteEventStore = class {
|
|
|
3440
3802
|
FROM memory_helpfulness mh
|
|
3441
3803
|
JOIN events e ON e.id = mh.event_id
|
|
3442
3804
|
WHERE mh.measured_at IS NOT NULL
|
|
3805
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
3443
3806
|
GROUP BY mh.event_id
|
|
3444
3807
|
ORDER BY avg_score DESC
|
|
3445
3808
|
LIMIT ?`,
|
|
@@ -3504,6 +3867,7 @@ var SQLiteEventStore = class {
|
|
|
3504
3867
|
FROM events_fts fts
|
|
3505
3868
|
JOIN events e ON e.id = fts.event_id
|
|
3506
3869
|
WHERE events_fts MATCH ?
|
|
3870
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
3507
3871
|
ORDER BY fts.rank
|
|
3508
3872
|
LIMIT ?`,
|
|
3509
3873
|
[searchTerms, limit]
|
|
@@ -3518,6 +3882,7 @@ var SQLiteEventStore = class {
|
|
|
3518
3882
|
this.db,
|
|
3519
3883
|
`SELECT *, 0 as rank FROM events
|
|
3520
3884
|
WHERE content LIKE ?
|
|
3885
|
+
AND ${notActiveQuarantinedSql()}
|
|
3521
3886
|
ORDER BY timestamp DESC
|
|
3522
3887
|
LIMIT ?`,
|
|
3523
3888
|
[likePattern, limit]
|
|
@@ -3724,6 +4089,7 @@ var SQLiteEventStore = class {
|
|
|
3724
4089
|
`SELECT turn_id, MIN(timestamp) as min_ts
|
|
3725
4090
|
FROM events
|
|
3726
4091
|
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
4092
|
+
AND ${maybeQuarantinePredicate(options)}
|
|
3727
4093
|
GROUP BY turn_id
|
|
3728
4094
|
ORDER BY min_ts DESC
|
|
3729
4095
|
LIMIT ? OFFSET ?`,
|
|
@@ -3731,7 +4097,7 @@ var SQLiteEventStore = class {
|
|
|
3731
4097
|
);
|
|
3732
4098
|
const turns = [];
|
|
3733
4099
|
for (const turnRow of turnRows) {
|
|
3734
|
-
const events = await this.getEventsByTurn(turnRow.turn_id);
|
|
4100
|
+
const events = await this.getEventsByTurn(turnRow.turn_id, options);
|
|
3735
4101
|
const promptEvent = events.find((e) => e.eventType === "user_prompt");
|
|
3736
4102
|
const toolEvents = events.filter((e) => e.eventType === "tool_observation");
|
|
3737
4103
|
const hasResponse = events.some((e) => e.eventType === "agent_response");
|
|
@@ -3750,11 +4116,11 @@ var SQLiteEventStore = class {
|
|
|
3750
4116
|
/**
|
|
3751
4117
|
* Get all events for a specific turn_id
|
|
3752
4118
|
*/
|
|
3753
|
-
async getEventsByTurn(turnId) {
|
|
4119
|
+
async getEventsByTurn(turnId, options) {
|
|
3754
4120
|
await this.initialize();
|
|
3755
4121
|
const rows = sqliteAll(
|
|
3756
4122
|
this.db,
|
|
3757
|
-
`SELECT * FROM events WHERE turn_id = ? ORDER BY timestamp ASC`,
|
|
4123
|
+
`SELECT * FROM events WHERE turn_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
|
|
3758
4124
|
[turnId]
|
|
3759
4125
|
);
|
|
3760
4126
|
return rows.map(this.rowToEvent);
|
|
@@ -3762,13 +4128,14 @@ var SQLiteEventStore = class {
|
|
|
3762
4128
|
/**
|
|
3763
4129
|
* Count total turns for a session
|
|
3764
4130
|
*/
|
|
3765
|
-
async countSessionTurns(sessionId) {
|
|
4131
|
+
async countSessionTurns(sessionId, options) {
|
|
3766
4132
|
await this.initialize();
|
|
3767
4133
|
const row = sqliteGet(
|
|
3768
4134
|
this.db,
|
|
3769
4135
|
`SELECT COUNT(DISTINCT turn_id) as count
|
|
3770
4136
|
FROM events
|
|
3771
|
-
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
4137
|
+
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
4138
|
+
AND ${maybeQuarantinePredicate(options)}`,
|
|
3772
4139
|
[sessionId]
|
|
3773
4140
|
);
|
|
3774
4141
|
return row?.count || 0;
|
|
@@ -3860,7 +4227,7 @@ var SQLiteEventStore = class {
|
|
|
3860
4227
|
content: row.content,
|
|
3861
4228
|
canonicalKey: row.canonical_key,
|
|
3862
4229
|
dedupeKey: row.dedupe_key,
|
|
3863
|
-
metadata:
|
|
4230
|
+
metadata: safeParseMetadataValue(row.metadata)
|
|
3864
4231
|
};
|
|
3865
4232
|
if (row.access_count !== void 0) {
|
|
3866
4233
|
event.access_count = row.access_count;
|
|
@@ -4012,6 +4379,7 @@ var VectorStore = class {
|
|
|
4012
4379
|
* Get total count of vectors
|
|
4013
4380
|
*/
|
|
4014
4381
|
async count() {
|
|
4382
|
+
await this.initialize();
|
|
4015
4383
|
if (!this.table)
|
|
4016
4384
|
return 0;
|
|
4017
4385
|
const result = await this.table.countRows();
|
|
@@ -4450,6 +4818,14 @@ var MemoryQueryService = class {
|
|
|
4450
4818
|
await this.initialize();
|
|
4451
4819
|
return this.getMaintenanceStore("getOutboxStats").getOutboxStats();
|
|
4452
4820
|
}
|
|
4821
|
+
async recoverStuckOutboxItems(options) {
|
|
4822
|
+
await this.initialize();
|
|
4823
|
+
return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
|
|
4824
|
+
}
|
|
4825
|
+
async repairLegacyProjectScope(options) {
|
|
4826
|
+
await this.initialize();
|
|
4827
|
+
return this.getMaintenanceStore("repairLegacyProjectScope").repairLegacyProjectScope(options);
|
|
4828
|
+
}
|
|
4453
4829
|
async getStats() {
|
|
4454
4830
|
await this.initialize();
|
|
4455
4831
|
const deps = this.getStatsDeps();
|
|
@@ -6072,18 +6448,18 @@ function assertDefaultRetrieverStore(eventStore) {
|
|
|
6072
6448
|
function createMemoryEngineServices(options) {
|
|
6073
6449
|
const factories = options.factories ?? {};
|
|
6074
6450
|
const storagePath = options.storagePath;
|
|
6075
|
-
if (!options.readOnly && !
|
|
6076
|
-
|
|
6451
|
+
if (!options.readOnly && !fs6.existsSync(storagePath)) {
|
|
6452
|
+
fs6.mkdirSync(storagePath, { recursive: true });
|
|
6077
6453
|
}
|
|
6078
6454
|
const sqliteStore = (factories.createSQLiteEventStore ?? defaultCreateSQLiteEventStore)(
|
|
6079
|
-
|
|
6455
|
+
path5.join(storagePath, "events.sqlite"),
|
|
6080
6456
|
{
|
|
6081
6457
|
readonly: options.readOnly,
|
|
6082
6458
|
markdownMirrorRoot: storagePath
|
|
6083
6459
|
}
|
|
6084
6460
|
);
|
|
6085
6461
|
const vectorStore = (factories.createVectorStore ?? defaultCreateVectorStore)(
|
|
6086
|
-
|
|
6462
|
+
path5.join(storagePath, "vectors")
|
|
6087
6463
|
);
|
|
6088
6464
|
const embeddingModel = options.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
6089
6465
|
const embedder = embeddingModel ? (factories.createEmbedder ?? defaultCreateEmbedder)(embeddingModel) : (factories.getDefaultEmbedder ?? getDefaultEmbedder)();
|
|
@@ -6494,8 +6870,8 @@ function createMemoryRuntimeService(deps) {
|
|
|
6494
6870
|
}
|
|
6495
6871
|
|
|
6496
6872
|
// src/extensions/shared-memory/shared-memory-services.ts
|
|
6497
|
-
import * as
|
|
6498
|
-
import * as
|
|
6873
|
+
import * as fs7 from "fs";
|
|
6874
|
+
import * as path6 from "path";
|
|
6499
6875
|
|
|
6500
6876
|
// src/core/shared-event-store.ts
|
|
6501
6877
|
var SharedEventStore = class {
|
|
@@ -7183,7 +7559,7 @@ var SharedMemoryServices = class {
|
|
|
7183
7559
|
this.ensureDirectory(sharedPath, { allowCreate: true });
|
|
7184
7560
|
const store = await this.openStore(sharedPath);
|
|
7185
7561
|
this.sharedVectorStore = this.factories.createSharedVectorStore(
|
|
7186
|
-
|
|
7562
|
+
path6.join(sharedPath, "vectors")
|
|
7187
7563
|
);
|
|
7188
7564
|
await this.sharedVectorStore.initialize();
|
|
7189
7565
|
this.sharedPromoter = this.factories.createSharedPromoter(
|
|
@@ -7256,7 +7632,7 @@ var SharedMemoryServices = class {
|
|
|
7256
7632
|
async createOpenStorePromise(sharedPath) {
|
|
7257
7633
|
if (!this.sharedEventStore) {
|
|
7258
7634
|
const sharedEventStore = this.factories.createSharedEventStore(
|
|
7259
|
-
|
|
7635
|
+
path6.join(sharedPath, "shared.duckdb")
|
|
7260
7636
|
);
|
|
7261
7637
|
await sharedEventStore.initialize();
|
|
7262
7638
|
this.sharedEventStore = sharedEventStore;
|
|
@@ -7276,9 +7652,9 @@ var SharedMemoryServices = class {
|
|
|
7276
7652
|
}
|
|
7277
7653
|
get factories() {
|
|
7278
7654
|
return {
|
|
7279
|
-
existsSync: this.options.factories?.existsSync ??
|
|
7655
|
+
existsSync: this.options.factories?.existsSync ?? fs7.existsSync,
|
|
7280
7656
|
mkdirSync: this.options.factories?.mkdirSync ?? ((targetPath) => {
|
|
7281
|
-
|
|
7657
|
+
fs7.mkdirSync(targetPath, { recursive: true });
|
|
7282
7658
|
}),
|
|
7283
7659
|
createSharedEventStore: this.options.factories?.createSharedEventStore ?? createSharedEventStore,
|
|
7284
7660
|
createSharedStore: this.options.factories?.createSharedStore ?? createSharedStore,
|
|
@@ -7393,33 +7769,11 @@ function createMemoryServiceComposition(options) {
|
|
|
7393
7769
|
}
|
|
7394
7770
|
function defaultExpandPath(targetPath) {
|
|
7395
7771
|
if (targetPath.startsWith("~")) {
|
|
7396
|
-
return
|
|
7772
|
+
return path7.join(os2.homedir(), targetPath.slice(1));
|
|
7397
7773
|
}
|
|
7398
7774
|
return targetPath;
|
|
7399
7775
|
}
|
|
7400
7776
|
|
|
7401
|
-
// src/core/registry/project-path.ts
|
|
7402
|
-
import * as crypto2 from "crypto";
|
|
7403
|
-
import * as fs7 from "fs";
|
|
7404
|
-
import * as os2 from "os";
|
|
7405
|
-
import * as path7 from "path";
|
|
7406
|
-
function normalizeProjectPath(projectPath) {
|
|
7407
|
-
const expanded = projectPath.startsWith("~") ? path7.join(os2.homedir(), projectPath.slice(1)) : projectPath;
|
|
7408
|
-
try {
|
|
7409
|
-
return fs7.realpathSync(expanded);
|
|
7410
|
-
} catch {
|
|
7411
|
-
return path7.resolve(expanded);
|
|
7412
|
-
}
|
|
7413
|
-
}
|
|
7414
|
-
function hashProjectPath(projectPath) {
|
|
7415
|
-
const normalizedPath = normalizeProjectPath(projectPath);
|
|
7416
|
-
return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
|
|
7417
|
-
}
|
|
7418
|
-
function getProjectStoragePath(projectPath) {
|
|
7419
|
-
const hash = hashProjectPath(projectPath);
|
|
7420
|
-
return path7.join(os2.homedir(), ".claude-code", "memory", "projects", hash);
|
|
7421
|
-
}
|
|
7422
|
-
|
|
7423
7777
|
// src/core/registry/session-registry.ts
|
|
7424
7778
|
import * as fs8 from "fs";
|
|
7425
7779
|
import * as os3 from "os";
|
|
@@ -7743,6 +8097,12 @@ var MemoryService = class {
|
|
|
7743
8097
|
async getOutboxStats() {
|
|
7744
8098
|
return this.queryService.getOutboxStats();
|
|
7745
8099
|
}
|
|
8100
|
+
async recoverStuckOutboxItems(options) {
|
|
8101
|
+
return this.queryService.recoverStuckOutboxItems(options);
|
|
8102
|
+
}
|
|
8103
|
+
async repairLegacyProjectScope(options) {
|
|
8104
|
+
return this.queryService.repairLegacyProjectScope(options);
|
|
8105
|
+
}
|
|
7746
8106
|
async getRetrievalTraceStats() {
|
|
7747
8107
|
return this.retrievalAnalyticsService.getRetrievalTraceStats();
|
|
7748
8108
|
}
|