@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.
- package/dist/index.js +510 -2106
- package/openclaw.plugin.json +3 -3
- package/package.json +4 -3
- package/scripts/faiss_index.py +141 -29
- package/dist/calibration-RKL2LRW4.js +0 -250
- package/dist/capsule-cli-EHZPMXBC.js +0 -33
- package/dist/capsule-crypto-JS67OSWM.js +0 -17
- package/dist/capsule-export-DX53CPIT.js +0 -17
- package/dist/capsule-import-4OXCPHOT.js +0 -16
- package/dist/capsule-merge-25AUN33Q.js +0 -195
- package/dist/causal-chain-BVTOWZKC.js +0 -23
- package/dist/causal-consolidation-IAIX53NV.js +0 -233
- package/dist/causal-retrieval-XAP6QKHZ.js +0 -182
- package/dist/causal-trajectory-graph-ZWQWZ7N5.js +0 -59
- package/dist/chunk-25J4PXDH.js +0 -30
- package/dist/chunk-2RQU6GVD.js +0 -453
- package/dist/chunk-3A5ELHTT.js +0 -61
- package/dist/chunk-3G7FAF6S.js +0 -60
- package/dist/chunk-3IKMUNW5.js +0 -177
- package/dist/chunk-4XDQ3KEC.js +0 -4048
- package/dist/chunk-5ZW5XJQ6.js +0 -128
- package/dist/chunk-6IWEAUN6.js +0 -148
- package/dist/chunk-7JOLBJJ5.js +0 -874
- package/dist/chunk-AJECKGY2.js +0 -896
- package/dist/chunk-B52XADV3.js +0 -244
- package/dist/chunk-BQZ7JACP.js +0 -389
- package/dist/chunk-BU5KJVWF.js +0 -78
- package/dist/chunk-BZ4EYURA.js +0 -181
- package/dist/chunk-CEL5ZLKP.js +0 -233
- package/dist/chunk-EH4AXGRO.js +0 -48
- package/dist/chunk-FDK3VJVE.js +0 -1295
- package/dist/chunk-I2KLQ2HA.js +0 -22
- package/dist/chunk-IO5WWY6A.js +0 -156
- package/dist/chunk-JC3FCKYL.js +0 -516
- package/dist/chunk-JZBOXOUC.js +0 -259
- package/dist/chunk-KC7KSQR4.js +0 -302
- package/dist/chunk-LZCGPRHS.js +0 -228
- package/dist/chunk-MBIFE6SA.js +0 -250
- package/dist/chunk-MXFJXUHC.js +0 -292
- package/dist/chunk-NUWDSTP7.js +0 -233
- package/dist/chunk-QCCP4RU5.js +0 -190
- package/dist/chunk-QMUQV5NP.js +0 -146
- package/dist/chunk-QQXJODFL.js +0 -328
- package/dist/chunk-SWOYEQN2.js +0 -1005
- package/dist/chunk-TH5FF5SC.js +0 -16
- package/dist/chunk-TXOEHSVP.js +0 -275
- package/dist/chunk-UFU5GGGA.js +0 -47
- package/dist/chunk-USSDZQKJ.js +0 -6533
- package/dist/chunk-UZJ7EERS.js +0 -272
- package/dist/chunk-VUZEEGP5.js +0 -725
- package/dist/chunk-W6EEFUCJ.js +0 -349
- package/dist/chunk-YGGGUTG3.js +0 -125
- package/dist/chunk-YJYZMLD5.js +0 -360
- package/dist/chunk-YKV4EFUI.js +0 -199
- package/dist/chunk-ZS6VABML.js +0 -185
- package/dist/cipher-E23BHBSO.js +0 -27
- package/dist/consolidation-undo-FKJZCJHS.js +0 -426
- package/dist/contradiction-review-WJRWNQ5N.js +0 -29
- package/dist/contradiction-scan-5X423QGT.js +0 -12
- package/dist/dreams-ledger-KDX44I7R.js +0 -290
- package/dist/engine-O46ZX4EZ.js +0 -16
- package/dist/extraction-judge-telemetry-O4ZVGLTU.js +0 -14
- package/dist/fallback-llm-43UMEXNJ.js +0 -10
- package/dist/first-start-migration-H2SAXAGR.js +0 -263
- package/dist/forget-ZECIDNL5.js +0 -68
- package/dist/fs-utils-OYXSZSVV.js +0 -39
- package/dist/graph-edge-decay-24ZKD5QL.js +0 -202
- package/dist/kdf-RXKIWHRU.js +0 -25
- package/dist/logger-XG7JKLPS.js +0 -9
- package/dist/memory-governance-6BCH6SZ5.js +0 -25
- package/dist/metadata-WK2TRPYZ.js +0 -20
- package/dist/migrate-from-identity-anchor-SNDNKHZD.js +0 -7
- package/dist/path-ZKO74XXC.js +0 -7
- package/dist/peers-W53WSDXG.js +0 -43
- package/dist/purge-IKJISXEQ.js +0 -204
- package/dist/resolution-BN35OXDS.js +0 -11
- package/dist/secure-store-6RWYM6GK.js +0 -155
- package/dist/state-4ITLYMAU.js +0 -7
- package/dist/state-store-ET3ADVY5.js +0 -14
- package/dist/storage-VEGU76NN.js +0 -27
- package/dist/tier-stats-ZRQBV6G2.js +0 -147
- package/dist/trace-IL2Y34EH.js +0 -289
- package/dist/tui-7KRDCMYK.js +0 -12
- package/dist/types-MBUINTB2.js +0 -30
package/dist/chunk-I2KLQ2HA.js
DELETED
|
@@ -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
|
-
};
|
package/dist/chunk-IO5WWY6A.js
DELETED
|
@@ -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
|
-
};
|
package/dist/chunk-JC3FCKYL.js
DELETED
|
@@ -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
|
-
};
|