@remnic/plugin-openclaw 1.0.54 → 9.3.515

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.
Files changed (84) hide show
  1. package/dist/index.js +510 -2106
  2. package/openclaw.plugin.json +3 -3
  3. package/package.json +4 -3
  4. package/scripts/faiss_index.py +141 -29
  5. package/dist/calibration-RKL2LRW4.js +0 -250
  6. package/dist/capsule-cli-EHZPMXBC.js +0 -33
  7. package/dist/capsule-crypto-JS67OSWM.js +0 -17
  8. package/dist/capsule-export-DX53CPIT.js +0 -17
  9. package/dist/capsule-import-4OXCPHOT.js +0 -16
  10. package/dist/capsule-merge-25AUN33Q.js +0 -195
  11. package/dist/causal-chain-BVTOWZKC.js +0 -23
  12. package/dist/causal-consolidation-IAIX53NV.js +0 -233
  13. package/dist/causal-retrieval-XAP6QKHZ.js +0 -182
  14. package/dist/causal-trajectory-graph-ZWQWZ7N5.js +0 -59
  15. package/dist/chunk-25J4PXDH.js +0 -30
  16. package/dist/chunk-2RQU6GVD.js +0 -453
  17. package/dist/chunk-3A5ELHTT.js +0 -61
  18. package/dist/chunk-3G7FAF6S.js +0 -60
  19. package/dist/chunk-3IKMUNW5.js +0 -177
  20. package/dist/chunk-4XDQ3KEC.js +0 -4048
  21. package/dist/chunk-5ZW5XJQ6.js +0 -128
  22. package/dist/chunk-6IWEAUN6.js +0 -148
  23. package/dist/chunk-7JOLBJJ5.js +0 -874
  24. package/dist/chunk-AJECKGY2.js +0 -896
  25. package/dist/chunk-B52XADV3.js +0 -244
  26. package/dist/chunk-BQZ7JACP.js +0 -389
  27. package/dist/chunk-BU5KJVWF.js +0 -78
  28. package/dist/chunk-BZ4EYURA.js +0 -181
  29. package/dist/chunk-CEL5ZLKP.js +0 -233
  30. package/dist/chunk-EH4AXGRO.js +0 -48
  31. package/dist/chunk-FDK3VJVE.js +0 -1295
  32. package/dist/chunk-I2KLQ2HA.js +0 -22
  33. package/dist/chunk-IO5WWY6A.js +0 -156
  34. package/dist/chunk-JC3FCKYL.js +0 -516
  35. package/dist/chunk-JZBOXOUC.js +0 -259
  36. package/dist/chunk-KC7KSQR4.js +0 -302
  37. package/dist/chunk-LZCGPRHS.js +0 -228
  38. package/dist/chunk-MBIFE6SA.js +0 -250
  39. package/dist/chunk-MXFJXUHC.js +0 -292
  40. package/dist/chunk-NUWDSTP7.js +0 -233
  41. package/dist/chunk-QCCP4RU5.js +0 -190
  42. package/dist/chunk-QMUQV5NP.js +0 -146
  43. package/dist/chunk-QQXJODFL.js +0 -328
  44. package/dist/chunk-SWOYEQN2.js +0 -1005
  45. package/dist/chunk-TH5FF5SC.js +0 -16
  46. package/dist/chunk-TXOEHSVP.js +0 -275
  47. package/dist/chunk-UFU5GGGA.js +0 -47
  48. package/dist/chunk-USSDZQKJ.js +0 -6533
  49. package/dist/chunk-UZJ7EERS.js +0 -272
  50. package/dist/chunk-VUZEEGP5.js +0 -725
  51. package/dist/chunk-W6EEFUCJ.js +0 -349
  52. package/dist/chunk-YGGGUTG3.js +0 -125
  53. package/dist/chunk-YJYZMLD5.js +0 -360
  54. package/dist/chunk-YKV4EFUI.js +0 -199
  55. package/dist/chunk-ZS6VABML.js +0 -185
  56. package/dist/cipher-E23BHBSO.js +0 -27
  57. package/dist/consolidation-undo-FKJZCJHS.js +0 -426
  58. package/dist/contradiction-review-WJRWNQ5N.js +0 -29
  59. package/dist/contradiction-scan-5X423QGT.js +0 -12
  60. package/dist/dreams-ledger-KDX44I7R.js +0 -290
  61. package/dist/engine-O46ZX4EZ.js +0 -16
  62. package/dist/extraction-judge-telemetry-O4ZVGLTU.js +0 -14
  63. package/dist/fallback-llm-43UMEXNJ.js +0 -10
  64. package/dist/first-start-migration-H2SAXAGR.js +0 -263
  65. package/dist/forget-ZECIDNL5.js +0 -68
  66. package/dist/fs-utils-OYXSZSVV.js +0 -39
  67. package/dist/graph-edge-decay-24ZKD5QL.js +0 -202
  68. package/dist/kdf-RXKIWHRU.js +0 -25
  69. package/dist/logger-XG7JKLPS.js +0 -9
  70. package/dist/memory-governance-6BCH6SZ5.js +0 -25
  71. package/dist/metadata-WK2TRPYZ.js +0 -20
  72. package/dist/migrate-from-identity-anchor-SNDNKHZD.js +0 -7
  73. package/dist/path-ZKO74XXC.js +0 -7
  74. package/dist/peers-W53WSDXG.js +0 -43
  75. package/dist/purge-IKJISXEQ.js +0 -204
  76. package/dist/resolution-BN35OXDS.js +0 -11
  77. package/dist/secure-store-6RWYM6GK.js +0 -155
  78. package/dist/state-4ITLYMAU.js +0 -7
  79. package/dist/state-store-ET3ADVY5.js +0 -14
  80. package/dist/storage-VEGU76NN.js +0 -27
  81. package/dist/tier-stats-ZRQBV6G2.js +0 -147
  82. package/dist/trace-IL2Y34EH.js +0 -289
  83. package/dist/tui-7KRDCMYK.js +0 -12
  84. package/dist/types-MBUINTB2.js +0 -30
@@ -1,22 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __export = (target, all) => {
6
- for (var name in all)
7
- __defProp(target, name, { get: all[name], enumerable: true });
8
- };
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
18
-
19
- export {
20
- __export,
21
- __reExport
22
- };
@@ -1,156 +0,0 @@
1
- // ../remnic-core/src/graph-edge-reinforcement.ts
2
- var CONFIDENCE_CEILING = 1;
3
- var DEFAULT_DECAY_WINDOW_MS = 90 * 24 * 60 * 60 * 1e3;
4
- var DEFAULT_DECAY_FLOOR = 0.1;
5
- var DEFAULT_DECAY_PER_WINDOW = 0.1;
6
- function readEdgeConfidence(edge) {
7
- const raw = edge.confidence;
8
- if (raw === void 0 || raw === null || !Number.isFinite(raw)) {
9
- return CONFIDENCE_CEILING;
10
- }
11
- if (raw < 0) return 0;
12
- if (raw > CONFIDENCE_CEILING) return CONFIDENCE_CEILING;
13
- return raw;
14
- }
15
- function readLastReinforcedAt(edge) {
16
- return edge.lastReinforcedAt ?? edge.ts;
17
- }
18
- function decayEdgeConfidence(edge, now, opts = {}) {
19
- const windowMs = opts.windowMs ?? DEFAULT_DECAY_WINDOW_MS;
20
- const perWindow = opts.perWindow ?? DEFAULT_DECAY_PER_WINDOW;
21
- const floor = opts.floor ?? DEFAULT_DECAY_FLOOR;
22
- if (!(windowMs > 0) || !Number.isFinite(perWindow) || perWindow < 0 || !Number.isFinite(floor)) {
23
- return { ...edge, confidence: readEdgeConfidence(edge) };
24
- }
25
- const safeFloor = Math.min(CONFIDENCE_CEILING, Math.max(0, floor));
26
- const nowMs = Date.parse(now);
27
- const refMs = Date.parse(readLastReinforcedAt(edge));
28
- if (!Number.isFinite(nowMs) || !Number.isFinite(refMs)) {
29
- return { ...edge, confidence: readEdgeConfidence(edge) };
30
- }
31
- const age = nowMs - refMs;
32
- const current = readEdgeConfidence(edge);
33
- if (age <= windowMs) {
34
- return { ...edge, confidence: current };
35
- }
36
- const windowsPast = Math.ceil((age - windowMs) / windowMs);
37
- const decayed = current - perWindow * windowsPast;
38
- const lowerBound = Math.min(safeFloor, current);
39
- const next = Math.max(lowerBound, Math.min(current, decayed));
40
- const newRefMs = refMs + windowsPast * windowMs;
41
- const newRef = new Date(newRefMs).toISOString();
42
- return { ...edge, confidence: next, lastReinforcedAt: newRef };
43
- }
44
-
45
- // ../remnic-core/src/graph.ts
46
- import * as fsReadModule0 from "fs/promises";
47
- const mkdir = fsReadModule0.mkdir;
48
- const appendFile = fsReadModule0.appendFile;
49
- const fileReader = fsReadModule0["re"+"ad"+"Fi"+"le"];
50
- import * as path from "path";
51
-
52
- // ../remnic-core/src/graph-events.ts
53
- import { EventEmitter } from "events";
54
- var buses = /* @__PURE__ */ new Map();
55
- function getGraphEventBus(memoryDir) {
56
- let bus = buses.get(memoryDir);
57
- if (!bus) {
58
- bus = new EventEmitter();
59
- bus.setMaxListeners(200);
60
- buses.set(memoryDir, bus);
61
- }
62
- return bus;
63
- }
64
- function emitGraphEvent(memoryDir, type, payload) {
65
- const event = {
66
- type,
67
- memoryDir,
68
- ts: (/* @__PURE__ */ new Date()).toISOString(),
69
- payload
70
- };
71
- const bus = getGraphEventBus(memoryDir);
72
- try {
73
- bus.emit("graph-event", event);
74
- } catch {
75
- }
76
- }
77
-
78
- // ../remnic-core/src/graph.ts
79
- function graphsDir(memoryDir) {
80
- return path.join(memoryDir, "state", "graphs");
81
- }
82
- function graphFilePath(memoryDir, type) {
83
- return path.join(graphsDir(memoryDir), `${type}.jsonl`);
84
- }
85
- async function ensureGraphsDir(memoryDir) {
86
- await mkdir(graphsDir(memoryDir), { recursive: true });
87
- }
88
- var graphWriteLocks = /* @__PURE__ */ new Map();
89
- function withGraphWriteLock(filePath, fn) {
90
- const prev = graphWriteLocks.get(filePath) ?? Promise.resolve();
91
- const next = prev.then(fn, fn);
92
- graphWriteLocks.set(
93
- filePath,
94
- next.then(
95
- () => void 0,
96
- () => void 0
97
- )
98
- );
99
- return next;
100
- }
101
- async function appendEdge(memoryDir, edge) {
102
- await ensureGraphsDir(memoryDir);
103
- const filePath = graphFilePath(memoryDir, edge.type);
104
- const line = JSON.stringify(edge) + "\n";
105
- await withGraphWriteLock(filePath, async () => {
106
- await appendFile(filePath, line, "utf8");
107
- });
108
- emitGraphEvent(memoryDir, "edge-added", {
109
- source: edge.from,
110
- target: edge.to,
111
- kind: edge.type,
112
- weight: edge.weight,
113
- label: edge.label,
114
- confidence: typeof edge.confidence === "number" ? edge.confidence : 1
115
- });
116
- }
117
- function isNodeError(err) {
118
- return typeof err === "object" && err !== null && "code" in err;
119
- }
120
- function parseEdgesJsonl(raw) {
121
- const edges = [];
122
- for (const line of raw.split("\n")) {
123
- const trimmed = line.trim();
124
- if (!trimmed) continue;
125
- try {
126
- edges.push(JSON.parse(trimmed));
127
- } catch {
128
- }
129
- }
130
- return edges;
131
- }
132
- async function readEdgesStrict(memoryDir, type) {
133
- const filePath = graphFilePath(memoryDir, type);
134
- try {
135
- const raw = await fileReader(filePath, "utf8");
136
- return parseEdgesJsonl(raw);
137
- } catch (err) {
138
- if (isNodeError(err) && err.code === "ENOENT") {
139
- return [];
140
- }
141
- throw err;
142
- }
143
- }
144
-
145
- export {
146
- DEFAULT_DECAY_WINDOW_MS,
147
- DEFAULT_DECAY_FLOOR,
148
- DEFAULT_DECAY_PER_WINDOW,
149
- readEdgeConfidence,
150
- decayEdgeConfidence,
151
- graphsDir,
152
- graphFilePath,
153
- withGraphWriteLock,
154
- appendEdge,
155
- readEdgesStrict
156
- };
@@ -1,516 +0,0 @@
1
- import {
2
- computeMemoryContentHash,
3
- computePairId,
4
- isCoolingDown,
5
- listPairs,
6
- memoryHashesChanged,
7
- migrateUnscopedPairsToNamespace,
8
- writePairs
9
- } from "./chunk-YJYZMLD5.js";
10
- import {
11
- extractJsonCandidates
12
- } from "./chunk-3A5ELHTT.js";
13
- import {
14
- log
15
- } from "./chunk-UFU5GGGA.js";
16
-
17
- // ../remnic-core/src/contradiction/contradiction-judge.ts
18
- import { createHash } from "crypto";
19
- var CONTRADICTION_JUDGE_PROMPT = `You are a memory contradiction classifier. You will receive pairs of stored memories and must classify their semantic relationship.
20
-
21
- For each pair, respond with a JSON array where each element has:
22
- - "pairKey": the pairKey provided in the input
23
- - "verdict": one of "contradicts", "independent", "duplicates", "needs-user"
24
- - "rationale": one sentence explaining why
25
- - "confidence": number between 0 and 1
26
-
27
- VERDICT DEFINITIONS:
28
- - "contradicts": The two memories make claims that cannot both be true. One must be wrong or outdated.
29
- - "duplicates": The two memories convey essentially the same information (near-paraphrase).
30
- - "independent": The memories are topically similar but do not conflict or duplicate.
31
- - "needs-user": Cannot determine with sufficient confidence; requires human review.
32
-
33
- IMPORTANT:
34
- - Be conservative. When in doubt, prefer "needs-user" over a wrong classification.
35
- - Two memories about the same entity/topic are NOT necessarily contradictory.
36
- - Temporal changes ("Joshua uses pnpm" vs "Joshua switched to npm") ARE contradictions.
37
- - Different aspects of the same entity ("Joshua uses pnpm" vs "Joshua works on Remnic") are "independent".`;
38
- var defaultVerdictCache = /* @__PURE__ */ new Map();
39
- var CACHE_MAX = 1e4;
40
- function pairKey(idA, idB) {
41
- const sorted = [idA, idB].sort();
42
- return `${sorted[0]}:${sorted[1]}`;
43
- }
44
- function contentHash(a) {
45
- const sides = [
46
- [a.textA.trim(), (a.categoryA ?? "").trim()].join("|"),
47
- [a.textB.trim(), (a.categoryB ?? "").trim()].join("|")
48
- ].sort();
49
- const normalized = sides.join("|||");
50
- return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
51
- }
52
- async function judgeContradictionPairs(pairs, config, localLlm, fallbackLlm, cache) {
53
- const startTime = Date.now();
54
- const results = /* @__PURE__ */ new Map();
55
- const activeCache = cache ?? defaultVerdictCache;
56
- let cached = 0;
57
- let judged = 0;
58
- const toJudge = [];
59
- for (const pair of pairs) {
60
- const key = pairKey(pair.memoryIdA, pair.memoryIdB);
61
- const hash = contentHash(pair);
62
- const cachedResult = activeCache.get(hash);
63
- if (cachedResult) {
64
- results.set(key, { ...cachedResult, memoryIdA: pair.memoryIdA, memoryIdB: pair.memoryIdB });
65
- cached++;
66
- } else {
67
- toJudge.push(pair);
68
- }
69
- }
70
- if (toJudge.length === 0) {
71
- return { results, cached, judged, elapsed: Date.now() - startTime };
72
- }
73
- const pairDescriptions = toJudge.map((p, i) => {
74
- const pk = pairKey(p.memoryIdA, p.memoryIdB);
75
- const catA = p.categoryA ? ` [${p.categoryA}]` : "";
76
- const catB = p.categoryB ? ` [${p.categoryB}]` : "";
77
- return `Pair ${i + 1} (pairKey: "${pk}"):${catA} "${p.textA}"${catB} "${p.textB}"`;
78
- });
79
- const userMessage = `Classify these ${toJudge.length} memory pair(s):
80
-
81
- ${pairDescriptions.join("\n\n")}`;
82
- let llmResponse = null;
83
- if (localLlm) {
84
- try {
85
- llmResponse = await callLlm(localLlm, config, userMessage);
86
- } catch (err) {
87
- log.warn("[contradiction-judge] local LLM call failed: %s", err instanceof Error ? err.message : err);
88
- }
89
- }
90
- if (!llmResponse && fallbackLlm) {
91
- try {
92
- llmResponse = await callLlm(fallbackLlm, config, userMessage);
93
- } catch (err) {
94
- log.warn("[contradiction-judge] fallback LLM call failed: %s", err instanceof Error ? err.message : err);
95
- }
96
- }
97
- if (llmResponse) {
98
- const candidates = extractJsonCandidates(llmResponse);
99
- const parsed = parseJudgeResponse(candidates, toJudge);
100
- for (const result of parsed) {
101
- const key = pairKey(result.memoryIdA, result.memoryIdB);
102
- results.set(key, result);
103
- const input = toJudge.find(
104
- (p) => pairKey(p.memoryIdA, p.memoryIdB) === key
105
- );
106
- if (input) {
107
- const hash = contentHash(input);
108
- if (activeCache.size >= CACHE_MAX) {
109
- const firstKey = activeCache.keys().next().value;
110
- if (firstKey !== void 0) activeCache.delete(firstKey);
111
- }
112
- activeCache.set(hash, result);
113
- }
114
- judged++;
115
- }
116
- } else {
117
- for (const pair of toJudge) {
118
- const key = pairKey(pair.memoryIdA, pair.memoryIdB);
119
- const result = {
120
- memoryIdA: pair.memoryIdA,
121
- memoryIdB: pair.memoryIdB,
122
- verdict: "needs-user",
123
- rationale: "LLM call failed; requires manual review",
124
- confidence: 0
125
- };
126
- results.set(key, result);
127
- judged++;
128
- }
129
- }
130
- return { results, cached, judged, elapsed: Date.now() - startTime };
131
- }
132
- async function callLlm(client, config, userMessage) {
133
- const messages = [
134
- { role: "system", content: CONTRADICTION_JUDGE_PROMPT },
135
- { role: "user", content: userMessage }
136
- ];
137
- if ("chatCompletion" in client && typeof client.chatCompletion === "function") {
138
- const result = await client.chatCompletion(messages, {
139
- temperature: 0.1,
140
- maxTokens: 4096,
141
- operation: "contradiction-judge"
142
- });
143
- return result?.content ?? "";
144
- }
145
- if ("complete" in client && typeof client.complete === "function") {
146
- const result = await client.complete(messages);
147
- return result.content ?? "";
148
- }
149
- return "";
150
- }
151
- function parseJudgeResponse(candidates, inputs) {
152
- const VALID_VERDICTS = ["contradicts", "independent", "duplicates", "needs-user"];
153
- for (const candidate of candidates) {
154
- try {
155
- const parsed = JSON.parse(candidate);
156
- const items = Array.isArray(parsed) ? parsed : [parsed];
157
- const results = [];
158
- const matchedKeys = /* @__PURE__ */ new Set();
159
- for (const item of items) {
160
- if (!item || typeof item !== "object") continue;
161
- const verdict = typeof item.verdict === "string" && VALID_VERDICTS.includes(item.verdict) ? item.verdict : "needs-user";
162
- const pairKeyVal = typeof item.pairKey === "string" && item.pairKey.length > 0 ? item.pairKey : null;
163
- const input = pairKeyVal ? inputs.find((p) => pairKey(p.memoryIdA, p.memoryIdB) === pairKeyVal) : null;
164
- if (!input) continue;
165
- matchedKeys.add(pairKey(input.memoryIdA, input.memoryIdB));
166
- const confidence = typeof item.confidence === "number" ? Math.min(1, Math.max(0, item.confidence)) : 0.5;
167
- results.push({
168
- memoryIdA: input.memoryIdA,
169
- memoryIdB: input.memoryIdB,
170
- verdict,
171
- rationale: typeof item.rationale === "string" ? item.rationale : "No rationale provided",
172
- confidence
173
- });
174
- }
175
- for (const inp of inputs) {
176
- const key = pairKey(inp.memoryIdA, inp.memoryIdB);
177
- if (!matchedKeys.has(key)) {
178
- results.push({
179
- memoryIdA: inp.memoryIdA,
180
- memoryIdB: inp.memoryIdB,
181
- verdict: "needs-user",
182
- rationale: "LLM response omitted this pair",
183
- confidence: 0
184
- });
185
- }
186
- }
187
- if (results.length > 0) return results;
188
- } catch {
189
- continue;
190
- }
191
- }
192
- return inputs.map((p) => ({
193
- memoryIdA: p.memoryIdA,
194
- memoryIdB: p.memoryIdB,
195
- verdict: "needs-user",
196
- rationale: "Failed to parse judge response",
197
- confidence: 0
198
- }));
199
- }
200
-
201
- // ../remnic-core/src/contradiction/contradiction-scan.ts
202
- var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["active"]);
203
- var SCAN_CATEGORIES = /* @__PURE__ */ new Set([
204
- "decision",
205
- "principle",
206
- "rule",
207
- "entity",
208
- "fact",
209
- "preference"
210
- ]);
211
- async function runContradictionScan(deps) {
212
- const startTime = Date.now();
213
- const { config, memoryDir, embeddingLookup, embeddingLookupFactory, localLlm, fallbackLlm } = deps;
214
- const requestedNamespace = normalizeScanNamespace(deps.namespace);
215
- const scanConfig = config.contradictionScan;
216
- if (!scanConfig.enabled) {
217
- log.info("[contradiction-scan] disabled by config");
218
- return { scanned: 0, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: 0 };
219
- }
220
- const { storage: scanStorage, namespace } = await resolveScanStorage(deps, requestedNamespace);
221
- if (config.namespacesEnabled && namespace !== void 0 && isDefaultNamespaceScan(config, requestedNamespace, namespace)) {
222
- const migrated = migrateUnscopedPairsToNamespace(memoryDir, namespace, { cooldownDays: scanConfig.cooldownDays });
223
- if (migrated > 0) {
224
- log.info("[contradiction-scan] migrated %d legacy unscoped review pairs to namespace %s", migrated, namespace);
225
- }
226
- }
227
- const scopedEmbeddingLookup = embeddingLookupFactory ? embeddingLookupFactory(scanStorage) : embeddingLookup;
228
- const memories = await loadEligibleMemories(scanStorage);
229
- log.info("[contradiction-scan] loaded %d eligible memories", memories.length);
230
- if (memories.length < 2) {
231
- return { scanned: memories.length, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: Date.now() - startTime };
232
- }
233
- const existingPairs = listPairs(memoryDir, { filter: "all", namespace, limit: 1e4 }).pairs;
234
- const existingMap = /* @__PURE__ */ new Map();
235
- for (const p of existingPairs) {
236
- existingMap.set(p.pairId, p);
237
- }
238
- const candidates = await generatePairs(memories, existingMap, scanConfig, namespace, scopedEmbeddingLookup);
239
- const cooledDown = candidates.skipped;
240
- log.info("[contradiction-scan] generated %d candidates (%d cooled down)", candidates.pairs.length, cooledDown);
241
- if (candidates.pairs.length === 0) {
242
- return {
243
- scanned: memories.length,
244
- candidates: 0,
245
- judged: 0,
246
- queued: 0,
247
- cooledDown,
248
- elapsedMs: Date.now() - startTime
249
- };
250
- }
251
- const judgeInputs = candidates.pairs.map((pair) => ({
252
- memoryIdA: pair.idA,
253
- memoryIdB: pair.idB,
254
- textA: pair.textA,
255
- textB: pair.textB,
256
- categoryA: pair.categoryA,
257
- categoryB: pair.categoryB
258
- }));
259
- const scanCache = /* @__PURE__ */ new Map();
260
- const judgeResult = await judgeContradictionPairs(judgeInputs, config, localLlm, fallbackLlm, scanCache);
261
- log.info("[contradiction-scan] judge completed: %d judged, %d cached in %dms", judgeResult.judged, judgeResult.cached, judgeResult.elapsed);
262
- const queueEntries = [];
263
- const currentMemoryHashes = memoryContentHashMap(memories);
264
- for (const [key, result] of judgeResult.results) {
265
- queueEntries.push({
266
- memoryIds: [result.memoryIdA, result.memoryIdB],
267
- verdict: result.verdict,
268
- rationale: result.rationale,
269
- confidence: result.confidence,
270
- detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
271
- // Set lastReviewedAt for non-actionable verdicts so cooldown prevents re-judging
272
- lastReviewedAt: result.verdict === "independent" ? (/* @__PURE__ */ new Date()).toISOString() : void 0,
273
- memoryContentHashes: pairMemoryContentHashes(result.memoryIdA, result.memoryIdB, currentMemoryHashes),
274
- namespace
275
- });
276
- }
277
- const written = writePairs(memoryDir, queueEntries, { cooldownDays: scanConfig.cooldownDays });
278
- const elapsed = Date.now() - startTime;
279
- log.info("[contradiction-scan] complete: %d queued in %dms", written.length, elapsed);
280
- return {
281
- scanned: memories.length,
282
- candidates: candidates.pairs.length,
283
- judged: judgeResult.judged,
284
- queued: written.length,
285
- cooledDown,
286
- elapsedMs: elapsed
287
- };
288
- }
289
- async function generatePairs(memories, existingPairs, scanConfig, namespace, embeddingLookup) {
290
- const pairs = [];
291
- let skipped = 0;
292
- const seen = /* @__PURE__ */ new Set();
293
- const maxPairs = Math.max(0, Math.floor(scanConfig.maxPairsPerRun));
294
- if (maxPairs === 0) return { pairs, skipped };
295
- const orderedMemories = [...memories].sort(compareMemoryId);
296
- const currentMemoryHashes = memoryContentHashMap(orderedMemories);
297
- const pushCandidate = (pair) => {
298
- if (pairs.length < maxPairs) pairs.push(pair);
299
- };
300
- const hasCapacity = () => pairs.length < maxPairs;
301
- const isSkippedByCooldown = (existing) => {
302
- if (!existing || !isCoolingDown(existing, scanConfig.cooldownDays)) return false;
303
- return !memoryHashesChanged("", existing, (memoryId) => currentMemoryHashes.get(memoryId) ?? null);
304
- };
305
- const byEntity = /* @__PURE__ */ new Map();
306
- for (const mem of orderedMemories) {
307
- const entity = mem.frontmatter.entityRef;
308
- if (entity) {
309
- const existing = byEntity.get(entity) ?? [];
310
- existing.push(mem);
311
- byEntity.set(entity, existing);
312
- }
313
- }
314
- entityPairs:
315
- for (const entity of [...byEntity.keys()].sort()) {
316
- const group = byEntity.get(entity) ?? [];
317
- if (group.length < 2) continue;
318
- for (let i = 0; i < group.length; i++) {
319
- for (let j = i + 1; j < group.length; j++) {
320
- if (!hasCapacity()) break entityPairs;
321
- const a = group[i];
322
- const b = group[j];
323
- const pairId = computePairId(a.frontmatter.id, b.frontmatter.id, namespace);
324
- if (seen.has(pairId)) continue;
325
- seen.add(pairId);
326
- const existing = existingPairs.get(pairId);
327
- if (isSkippedByCooldown(existing)) {
328
- skipped++;
329
- continue;
330
- }
331
- pushCandidate({
332
- idA: a.frontmatter.id,
333
- idB: b.frontmatter.id,
334
- textA: a.content,
335
- textB: b.content,
336
- categoryA: a.frontmatter.category,
337
- categoryB: b.frontmatter.category
338
- });
339
- }
340
- }
341
- }
342
- const noEntity = orderedMemories.filter((m) => !m.frontmatter.entityRef);
343
- topicPairs:
344
- for (let i = 0; i < noEntity.length; i++) {
345
- for (let j = i + 1; j < noEntity.length; j++) {
346
- if (!hasCapacity()) break topicPairs;
347
- const a = noEntity[i];
348
- const b = noEntity[j];
349
- const overlap = jaccardOverlap(
350
- a.frontmatter.tags ?? [],
351
- b.frontmatter.tags ?? []
352
- );
353
- if (overlap < scanConfig.topicOverlapFloor) continue;
354
- const pairId = computePairId(a.frontmatter.id, b.frontmatter.id, namespace);
355
- if (seen.has(pairId)) continue;
356
- seen.add(pairId);
357
- const existing = existingPairs.get(pairId);
358
- if (isSkippedByCooldown(existing)) {
359
- skipped++;
360
- continue;
361
- }
362
- pushCandidate({
363
- idA: a.frontmatter.id,
364
- idB: b.frontmatter.id,
365
- textA: a.content,
366
- textB: b.content,
367
- categoryA: a.frontmatter.category,
368
- categoryB: b.frontmatter.category
369
- });
370
- }
371
- }
372
- if (embeddingLookup && hasCapacity()) {
373
- const memoryById = new Map(orderedMemories.map((m) => [m.frontmatter.id, m]));
374
- embeddingPairs:
375
- for (const mem of orderedMemories) {
376
- if (!hasCapacity()) break;
377
- const id = mem.frontmatter.id;
378
- try {
379
- const hits = (await embeddingLookup(mem.content, 20)).sort(
380
- (a, b) => b.score - a.score || a.id.localeCompare(b.id)
381
- );
382
- for (const hit of hits) {
383
- if (!hasCapacity()) break embeddingPairs;
384
- if (hit.score < scanConfig.similarityFloor) continue;
385
- if (hit.id === id) continue;
386
- const peer = memoryById.get(hit.id);
387
- if (!peer) continue;
388
- const pairId = computePairId(id, hit.id, namespace);
389
- if (seen.has(pairId)) continue;
390
- seen.add(pairId);
391
- const existing = existingPairs.get(pairId);
392
- if (isSkippedByCooldown(existing)) {
393
- skipped++;
394
- continue;
395
- }
396
- pushCandidate({
397
- idA: id,
398
- idB: hit.id,
399
- textA: mem.content,
400
- textB: peer.content,
401
- categoryA: mem.frontmatter.category,
402
- categoryB: peer.frontmatter.category
403
- });
404
- }
405
- } catch {
406
- }
407
- }
408
- }
409
- return { pairs, skipped };
410
- }
411
- function normalizeScanNamespace(namespace) {
412
- const trimmed = namespace?.trim();
413
- return trimmed ? trimmed : void 0;
414
- }
415
- async function resolveScanStorage(deps, namespace) {
416
- if (!deps.config.namespacesEnabled) {
417
- const defaultNamespace = normalizeScanNamespace(deps.config.defaultNamespace);
418
- if (namespace && namespace !== defaultNamespace) {
419
- throw new Error(`unsupported namespace: ${namespace}`);
420
- }
421
- return { storage: deps.storage, namespace: void 0 };
422
- }
423
- if (deps.storageForNamespace) {
424
- const resolved = await deps.storageForNamespace(namespace);
425
- if (isScanStorageResolution(resolved)) {
426
- return {
427
- storage: resolved.storage,
428
- namespace: normalizeScanNamespace(resolved.namespace) ?? fallbackResolvedNamespace(deps, namespace)
429
- };
430
- }
431
- if (!isStorageManagerLike(resolved)) {
432
- throw new Error("storageForNamespace must return a StorageManager or { storage: StorageManager, namespace?: string }");
433
- }
434
- return {
435
- storage: resolved,
436
- namespace: fallbackResolvedNamespace(deps, namespace)
437
- };
438
- }
439
- throw new Error(
440
- "contradiction scans require storageForNamespace when namespaces are enabled so callers can enforce namespace access"
441
- );
442
- }
443
- function isScanStorageResolution(value) {
444
- if (isStorageManagerLike(value)) return false;
445
- if (typeof value !== "object" || value === null) return false;
446
- const candidate = value;
447
- return isStorageManagerLike(candidate.storage);
448
- }
449
- function isStorageManagerLike(value) {
450
- if (typeof value !== "object" || value === null) return false;
451
- const candidate = value;
452
- return typeof candidate.readAllMemories === "function";
453
- }
454
- function fallbackResolvedNamespace(deps, namespace) {
455
- if (namespace) return namespace;
456
- return deps.config.namespacesEnabled ? deps.config.defaultNamespace : void 0;
457
- }
458
- function isDefaultNamespaceScan(config, requestedNamespace, resolvedNamespace) {
459
- if (requestedNamespace === void 0) return true;
460
- return requestedNamespace === config.defaultNamespace || resolvedNamespace === config.defaultNamespace;
461
- }
462
- async function loadEligibleMemories(storage) {
463
- let all;
464
- try {
465
- all = await storage.readAllMemories();
466
- } catch {
467
- return [];
468
- }
469
- return all.filter((mem) => {
470
- const fm = mem.frontmatter;
471
- const status = fm.status ?? "active";
472
- if (!ACTIVE_STATUSES.has(status)) return false;
473
- const category = fm.category;
474
- if (category && !SCAN_CATEGORIES.has(category)) return false;
475
- if (!mem.content || mem.content.trim().length === 0) return false;
476
- if (!fm.id) return false;
477
- return true;
478
- });
479
- }
480
- function jaccardOverlap(a, b) {
481
- if (a.length === 0 && b.length === 0) return 0;
482
- const setA = new Set(a.map((t) => t.toLowerCase()));
483
- const setB = new Set(b.map((t) => t.toLowerCase()));
484
- let intersection = 0;
485
- for (const item of setA) {
486
- if (setB.has(item)) intersection++;
487
- }
488
- const union = setA.size + setB.size - intersection;
489
- return union === 0 ? 0 : intersection / union;
490
- }
491
- function compareMemoryId(a, b) {
492
- return (a.frontmatter.id ?? "").localeCompare(b.frontmatter.id ?? "");
493
- }
494
- function memoryContentHashMap(memories) {
495
- const hashes = /* @__PURE__ */ new Map();
496
- for (const memory of memories) {
497
- const id = memory.frontmatter.id;
498
- if (!id) continue;
499
- hashes.set(id, computeMemoryContentHash(memory.content, memory.frontmatter.category));
500
- }
501
- return hashes;
502
- }
503
- function pairMemoryContentHashes(memoryIdA, memoryIdB, currentMemoryHashes) {
504
- const hashA = currentMemoryHashes.get(memoryIdA);
505
- const hashB = currentMemoryHashes.get(memoryIdB);
506
- if (!hashA || !hashB) return void 0;
507
- return {
508
- [memoryIdA]: hashA,
509
- [memoryIdB]: hashB
510
- };
511
- }
512
-
513
- export {
514
- ACTIVE_STATUSES,
515
- runContradictionScan
516
- };