@remnic/plugin-openclaw 1.0.35 → 1.0.36
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/README.md +38 -4
- package/dist/{calibration-Z5WWNV7U.js → calibration-RKL2LRW4.js} +4 -4
- package/dist/{capsule-cli-GBM3WPAM.js → capsule-cli-EHZPMXBC.js} +2 -2
- package/dist/{capsule-crypto-K3IRTKRH.js → capsule-crypto-JS67OSWM.js} +3 -3
- package/dist/capsule-export-YPDWRB3C.js +17 -0
- package/dist/capsule-import-SWPOFG6F.js +16 -0
- package/dist/{capsule-merge-IWOQ34KL.js → capsule-merge-YXAF7ZJW.js} +7 -7
- package/dist/{causal-chain-WYN5QOPS.js → causal-chain-BVTOWZKC.js} +4 -4
- package/dist/{causal-consolidation-C64NNE4T.js → causal-consolidation-DRPM2KOE.js} +13 -10
- package/dist/{causal-retrieval-NZHQOZOE.js → causal-retrieval-XAP6QKHZ.js} +4 -5
- package/dist/{causal-trajectory-graph-VBPE2WPM.js → causal-trajectory-graph-ZWQWZ7N5.js} +2 -2
- package/dist/{chunk-5LE4HTVL.js → chunk-25J4PXDH.js} +0 -18
- package/dist/{chunk-FGTYFLL5.js → chunk-3IHGISUN.js} +29 -32
- package/dist/{chunk-6UFI73TJ.js → chunk-3IKMUNW5.js} +53 -46
- package/dist/{chunk-EXDYWXMB.js → chunk-4XDQ3KEC.js} +1 -2
- package/dist/{chunk-JGIUTWZS.js → chunk-6O3H3DPL.js} +2 -2
- package/dist/{chunk-UTDLHBBV.js → chunk-BLC3RQNV.js} +5 -555
- package/dist/{chunk-4G2XCSD2.js → chunk-BZ4EYURA.js} +0 -5
- package/dist/{chunk-4LYQ4ONL.js → chunk-E4RM7637.js} +1 -1
- package/dist/{chunk-TDRJVMUP.js → chunk-EH4AXGRO.js} +0 -12
- package/dist/{chunk-EYCLXMIV.js → chunk-G3CZA4SD.js} +9 -427
- package/dist/chunk-I2KLQ2HA.js +22 -0
- package/dist/chunk-IO5WWY6A.js +156 -0
- package/dist/{contradiction-scan-A5NOTZPN.js → chunk-JC3FCKYL.js} +189 -86
- package/dist/{chunk-SVSQAG6M.js → chunk-KC7KSQR4.js} +47 -28
- package/dist/chunk-LZCGPRHS.js +228 -0
- package/dist/{chunk-CXM7EBAO.js → chunk-MXFJXUHC.js} +1 -1
- package/dist/{chunk-L6I4MQKO.js → chunk-NNAN63QK.js} +6 -6
- package/dist/{chunk-VRGUUHBV.js → chunk-NUWDSTP7.js} +1 -1
- package/dist/{chunk-6OJAU466.js → chunk-QMUQV5NP.js} +0 -1
- package/dist/{chunk-LLUROTZJ.js → chunk-QQXJODFL.js} +9 -9
- package/dist/{chunk-6F6EKSVP.js → chunk-QXXEF7VI.js} +1 -1
- package/dist/{chunk-CMKR6NDQ.js → chunk-SEGEX7W4.js} +73 -241
- package/dist/{chunk-VFULKFKI.js → chunk-SWOYEQN2.js} +42 -17
- package/dist/chunk-TH5FF5SC.js +16 -0
- package/dist/chunk-UZJ7EERS.js +272 -0
- package/dist/chunk-YJYZMLD5.js +360 -0
- package/dist/{chunk-NKVIN6RD.js → chunk-YKV4EFUI.js} +84 -2
- package/dist/{chunk-SSFTU6LP.js → chunk-ZS6VABML.js} +4 -4
- package/dist/{cipher-VHAFCG7Z.js → cipher-E23BHBSO.js} +1 -1
- package/dist/{consolidation-undo-5ZSX4MWO.js → consolidation-undo-FKJZCJHS.js} +2 -2
- package/dist/contradiction-review-WJRWNQ5N.js +29 -0
- package/dist/contradiction-scan-5X423QGT.js +12 -0
- package/dist/{dreams-ledger-3I52ISYR.js → dreams-ledger-KDX44I7R.js} +1 -1
- package/dist/{engine-47AKKYJ4.js → engine-5P774HTZ.js} +6 -6
- package/dist/{extraction-judge-telemetry-GHOTVYMP.js → extraction-judge-telemetry-O4ZVGLTU.js} +1 -1
- package/dist/{fallback-llm-45A755XP.js → fallback-llm-43UMEXNJ.js} +3 -3
- package/dist/{first-start-migration-I24M2JEE.js → first-start-migration-H2SAXAGR.js} +4 -4
- package/dist/{forget-NI4RBDPB.js → forget-ZECIDNL5.js} +1 -1
- package/dist/{fs-utils-PZRI2HDZ.js → fs-utils-OYXSZSVV.js} +12 -2
- package/dist/{graph-edge-decay-5CVKWBYH.js → graph-edge-decay-24ZKD5QL.js} +5 -5
- package/dist/index.js +7091 -84285
- package/dist/{kdf-H5B23ZM2.js → kdf-RXKIWHRU.js} +1 -1
- package/dist/legacy-hook-compat-QHHKF4GK.js +2 -0
- package/dist/{logger-TNOKCH7X.js → logger-XG7JKLPS.js} +1 -1
- package/dist/{memory-governance-QS7Z425Y.js → memory-governance-6K4M4YXD.js} +5 -5
- package/dist/{metadata-JAGIWHEA.js → metadata-WK2TRPYZ.js} +1 -1
- package/dist/{migrate-from-identity-anchor-7MMSPEUM.js → migrate-from-identity-anchor-SNDNKHZD.js} +1 -1
- package/dist/path-ZKO74XXC.js +7 -0
- package/dist/{peers-KRFXWRQ6.js → peers-W53WSDXG.js} +1 -1
- package/dist/{purge-XN2VSPZ2.js → purge-IKJISXEQ.js} +1 -1
- package/dist/resolution-BN35OXDS.js +11 -0
- package/dist/{secure-store-A4NGCNXV.js → secure-store-F75I54O5.js} +3 -3
- package/dist/{state-PVISYXRH.js → state-4ITLYMAU.js} +1 -1
- package/dist/{state-store-N6TFBFSP.js → state-store-ET3ADVY5.js} +3 -3
- package/dist/{storage-DDYQGLXA.js → storage-5EY6T7ON.js} +3 -3
- package/dist/{tier-stats-IZNW66NC.js → tier-stats-ZRQBV6G2.js} +4 -4
- package/dist/{trace-NJESSGH7.js → trace-IL2Y34EH.js} +1 -1
- package/dist/{tui-MGK2LYJY.js → tui-7KRDCMYK.js} +1 -1
- package/dist/{types-R4DO7AKM.js → types-7L34HYDW.js} +3 -3
- package/openclaw.plugin.json +17 -8
- package/package.json +8 -5
- package/scripts/faiss_index.py +756 -0
- package/scripts/faiss_requirements.txt +3 -0
- package/dist/capsule-export-IXVERCQG.js +0 -17
- package/dist/capsule-import-IA6VIOPQ.js +0 -16
- package/dist/chunk-3GUF7RQI.js +0 -559
- package/dist/chunk-7OQEPGQF.js +0 -533
- package/dist/chunk-DIZW6H5J.js +0 -136
- package/dist/chunk-FQRSVYY4.js +0 -110
- package/dist/chunk-GUSMRW4H.js +0 -12
- package/dist/chunk-MLKGABMK.js +0 -9
- package/dist/chunk-WPINX4MF.js +0 -380
- package/dist/contradiction-review-SVGBS3V5.js +0 -21
- package/dist/legacy-hook-compat-XQ7FP6FV.js +0 -35
- package/dist/path-JIEGNWFL.js +0 -7
- package/dist/resolution-YITUVUTH.js +0 -100
|
@@ -0,0 +1,156 @@
|
|
|
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,16 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
+
computeMemoryContentHash,
|
|
2
3
|
computePairId,
|
|
3
4
|
isCoolingDown,
|
|
4
5
|
listPairs,
|
|
6
|
+
memoryHashesChanged,
|
|
7
|
+
migrateUnscopedPairsToNamespace,
|
|
5
8
|
writePairs
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-YJYZMLD5.js";
|
|
7
10
|
import {
|
|
8
11
|
extractJsonCandidates
|
|
9
12
|
} from "./chunk-3A5ELHTT.js";
|
|
10
13
|
import {
|
|
11
14
|
log
|
|
12
15
|
} from "./chunk-UFU5GGGA.js";
|
|
13
|
-
import "./chunk-MLKGABMK.js";
|
|
14
16
|
|
|
15
17
|
// ../remnic-core/src/contradiction/contradiction-judge.ts
|
|
16
18
|
import { createHash } from "crypto";
|
|
@@ -157,15 +159,14 @@ function parseJudgeResponse(candidates, inputs) {
|
|
|
157
159
|
for (const item of items) {
|
|
158
160
|
if (!item || typeof item !== "object") continue;
|
|
159
161
|
const verdict = typeof item.verdict === "string" && VALID_VERDICTS.includes(item.verdict) ? item.verdict : "needs-user";
|
|
160
|
-
const pairKeyVal = typeof item.pairKey === "string" ? item.pairKey : null;
|
|
162
|
+
const pairKeyVal = typeof item.pairKey === "string" && item.pairKey.length > 0 ? item.pairKey : null;
|
|
161
163
|
const input = pairKeyVal ? inputs.find((p) => pairKey(p.memoryIdA, p.memoryIdB) === pairKeyVal) : null;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
matchedKeys.add(pairKey(fallbackInput.memoryIdA, fallbackInput.memoryIdB));
|
|
164
|
+
if (!input) continue;
|
|
165
|
+
matchedKeys.add(pairKey(input.memoryIdA, input.memoryIdB));
|
|
165
166
|
const confidence = typeof item.confidence === "number" ? Math.min(1, Math.max(0, item.confidence)) : 0.5;
|
|
166
167
|
results.push({
|
|
167
|
-
memoryIdA:
|
|
168
|
-
memoryIdB:
|
|
168
|
+
memoryIdA: input.memoryIdA,
|
|
169
|
+
memoryIdB: input.memoryIdB,
|
|
169
170
|
verdict,
|
|
170
171
|
rationale: typeof item.rationale === "string" ? item.rationale : "No rationale provided",
|
|
171
172
|
confidence
|
|
@@ -209,14 +210,22 @@ var SCAN_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
209
210
|
]);
|
|
210
211
|
async function runContradictionScan(deps) {
|
|
211
212
|
const startTime = Date.now();
|
|
212
|
-
const {
|
|
213
|
+
const { config, memoryDir, embeddingLookup, embeddingLookupFactory, localLlm, fallbackLlm } = deps;
|
|
214
|
+
const requestedNamespace = normalizeScanNamespace(deps.namespace);
|
|
213
215
|
const scanConfig = config.contradictionScan;
|
|
214
|
-
const scopedEmbeddingLookup = embeddingLookupFactory ? embeddingLookupFactory(storage) : embeddingLookup;
|
|
215
216
|
if (!scanConfig.enabled) {
|
|
216
217
|
log.info("[contradiction-scan] disabled by config");
|
|
217
218
|
return { scanned: 0, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: 0 };
|
|
218
219
|
}
|
|
219
|
-
const
|
|
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);
|
|
220
229
|
log.info("[contradiction-scan] loaded %d eligible memories", memories.length);
|
|
221
230
|
if (memories.length < 2) {
|
|
222
231
|
return { scanned: memories.length, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: Date.now() - startTime };
|
|
@@ -226,7 +235,7 @@ async function runContradictionScan(deps) {
|
|
|
226
235
|
for (const p of existingPairs) {
|
|
227
236
|
existingMap.set(p.pairId, p);
|
|
228
237
|
}
|
|
229
|
-
const candidates = await generatePairs(memories, existingMap, scanConfig, scopedEmbeddingLookup);
|
|
238
|
+
const candidates = await generatePairs(memories, existingMap, scanConfig, namespace, scopedEmbeddingLookup);
|
|
230
239
|
const cooledDown = candidates.skipped;
|
|
231
240
|
log.info("[contradiction-scan] generated %d candidates (%d cooled down)", candidates.pairs.length, cooledDown);
|
|
232
241
|
if (candidates.pairs.length === 0) {
|
|
@@ -239,8 +248,7 @@ async function runContradictionScan(deps) {
|
|
|
239
248
|
elapsedMs: Date.now() - startTime
|
|
240
249
|
};
|
|
241
250
|
}
|
|
242
|
-
const
|
|
243
|
-
const judgeInputs = capped.map((pair) => ({
|
|
251
|
+
const judgeInputs = candidates.pairs.map((pair) => ({
|
|
244
252
|
memoryIdA: pair.idA,
|
|
245
253
|
memoryIdB: pair.idB,
|
|
246
254
|
textA: pair.textA,
|
|
@@ -252,6 +260,7 @@ async function runContradictionScan(deps) {
|
|
|
252
260
|
const judgeResult = await judgeContradictionPairs(judgeInputs, config, localLlm, fallbackLlm, scanCache);
|
|
253
261
|
log.info("[contradiction-scan] judge completed: %d judged, %d cached in %dms", judgeResult.judged, judgeResult.cached, judgeResult.elapsed);
|
|
254
262
|
const queueEntries = [];
|
|
263
|
+
const currentMemoryHashes = memoryContentHashMap(memories);
|
|
255
264
|
for (const [key, result] of judgeResult.results) {
|
|
256
265
|
queueEntries.push({
|
|
257
266
|
memoryIds: [result.memoryIdA, result.memoryIdB],
|
|
@@ -261,10 +270,11 @@ async function runContradictionScan(deps) {
|
|
|
261
270
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
262
271
|
// Set lastReviewedAt for non-actionable verdicts so cooldown prevents re-judging
|
|
263
272
|
lastReviewedAt: result.verdict === "independent" ? (/* @__PURE__ */ new Date()).toISOString() : void 0,
|
|
273
|
+
memoryContentHashes: pairMemoryContentHashes(result.memoryIdA, result.memoryIdB, currentMemoryHashes),
|
|
264
274
|
namespace
|
|
265
275
|
});
|
|
266
276
|
}
|
|
267
|
-
const written = writePairs(memoryDir, queueEntries);
|
|
277
|
+
const written = writePairs(memoryDir, queueEntries, { cooldownDays: scanConfig.cooldownDays });
|
|
268
278
|
const elapsed = Date.now() - startTime;
|
|
269
279
|
log.info("[contradiction-scan] complete: %d queued in %dms", written.length, elapsed);
|
|
270
280
|
return {
|
|
@@ -276,13 +286,24 @@ async function runContradictionScan(deps) {
|
|
|
276
286
|
elapsedMs: elapsed
|
|
277
287
|
};
|
|
278
288
|
}
|
|
279
|
-
async function generatePairs(memories, existingPairs, scanConfig, embeddingLookup) {
|
|
289
|
+
async function generatePairs(memories, existingPairs, scanConfig, namespace, embeddingLookup) {
|
|
280
290
|
const pairs = [];
|
|
281
|
-
const embeddingPairs = [];
|
|
282
291
|
let skipped = 0;
|
|
283
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
|
+
};
|
|
284
305
|
const byEntity = /* @__PURE__ */ new Map();
|
|
285
|
-
for (const mem of
|
|
306
|
+
for (const mem of orderedMemories) {
|
|
286
307
|
const entity = mem.frontmatter.entityRef;
|
|
287
308
|
if (entity) {
|
|
288
309
|
const existing = byEntity.get(entity) ?? [];
|
|
@@ -290,21 +311,55 @@ async function generatePairs(memories, existingPairs, scanConfig, embeddingLooku
|
|
|
290
311
|
byEntity.set(entity, existing);
|
|
291
312
|
}
|
|
292
313
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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);
|
|
300
355
|
if (seen.has(pairId)) continue;
|
|
301
356
|
seen.add(pairId);
|
|
302
357
|
const existing = existingPairs.get(pairId);
|
|
303
|
-
if (
|
|
358
|
+
if (isSkippedByCooldown(existing)) {
|
|
304
359
|
skipped++;
|
|
305
360
|
continue;
|
|
306
361
|
}
|
|
307
|
-
|
|
362
|
+
pushCandidate({
|
|
308
363
|
idA: a.frontmatter.id,
|
|
309
364
|
idB: b.frontmatter.id,
|
|
310
365
|
textA: a.content,
|
|
@@ -314,71 +369,97 @@ async function generatePairs(memories, existingPairs, scanConfig, embeddingLooku
|
|
|
314
369
|
});
|
|
315
370
|
}
|
|
316
371
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const id = mem.frontmatter.id;
|
|
350
|
-
try {
|
|
351
|
-
const hits = await embeddingLookup(mem.content, 20);
|
|
352
|
-
for (const hit of hits) {
|
|
353
|
-
if (hit.score < scanConfig.similarityFloor) continue;
|
|
354
|
-
if (hit.id === id) continue;
|
|
355
|
-
const peer = memoryById.get(hit.id);
|
|
356
|
-
if (!peer) continue;
|
|
357
|
-
const pairId = computePairId(id, hit.id);
|
|
358
|
-
if (seen.has(pairId)) continue;
|
|
359
|
-
seen.add(pairId);
|
|
360
|
-
const existing = existingPairs.get(pairId);
|
|
361
|
-
if (existing && isCoolingDown(existing, scanConfig.cooldownDays)) {
|
|
362
|
-
skipped++;
|
|
363
|
-
continue;
|
|
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
|
+
});
|
|
364
404
|
}
|
|
365
|
-
|
|
366
|
-
idA: id,
|
|
367
|
-
idB: hit.id,
|
|
368
|
-
textA: mem.content,
|
|
369
|
-
textB: peer.content,
|
|
370
|
-
categoryA: mem.frontmatter.category,
|
|
371
|
-
categoryB: peer.frontmatter.category
|
|
372
|
-
});
|
|
405
|
+
} catch {
|
|
373
406
|
}
|
|
374
|
-
} catch {
|
|
375
407
|
}
|
|
376
|
-
}
|
|
377
408
|
}
|
|
378
|
-
pairs.push(...embeddingPairs);
|
|
379
409
|
return { pairs, skipped };
|
|
380
410
|
}
|
|
381
|
-
|
|
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) {
|
|
382
463
|
let all;
|
|
383
464
|
try {
|
|
384
465
|
all = await storage.readAllMemories();
|
|
@@ -407,6 +488,28 @@ function jaccardOverlap(a, b) {
|
|
|
407
488
|
const union = setA.size + setB.size - intersection;
|
|
408
489
|
return union === 0 ? 0 : intersection / union;
|
|
409
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
|
+
|
|
410
513
|
export {
|
|
411
514
|
ACTIVE_STATUSES,
|
|
412
515
|
runContradictionScan
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
countRecallTokenOverlap,
|
|
3
3
|
normalizeRecallTokens,
|
|
4
|
+
resolveCausalTrajectoryStoreDir,
|
|
4
5
|
topicOverlapScore
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-3IKMUNW5.js";
|
|
6
7
|
import {
|
|
7
8
|
assertIsoRecordedAt,
|
|
8
9
|
assertString,
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
import {
|
|
13
14
|
listJsonFiles,
|
|
14
15
|
readJsonFile
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-25J4PXDH.js";
|
|
16
17
|
import {
|
|
17
18
|
log
|
|
18
19
|
} from "./chunk-UFU5GGGA.js";
|
|
@@ -67,7 +68,7 @@ function makeEdgeId(fromId, toId) {
|
|
|
67
68
|
return `edge-${digest}`;
|
|
68
69
|
}
|
|
69
70
|
function resolveChainsDir(memoryDir, causalTrajectoryStoreDir) {
|
|
70
|
-
const root =
|
|
71
|
+
const root = resolveCausalTrajectoryStoreDir(memoryDir, causalTrajectoryStoreDir);
|
|
71
72
|
return path.join(root, "chains");
|
|
72
73
|
}
|
|
73
74
|
function chainIndexPath(chainsDir) {
|
|
@@ -77,6 +78,22 @@ function edgeFilePath(chainsDir, edge) {
|
|
|
77
78
|
const day = recordStoreDay(edge.createdAt);
|
|
78
79
|
return path.join(chainsDir, "edges", day, `${edge.edgeId}.json`);
|
|
79
80
|
}
|
|
81
|
+
var chainMutationQueues = /* @__PURE__ */ new Map();
|
|
82
|
+
function enqueueChainMutation(chainsDir, op) {
|
|
83
|
+
const key = path.resolve(chainsDir);
|
|
84
|
+
const previous = chainMutationQueues.get(key) ?? Promise.resolve();
|
|
85
|
+
const run = previous.catch(() => {
|
|
86
|
+
}).then(op);
|
|
87
|
+
const settled = run.catch(() => {
|
|
88
|
+
});
|
|
89
|
+
chainMutationQueues.set(key, settled);
|
|
90
|
+
void settled.finally(() => {
|
|
91
|
+
if (chainMutationQueues.get(key) === settled) {
|
|
92
|
+
chainMutationQueues.delete(key);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return run;
|
|
96
|
+
}
|
|
80
97
|
async function readChainIndex(chainsDir) {
|
|
81
98
|
try {
|
|
82
99
|
const raw = JSON.parse(await fileReader(chainIndexPath(chainsDir), "utf8"));
|
|
@@ -213,7 +230,7 @@ function scoreStitchCandidate(newTrajectory, candidate) {
|
|
|
213
230
|
return { trajectory: candidate, score, edgeType, stitchMethod: dominantMethod };
|
|
214
231
|
}
|
|
215
232
|
async function readRecentTrajectories(memoryDir, causalTrajectoryStoreDir, currentSessionKey, lookbackDays) {
|
|
216
|
-
const root =
|
|
233
|
+
const root = resolveCausalTrajectoryStoreDir(memoryDir, causalTrajectoryStoreDir);
|
|
217
234
|
const trajectoriesDir = path.join(root, "trajectories");
|
|
218
235
|
const files = await listJsonFiles(trajectoriesDir).catch(() => []);
|
|
219
236
|
if (files.length === 0) return [];
|
|
@@ -246,30 +263,32 @@ async function stitchCausalChain(options) {
|
|
|
246
263
|
if (candidates.length === 0) return [];
|
|
247
264
|
const scored = candidates.map((c) => scoreStitchCandidate(newTrajectory, c)).filter((s) => s.score >= stitchConfig.minScore).sort((a, b) => b.score - a.score).slice(0, stitchConfig.maxEdgesPerTrajectory);
|
|
248
265
|
if (scored.length === 0) return [];
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
266
|
+
return enqueueChainMutation(chainsDir, async () => {
|
|
267
|
+
const index = await readChainIndex(chainsDir);
|
|
268
|
+
const newEdges = [];
|
|
269
|
+
for (const candidate of scored) {
|
|
270
|
+
const edgeId = makeEdgeId(candidate.trajectory.trajectoryId, newTrajectory.trajectoryId);
|
|
271
|
+
if (index.edges[edgeId]) continue;
|
|
272
|
+
const edge = {
|
|
273
|
+
schemaVersion: 1,
|
|
274
|
+
edgeId,
|
|
275
|
+
fromTrajectoryId: candidate.trajectory.trajectoryId,
|
|
276
|
+
toTrajectoryId: newTrajectory.trajectoryId,
|
|
277
|
+
edgeType: candidate.edgeType,
|
|
278
|
+
confidence: Math.min(1, candidate.score / 10),
|
|
279
|
+
stitchMethod: candidate.stitchMethod,
|
|
280
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
281
|
+
};
|
|
282
|
+
addEdgeToIndex(index, edge);
|
|
283
|
+
await persistEdge(chainsDir, edge);
|
|
284
|
+
newEdges.push(edge);
|
|
285
|
+
}
|
|
286
|
+
if (newEdges.length > 0) {
|
|
287
|
+
await writeChainIndex(chainsDir, index);
|
|
288
|
+
log.debug(`[cmc] stitched ${newEdges.length} causal edge(s) for trajectory ${newTrajectory.trajectoryId}`);
|
|
289
|
+
}
|
|
290
|
+
return newEdges;
|
|
291
|
+
});
|
|
273
292
|
}
|
|
274
293
|
|
|
275
294
|
export {
|