@remnic/plugin-openclaw 1.0.53 → 9.3.515

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/index.js +510 -2071
  2. package/openclaw.plugin.json +3 -3
  3. package/package.json +4 -3
  4. package/scripts/faiss_index.py +141 -29
  5. package/dist/calibration-RKL2LRW4.js +0 -250
  6. package/dist/capsule-cli-EHZPMXBC.js +0 -33
  7. package/dist/capsule-crypto-JS67OSWM.js +0 -17
  8. package/dist/capsule-export-DX53CPIT.js +0 -17
  9. package/dist/capsule-import-4OXCPHOT.js +0 -16
  10. package/dist/capsule-merge-25AUN33Q.js +0 -195
  11. package/dist/causal-chain-BVTOWZKC.js +0 -23
  12. package/dist/causal-consolidation-V533DIGW.js +0 -233
  13. package/dist/causal-retrieval-XAP6QKHZ.js +0 -182
  14. package/dist/causal-trajectory-graph-ZWQWZ7N5.js +0 -59
  15. package/dist/chunk-25J4PXDH.js +0 -30
  16. package/dist/chunk-2RQU6GVD.js +0 -453
  17. package/dist/chunk-3A5ELHTT.js +0 -61
  18. package/dist/chunk-3G7FAF6S.js +0 -60
  19. package/dist/chunk-3IKMUNW5.js +0 -177
  20. package/dist/chunk-4XDQ3KEC.js +0 -4048
  21. package/dist/chunk-5ZW5XJQ6.js +0 -128
  22. package/dist/chunk-6IWEAUN6.js +0 -148
  23. package/dist/chunk-7JOLBJJ5.js +0 -874
  24. package/dist/chunk-B52XADV3.js +0 -244
  25. package/dist/chunk-BQZ7JACP.js +0 -389
  26. package/dist/chunk-BU5KJVWF.js +0 -78
  27. package/dist/chunk-BZ4EYURA.js +0 -181
  28. package/dist/chunk-CEL5ZLKP.js +0 -233
  29. package/dist/chunk-EH4AXGRO.js +0 -48
  30. package/dist/chunk-I2KLQ2HA.js +0 -22
  31. package/dist/chunk-IO5WWY6A.js +0 -156
  32. package/dist/chunk-JC3FCKYL.js +0 -516
  33. package/dist/chunk-JZBOXOUC.js +0 -259
  34. package/dist/chunk-KC7KSQR4.js +0 -302
  35. package/dist/chunk-LZCGPRHS.js +0 -228
  36. package/dist/chunk-MBIFE6SA.js +0 -250
  37. package/dist/chunk-MWLY7VQX.js +0 -896
  38. package/dist/chunk-MXFJXUHC.js +0 -292
  39. package/dist/chunk-NUWDSTP7.js +0 -233
  40. package/dist/chunk-PSAPZGDX.js +0 -725
  41. package/dist/chunk-QCCP4RU5.js +0 -190
  42. package/dist/chunk-QMUQV5NP.js +0 -146
  43. package/dist/chunk-QQXJODFL.js +0 -328
  44. package/dist/chunk-QVTGUY3H.js +0 -1295
  45. package/dist/chunk-SWOYEQN2.js +0 -1005
  46. package/dist/chunk-TH5FF5SC.js +0 -16
  47. package/dist/chunk-TXOEHSVP.js +0 -275
  48. package/dist/chunk-UFU5GGGA.js +0 -47
  49. package/dist/chunk-UZJ7EERS.js +0 -272
  50. package/dist/chunk-W6EEFUCJ.js +0 -349
  51. package/dist/chunk-YGGGUTG3.js +0 -125
  52. package/dist/chunk-YJYZMLD5.js +0 -360
  53. package/dist/chunk-YKV4EFUI.js +0 -199
  54. package/dist/chunk-YXDXRE6N.js +0 -6500
  55. package/dist/chunk-ZS6VABML.js +0 -185
  56. package/dist/cipher-E23BHBSO.js +0 -27
  57. package/dist/consolidation-undo-FKJZCJHS.js +0 -426
  58. package/dist/contradiction-review-WJRWNQ5N.js +0 -29
  59. package/dist/contradiction-scan-5X423QGT.js +0 -12
  60. package/dist/dreams-ledger-KDX44I7R.js +0 -290
  61. package/dist/engine-7VG3IXKN.js +0 -16
  62. package/dist/extraction-judge-telemetry-O4ZVGLTU.js +0 -14
  63. package/dist/fallback-llm-43UMEXNJ.js +0 -10
  64. package/dist/first-start-migration-H2SAXAGR.js +0 -263
  65. package/dist/forget-ZECIDNL5.js +0 -68
  66. package/dist/fs-utils-OYXSZSVV.js +0 -39
  67. package/dist/graph-edge-decay-24ZKD5QL.js +0 -202
  68. package/dist/kdf-RXKIWHRU.js +0 -25
  69. package/dist/logger-XG7JKLPS.js +0 -9
  70. package/dist/memory-governance-TK4B4ZZ2.js +0 -25
  71. package/dist/metadata-WK2TRPYZ.js +0 -20
  72. package/dist/migrate-from-identity-anchor-SNDNKHZD.js +0 -7
  73. package/dist/path-ZKO74XXC.js +0 -7
  74. package/dist/peers-W53WSDXG.js +0 -43
  75. package/dist/purge-IKJISXEQ.js +0 -204
  76. package/dist/resolution-BN35OXDS.js +0 -11
  77. package/dist/secure-store-6RWYM6GK.js +0 -155
  78. package/dist/state-4ITLYMAU.js +0 -7
  79. package/dist/state-store-ET3ADVY5.js +0 -14
  80. package/dist/storage-HUNVVHZ7.js +0 -27
  81. package/dist/tier-stats-ZRQBV6G2.js +0 -147
  82. package/dist/trace-IL2Y34EH.js +0 -289
  83. package/dist/tui-7KRDCMYK.js +0 -12
  84. package/dist/types-MBUINTB2.js +0 -30
@@ -1,16 +0,0 @@
1
- // ../remnic-core/src/routing/engine.ts
2
- function normalizeNamespace(namespace) {
3
- return namespace.trim();
4
- }
5
- function isSafeRouteNamespace(namespace) {
6
- const value = normalizeNamespace(namespace);
7
- if (value.length === 0) return false;
8
- if (value === ".") return false;
9
- if (value.includes("/") || value.includes("\\")) return false;
10
- if (value.includes("..")) return false;
11
- return /^[A-Za-z0-9._-]{1,64}$/.test(value);
12
- }
13
-
14
- export {
15
- isSafeRouteNamespace
16
- };
@@ -1,275 +0,0 @@
1
- import {
2
- listFilesRecursive,
3
- sha256File,
4
- toPosixRelPath,
5
- writeJsonFile
6
- } from "./chunk-YKV4EFUI.js";
7
- import {
8
- CAPSULE_ID_PATTERN,
9
- CapsuleBlockSchema,
10
- ExportBundleV2Schema,
11
- ExportManifestV2Schema
12
- } from "./chunk-QCCP4RU5.js";
13
- import {
14
- encryptCapsuleFile
15
- } from "./chunk-ZS6VABML.js";
16
-
17
- // ../remnic-core/src/transfer/capsule-export.ts
18
- import * as fsReadModule0 from "fs/promises";
19
- const mkdir = fsReadModule0.mkdir;
20
- const fileReader = fsReadModule0["re"+"ad"+"Fi"+"le"];
21
- const stat = fsReadModule0.stat;
22
- const writeFile = fsReadModule0.writeFile;
23
- import path2 from "path";
24
- import { gzipSync } from "zlib";
25
-
26
- // ../remnic-core/src/transfer/constants.ts
27
- var EXPORT_FORMAT = "openclaw-engram-export";
28
- var CAPSULE_SCHEMA_VERSION = 2;
29
-
30
- // ../remnic-core/src/transfer/exclusions.ts
31
- import path from "path";
32
- var DEFAULT_TRANSFER_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
33
- "node_modules",
34
- ".git",
35
- ".secure-store",
36
- ".capsules"
37
- ]);
38
- function computeTransferOutputRel(rootAbs, outputAbs) {
39
- const rel = path.relative(rootAbs, outputAbs);
40
- if (rel === "") {
41
- throw new Error("transfer export output path must not equal the memory directory");
42
- }
43
- if (rel === ".." || rel.startsWith(`..${path.sep}`)) return null;
44
- if (path.isAbsolute(rel)) return null;
45
- return rel.split(path.sep).join("/");
46
- }
47
-
48
- // ../remnic-core/src/transfer/capsule-export.ts
49
- var DEFAULT_EXCLUDE_DIRS = DEFAULT_TRANSFER_EXCLUDE_DIRS;
50
- var TRANSCRIPTS_DIR = "transcripts";
51
- var PEERS_DIR = "peers";
52
- async function exportCapsule(opts) {
53
- validateName(opts.name);
54
- const sinceMs = parseSince(opts.since);
55
- const includeKinds = normalizeIncludeKinds(opts.includeKinds);
56
- const peerFilter = normalizePeerIds(opts.peerIds);
57
- const transcriptsOverride = opts.includeTranscripts === true;
58
- const rootAbs = path2.resolve(opts.root);
59
- await assertIsDirectory(rootAbs);
60
- const outDirAbs = path2.resolve(opts.outDir ?? path2.join(rootAbs, ".capsules"));
61
- if (outDirAbs === rootAbs) {
62
- throw new Error(
63
- "exportCapsule: 'outDir' must not equal 'root'. Choose a separate directory (default: <root>/.capsules) so the export does not overwrite or shadow the source tree."
64
- );
65
- }
66
- await mkdir(outDirAbs, { recursive: true });
67
- const outDirRelPosix = computeTransferOutputRel(rootAbs, outDirAbs);
68
- const filesAbs = await listFilesRecursive(rootAbs);
69
- const records = [];
70
- const manifestFiles = [];
71
- for (const abs of filesAbs) {
72
- const relPosix = toPosixRelPath(abs, rootAbs);
73
- if (!shouldInclude(relPosix, includeKinds, peerFilter, outDirRelPosix, transcriptsOverride)) continue;
74
- if (sinceMs !== null) {
75
- const st = await stat(abs);
76
- if (st.mtimeMs < sinceMs) continue;
77
- }
78
- const content = await fileReader(abs, "utf-8");
79
- records.push({ path: relPosix, content });
80
- const { sha256, bytes } = await sha256File(abs);
81
- manifestFiles.push({ path: relPosix, sha256, bytes });
82
- }
83
- records.sort((a, b) => a.path.localeCompare(b.path));
84
- manifestFiles.sort((a, b) => a.path.localeCompare(b.path));
85
- const capsule = buildCapsuleBlock(opts.name, opts.capsule);
86
- const includesTranscripts = transcriptsOverride || (includeKinds ?? /* @__PURE__ */ new Set()).has(TRANSCRIPTS_DIR);
87
- const createdAtMs = opts.now ?? Date.now();
88
- const manifest = ExportManifestV2Schema.parse({
89
- format: EXPORT_FORMAT,
90
- schemaVersion: CAPSULE_SCHEMA_VERSION,
91
- createdAt: new Date(createdAtMs).toISOString(),
92
- pluginVersion: opts.pluginVersion ?? "0.0.0",
93
- includesTranscripts,
94
- files: manifestFiles,
95
- capsule
96
- });
97
- const bundle = ExportBundleV2Schema.parse({
98
- manifest,
99
- records
100
- });
101
- const archivePath = path2.join(outDirAbs, `${opts.name}.capsule.json.gz`);
102
- const manifestPath = path2.join(outDirAbs, `${opts.name}.manifest.json`);
103
- await writeJsonFile(manifestPath, manifest);
104
- const json = JSON.stringify(bundle);
105
- const gz = gzipSync(Buffer.from(json, "utf-8"));
106
- await writeFile(archivePath, gz);
107
- if (opts.encrypt === true) {
108
- if (!opts.memoryDir) {
109
- throw new Error(
110
- "exportCapsule: 'memoryDir' is required when 'encrypt' is true so the secure-store key can be retrieved."
111
- );
112
- }
113
- const { encPath } = await encryptCapsuleFile({
114
- sourceGzPath: archivePath,
115
- memoryDir: opts.memoryDir
116
- });
117
- const { unlink } = await import("fs/promises");
118
- await unlink(archivePath);
119
- return { archivePath: encPath, manifestPath, manifest, encryptedArchivePath: encPath };
120
- }
121
- return { archivePath, manifestPath, manifest, encryptedArchivePath: null };
122
- }
123
- function validateName(name) {
124
- if (typeof name !== "string" || !CAPSULE_ID_PATTERN.test(name)) {
125
- throw new Error(
126
- `exportCapsule: invalid capsule name. Expected /${CAPSULE_ID_PATTERN.source}/`
127
- );
128
- }
129
- if (name.length > 64) {
130
- throw new Error(
131
- "exportCapsule: invalid capsule name. Must be 64 characters or fewer."
132
- );
133
- }
134
- }
135
- var ISO_8601_RE = /^\d{4}-\d{2}-\d{2}(?:[Tt]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:[Zz]|[+-]\d{2}:?\d{2}))?$/;
136
- function parseSince(since) {
137
- if (since === void 0) return null;
138
- if (typeof since !== "string" || since.trim() === "") {
139
- throw new Error("exportCapsule: 'since' must be a non-empty ISO-8601 string");
140
- }
141
- if (!ISO_8601_RE.test(since)) {
142
- throw new Error(
143
- `exportCapsule: 'since' is not a valid ISO-8601 timestamp: ${since}`
144
- );
145
- }
146
- const ms = Date.parse(since);
147
- if (!Number.isFinite(ms)) {
148
- throw new Error(
149
- `exportCapsule: 'since' is not a valid ISO-8601 timestamp: ${since}`
150
- );
151
- }
152
- assertCalendarRoundTrip(since, ms);
153
- return ms;
154
- }
155
- function isValidCapsuleSince(since) {
156
- try {
157
- parseSince(since);
158
- return true;
159
- } catch {
160
- return false;
161
- }
162
- }
163
- function assertCalendarRoundTrip(since, ms) {
164
- const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(since);
165
- if (!m) return;
166
- const wantY = Number(m[1]);
167
- const wantMo = Number(m[2]);
168
- const wantD = Number(m[3]);
169
- const offsetMatch = /([+-])(\d{2}):?(\d{2})$/.exec(since);
170
- let displayMs = ms;
171
- if (offsetMatch) {
172
- const sign = offsetMatch[1] === "-" ? -1 : 1;
173
- const offsetMin = sign * (Number(offsetMatch[2]) * 60 + Number(offsetMatch[3]));
174
- displayMs = ms + offsetMin * 6e4;
175
- }
176
- const dd = new Date(displayMs);
177
- const gotY = dd.getUTCFullYear();
178
- const gotMo = dd.getUTCMonth() + 1;
179
- const gotD = dd.getUTCDate();
180
- if (gotY !== wantY || gotMo !== wantMo || gotD !== wantD) {
181
- throw new Error(
182
- `exportCapsule: 'since' is not a valid ISO-8601 timestamp: ${since}`
183
- );
184
- }
185
- }
186
- function normalizeIncludeKinds(kinds) {
187
- if (kinds === void 0) return null;
188
- const set = /* @__PURE__ */ new Set();
189
- for (const raw of kinds) {
190
- if (typeof raw !== "string" || raw.trim() === "") {
191
- throw new Error("exportCapsule: 'includeKinds' entries must be non-empty strings");
192
- }
193
- if (raw.includes("/") || raw.includes("\\")) {
194
- throw new Error(
195
- `exportCapsule: 'includeKinds' entries must be top-level segment names, got: ${raw}`
196
- );
197
- }
198
- set.add(raw);
199
- }
200
- return set;
201
- }
202
- function normalizePeerIds(peerIds) {
203
- if (peerIds === void 0) return null;
204
- const set = /* @__PURE__ */ new Set();
205
- for (const raw of peerIds) {
206
- if (typeof raw !== "string" || raw.trim() === "") {
207
- throw new Error("exportCapsule: 'peerIds' entries must be non-empty strings");
208
- }
209
- if (raw.includes("/") || raw.includes("\\") || raw === "." || raw === "..") {
210
- throw new Error(
211
- `exportCapsule: 'peerIds' entries must be plain segment names, got: ${raw}`
212
- );
213
- }
214
- set.add(raw);
215
- }
216
- return set;
217
- }
218
- async function assertIsDirectory(absPath) {
219
- const st = await stat(absPath).catch(() => null);
220
- if (!st || !st.isDirectory()) {
221
- throw new Error(
222
- `exportCapsule: 'root' must be an existing directory: ${absPath}`
223
- );
224
- }
225
- }
226
- function shouldInclude(relPosix, includeKinds, peerFilter, outDirRelPosix, transcriptsOverride = false) {
227
- const parts = relPosix.split("/");
228
- if (parts.some((p) => DEFAULT_EXCLUDE_DIRS.has(p))) return false;
229
- if (outDirRelPosix !== null) {
230
- if (relPosix === outDirRelPosix) return false;
231
- if (relPosix.startsWith(`${outDirRelPosix}/`)) return false;
232
- }
233
- const top = parts[0];
234
- if (top === TRANSCRIPTS_DIR && !transcriptsOverride && (includeKinds === null || !includeKinds.has(TRANSCRIPTS_DIR))) {
235
- return false;
236
- }
237
- if (includeKinds !== null) {
238
- if (parts.length < 2) return false;
239
- if (!includeKinds.has(top)) return false;
240
- }
241
- if (top === PEERS_DIR && peerFilter !== null) {
242
- if (peerFilter.size === 0) return false;
243
- if (parts.length < 2) return false;
244
- if (!peerFilter.has(parts[1])) return false;
245
- }
246
- return true;
247
- }
248
- function buildCapsuleBlock(name, override) {
249
- const parent = override?.parent ?? null;
250
- const parentCapsule = override && Object.prototype.hasOwnProperty.call(override, "parentCapsule") ? override.parentCapsule ?? null : parent?.capsuleId ?? null;
251
- const merged = {
252
- id: name,
253
- version: override?.version ?? "0.1.0",
254
- schemaVersion: override?.schemaVersion ?? "taxonomy-v1",
255
- parentCapsule,
256
- parent,
257
- description: override?.description ?? "",
258
- retrievalPolicy: override?.retrievalPolicy ?? {
259
- tierWeights: {},
260
- directAnswerEnabled: false
261
- },
262
- includes: override?.includes ?? {
263
- taxonomy: false,
264
- identityAnchors: false,
265
- peerProfiles: false,
266
- procedural: false
267
- }
268
- };
269
- return CapsuleBlockSchema.parse(merged);
270
- }
271
-
272
- export {
273
- exportCapsule,
274
- isValidCapsuleSince
275
- };
@@ -1,47 +0,0 @@
1
- // ../remnic-core/src/logger.ts
2
- var NOOP_LOGGER = {
3
- info() {
4
- },
5
- warn() {
6
- },
7
- error() {
8
- },
9
- debug() {
10
- }
11
- };
12
- var _backend = NOOP_LOGGER;
13
- var _debug = false;
14
- var CONSOLE_LOGGER = {
15
- info: console.info.bind(console),
16
- warn: console.warn.bind(console),
17
- error: console.error.bind(console),
18
- debug: console.debug.bind(console)
19
- };
20
- function initLogger(backend, debug) {
21
- _backend = backend ?? CONSOLE_LOGGER;
22
- _debug = debug ?? false;
23
- }
24
- var log = {
25
- info(msg, ...args) {
26
- _backend.info(`remnic: ${msg}`, ...args);
27
- },
28
- warn(msg, ...args) {
29
- _backend.warn(`remnic: ${msg}`, ...args);
30
- },
31
- error(msg, err) {
32
- const detail = err instanceof Error ? err.message : err ? String(err) : "";
33
- _backend.error(
34
- `remnic: ${msg}${detail ? ` \u2014 ${detail}` : ""}`
35
- );
36
- },
37
- debug(msg, ...args) {
38
- if (!_debug) return;
39
- const fn = _backend.debug ?? _backend.info;
40
- fn(`remnic [debug]: ${msg}`, ...args);
41
- }
42
- };
43
-
44
- export {
45
- initLogger,
46
- log
47
- };
@@ -1,272 +0,0 @@
1
- import {
2
- readPair,
3
- resolvePair
4
- } from "./chunk-YJYZMLD5.js";
5
- import {
6
- log
7
- } from "./chunk-UFU5GGGA.js";
8
-
9
- // ../remnic-core/src/contradiction/resolution.ts
10
- var VALID_VERBS = ["keep-a", "keep-b", "merge", "both-valid", "needs-more-context"];
11
- function isValidResolutionVerb(value) {
12
- return VALID_VERBS.includes(value);
13
- }
14
- async function executeResolution(memoryDir, storage, pairId, verb, options = {}) {
15
- if (typeof verb !== "string" || !isValidResolutionVerb(verb)) {
16
- throw new Error(`Invalid contradiction resolution verb: ${String(verb)}`);
17
- }
18
- const pair = readPair(memoryDir, pairId);
19
- if (!pair) {
20
- return { pairId, verb, affectedIds: [], message: `Pair ${pairId} not found` };
21
- }
22
- if (pair.namespace && !options.storageForNamespace) {
23
- throw new Error(
24
- "contradiction resolution requires storageForNamespace for namespaced pairs so callers resolve the correct namespace storage"
25
- );
26
- }
27
- const resolutionStorage = options.storageForNamespace ? await options.storageForNamespace(pair.namespace) : storage;
28
- if (pair.resolution && pair.resolution !== "needs-more-context") {
29
- return { pairId, verb, affectedIds: [], message: `Pair already resolved with verb "${pair.resolution}"` };
30
- }
31
- const [idA, idB] = pair.memoryIds;
32
- const affectedIds = [];
33
- let message = "";
34
- let supersedeFailed = false;
35
- switch (verb) {
36
- case "keep-a": {
37
- const keepTarget = await validateKeepTarget(resolutionStorage, pairId, idA);
38
- if (!keepTarget.ok) {
39
- supersedeFailed = true;
40
- message = keepTarget.message;
41
- break;
42
- }
43
- const sourceB = await loadSourceSnapshot(resolutionStorage, idB);
44
- const ok = sourceB ? await supersedeSafe(resolutionStorage, idB, idA, "contradiction-resolution:keep-a") : false;
45
- if (ok) {
46
- affectedIds.push(idB);
47
- message = `Kept ${idA}, superseded ${idB}`;
48
- } else {
49
- supersedeFailed = true;
50
- const rolledBack = sourceB ? await restoreMemorySnapshot(resolutionStorage, sourceB, "contradiction-resolution:keep-a-rollback") : false;
51
- message = rolledBack ? `Supersede failed for ${idB}; restored ${idB} and did not resolve` : `Supersede failed for ${idB}; rollback incomplete for ${idB} and pair is not resolved`;
52
- }
53
- break;
54
- }
55
- case "keep-b": {
56
- const keepTarget = await validateKeepTarget(resolutionStorage, pairId, idB);
57
- if (!keepTarget.ok) {
58
- supersedeFailed = true;
59
- message = keepTarget.message;
60
- break;
61
- }
62
- const sourceA = await loadSourceSnapshot(resolutionStorage, idA);
63
- const ok = sourceA ? await supersedeSafe(resolutionStorage, idA, idB, "contradiction-resolution:keep-b") : false;
64
- if (ok) {
65
- affectedIds.push(idA);
66
- message = `Kept ${idB}, superseded ${idA}`;
67
- } else {
68
- supersedeFailed = true;
69
- const rolledBack = sourceA ? await restoreMemorySnapshot(resolutionStorage, sourceA, "contradiction-resolution:keep-b-rollback") : false;
70
- message = rolledBack ? `Supersede failed for ${idA}; restored ${idA} and did not resolve` : `Supersede failed for ${idA}; rollback incomplete for ${idA} and pair is not resolved`;
71
- }
72
- break;
73
- }
74
- case "merge": {
75
- const replacement = await prepareMergeReplacement(resolutionStorage, pairId, idA, idB, options);
76
- if (!replacement.ok) {
77
- supersedeFailed = true;
78
- message = replacement.message;
79
- break;
80
- }
81
- const okA = await supersedeSafe(resolutionStorage, idA, replacement.mergedId, "contradiction-resolution:merge");
82
- if (!okA) {
83
- supersedeFailed = true;
84
- const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);
85
- message = rolledBackA ? `Merge failed for ${idA}; restored ${idA} and did not resolve` : `Merge failed for ${idA}; rollback incomplete for ${idA} and pair is not resolved`;
86
- if (rolledBackA) {
87
- await cleanupCreatedReplacement(resolutionStorage, replacement);
88
- }
89
- break;
90
- }
91
- const okB = await supersedeSafe(resolutionStorage, idB, replacement.mergedId, "contradiction-resolution:merge");
92
- if (!okB) {
93
- supersedeFailed = true;
94
- const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);
95
- const rolledBackB = await restoreMemorySnapshot(resolutionStorage, replacement.sourceB);
96
- message = rolledBackA && rolledBackB ? `Merge failed for ${idB}; restored ${idA} and ${idB} and did not resolve` : `Merge failed for ${idB}; rollback incomplete for ${[
97
- rolledBackA ? void 0 : idA,
98
- rolledBackB ? void 0 : idB
99
- ].filter(Boolean).join(", ")} and pair is not resolved`;
100
- if (rolledBackA && rolledBackB) {
101
- await cleanupCreatedReplacement(resolutionStorage, replacement);
102
- }
103
- break;
104
- }
105
- affectedIds.push(idA, idB);
106
- message = `Both memories superseded by merged ${replacement.mergedId}`;
107
- break;
108
- }
109
- case "both-valid": {
110
- message = "Pair marked as both-valid; cooldown applied";
111
- break;
112
- }
113
- case "needs-more-context": {
114
- message = "Deferred; no action taken, short cooldown applied";
115
- break;
116
- }
117
- }
118
- if (!supersedeFailed) {
119
- resolvePair(memoryDir, pairId, verb);
120
- }
121
- log.info("[contradiction-resolution] pair=%s verb=%s affected=%d", pairId, verb, affectedIds.length);
122
- return { pairId, verb, affectedIds, message };
123
- }
124
- async function prepareMergeReplacement(storage, pairId, idA, idB, options) {
125
- const sourceA = await storage.getMemoryById(idA);
126
- const sourceB = await storage.getMemoryById(idB);
127
- if (!sourceA || !sourceB) {
128
- return { ok: false, message: `Merge requires both source memories to exist; not resolving ${pairId}` };
129
- }
130
- const requestedMergedId = options.mergedMemoryId?.trim();
131
- if (requestedMergedId) {
132
- if (requestedMergedId === idA || requestedMergedId === idB) {
133
- return { ok: false, message: "Merge replacement must be distinct from both source memories; not resolving" };
134
- }
135
- const replacement2 = await storage.getMemoryById(requestedMergedId);
136
- if (!replacement2) {
137
- return { ok: false, message: `Merged memory ${requestedMergedId} not found; not resolving` };
138
- }
139
- const replacementStatus = replacement2.frontmatter.status ?? "active";
140
- if (replacementStatus !== "active") {
141
- return {
142
- ok: false,
143
- message: `Merged memory ${requestedMergedId} is ${replacementStatus}; not resolving`
144
- };
145
- }
146
- return { ok: true, mergedId: requestedMergedId, sourceA, sourceB, created: false };
147
- }
148
- const mergedContent = options.mergedContent;
149
- if (typeof mergedContent !== "string" || mergedContent.trim().length === 0) {
150
- return {
151
- ok: false,
152
- message: "Merge requires mergedMemoryId or mergedContent; no memories changed"
153
- };
154
- }
155
- const category = options.mergedCategory ?? mergedMemoryCategory(sourceA, sourceB);
156
- let mergedId;
157
- try {
158
- mergedId = await storage.writeMemory(category, mergedContent, {
159
- actor: "contradiction-resolution",
160
- confidence: Math.min(sourceA.frontmatter.confidence ?? 0.8, sourceB.frontmatter.confidence ?? 0.8),
161
- tags: ["contradiction-resolution", "merge"],
162
- source: "contradiction-resolution",
163
- lineage: [idA, idB],
164
- derivedFrom: [idA, idB],
165
- derivedVia: "merge"
166
- });
167
- } catch (err) {
168
- log.warn(
169
- "[contradiction-resolution] merged memory creation failed for %s: %s",
170
- pairId,
171
- err instanceof Error ? err.message : err
172
- );
173
- return { ok: false, message: `Merged memory could not be created; not resolving ${pairId}` };
174
- }
175
- const replacement = await storage.getMemoryById(mergedId);
176
- if (!replacement) {
177
- await cleanupMemoryId(storage, mergedId);
178
- return { ok: false, message: `Merged memory ${mergedId} could not be verified; not resolving` };
179
- }
180
- return { ok: true, mergedId, sourceA, sourceB, created: true };
181
- }
182
- function mergedMemoryCategory(sourceA, sourceB) {
183
- return sourceA.frontmatter.category === sourceB.frontmatter.category ? sourceA.frontmatter.category : "fact";
184
- }
185
- async function validateKeepTarget(storage, pairId, keepId) {
186
- const target = await loadSourceSnapshot(storage, keepId);
187
- if (!target) {
188
- return { ok: false, message: `Kept memory ${keepId} not found; not resolving ${pairId}` };
189
- }
190
- const status = target.frontmatter.status ?? "active";
191
- if (status !== "active") {
192
- return { ok: false, message: `Kept memory ${keepId} is ${status}; not resolving ${pairId}` };
193
- }
194
- return { ok: true };
195
- }
196
- async function loadSourceSnapshot(storage, memoryId) {
197
- try {
198
- return await storage.getMemoryById(memoryId);
199
- } catch (err) {
200
- log.warn(
201
- "[contradiction-resolution] source snapshot failed for %s: %s",
202
- memoryId,
203
- err instanceof Error ? err.message : err
204
- );
205
- return null;
206
- }
207
- }
208
- async function restoreMemorySnapshot(storage, memory, reasonCode = "contradiction-resolution:merge-rollback") {
209
- try {
210
- const current = await storage.getMemoryById(memory.frontmatter.id);
211
- if (!current) return false;
212
- const restoredFrontmatter = {
213
- ...memory.frontmatter,
214
- status: memory.frontmatter.status,
215
- supersededBy: memory.frontmatter.supersededBy,
216
- supersededAt: memory.frontmatter.supersededAt
217
- };
218
- return await storage.writeMemoryFrontmatter(current, restoredFrontmatter, {
219
- actor: "contradiction-resolution",
220
- reasonCode
221
- });
222
- } catch (err) {
223
- log.warn(
224
- "[contradiction-resolution] rollback failed for %s: %s",
225
- memory.frontmatter.id,
226
- err instanceof Error ? err.message : err
227
- );
228
- return false;
229
- }
230
- }
231
- async function cleanupCreatedReplacement(storage, replacement) {
232
- if (!replacement.created) return;
233
- await cleanupMemoryId(storage, replacement.mergedId);
234
- }
235
- async function cleanupMemoryId(storage, memoryId) {
236
- try {
237
- const memory = await storage.getMemoryById(memoryId);
238
- const invalidated = await storage.invalidateMemory(memoryId);
239
- if (invalidated && memory?.frontmatter.category === "fact") {
240
- await storage.removeFactContentHashesForMemories([memory]);
241
- }
242
- } catch (err) {
243
- log.warn(
244
- "[contradiction-resolution] cleanup failed for merged memory %s: %s",
245
- memoryId,
246
- err instanceof Error ? err.message : err
247
- );
248
- }
249
- }
250
- async function supersedeSafe(storage, oldId, newId, reason) {
251
- try {
252
- const result = await storage.supersedeMemory(oldId, newId, reason);
253
- if (result === false) {
254
- log.warn("[contradiction-resolution] supersede returned false for %s \u2192 %s", oldId, newId);
255
- return false;
256
- }
257
- return true;
258
- } catch (err) {
259
- log.warn(
260
- "[contradiction-resolution] supersede failed %s \u2192 %s: %s",
261
- oldId,
262
- newId,
263
- err instanceof Error ? err.message : err
264
- );
265
- return false;
266
- }
267
- }
268
-
269
- export {
270
- isValidResolutionVerb,
271
- executeResolution
272
- };