@remnic/plugin-openclaw 1.0.34 → 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 +59 -10
- package/dist/{calibration-JD4AU7FB.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-DSLFN64P.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-4UA6KMRO.js → chunk-6O3H3DPL.js} +2 -2
- package/dist/{chunk-7NMHI4IC.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-ZXLYEVOP.js → chunk-G3CZA4SD.js} +60 -362
- package/dist/chunk-I2KLQ2HA.js +22 -0
- package/dist/chunk-IO5WWY6A.js +156 -0
- package/dist/{contradiction-scan-U3QKHWQN.js → chunk-JC3FCKYL.js} +191 -87
- 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-NDZNURDM.js → chunk-SEGEX7W4.js} +73 -241
- package/dist/{chunk-7NUFIRM3.js → chunk-SWOYEQN2.js} +97 -21
- 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-57HLTQBN.js → engine-5P774HTZ.js} +6 -6
- package/dist/{extraction-judge-telemetry-GHOTVYMP.js → extraction-judge-telemetry-O4ZVGLTU.js} +1 -1
- package/dist/{fallback-llm-33SPYXQY.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 +7187 -71983
- 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-FEQCA35V.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-R3V6ZFQT.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 +153 -20
- package/package.json +18 -9
- 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";
|
|
@@ -135,7 +137,8 @@ async function callLlm(client, config, userMessage) {
|
|
|
135
137
|
if ("chatCompletion" in client && typeof client.chatCompletion === "function") {
|
|
136
138
|
const result = await client.chatCompletion(messages, {
|
|
137
139
|
temperature: 0.1,
|
|
138
|
-
maxTokens: 4096
|
|
140
|
+
maxTokens: 4096,
|
|
141
|
+
operation: "contradiction-judge"
|
|
139
142
|
});
|
|
140
143
|
return result?.content ?? "";
|
|
141
144
|
}
|
|
@@ -156,15 +159,14 @@ function parseJudgeResponse(candidates, inputs) {
|
|
|
156
159
|
for (const item of items) {
|
|
157
160
|
if (!item || typeof item !== "object") continue;
|
|
158
161
|
const verdict = typeof item.verdict === "string" && VALID_VERDICTS.includes(item.verdict) ? item.verdict : "needs-user";
|
|
159
|
-
const pairKeyVal = typeof item.pairKey === "string" ? item.pairKey : null;
|
|
162
|
+
const pairKeyVal = typeof item.pairKey === "string" && item.pairKey.length > 0 ? item.pairKey : null;
|
|
160
163
|
const input = pairKeyVal ? inputs.find((p) => pairKey(p.memoryIdA, p.memoryIdB) === pairKeyVal) : null;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
matchedKeys.add(pairKey(fallbackInput.memoryIdA, fallbackInput.memoryIdB));
|
|
164
|
+
if (!input) continue;
|
|
165
|
+
matchedKeys.add(pairKey(input.memoryIdA, input.memoryIdB));
|
|
164
166
|
const confidence = typeof item.confidence === "number" ? Math.min(1, Math.max(0, item.confidence)) : 0.5;
|
|
165
167
|
results.push({
|
|
166
|
-
memoryIdA:
|
|
167
|
-
memoryIdB:
|
|
168
|
+
memoryIdA: input.memoryIdA,
|
|
169
|
+
memoryIdB: input.memoryIdB,
|
|
168
170
|
verdict,
|
|
169
171
|
rationale: typeof item.rationale === "string" ? item.rationale : "No rationale provided",
|
|
170
172
|
confidence
|
|
@@ -208,14 +210,22 @@ var SCAN_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
208
210
|
]);
|
|
209
211
|
async function runContradictionScan(deps) {
|
|
210
212
|
const startTime = Date.now();
|
|
211
|
-
const {
|
|
213
|
+
const { config, memoryDir, embeddingLookup, embeddingLookupFactory, localLlm, fallbackLlm } = deps;
|
|
214
|
+
const requestedNamespace = normalizeScanNamespace(deps.namespace);
|
|
212
215
|
const scanConfig = config.contradictionScan;
|
|
213
|
-
const scopedEmbeddingLookup = embeddingLookupFactory ? embeddingLookupFactory(storage) : embeddingLookup;
|
|
214
216
|
if (!scanConfig.enabled) {
|
|
215
217
|
log.info("[contradiction-scan] disabled by config");
|
|
216
218
|
return { scanned: 0, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: 0 };
|
|
217
219
|
}
|
|
218
|
-
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);
|
|
219
229
|
log.info("[contradiction-scan] loaded %d eligible memories", memories.length);
|
|
220
230
|
if (memories.length < 2) {
|
|
221
231
|
return { scanned: memories.length, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: Date.now() - startTime };
|
|
@@ -225,7 +235,7 @@ async function runContradictionScan(deps) {
|
|
|
225
235
|
for (const p of existingPairs) {
|
|
226
236
|
existingMap.set(p.pairId, p);
|
|
227
237
|
}
|
|
228
|
-
const candidates = await generatePairs(memories, existingMap, scanConfig, scopedEmbeddingLookup);
|
|
238
|
+
const candidates = await generatePairs(memories, existingMap, scanConfig, namespace, scopedEmbeddingLookup);
|
|
229
239
|
const cooledDown = candidates.skipped;
|
|
230
240
|
log.info("[contradiction-scan] generated %d candidates (%d cooled down)", candidates.pairs.length, cooledDown);
|
|
231
241
|
if (candidates.pairs.length === 0) {
|
|
@@ -238,8 +248,7 @@ async function runContradictionScan(deps) {
|
|
|
238
248
|
elapsedMs: Date.now() - startTime
|
|
239
249
|
};
|
|
240
250
|
}
|
|
241
|
-
const
|
|
242
|
-
const judgeInputs = capped.map((pair) => ({
|
|
251
|
+
const judgeInputs = candidates.pairs.map((pair) => ({
|
|
243
252
|
memoryIdA: pair.idA,
|
|
244
253
|
memoryIdB: pair.idB,
|
|
245
254
|
textA: pair.textA,
|
|
@@ -251,6 +260,7 @@ async function runContradictionScan(deps) {
|
|
|
251
260
|
const judgeResult = await judgeContradictionPairs(judgeInputs, config, localLlm, fallbackLlm, scanCache);
|
|
252
261
|
log.info("[contradiction-scan] judge completed: %d judged, %d cached in %dms", judgeResult.judged, judgeResult.cached, judgeResult.elapsed);
|
|
253
262
|
const queueEntries = [];
|
|
263
|
+
const currentMemoryHashes = memoryContentHashMap(memories);
|
|
254
264
|
for (const [key, result] of judgeResult.results) {
|
|
255
265
|
queueEntries.push({
|
|
256
266
|
memoryIds: [result.memoryIdA, result.memoryIdB],
|
|
@@ -260,10 +270,11 @@ async function runContradictionScan(deps) {
|
|
|
260
270
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
261
271
|
// Set lastReviewedAt for non-actionable verdicts so cooldown prevents re-judging
|
|
262
272
|
lastReviewedAt: result.verdict === "independent" ? (/* @__PURE__ */ new Date()).toISOString() : void 0,
|
|
273
|
+
memoryContentHashes: pairMemoryContentHashes(result.memoryIdA, result.memoryIdB, currentMemoryHashes),
|
|
263
274
|
namespace
|
|
264
275
|
});
|
|
265
276
|
}
|
|
266
|
-
const written = writePairs(memoryDir, queueEntries);
|
|
277
|
+
const written = writePairs(memoryDir, queueEntries, { cooldownDays: scanConfig.cooldownDays });
|
|
267
278
|
const elapsed = Date.now() - startTime;
|
|
268
279
|
log.info("[contradiction-scan] complete: %d queued in %dms", written.length, elapsed);
|
|
269
280
|
return {
|
|
@@ -275,13 +286,24 @@ async function runContradictionScan(deps) {
|
|
|
275
286
|
elapsedMs: elapsed
|
|
276
287
|
};
|
|
277
288
|
}
|
|
278
|
-
async function generatePairs(memories, existingPairs, scanConfig, embeddingLookup) {
|
|
289
|
+
async function generatePairs(memories, existingPairs, scanConfig, namespace, embeddingLookup) {
|
|
279
290
|
const pairs = [];
|
|
280
|
-
const embeddingPairs = [];
|
|
281
291
|
let skipped = 0;
|
|
282
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
|
+
};
|
|
283
305
|
const byEntity = /* @__PURE__ */ new Map();
|
|
284
|
-
for (const mem of
|
|
306
|
+
for (const mem of orderedMemories) {
|
|
285
307
|
const entity = mem.frontmatter.entityRef;
|
|
286
308
|
if (entity) {
|
|
287
309
|
const existing = byEntity.get(entity) ?? [];
|
|
@@ -289,21 +311,55 @@ async function generatePairs(memories, existingPairs, scanConfig, embeddingLooku
|
|
|
289
311
|
byEntity.set(entity, existing);
|
|
290
312
|
}
|
|
291
313
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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);
|
|
299
355
|
if (seen.has(pairId)) continue;
|
|
300
356
|
seen.add(pairId);
|
|
301
357
|
const existing = existingPairs.get(pairId);
|
|
302
|
-
if (
|
|
358
|
+
if (isSkippedByCooldown(existing)) {
|
|
303
359
|
skipped++;
|
|
304
360
|
continue;
|
|
305
361
|
}
|
|
306
|
-
|
|
362
|
+
pushCandidate({
|
|
307
363
|
idA: a.frontmatter.id,
|
|
308
364
|
idB: b.frontmatter.id,
|
|
309
365
|
textA: a.content,
|
|
@@ -313,71 +369,97 @@ async function generatePairs(memories, existingPairs, scanConfig, embeddingLooku
|
|
|
313
369
|
});
|
|
314
370
|
}
|
|
315
371
|
}
|
|
316
|
-
|
|
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
|
-
const id = mem.frontmatter.id;
|
|
349
|
-
try {
|
|
350
|
-
const hits = await embeddingLookup(mem.content, 20);
|
|
351
|
-
for (const hit of hits) {
|
|
352
|
-
if (hit.score < scanConfig.similarityFloor) continue;
|
|
353
|
-
if (hit.id === id) continue;
|
|
354
|
-
const peer = memoryById.get(hit.id);
|
|
355
|
-
if (!peer) continue;
|
|
356
|
-
const pairId = computePairId(id, hit.id);
|
|
357
|
-
if (seen.has(pairId)) continue;
|
|
358
|
-
seen.add(pairId);
|
|
359
|
-
const existing = existingPairs.get(pairId);
|
|
360
|
-
if (existing && isCoolingDown(existing, scanConfig.cooldownDays)) {
|
|
361
|
-
skipped++;
|
|
362
|
-
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
|
+
});
|
|
363
404
|
}
|
|
364
|
-
|
|
365
|
-
idA: id,
|
|
366
|
-
idB: hit.id,
|
|
367
|
-
textA: mem.content,
|
|
368
|
-
textB: peer.content,
|
|
369
|
-
categoryA: mem.frontmatter.category,
|
|
370
|
-
categoryB: peer.frontmatter.category
|
|
371
|
-
});
|
|
405
|
+
} catch {
|
|
372
406
|
}
|
|
373
|
-
} catch {
|
|
374
407
|
}
|
|
375
|
-
}
|
|
376
408
|
}
|
|
377
|
-
pairs.push(...embeddingPairs);
|
|
378
409
|
return { pairs, skipped };
|
|
379
410
|
}
|
|
380
|
-
|
|
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) {
|
|
381
463
|
let all;
|
|
382
464
|
try {
|
|
383
465
|
all = await storage.readAllMemories();
|
|
@@ -406,6 +488,28 @@ function jaccardOverlap(a, b) {
|
|
|
406
488
|
const union = setA.size + setB.size - intersection;
|
|
407
489
|
return union === 0 ? 0 : intersection / union;
|
|
408
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
|
+
|
|
409
513
|
export {
|
|
410
514
|
ACTIVE_STATUSES,
|
|
411
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 {
|