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
package/dist/mcp/index.js
CHANGED
|
@@ -10436,8 +10436,8 @@ import * as path14 from "node:path";
|
|
|
10436
10436
|
import * as os5 from "os";
|
|
10437
10437
|
|
|
10438
10438
|
// src/core/engine/memory-service-composition.ts
|
|
10439
|
-
import * as
|
|
10440
|
-
import * as
|
|
10439
|
+
import * as os2 from "os";
|
|
10440
|
+
import * as path7 from "path";
|
|
10441
10441
|
|
|
10442
10442
|
// src/core/metadata-extractor.ts
|
|
10443
10443
|
function createToolObservationEmbedding(toolName, metadata, success) {
|
|
@@ -11948,8 +11948,8 @@ function createEndlessMemoryServices(options) {
|
|
|
11948
11948
|
}
|
|
11949
11949
|
|
|
11950
11950
|
// src/core/engine/memory-engine-services.ts
|
|
11951
|
-
import * as
|
|
11952
|
-
import * as
|
|
11951
|
+
import * as fs6 from "fs";
|
|
11952
|
+
import * as path5 from "path";
|
|
11953
11953
|
|
|
11954
11954
|
// src/extensions/vector/embedder.ts
|
|
11955
11955
|
var DEFAULT_EMBEDDING_MODEL = "Xenova/multilingual-e5-small";
|
|
@@ -12627,14 +12627,39 @@ function makeDedupeKey(content, sessionId) {
|
|
|
12627
12627
|
return `${sessionId}:${contentHash}`;
|
|
12628
12628
|
}
|
|
12629
12629
|
|
|
12630
|
+
// src/core/sqlite-event-store.ts
|
|
12631
|
+
import * as nodePath2 from "path";
|
|
12632
|
+
|
|
12633
|
+
// src/core/registry/project-path.ts
|
|
12634
|
+
import * as crypto2 from "crypto";
|
|
12635
|
+
import * as fs3 from "fs";
|
|
12636
|
+
import * as os from "os";
|
|
12637
|
+
import * as path3 from "path";
|
|
12638
|
+
function normalizeProjectPath(projectPath) {
|
|
12639
|
+
const expanded = projectPath.startsWith("~") ? path3.join(os.homedir(), projectPath.slice(1)) : projectPath;
|
|
12640
|
+
try {
|
|
12641
|
+
return fs3.realpathSync(expanded);
|
|
12642
|
+
} catch {
|
|
12643
|
+
return path3.resolve(expanded);
|
|
12644
|
+
}
|
|
12645
|
+
}
|
|
12646
|
+
function hashProjectPath(projectPath) {
|
|
12647
|
+
const normalizedPath = normalizeProjectPath(projectPath);
|
|
12648
|
+
return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
|
|
12649
|
+
}
|
|
12650
|
+
function getProjectStoragePath(projectPath) {
|
|
12651
|
+
const hash = hashProjectPath(projectPath);
|
|
12652
|
+
return path3.join(os.homedir(), ".claude-code", "memory", "projects", hash);
|
|
12653
|
+
}
|
|
12654
|
+
|
|
12630
12655
|
// src/core/sqlite-wrapper.ts
|
|
12631
12656
|
import Database from "better-sqlite3";
|
|
12632
|
-
import * as
|
|
12657
|
+
import * as fs4 from "fs";
|
|
12633
12658
|
import * as nodePath from "path";
|
|
12634
12659
|
function createSQLiteDatabase(path15, options) {
|
|
12635
12660
|
const dir = nodePath.dirname(path15);
|
|
12636
|
-
if (!
|
|
12637
|
-
|
|
12661
|
+
if (!fs4.existsSync(dir)) {
|
|
12662
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
12638
12663
|
}
|
|
12639
12664
|
const db = new Database(path15, {
|
|
12640
12665
|
readonly: options?.readonly ?? false
|
|
@@ -12683,8 +12708,8 @@ function toSQLiteTimestamp(date) {
|
|
|
12683
12708
|
}
|
|
12684
12709
|
|
|
12685
12710
|
// src/core/markdown-mirror.ts
|
|
12686
|
-
import * as
|
|
12687
|
-
import * as
|
|
12711
|
+
import * as fs5 from "fs/promises";
|
|
12712
|
+
import * as path4 from "path";
|
|
12688
12713
|
var DEFAULT_NAMESPACE = "default";
|
|
12689
12714
|
var DEFAULT_CATEGORY = "uncategorized";
|
|
12690
12715
|
function sanitizeSegment2(input, fallback) {
|
|
@@ -12713,7 +12738,7 @@ function buildMirrorPath2(rootDir, event) {
|
|
|
12713
12738
|
const yyyy = d.getFullYear();
|
|
12714
12739
|
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
12715
12740
|
const dd = String(d.getDate()).padStart(2, "0");
|
|
12716
|
-
return
|
|
12741
|
+
return path4.join(rootDir, "memory", namespace, ...categories, `${yyyy}-${mm}-${dd}.md`);
|
|
12717
12742
|
}
|
|
12718
12743
|
function formatMirrorEntry(event) {
|
|
12719
12744
|
const category = Array.isArray(event.metadata?.categoryPath) ? event.metadata.categoryPath.join("/") : String(event.metadata?.category ?? event.eventType);
|
|
@@ -12734,8 +12759,8 @@ var MarkdownMirror2 = class {
|
|
|
12734
12759
|
}
|
|
12735
12760
|
async append(event) {
|
|
12736
12761
|
const outPath = buildMirrorPath2(this.rootDir, event);
|
|
12737
|
-
await
|
|
12738
|
-
await
|
|
12762
|
+
await fs5.mkdir(path4.dirname(outPath), { recursive: true });
|
|
12763
|
+
await fs5.appendFile(outPath, formatMirrorEntry(event), "utf8");
|
|
12739
12764
|
return outPath;
|
|
12740
12765
|
}
|
|
12741
12766
|
};
|
|
@@ -12748,6 +12773,143 @@ function normalizeQueryRewriteKind(value) {
|
|
|
12748
12773
|
return "none";
|
|
12749
12774
|
}
|
|
12750
12775
|
var REWRITTEN_QUERY_REWRITE_KIND_SQL = `LOWER(TRIM(COALESCE(query_rewrite_kind, 'none'))) IN ('follow-up-context', 'intent-rewrite')`;
|
|
12776
|
+
var DEFAULT_OUTBOX_STUCK_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
12777
|
+
var DEFAULT_OUTBOX_MAX_RETRIES = 3;
|
|
12778
|
+
function emptyOutboxRecoveryResult() {
|
|
12779
|
+
return {
|
|
12780
|
+
embedding: { recoveredProcessing: 0, retriedFailed: 0 },
|
|
12781
|
+
vector: { recoveredProcessing: 0, retriedFailed: 0 }
|
|
12782
|
+
};
|
|
12783
|
+
}
|
|
12784
|
+
function isRecord(value) {
|
|
12785
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12786
|
+
}
|
|
12787
|
+
function getNestedRecord(root, path15) {
|
|
12788
|
+
let cursor = root;
|
|
12789
|
+
for (const key of path15) {
|
|
12790
|
+
if (!isRecord(cursor))
|
|
12791
|
+
return void 0;
|
|
12792
|
+
cursor = cursor[key];
|
|
12793
|
+
}
|
|
12794
|
+
return isRecord(cursor) ? cursor : void 0;
|
|
12795
|
+
}
|
|
12796
|
+
function getNestedString(root, path15) {
|
|
12797
|
+
let cursor = root;
|
|
12798
|
+
for (const key of path15) {
|
|
12799
|
+
if (!isRecord(cursor))
|
|
12800
|
+
return void 0;
|
|
12801
|
+
cursor = cursor[key];
|
|
12802
|
+
}
|
|
12803
|
+
return typeof cursor === "string" && cursor.length > 0 ? cursor : void 0;
|
|
12804
|
+
}
|
|
12805
|
+
function metadataProjectHash(metadata) {
|
|
12806
|
+
return getNestedString(metadata, ["scope", "project", "hash"]);
|
|
12807
|
+
}
|
|
12808
|
+
function metadataProjectPaths(metadata) {
|
|
12809
|
+
const candidates = [
|
|
12810
|
+
getNestedString(metadata, ["projectPath"]),
|
|
12811
|
+
getNestedString(metadata, ["sourceProjectPath"]),
|
|
12812
|
+
getNestedString(metadata, ["scope", "project", "path"])
|
|
12813
|
+
];
|
|
12814
|
+
const paths = [];
|
|
12815
|
+
for (const value of candidates) {
|
|
12816
|
+
if (value && !paths.includes(value))
|
|
12817
|
+
paths.push(value);
|
|
12818
|
+
}
|
|
12819
|
+
return paths;
|
|
12820
|
+
}
|
|
12821
|
+
function metadataProjectPath(metadata) {
|
|
12822
|
+
return metadataProjectPaths(metadata)[0];
|
|
12823
|
+
}
|
|
12824
|
+
function isActiveQuarantinedMetadata(metadata) {
|
|
12825
|
+
const quarantine = getNestedRecord(metadata, ["quarantine"]);
|
|
12826
|
+
return quarantine?.status === "active";
|
|
12827
|
+
}
|
|
12828
|
+
function activeQuarantineStatusExpression(column = "metadata") {
|
|
12829
|
+
return `COALESCE(json_extract(CASE WHEN json_valid(${column}) THEN ${column} ELSE '{}' END, '$.quarantine.status'), '')`;
|
|
12830
|
+
}
|
|
12831
|
+
function notActiveQuarantinedSql(column = "metadata") {
|
|
12832
|
+
return `${activeQuarantineStatusExpression(column)} != 'active'`;
|
|
12833
|
+
}
|
|
12834
|
+
function maybeQuarantinePredicate(options, column = "metadata") {
|
|
12835
|
+
return options?.includeQuarantined ? "1=1" : notActiveQuarantinedSql(column);
|
|
12836
|
+
}
|
|
12837
|
+
function safeParseMetadataValue(value) {
|
|
12838
|
+
if (!value)
|
|
12839
|
+
return void 0;
|
|
12840
|
+
if (typeof value === "object")
|
|
12841
|
+
return isRecord(value) ? value : void 0;
|
|
12842
|
+
if (typeof value !== "string")
|
|
12843
|
+
return void 0;
|
|
12844
|
+
try {
|
|
12845
|
+
const parsed = JSON.parse(value);
|
|
12846
|
+
return isRecord(parsed) ? parsed : void 0;
|
|
12847
|
+
} catch {
|
|
12848
|
+
return void 0;
|
|
12849
|
+
}
|
|
12850
|
+
}
|
|
12851
|
+
function isImportedOrLegacyScopedMetadata(metadata) {
|
|
12852
|
+
if (!metadata)
|
|
12853
|
+
return false;
|
|
12854
|
+
return Boolean(
|
|
12855
|
+
metadata.importedFrom || metadata.sourceSessionId || metadata.sourceSessionHash || metadata.hermesSource || metadata.projectPath || metadata.sourceProjectPath || metadata.source === "hermes" || metadata.source === "claude" || metadata.source === "codex"
|
|
12856
|
+
);
|
|
12857
|
+
}
|
|
12858
|
+
function addMetadataTag(metadata, tag) {
|
|
12859
|
+
const current = Array.isArray(metadata.tags) ? metadata.tags.filter((value) => typeof value === "string") : [];
|
|
12860
|
+
if (!current.includes(tag))
|
|
12861
|
+
metadata.tags = [...current, tag];
|
|
12862
|
+
}
|
|
12863
|
+
function buildRepairResult(projectHash, dryRun) {
|
|
12864
|
+
return {
|
|
12865
|
+
dryRun,
|
|
12866
|
+
projectHash,
|
|
12867
|
+
scanned: 0,
|
|
12868
|
+
repaired: 0,
|
|
12869
|
+
quarantined: 0,
|
|
12870
|
+
alreadyScoped: 0,
|
|
12871
|
+
skipped: 0,
|
|
12872
|
+
samples: []
|
|
12873
|
+
};
|
|
12874
|
+
}
|
|
12875
|
+
function normalizeRepoName(value) {
|
|
12876
|
+
return value.replace(/\.git$/i, "").trim().toLowerCase();
|
|
12877
|
+
}
|
|
12878
|
+
function projectBasename(projectPath) {
|
|
12879
|
+
if (!projectPath)
|
|
12880
|
+
return void 0;
|
|
12881
|
+
const trimmed = projectPath.replace(/[\\/]+$/, "");
|
|
12882
|
+
const basename4 = nodePath2.basename(trimmed);
|
|
12883
|
+
return basename4 ? normalizeRepoName(basename4) : void 0;
|
|
12884
|
+
}
|
|
12885
|
+
function isProjectScopeRepairExplanation(content) {
|
|
12886
|
+
const normalized = content.toLowerCase();
|
|
12887
|
+
const hasRepairContext = /project[- ]scope|mis[- ]scoped|quarantine|contamination|legacy|오염|격리|repair/.test(normalized);
|
|
12888
|
+
const hasExplanationContext = /example|detector|trap|not a .*project task|기억|메모리|설명|수정|검증/.test(normalized);
|
|
12889
|
+
return hasRepairContext && hasExplanationContext;
|
|
12890
|
+
}
|
|
12891
|
+
function hasConflictingContentProjectHint(content, projectPath) {
|
|
12892
|
+
const currentName = projectBasename(projectPath);
|
|
12893
|
+
if (!currentName)
|
|
12894
|
+
return false;
|
|
12895
|
+
if (isProjectScopeRepairExplanation(content))
|
|
12896
|
+
return false;
|
|
12897
|
+
const githubRepoPattern = /github\.com[:/]([^/\s`'"#)]+)\/([^/\s`'"#)]+)(?:\.git)?/gi;
|
|
12898
|
+
let githubMatch;
|
|
12899
|
+
while ((githubMatch = githubRepoPattern.exec(content)) !== null) {
|
|
12900
|
+
const repo = normalizeRepoName(githubMatch[2] || "");
|
|
12901
|
+
if (repo && repo !== currentName)
|
|
12902
|
+
return true;
|
|
12903
|
+
}
|
|
12904
|
+
const workspacePathPattern = /\/workspace\/([^/\s`'"#)]+)/gi;
|
|
12905
|
+
let workspaceMatch;
|
|
12906
|
+
while ((workspaceMatch = workspacePathPattern.exec(content)) !== null) {
|
|
12907
|
+
const repo = normalizeRepoName(workspaceMatch[1] || "");
|
|
12908
|
+
if (repo && repo !== currentName)
|
|
12909
|
+
return true;
|
|
12910
|
+
}
|
|
12911
|
+
return false;
|
|
12912
|
+
}
|
|
12751
12913
|
var SQLiteEventStore = class {
|
|
12752
12914
|
db;
|
|
12753
12915
|
initialized = false;
|
|
@@ -13246,11 +13408,11 @@ var SQLiteEventStore = class {
|
|
|
13246
13408
|
/**
|
|
13247
13409
|
* Get events by session ID
|
|
13248
13410
|
*/
|
|
13249
|
-
async getSessionEvents(sessionId) {
|
|
13411
|
+
async getSessionEvents(sessionId, options) {
|
|
13250
13412
|
await this.initialize();
|
|
13251
13413
|
const rows = sqliteAll(
|
|
13252
13414
|
this.db,
|
|
13253
|
-
`SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC`,
|
|
13415
|
+
`SELECT * FROM events WHERE session_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
|
|
13254
13416
|
[sessionId]
|
|
13255
13417
|
);
|
|
13256
13418
|
return rows.map(this.rowToEvent);
|
|
@@ -13258,11 +13420,11 @@ var SQLiteEventStore = class {
|
|
|
13258
13420
|
/**
|
|
13259
13421
|
* Get recent events
|
|
13260
13422
|
*/
|
|
13261
|
-
async getRecentEvents(limit = 100) {
|
|
13423
|
+
async getRecentEvents(limit = 100, options) {
|
|
13262
13424
|
await this.initialize();
|
|
13263
13425
|
const rows = sqliteAll(
|
|
13264
13426
|
this.db,
|
|
13265
|
-
`SELECT * FROM events ORDER BY timestamp DESC LIMIT ?`,
|
|
13427
|
+
`SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp DESC LIMIT ?`,
|
|
13266
13428
|
[limit]
|
|
13267
13429
|
);
|
|
13268
13430
|
return rows.map(this.rowToEvent);
|
|
@@ -13270,11 +13432,11 @@ var SQLiteEventStore = class {
|
|
|
13270
13432
|
/**
|
|
13271
13433
|
* Get event by ID
|
|
13272
13434
|
*/
|
|
13273
|
-
async getEvent(id) {
|
|
13435
|
+
async getEvent(id, options) {
|
|
13274
13436
|
await this.initialize();
|
|
13275
13437
|
const row = sqliteGet(
|
|
13276
13438
|
this.db,
|
|
13277
|
-
`SELECT * FROM events WHERE id =
|
|
13439
|
+
`SELECT * FROM events WHERE id = ? AND ${maybeQuarantinePredicate(options)}`,
|
|
13278
13440
|
[id]
|
|
13279
13441
|
);
|
|
13280
13442
|
if (!row)
|
|
@@ -13284,11 +13446,11 @@ var SQLiteEventStore = class {
|
|
|
13284
13446
|
/**
|
|
13285
13447
|
* Get events since a timestamp (for sync)
|
|
13286
13448
|
*/
|
|
13287
|
-
async getEventsSince(timestamp, limit = 1e3) {
|
|
13449
|
+
async getEventsSince(timestamp, limit = 1e3, options) {
|
|
13288
13450
|
await this.initialize();
|
|
13289
13451
|
const rows = sqliteAll(
|
|
13290
13452
|
this.db,
|
|
13291
|
-
`SELECT * FROM events WHERE timestamp > ? ORDER BY timestamp ASC LIMIT ?`,
|
|
13453
|
+
`SELECT * FROM events WHERE timestamp > ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ?`,
|
|
13292
13454
|
[timestamp, limit]
|
|
13293
13455
|
);
|
|
13294
13456
|
return rows.map(this.rowToEvent);
|
|
@@ -13297,11 +13459,11 @@ var SQLiteEventStore = class {
|
|
|
13297
13459
|
* Get events since a SQLite rowid (for robust incremental replication).
|
|
13298
13460
|
* Rowid is monotonic for append-only tables, independent of client timestamps.
|
|
13299
13461
|
*/
|
|
13300
|
-
async getEventsSinceRowid(lastRowid, limit = 1e3) {
|
|
13462
|
+
async getEventsSinceRowid(lastRowid, limit = 1e3, options) {
|
|
13301
13463
|
await this.initialize();
|
|
13302
13464
|
const rows = sqliteAll(
|
|
13303
13465
|
this.db,
|
|
13304
|
-
`SELECT rowid as _rowid, * FROM events WHERE rowid > ? ORDER BY rowid ASC LIMIT ?`,
|
|
13466
|
+
`SELECT rowid as _rowid, * FROM events WHERE rowid > ? AND ${maybeQuarantinePredicate(options)} ORDER BY rowid ASC LIMIT ?`,
|
|
13305
13467
|
[lastRowid, limit]
|
|
13306
13468
|
);
|
|
13307
13469
|
return rows.map((row) => ({
|
|
@@ -13498,7 +13660,9 @@ var SQLiteEventStore = class {
|
|
|
13498
13660
|
const placeholders = ids.map(() => "?").join(",");
|
|
13499
13661
|
sqliteRun(
|
|
13500
13662
|
this.db,
|
|
13501
|
-
`UPDATE embedding_outbox
|
|
13663
|
+
`UPDATE embedding_outbox
|
|
13664
|
+
SET status = 'processing', processed_at = datetime('now'), error_message = NULL
|
|
13665
|
+
WHERE id IN (${placeholders})`,
|
|
13502
13666
|
ids
|
|
13503
13667
|
);
|
|
13504
13668
|
return pending.map((row) => ({
|
|
@@ -13534,19 +13698,19 @@ var SQLiteEventStore = class {
|
|
|
13534
13698
|
/**
|
|
13535
13699
|
* Count total events
|
|
13536
13700
|
*/
|
|
13537
|
-
async countEvents() {
|
|
13701
|
+
async countEvents(options) {
|
|
13538
13702
|
await this.initialize();
|
|
13539
|
-
const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events`);
|
|
13703
|
+
const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events WHERE ${maybeQuarantinePredicate(options)}`);
|
|
13540
13704
|
return row?.count || 0;
|
|
13541
13705
|
}
|
|
13542
13706
|
/**
|
|
13543
13707
|
* Get events page in timestamp ascending order (stable migration/reindex scans)
|
|
13544
13708
|
*/
|
|
13545
|
-
async getEventsPage(limit = 1e3, offset = 0) {
|
|
13709
|
+
async getEventsPage(limit = 1e3, offset = 0, options) {
|
|
13546
13710
|
await this.initialize();
|
|
13547
13711
|
const rows = sqliteAll(
|
|
13548
13712
|
this.db,
|
|
13549
|
-
`SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
13713
|
+
`SELECT * FROM events WHERE ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
13550
13714
|
[limit, offset]
|
|
13551
13715
|
);
|
|
13552
13716
|
return rows.map(this.rowToEvent);
|
|
@@ -13568,6 +13732,197 @@ var SQLiteEventStore = class {
|
|
|
13568
13732
|
[error, ...ids]
|
|
13569
13733
|
);
|
|
13570
13734
|
}
|
|
13735
|
+
/**
|
|
13736
|
+
* Recover abandoned outbox work after a worker/process crash.
|
|
13737
|
+
*
|
|
13738
|
+
* Rows in `processing` are claimed work. If the process exits before marking
|
|
13739
|
+
* them done/failed, they otherwise remain invisible to future processing.
|
|
13740
|
+
* Recovery is deliberately age-gated so an active worker is not disturbed.
|
|
13741
|
+
*/
|
|
13742
|
+
async recoverStuckOutboxItems(options = {}) {
|
|
13743
|
+
await this.initialize();
|
|
13744
|
+
const thresholdMs = Number.isFinite(options.stuckThresholdMs) && (options.stuckThresholdMs ?? 0) >= 0 ? options.stuckThresholdMs : DEFAULT_OUTBOX_STUCK_THRESHOLD_MS;
|
|
13745
|
+
const maxRetries = Number.isFinite(options.maxRetries) && (options.maxRetries ?? 0) > 0 ? options.maxRetries : DEFAULT_OUTBOX_MAX_RETRIES;
|
|
13746
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
13747
|
+
const threshold = new Date(now.getTime() - thresholdMs).toISOString();
|
|
13748
|
+
const result = emptyOutboxRecoveryResult();
|
|
13749
|
+
const embeddingRecovered = sqliteRun(
|
|
13750
|
+
this.db,
|
|
13751
|
+
`UPDATE embedding_outbox
|
|
13752
|
+
SET status = 'pending', processed_at = NULL, error_message = NULL
|
|
13753
|
+
WHERE status = 'processing'
|
|
13754
|
+
AND datetime(COALESCE(processed_at, created_at)) < datetime(?)`,
|
|
13755
|
+
[threshold]
|
|
13756
|
+
);
|
|
13757
|
+
result.embedding.recoveredProcessing = Number(embeddingRecovered.changes ?? 0);
|
|
13758
|
+
const embeddingRetried = sqliteRun(
|
|
13759
|
+
this.db,
|
|
13760
|
+
`UPDATE embedding_outbox
|
|
13761
|
+
SET status = 'pending', error_message = NULL
|
|
13762
|
+
WHERE status = 'failed'
|
|
13763
|
+
AND retry_count < ?`,
|
|
13764
|
+
[maxRetries]
|
|
13765
|
+
);
|
|
13766
|
+
result.embedding.retriedFailed = Number(embeddingRetried.changes ?? 0);
|
|
13767
|
+
const vectorRecovered = sqliteRun(
|
|
13768
|
+
this.db,
|
|
13769
|
+
`UPDATE vector_outbox
|
|
13770
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
13771
|
+
WHERE status = 'processing'
|
|
13772
|
+
AND datetime(updated_at) < datetime(?)`,
|
|
13773
|
+
[now.toISOString(), threshold]
|
|
13774
|
+
);
|
|
13775
|
+
result.vector.recoveredProcessing = Number(vectorRecovered.changes ?? 0);
|
|
13776
|
+
const vectorRetried = sqliteRun(
|
|
13777
|
+
this.db,
|
|
13778
|
+
`UPDATE vector_outbox
|
|
13779
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
13780
|
+
WHERE status = 'failed'
|
|
13781
|
+
AND retry_count < ?`,
|
|
13782
|
+
[now.toISOString(), maxRetries]
|
|
13783
|
+
);
|
|
13784
|
+
result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
|
|
13785
|
+
return result;
|
|
13786
|
+
}
|
|
13787
|
+
/**
|
|
13788
|
+
* Repair legacy imported events that predate canonical project scope metadata.
|
|
13789
|
+
*
|
|
13790
|
+
* Same-project legacy rows are tagged with scope.project.hash. Rows that look
|
|
13791
|
+
* imported but cannot be proven to belong to this project are quarantined so
|
|
13792
|
+
* dashboard default reads/search do not surface cross-project contamination.
|
|
13793
|
+
*/
|
|
13794
|
+
async repairLegacyProjectScope(options = {}) {
|
|
13795
|
+
await this.initialize();
|
|
13796
|
+
const projectHash = options.projectHash || (options.projectPath ? hashProjectPath(options.projectPath) : void 0);
|
|
13797
|
+
if (!projectHash) {
|
|
13798
|
+
throw new Error("repairLegacyProjectScope requires projectPath or projectHash");
|
|
13799
|
+
}
|
|
13800
|
+
if (options.projectPath && options.projectHash && hashProjectPath(options.projectPath) !== options.projectHash) {
|
|
13801
|
+
throw new Error("repairLegacyProjectScope projectPath and projectHash refer to different project stores");
|
|
13802
|
+
}
|
|
13803
|
+
const dryRun = options.dryRun === true;
|
|
13804
|
+
const nowIso = (options.now || /* @__PURE__ */ new Date()).toISOString();
|
|
13805
|
+
const result = buildRepairResult(projectHash, dryRun);
|
|
13806
|
+
const rows = sqliteAll(
|
|
13807
|
+
this.db,
|
|
13808
|
+
`SELECT e.id, e.content, e.metadata, s.project_path as session_project_path
|
|
13809
|
+
FROM events e
|
|
13810
|
+
LEFT JOIN sessions s ON s.id = e.session_id
|
|
13811
|
+
ORDER BY e.timestamp ASC`,
|
|
13812
|
+
[]
|
|
13813
|
+
);
|
|
13814
|
+
const sample = (entry) => {
|
|
13815
|
+
if (result.samples.length < 20)
|
|
13816
|
+
result.samples.push(entry);
|
|
13817
|
+
};
|
|
13818
|
+
for (const row of rows) {
|
|
13819
|
+
result.scanned++;
|
|
13820
|
+
let metadata = {};
|
|
13821
|
+
let metadataParseInvalid = false;
|
|
13822
|
+
if (row.metadata) {
|
|
13823
|
+
const parsed = safeParseMetadataValue(row.metadata);
|
|
13824
|
+
if (parsed) {
|
|
13825
|
+
metadata = parsed;
|
|
13826
|
+
} else {
|
|
13827
|
+
metadataParseInvalid = true;
|
|
13828
|
+
}
|
|
13829
|
+
}
|
|
13830
|
+
if (isActiveQuarantinedMetadata(metadata)) {
|
|
13831
|
+
result.skipped++;
|
|
13832
|
+
continue;
|
|
13833
|
+
}
|
|
13834
|
+
const currentHash = metadataProjectHash(metadata);
|
|
13835
|
+
const explicitPath = metadataProjectPath(metadata);
|
|
13836
|
+
const sessionProjectPath = typeof row.session_project_path === "string" && row.session_project_path.length > 0 ? row.session_project_path : void 0;
|
|
13837
|
+
const candidatePaths = metadataProjectPaths(metadata);
|
|
13838
|
+
if (sessionProjectPath && !candidatePaths.includes(sessionProjectPath)) {
|
|
13839
|
+
candidatePaths.push(sessionProjectPath);
|
|
13840
|
+
}
|
|
13841
|
+
const importedOrLegacy = metadataParseInvalid || isImportedOrLegacyScopedMetadata(metadata) || Boolean(sessionProjectPath);
|
|
13842
|
+
const pathHashes = candidatePaths.map((candidate) => {
|
|
13843
|
+
try {
|
|
13844
|
+
return { path: candidate, hash: hashProjectPath(candidate) };
|
|
13845
|
+
} catch {
|
|
13846
|
+
return { path: candidate, hash: void 0 };
|
|
13847
|
+
}
|
|
13848
|
+
});
|
|
13849
|
+
const matchingPath = pathHashes.find((candidate) => candidate.hash === projectHash);
|
|
13850
|
+
const foreignPath = pathHashes.find((candidate) => candidate.hash && candidate.hash !== projectHash);
|
|
13851
|
+
let action = "skipped";
|
|
13852
|
+
let reason;
|
|
13853
|
+
let observedProjectHash;
|
|
13854
|
+
if (foreignPath) {
|
|
13855
|
+
action = "quarantined";
|
|
13856
|
+
reason = "project-path-mismatch";
|
|
13857
|
+
observedProjectHash = foreignPath.hash;
|
|
13858
|
+
} else if (currentHash === projectHash && importedOrLegacy && hasConflictingContentProjectHint(row.content, options.projectPath)) {
|
|
13859
|
+
action = "quarantined";
|
|
13860
|
+
reason = "content-project-mismatch";
|
|
13861
|
+
} else if (currentHash === projectHash) {
|
|
13862
|
+
result.alreadyScoped++;
|
|
13863
|
+
continue;
|
|
13864
|
+
} else if (currentHash && currentHash !== projectHash) {
|
|
13865
|
+
action = "quarantined";
|
|
13866
|
+
reason = "scope-hash-mismatch";
|
|
13867
|
+
observedProjectHash = currentHash;
|
|
13868
|
+
} else if (matchingPath) {
|
|
13869
|
+
action = "repaired";
|
|
13870
|
+
reason = matchingPath.path === sessionProjectPath && matchingPath.path !== explicitPath ? "session-project-path" : "same-project-path";
|
|
13871
|
+
} else if (candidatePaths.length > 0) {
|
|
13872
|
+
action = "quarantined";
|
|
13873
|
+
reason = "project-path-mismatch";
|
|
13874
|
+
} else if (importedOrLegacy) {
|
|
13875
|
+
action = "quarantined";
|
|
13876
|
+
reason = "missing-project-scope";
|
|
13877
|
+
}
|
|
13878
|
+
if (action === "skipped" || !reason) {
|
|
13879
|
+
result.skipped++;
|
|
13880
|
+
continue;
|
|
13881
|
+
}
|
|
13882
|
+
if (action === "repaired") {
|
|
13883
|
+
const scope = isRecord(metadata.scope) ? { ...metadata.scope } : {};
|
|
13884
|
+
const project = isRecord(scope.project) ? { ...scope.project } : {};
|
|
13885
|
+
project.hash = projectHash;
|
|
13886
|
+
scope.project = project;
|
|
13887
|
+
metadata.scope = scope;
|
|
13888
|
+
metadata.repair = {
|
|
13889
|
+
...isRecord(metadata.repair) ? metadata.repair : {},
|
|
13890
|
+
legacyProjectScope: {
|
|
13891
|
+
action,
|
|
13892
|
+
reason,
|
|
13893
|
+
repairedAt: nowIso
|
|
13894
|
+
}
|
|
13895
|
+
};
|
|
13896
|
+
addMetadataTag(metadata, `proj:${projectHash}`);
|
|
13897
|
+
result.repaired++;
|
|
13898
|
+
} else {
|
|
13899
|
+
metadata.quarantine = {
|
|
13900
|
+
...isRecord(metadata.quarantine) ? metadata.quarantine : {},
|
|
13901
|
+
status: "active",
|
|
13902
|
+
category: "project-scope",
|
|
13903
|
+
reason,
|
|
13904
|
+
detectedAt: nowIso,
|
|
13905
|
+
expectedProjectHash: projectHash,
|
|
13906
|
+
...observedProjectHash ? { observedProjectHash } : {}
|
|
13907
|
+
};
|
|
13908
|
+
metadata.repair = {
|
|
13909
|
+
...isRecord(metadata.repair) ? metadata.repair : {},
|
|
13910
|
+
legacyProjectScope: {
|
|
13911
|
+
action,
|
|
13912
|
+
reason,
|
|
13913
|
+
repairedAt: nowIso
|
|
13914
|
+
}
|
|
13915
|
+
};
|
|
13916
|
+
addMetadataTag(metadata, "quarantine:project-scope");
|
|
13917
|
+
result.quarantined++;
|
|
13918
|
+
}
|
|
13919
|
+
sample({ eventId: row.id, action, reason });
|
|
13920
|
+
if (!dryRun) {
|
|
13921
|
+
sqliteRun(this.db, `UPDATE events SET metadata = ? WHERE id = ?`, [JSON.stringify(metadata), row.id]);
|
|
13922
|
+
}
|
|
13923
|
+
}
|
|
13924
|
+
return result;
|
|
13925
|
+
}
|
|
13571
13926
|
/**
|
|
13572
13927
|
* Get embedding/vector outbox health statistics
|
|
13573
13928
|
*/
|
|
@@ -13615,7 +13970,11 @@ var SQLiteEventStore = class {
|
|
|
13615
13970
|
await this.initialize();
|
|
13616
13971
|
const rows = sqliteAll(
|
|
13617
13972
|
this.db,
|
|
13618
|
-
`SELECT level, COUNT(*) as count
|
|
13973
|
+
`SELECT ml.level, COUNT(*) as count
|
|
13974
|
+
FROM memory_levels ml
|
|
13975
|
+
INNER JOIN events e ON e.id = ml.event_id
|
|
13976
|
+
WHERE ${notActiveQuarantinedSql("e.metadata")}
|
|
13977
|
+
GROUP BY ml.level`
|
|
13619
13978
|
);
|
|
13620
13979
|
return rows;
|
|
13621
13980
|
}
|
|
@@ -13631,6 +13990,7 @@ var SQLiteEventStore = class {
|
|
|
13631
13990
|
`SELECT e.* FROM events e
|
|
13632
13991
|
INNER JOIN memory_levels ml ON e.id = ml.event_id
|
|
13633
13992
|
WHERE ml.level = ?
|
|
13993
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
13634
13994
|
ORDER BY e.timestamp DESC
|
|
13635
13995
|
LIMIT ? OFFSET ?`,
|
|
13636
13996
|
[level, limit, offset]
|
|
@@ -13723,12 +14083,13 @@ var SQLiteEventStore = class {
|
|
|
13723
14083
|
/**
|
|
13724
14084
|
* Get most accessed memories (falls back to recent events if none accessed)
|
|
13725
14085
|
*/
|
|
13726
|
-
async getMostAccessed(limit = 10) {
|
|
14086
|
+
async getMostAccessed(limit = 10, options) {
|
|
13727
14087
|
await this.initialize();
|
|
13728
14088
|
let rows = sqliteAll(
|
|
13729
14089
|
this.db,
|
|
13730
14090
|
`SELECT * FROM events
|
|
13731
14091
|
WHERE access_count > 0
|
|
14092
|
+
AND ${maybeQuarantinePredicate(options)}
|
|
13732
14093
|
ORDER BY access_count DESC, last_accessed_at DESC
|
|
13733
14094
|
LIMIT ?`,
|
|
13734
14095
|
[limit]
|
|
@@ -13737,6 +14098,7 @@ var SQLiteEventStore = class {
|
|
|
13737
14098
|
rows = sqliteAll(
|
|
13738
14099
|
this.db,
|
|
13739
14100
|
`SELECT * FROM events
|
|
14101
|
+
WHERE ${maybeQuarantinePredicate(options)}
|
|
13740
14102
|
ORDER BY timestamp DESC
|
|
13741
14103
|
LIMIT ?`,
|
|
13742
14104
|
[limit]
|
|
@@ -13867,6 +14229,7 @@ var SQLiteEventStore = class {
|
|
|
13867
14229
|
FROM memory_helpfulness mh
|
|
13868
14230
|
JOIN events e ON e.id = mh.event_id
|
|
13869
14231
|
WHERE mh.measured_at IS NOT NULL
|
|
14232
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
13870
14233
|
GROUP BY mh.event_id
|
|
13871
14234
|
ORDER BY avg_score DESC
|
|
13872
14235
|
LIMIT ?`,
|
|
@@ -13931,6 +14294,7 @@ var SQLiteEventStore = class {
|
|
|
13931
14294
|
FROM events_fts fts
|
|
13932
14295
|
JOIN events e ON e.id = fts.event_id
|
|
13933
14296
|
WHERE events_fts MATCH ?
|
|
14297
|
+
AND ${notActiveQuarantinedSql("e.metadata")}
|
|
13934
14298
|
ORDER BY fts.rank
|
|
13935
14299
|
LIMIT ?`,
|
|
13936
14300
|
[searchTerms, limit]
|
|
@@ -13945,6 +14309,7 @@ var SQLiteEventStore = class {
|
|
|
13945
14309
|
this.db,
|
|
13946
14310
|
`SELECT *, 0 as rank FROM events
|
|
13947
14311
|
WHERE content LIKE ?
|
|
14312
|
+
AND ${notActiveQuarantinedSql()}
|
|
13948
14313
|
ORDER BY timestamp DESC
|
|
13949
14314
|
LIMIT ?`,
|
|
13950
14315
|
[likePattern, limit]
|
|
@@ -14151,6 +14516,7 @@ var SQLiteEventStore = class {
|
|
|
14151
14516
|
`SELECT turn_id, MIN(timestamp) as min_ts
|
|
14152
14517
|
FROM events
|
|
14153
14518
|
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
14519
|
+
AND ${maybeQuarantinePredicate(options)}
|
|
14154
14520
|
GROUP BY turn_id
|
|
14155
14521
|
ORDER BY min_ts DESC
|
|
14156
14522
|
LIMIT ? OFFSET ?`,
|
|
@@ -14158,7 +14524,7 @@ var SQLiteEventStore = class {
|
|
|
14158
14524
|
);
|
|
14159
14525
|
const turns = [];
|
|
14160
14526
|
for (const turnRow of turnRows) {
|
|
14161
|
-
const events = await this.getEventsByTurn(turnRow.turn_id);
|
|
14527
|
+
const events = await this.getEventsByTurn(turnRow.turn_id, options);
|
|
14162
14528
|
const promptEvent = events.find((e) => e.eventType === "user_prompt");
|
|
14163
14529
|
const toolEvents = events.filter((e) => e.eventType === "tool_observation");
|
|
14164
14530
|
const hasResponse = events.some((e) => e.eventType === "agent_response");
|
|
@@ -14177,11 +14543,11 @@ var SQLiteEventStore = class {
|
|
|
14177
14543
|
/**
|
|
14178
14544
|
* Get all events for a specific turn_id
|
|
14179
14545
|
*/
|
|
14180
|
-
async getEventsByTurn(turnId) {
|
|
14546
|
+
async getEventsByTurn(turnId, options) {
|
|
14181
14547
|
await this.initialize();
|
|
14182
14548
|
const rows = sqliteAll(
|
|
14183
14549
|
this.db,
|
|
14184
|
-
`SELECT * FROM events WHERE turn_id = ? ORDER BY timestamp ASC`,
|
|
14550
|
+
`SELECT * FROM events WHERE turn_id = ? AND ${maybeQuarantinePredicate(options)} ORDER BY timestamp ASC`,
|
|
14185
14551
|
[turnId]
|
|
14186
14552
|
);
|
|
14187
14553
|
return rows.map(this.rowToEvent);
|
|
@@ -14189,13 +14555,14 @@ var SQLiteEventStore = class {
|
|
|
14189
14555
|
/**
|
|
14190
14556
|
* Count total turns for a session
|
|
14191
14557
|
*/
|
|
14192
|
-
async countSessionTurns(sessionId) {
|
|
14558
|
+
async countSessionTurns(sessionId, options) {
|
|
14193
14559
|
await this.initialize();
|
|
14194
14560
|
const row = sqliteGet(
|
|
14195
14561
|
this.db,
|
|
14196
14562
|
`SELECT COUNT(DISTINCT turn_id) as count
|
|
14197
14563
|
FROM events
|
|
14198
|
-
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
14564
|
+
WHERE session_id = ? AND turn_id IS NOT NULL
|
|
14565
|
+
AND ${maybeQuarantinePredicate(options)}`,
|
|
14199
14566
|
[sessionId]
|
|
14200
14567
|
);
|
|
14201
14568
|
return row?.count || 0;
|
|
@@ -14287,7 +14654,7 @@ var SQLiteEventStore = class {
|
|
|
14287
14654
|
content: row.content,
|
|
14288
14655
|
canonicalKey: row.canonical_key,
|
|
14289
14656
|
dedupeKey: row.dedupe_key,
|
|
14290
|
-
metadata:
|
|
14657
|
+
metadata: safeParseMetadataValue(row.metadata)
|
|
14291
14658
|
};
|
|
14292
14659
|
if (row.access_count !== void 0) {
|
|
14293
14660
|
event.access_count = row.access_count;
|
|
@@ -14439,6 +14806,7 @@ var VectorStore = class {
|
|
|
14439
14806
|
* Get total count of vectors
|
|
14440
14807
|
*/
|
|
14441
14808
|
async count() {
|
|
14809
|
+
await this.initialize();
|
|
14442
14810
|
if (!this.table)
|
|
14443
14811
|
return 0;
|
|
14444
14812
|
const result = await this.table.countRows();
|
|
@@ -14877,6 +15245,14 @@ var MemoryQueryService = class {
|
|
|
14877
15245
|
await this.initialize();
|
|
14878
15246
|
return this.getMaintenanceStore("getOutboxStats").getOutboxStats();
|
|
14879
15247
|
}
|
|
15248
|
+
async recoverStuckOutboxItems(options) {
|
|
15249
|
+
await this.initialize();
|
|
15250
|
+
return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
|
|
15251
|
+
}
|
|
15252
|
+
async repairLegacyProjectScope(options) {
|
|
15253
|
+
await this.initialize();
|
|
15254
|
+
return this.getMaintenanceStore("repairLegacyProjectScope").repairLegacyProjectScope(options);
|
|
15255
|
+
}
|
|
14880
15256
|
async getStats() {
|
|
14881
15257
|
await this.initialize();
|
|
14882
15258
|
const deps = this.getStatsDeps();
|
|
@@ -16528,18 +16904,18 @@ function assertDefaultRetrieverStore(eventStore) {
|
|
|
16528
16904
|
function createMemoryEngineServices(options) {
|
|
16529
16905
|
const factories = options.factories ?? {};
|
|
16530
16906
|
const storagePath = options.storagePath;
|
|
16531
|
-
if (!options.readOnly && !
|
|
16532
|
-
|
|
16907
|
+
if (!options.readOnly && !fs6.existsSync(storagePath)) {
|
|
16908
|
+
fs6.mkdirSync(storagePath, { recursive: true });
|
|
16533
16909
|
}
|
|
16534
16910
|
const sqliteStore = (factories.createSQLiteEventStore ?? defaultCreateSQLiteEventStore)(
|
|
16535
|
-
|
|
16911
|
+
path5.join(storagePath, "events.sqlite"),
|
|
16536
16912
|
{
|
|
16537
16913
|
readonly: options.readOnly,
|
|
16538
16914
|
markdownMirrorRoot: storagePath
|
|
16539
16915
|
}
|
|
16540
16916
|
);
|
|
16541
16917
|
const vectorStore = (factories.createVectorStore ?? defaultCreateVectorStore)(
|
|
16542
|
-
|
|
16918
|
+
path5.join(storagePath, "vectors")
|
|
16543
16919
|
);
|
|
16544
16920
|
const embeddingModel = options.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
16545
16921
|
const embedder = embeddingModel ? (factories.createEmbedder ?? defaultCreateEmbedder)(embeddingModel) : (factories.getDefaultEmbedder ?? getDefaultEmbedder)();
|
|
@@ -16950,8 +17326,8 @@ function createMemoryRuntimeService(deps) {
|
|
|
16950
17326
|
}
|
|
16951
17327
|
|
|
16952
17328
|
// src/extensions/shared-memory/shared-memory-services.ts
|
|
16953
|
-
import * as
|
|
16954
|
-
import * as
|
|
17329
|
+
import * as fs7 from "fs";
|
|
17330
|
+
import * as path6 from "path";
|
|
16955
17331
|
|
|
16956
17332
|
// src/core/shared-event-store.ts
|
|
16957
17333
|
var SharedEventStore = class {
|
|
@@ -17639,7 +18015,7 @@ var SharedMemoryServices = class {
|
|
|
17639
18015
|
this.ensureDirectory(sharedPath, { allowCreate: true });
|
|
17640
18016
|
const store = await this.openStore(sharedPath);
|
|
17641
18017
|
this.sharedVectorStore = this.factories.createSharedVectorStore(
|
|
17642
|
-
|
|
18018
|
+
path6.join(sharedPath, "vectors")
|
|
17643
18019
|
);
|
|
17644
18020
|
await this.sharedVectorStore.initialize();
|
|
17645
18021
|
this.sharedPromoter = this.factories.createSharedPromoter(
|
|
@@ -17712,7 +18088,7 @@ var SharedMemoryServices = class {
|
|
|
17712
18088
|
async createOpenStorePromise(sharedPath) {
|
|
17713
18089
|
if (!this.sharedEventStore) {
|
|
17714
18090
|
const sharedEventStore = this.factories.createSharedEventStore(
|
|
17715
|
-
|
|
18091
|
+
path6.join(sharedPath, "shared.duckdb")
|
|
17716
18092
|
);
|
|
17717
18093
|
await sharedEventStore.initialize();
|
|
17718
18094
|
this.sharedEventStore = sharedEventStore;
|
|
@@ -17732,9 +18108,9 @@ var SharedMemoryServices = class {
|
|
|
17732
18108
|
}
|
|
17733
18109
|
get factories() {
|
|
17734
18110
|
return {
|
|
17735
|
-
existsSync: this.options.factories?.existsSync ??
|
|
18111
|
+
existsSync: this.options.factories?.existsSync ?? fs7.existsSync,
|
|
17736
18112
|
mkdirSync: this.options.factories?.mkdirSync ?? ((targetPath) => {
|
|
17737
|
-
|
|
18113
|
+
fs7.mkdirSync(targetPath, { recursive: true });
|
|
17738
18114
|
}),
|
|
17739
18115
|
createSharedEventStore: this.options.factories?.createSharedEventStore ?? createSharedEventStore,
|
|
17740
18116
|
createSharedStore: this.options.factories?.createSharedStore ?? createSharedStore,
|
|
@@ -17849,33 +18225,11 @@ function createMemoryServiceComposition(options) {
|
|
|
17849
18225
|
}
|
|
17850
18226
|
function defaultExpandPath(targetPath) {
|
|
17851
18227
|
if (targetPath.startsWith("~")) {
|
|
17852
|
-
return
|
|
18228
|
+
return path7.join(os2.homedir(), targetPath.slice(1));
|
|
17853
18229
|
}
|
|
17854
18230
|
return targetPath;
|
|
17855
18231
|
}
|
|
17856
18232
|
|
|
17857
|
-
// src/core/registry/project-path.ts
|
|
17858
|
-
import * as crypto2 from "crypto";
|
|
17859
|
-
import * as fs7 from "fs";
|
|
17860
|
-
import * as os2 from "os";
|
|
17861
|
-
import * as path7 from "path";
|
|
17862
|
-
function normalizeProjectPath(projectPath) {
|
|
17863
|
-
const expanded = projectPath.startsWith("~") ? path7.join(os2.homedir(), projectPath.slice(1)) : projectPath;
|
|
17864
|
-
try {
|
|
17865
|
-
return fs7.realpathSync(expanded);
|
|
17866
|
-
} catch {
|
|
17867
|
-
return path7.resolve(expanded);
|
|
17868
|
-
}
|
|
17869
|
-
}
|
|
17870
|
-
function hashProjectPath(projectPath) {
|
|
17871
|
-
const normalizedPath = normalizeProjectPath(projectPath);
|
|
17872
|
-
return crypto2.createHash("sha256").update(normalizedPath).digest("hex").slice(0, 8);
|
|
17873
|
-
}
|
|
17874
|
-
function getProjectStoragePath(projectPath) {
|
|
17875
|
-
const hash = hashProjectPath(projectPath);
|
|
17876
|
-
return path7.join(os2.homedir(), ".claude-code", "memory", "projects", hash);
|
|
17877
|
-
}
|
|
17878
|
-
|
|
17879
18233
|
// src/core/registry/session-registry.ts
|
|
17880
18234
|
import * as fs8 from "fs";
|
|
17881
18235
|
import * as os3 from "os";
|
|
@@ -18199,6 +18553,12 @@ var MemoryService = class {
|
|
|
18199
18553
|
async getOutboxStats() {
|
|
18200
18554
|
return this.queryService.getOutboxStats();
|
|
18201
18555
|
}
|
|
18556
|
+
async recoverStuckOutboxItems(options) {
|
|
18557
|
+
return this.queryService.recoverStuckOutboxItems(options);
|
|
18558
|
+
}
|
|
18559
|
+
async repairLegacyProjectScope(options) {
|
|
18560
|
+
return this.queryService.repairLegacyProjectScope(options);
|
|
18561
|
+
}
|
|
18202
18562
|
async getRetrievalTraceStats() {
|
|
18203
18563
|
return this.retrievalAnalyticsService.getRetrievalTraceStats();
|
|
18204
18564
|
}
|
|
@@ -18928,7 +19288,7 @@ import * as os7 from "os";
|
|
|
18928
19288
|
import * as readline2 from "readline";
|
|
18929
19289
|
import { createHash as createHash3, randomUUID as randomUUID9 } from "crypto";
|
|
18930
19290
|
var CODEX_VALIDATION_DEFAULT_MAX_CONTENT_CHARS = 1e4;
|
|
18931
|
-
function
|
|
19291
|
+
function isRecord2(value) {
|
|
18932
19292
|
return typeof value === "object" && value !== null;
|
|
18933
19293
|
}
|
|
18934
19294
|
function normalizeMaybeRealpath(p) {
|
|
@@ -18945,7 +19305,7 @@ function extractCodexContentText(content, maxContentChars = CODEX_VALIDATION_DEF
|
|
|
18945
19305
|
texts.push(content);
|
|
18946
19306
|
} else if (Array.isArray(content)) {
|
|
18947
19307
|
for (const block of content) {
|
|
18948
|
-
if (!
|
|
19308
|
+
if (!isRecord2(block))
|
|
18949
19309
|
continue;
|
|
18950
19310
|
const b = block;
|
|
18951
19311
|
const t = typeof b.type === "string" ? b.type : "";
|
|
@@ -19040,7 +19400,7 @@ var CodexSessionHistoryImporter = class {
|
|
|
19040
19400
|
const obj = JSON.parse(line);
|
|
19041
19401
|
if (obj.type !== "session_meta")
|
|
19042
19402
|
continue;
|
|
19043
|
-
if (!
|
|
19403
|
+
if (!isRecord2(obj.payload))
|
|
19044
19404
|
break;
|
|
19045
19405
|
const payload = obj.payload;
|
|
19046
19406
|
const sessionId = typeof payload.id === "string" ? payload.id : null;
|
|
@@ -19256,7 +19616,7 @@ var CodexSessionHistoryImporter = class {
|
|
|
19256
19616
|
try {
|
|
19257
19617
|
const entry = JSON.parse(line);
|
|
19258
19618
|
result.totalMessages++;
|
|
19259
|
-
if (entry.type === "response_item" &&
|
|
19619
|
+
if (entry.type === "response_item" && isRecord2(entry.payload)) {
|
|
19260
19620
|
const payload = entry.payload;
|
|
19261
19621
|
if (payload.type !== "message")
|
|
19262
19622
|
continue;
|
|
@@ -19448,10 +19808,10 @@ var SENSITIVE_PATTERNS = [
|
|
|
19448
19808
|
// Redact the whole URI so usernames, credentials, hosts, paths, and query
|
|
19449
19809
|
// params do not leak either.
|
|
19450
19810
|
/\b[a-z][a-z0-9+.-]*:\/\/[^\s'"`<>/@]+@[^\s'"`<>]+/gi,
|
|
19451
|
-
/password\s*[:=]\s*['"]?[^\s'"]+/gi,
|
|
19452
|
-
/api[_
|
|
19453
|
-
/secret\s*[:=]\s*['"]?[^\s'"]+/gi,
|
|
19454
|
-
/token\s*[:=]\s*['"]?[^\s'"]+/gi,
|
|
19811
|
+
/(?:[\w.-]+[-_])?password\s*[:=]\s*(?!\[REDACTED\])['"]?[^\s'"]+/gi,
|
|
19812
|
+
/(?:[\w.-]+[-_])?api[-_]?key\s*[:=]\s*(?!\[REDACTED\])['"]?[^\s'"]+/gi,
|
|
19813
|
+
/(?:[\w.-]+[-_])?secret\s*[:=]\s*(?!\[REDACTED\])['"]?[^\s'"]+/gi,
|
|
19814
|
+
/(?:[\w.-]+[-_])?token\s*[:=]\s*(?!\[REDACTED\])['"]?[^\s'"]+/gi,
|
|
19455
19815
|
/bearer\s+[a-zA-Z0-9\-_.]+/gi,
|
|
19456
19816
|
/AWS[_-]?ACCESS[_-]?KEY[_-]?ID\s*[:=]\s*['"]?[A-Z0-9]+/gi,
|
|
19457
19817
|
/AWS[_-]?SECRET[_-]?ACCESS[_-]?KEY\s*[:=]\s*['"]?[^\s'"]+/gi,
|
|
@@ -19461,8 +19821,37 @@ var SENSITIVE_PATTERNS = [
|
|
|
19461
19821
|
/sk-[a-zA-Z0-9]{48}/g
|
|
19462
19822
|
// OpenAI API Key
|
|
19463
19823
|
];
|
|
19464
|
-
|
|
19824
|
+
var CLI_SECRET_OPTION_PATTERNS = [
|
|
19825
|
+
/(--(?:[a-z0-9]+[-_])*(?:password|secret|api[-_]?key|token|bearer)(?:[-_][a-z0-9]+)*(?:\s+|=))(?:"[^"]*"|'[^']*'|[^\s'"`<>]+)/gi
|
|
19826
|
+
];
|
|
19827
|
+
var URL_FOLLOWING_SECRET_PATTERN = /((?:https?:\/\/[^\s'"`<>]+)\s*\r?\n\s*)([A-Za-z0-9!@#$%^&*._+\-~:=/]{6,})(?=\s*(?:\r?\n|$))/gi;
|
|
19828
|
+
function looksLikePastedSecret(value) {
|
|
19829
|
+
return value.length >= 8 && /(?:\d|[^A-Za-z0-9])/.test(value);
|
|
19830
|
+
}
|
|
19831
|
+
function maskUrlFollowingSecret(value) {
|
|
19832
|
+
let count = 0;
|
|
19833
|
+
const content = value.replace(URL_FOLLOWING_SECRET_PATTERN, (_match, prefix, secret) => {
|
|
19834
|
+
if (!looksLikePastedSecret(secret))
|
|
19835
|
+
return `${prefix}${secret}`;
|
|
19836
|
+
count++;
|
|
19837
|
+
return `${prefix}[REDACTED]`;
|
|
19838
|
+
});
|
|
19839
|
+
return { content, count };
|
|
19840
|
+
}
|
|
19841
|
+
function maskCliSecretOptions(value) {
|
|
19842
|
+
let count = 0;
|
|
19465
19843
|
let filtered = value;
|
|
19844
|
+
for (const pattern of CLI_SECRET_OPTION_PATTERNS) {
|
|
19845
|
+
pattern.lastIndex = 0;
|
|
19846
|
+
filtered = filtered.replace(pattern, (_match, prefix) => {
|
|
19847
|
+
count++;
|
|
19848
|
+
return `${prefix}[REDACTED]`;
|
|
19849
|
+
});
|
|
19850
|
+
}
|
|
19851
|
+
return { content: filtered, count };
|
|
19852
|
+
}
|
|
19853
|
+
function maskSensitiveString(value) {
|
|
19854
|
+
let filtered = maskCliSecretOptions(value).content;
|
|
19466
19855
|
for (const pattern of SENSITIVE_PATTERNS) {
|
|
19467
19856
|
pattern.lastIndex = 0;
|
|
19468
19857
|
filtered = filtered.replace(pattern, "[REDACTED]");
|
|
@@ -19482,6 +19871,12 @@ function applyPrivacyFilter(content, config) {
|
|
|
19482
19871
|
filtered = tagResult.filtered;
|
|
19483
19872
|
privateTagCount = tagResult.stats.count;
|
|
19484
19873
|
}
|
|
19874
|
+
const cliResult = maskCliSecretOptions(filtered);
|
|
19875
|
+
filtered = cliResult.content;
|
|
19876
|
+
patternMatchCount += cliResult.count;
|
|
19877
|
+
const urlSecretResult = maskUrlFollowingSecret(filtered);
|
|
19878
|
+
filtered = urlSecretResult.content;
|
|
19879
|
+
patternMatchCount += urlSecretResult.count;
|
|
19485
19880
|
for (const pattern of SENSITIVE_PATTERNS) {
|
|
19486
19881
|
pattern.lastIndex = 0;
|
|
19487
19882
|
const matches = filtered.match(pattern);
|
|
@@ -19493,13 +19888,13 @@ function applyPrivacyFilter(content, config) {
|
|
|
19493
19888
|
for (const patternStr of config.excludePatterns || []) {
|
|
19494
19889
|
try {
|
|
19495
19890
|
const regex = new RegExp(
|
|
19496
|
-
`(${patternStr})\\s*[:=]\\s*['"]?[^\\s'"]+`,
|
|
19891
|
+
`(^|[^\\w-])(${patternStr})\\s*[:=]\\s*['"]?[^\\s'"]+`,
|
|
19497
19892
|
"gi"
|
|
19498
19893
|
);
|
|
19499
19894
|
const matches = filtered.match(regex);
|
|
19500
19895
|
if (matches) {
|
|
19501
19896
|
patternMatchCount += matches.length;
|
|
19502
|
-
filtered = filtered.replace(regex, "[REDACTED]");
|
|
19897
|
+
filtered = filtered.replace(regex, "$1[REDACTED]");
|
|
19503
19898
|
}
|
|
19504
19899
|
} catch {
|
|
19505
19900
|
}
|