@remnic/plugin-openclaw 1.0.10 → 1.0.11
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/{calibration-674TDQNV.js → calibration-WCHOK6DX.js} +12 -4
- package/dist/capsule-cli-TFKLAG3S.js +329 -0
- package/dist/capsule-crypto-K3IRTKRH.js +17 -0
- package/dist/capsule-export-CVA3CKUQ.js +265 -0
- package/dist/capsule-import-CFX7BY5W.js +16 -0
- package/dist/capsule-merge-7RVOHJK3.js +189 -0
- package/dist/{causal-chain-OKDZSDEB.js → causal-chain-WYN5QOPS.js} +3 -2
- package/dist/{causal-consolidation-5BEXLQV5.js → causal-consolidation-JD6KJJH6.js} +16 -12
- package/dist/{causal-retrieval-3BKBXVXD.js → causal-retrieval-NZHQOZOE.js} +6 -5
- package/dist/{causal-trajectory-graph-RQIT37DN.js → causal-trajectory-graph-VBPE2WPM.js} +1 -1
- package/dist/chunk-37NKFWSO.js +233 -0
- package/dist/chunk-3G7FAF6S.js +60 -0
- package/dist/{chunk-Z7GRLVK3.js → chunk-3GUF7RQI.js} +235 -19
- package/dist/chunk-3I7RHWYT.js +214 -0
- package/dist/chunk-4G2XCSD2.js +186 -0
- package/dist/chunk-6IWEAUN6.js +148 -0
- package/dist/{chunk-LN5UZQVG.js → chunk-6UFI73TJ.js} +5 -3
- package/dist/chunk-7OQEPGQF.js +529 -0
- package/dist/chunk-B52XADV3.js +244 -0
- package/dist/chunk-BU5KJVWF.js +78 -0
- package/dist/chunk-CXM7EBAO.js +289 -0
- package/dist/chunk-ETJZRIAM.js +227 -0
- package/dist/chunk-FQRSVYY4.js +110 -0
- package/dist/chunk-HRGFO6AW.js +349 -0
- package/dist/chunk-I6B2W2IY.js +47 -0
- package/dist/chunk-JZBOXOUC.js +259 -0
- package/dist/chunk-K7EUBNDD.js +185 -0
- package/dist/chunk-L4PRBB2A.js +1860 -0
- package/dist/chunk-MBIFE6SA.js +250 -0
- package/dist/chunk-N7EOZY6F.js +400 -0
- package/dist/chunk-NKVIN6RD.js +118 -0
- package/dist/chunk-OEI7GLV2.js +17 -0
- package/dist/{chunk-S2ISS4AH.js → chunk-P3DIW2SD.js} +10 -10
- package/dist/{chunk-7TENHBV2.js → chunk-RQCTMECT.js} +10 -48
- package/dist/chunk-SSFTU6LP.js +182 -0
- package/dist/{chunk-BXTMZDRT.js → chunk-SVSQAG6M.js} +7 -5
- package/dist/chunk-TLVIQLB4.js +874 -0
- package/dist/{chunk-JJSNPSCD.js → chunk-TNH24SF6.js} +352 -50
- package/dist/chunk-TVKKIS53.js +720 -0
- package/dist/{chunk-YHH3SXKD.js → chunk-WPINX4MF.js} +1 -59
- package/dist/{chunk-HCFFXBLV.js → chunk-XMSDA5WA.js} +5 -1861
- package/dist/chunk-YGGGUTG3.js +125 -0
- package/dist/chunk-YGXXBRV7.js +10 -0
- package/dist/cipher-VHAFCG7Z.js +27 -0
- package/dist/dreams-ledger-3I52ISYR.js +285 -0
- package/dist/{engine-65C2J63X.js → engine-VMTFKFGO.js} +5 -2
- package/dist/{fallback-llm-LVK5PDIM.js → fallback-llm-WCWNGIQ3.js} +2 -1
- package/dist/first-start-migration-I24M2JEE.js +258 -0
- package/dist/forget-NI4RBDPB.js +68 -0
- package/dist/fs-utils-PZRI2HDZ.js +29 -0
- package/dist/graph-edge-decay-5CVKWBYH.js +203 -0
- package/dist/index.js +9775 -2900
- package/dist/kdf-H5B23ZM2.js +25 -0
- package/dist/memory-governance-DWGFV4FX.js +25 -0
- package/dist/metadata-JAGIWHEA.js +20 -0
- package/dist/migrate-from-identity-anchor-N3354WMP.js +7 -0
- package/dist/path-5LCUBAAZ.js +8 -0
- package/dist/peers-JF2I6RCR.js +43 -0
- package/dist/purge-XN2VSPZ2.js +204 -0
- package/dist/secure-store-FWJ7LBPH.js +149 -0
- package/dist/state-PVISYXRH.js +7 -0
- package/dist/state-store-LP5BO6SF.js +15 -0
- package/dist/{storage-DM4ZGOCN.js → storage-T2OGFUF4.js} +3 -1
- package/dist/tier-stats-IZNW66NC.js +147 -0
- package/dist/trace-NJESSGH7.js +289 -0
- package/dist/tui-MGK2LYJY.js +12 -0
- package/dist/types-H5R5D3WF.js +30 -0
- package/openclaw.plugin.json +519 -4
- package/package.json +1 -1
|
@@ -1,6 +1,84 @@
|
|
|
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
|
+
|
|
1
45
|
// ../remnic-core/src/graph.ts
|
|
2
46
|
import { mkdir, appendFile, readFile } from "fs/promises";
|
|
3
47
|
import * as path from "path";
|
|
48
|
+
|
|
49
|
+
// ../remnic-core/src/graph-events.ts
|
|
50
|
+
import { EventEmitter } from "events";
|
|
51
|
+
var buses = /* @__PURE__ */ new Map();
|
|
52
|
+
function getGraphEventBus(memoryDir) {
|
|
53
|
+
let bus = buses.get(memoryDir);
|
|
54
|
+
if (!bus) {
|
|
55
|
+
bus = new EventEmitter();
|
|
56
|
+
bus.setMaxListeners(200);
|
|
57
|
+
buses.set(memoryDir, bus);
|
|
58
|
+
}
|
|
59
|
+
return bus;
|
|
60
|
+
}
|
|
61
|
+
function emitGraphEvent(memoryDir, type, payload) {
|
|
62
|
+
const event = {
|
|
63
|
+
type,
|
|
64
|
+
memoryDir,
|
|
65
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
66
|
+
payload
|
|
67
|
+
};
|
|
68
|
+
const bus = getGraphEventBus(memoryDir);
|
|
69
|
+
try {
|
|
70
|
+
bus.emit("graph-event", event);
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function subscribeGraphEvents(memoryDir, listener) {
|
|
75
|
+
const bus = getGraphEventBus(memoryDir);
|
|
76
|
+
bus.on("graph-event", listener);
|
|
77
|
+
return () => bus.off("graph-event", listener);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ../remnic-core/src/graph.ts
|
|
81
|
+
var DEFAULT_GRAPH_TRAVERSAL_CONFIDENCE_FLOOR = 0.2;
|
|
4
82
|
var CAUSAL_PHRASES = [
|
|
5
83
|
"as a result",
|
|
6
84
|
"led to",
|
|
@@ -18,29 +96,71 @@ function graphFilePath(memoryDir, type) {
|
|
|
18
96
|
async function ensureGraphsDir(memoryDir) {
|
|
19
97
|
await mkdir(graphsDir(memoryDir), { recursive: true });
|
|
20
98
|
}
|
|
99
|
+
var graphWriteLocks = /* @__PURE__ */ new Map();
|
|
100
|
+
function withGraphWriteLock(filePath, fn) {
|
|
101
|
+
const prev = graphWriteLocks.get(filePath) ?? Promise.resolve();
|
|
102
|
+
const next = prev.then(fn, fn);
|
|
103
|
+
graphWriteLocks.set(
|
|
104
|
+
filePath,
|
|
105
|
+
next.then(
|
|
106
|
+
() => void 0,
|
|
107
|
+
() => void 0
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
return next;
|
|
111
|
+
}
|
|
21
112
|
async function appendEdge(memoryDir, edge) {
|
|
22
113
|
await ensureGraphsDir(memoryDir);
|
|
114
|
+
const filePath = graphFilePath(memoryDir, edge.type);
|
|
23
115
|
const line = JSON.stringify(edge) + "\n";
|
|
24
|
-
await
|
|
116
|
+
await withGraphWriteLock(filePath, async () => {
|
|
117
|
+
await appendFile(filePath, line, "utf8");
|
|
118
|
+
});
|
|
119
|
+
emitGraphEvent(memoryDir, "edge-added", {
|
|
120
|
+
source: edge.from,
|
|
121
|
+
target: edge.to,
|
|
122
|
+
kind: edge.type,
|
|
123
|
+
weight: edge.weight,
|
|
124
|
+
label: edge.label,
|
|
125
|
+
confidence: typeof edge.confidence === "number" ? edge.confidence : 1
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function isNodeError(err) {
|
|
129
|
+
return typeof err === "object" && err !== null && "code" in err;
|
|
130
|
+
}
|
|
131
|
+
function parseEdgesJsonl(raw) {
|
|
132
|
+
const edges = [];
|
|
133
|
+
for (const line of raw.split("\n")) {
|
|
134
|
+
const trimmed = line.trim();
|
|
135
|
+
if (!trimmed) continue;
|
|
136
|
+
try {
|
|
137
|
+
edges.push(JSON.parse(trimmed));
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return edges;
|
|
25
142
|
}
|
|
26
143
|
async function readEdges(memoryDir, type) {
|
|
27
144
|
const filePath = graphFilePath(memoryDir, type);
|
|
28
145
|
try {
|
|
29
146
|
const raw = await readFile(filePath, "utf8");
|
|
30
|
-
|
|
31
|
-
for (const line of raw.split("\n")) {
|
|
32
|
-
const trimmed = line.trim();
|
|
33
|
-
if (!trimmed) continue;
|
|
34
|
-
try {
|
|
35
|
-
edges.push(JSON.parse(trimmed));
|
|
36
|
-
} catch {
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return edges;
|
|
147
|
+
return parseEdgesJsonl(raw);
|
|
40
148
|
} catch {
|
|
41
149
|
return [];
|
|
42
150
|
}
|
|
43
151
|
}
|
|
152
|
+
async function readEdgesStrict(memoryDir, type) {
|
|
153
|
+
const filePath = graphFilePath(memoryDir, type);
|
|
154
|
+
try {
|
|
155
|
+
const raw = await readFile(filePath, "utf8");
|
|
156
|
+
return parseEdgesJsonl(raw);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
if (isNodeError(err) && err.code === "ENOENT") {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
44
164
|
async function readAllEdges(memoryDir, config) {
|
|
45
165
|
const parts = await Promise.all([
|
|
46
166
|
config.entityGraphEnabled ? readEdges(memoryDir, "entity") : Promise.resolve([]),
|
|
@@ -238,21 +358,38 @@ var GraphIndex = class _GraphIndex {
|
|
|
238
358
|
* Spreading activation BFS (SYNAPSE-inspired).
|
|
239
359
|
*
|
|
240
360
|
* Starting from `seeds`, traverse the combined graph for up to `maxSteps` hops.
|
|
241
|
-
* Each candidate gets an activation score = edge.weight × decay^hop.
|
|
242
|
-
* Returns top-N candidate paths sorted by descending activation score.
|
|
361
|
+
* Each candidate gets an activation score = edge.weight × edgeConfidence × decay^hop.
|
|
243
362
|
*
|
|
244
|
-
*
|
|
363
|
+
* Issue #681 PR 3/3 — confidence-aware traversal:
|
|
364
|
+
* - Each edge's `weight` is multiplied by its `confidence` (legacy edges
|
|
365
|
+
* missing `confidence` are treated as 1.0, preserving prior behavior).
|
|
366
|
+
* - Edges with `confidence < graphTraversalConfidenceFloor` are pruned and
|
|
367
|
+
* contribute neither activation nor downstream neighbors.
|
|
368
|
+
* - When `graphTraversalPageRankIterations > 0`, an additional PageRank-
|
|
369
|
+
* style refinement pass redistributes activation along confidence-weighted
|
|
370
|
+
* edges, sharpening the ranking among multi-hop candidates.
|
|
371
|
+
* - Per-result provenance includes the highest-confidence edge that landed
|
|
372
|
+
* on each candidate, so the X-ray surface can attribute pruning and
|
|
373
|
+
* ranking decisions back to specific edges.
|
|
374
|
+
*
|
|
375
|
+
* @param seeds - initial memory paths to expand from (e.g. QMD top results)
|
|
245
376
|
* @param maxSteps - max BFS hops (from config: maxGraphTraversalSteps)
|
|
246
|
-
* @returns Array of {path, score} sorted descending, not including seed paths
|
|
377
|
+
* @returns Array of {path, score, edgeConfidence, ...} sorted descending, not including seed paths
|
|
247
378
|
*/
|
|
248
|
-
async spreadingActivation(seeds, maxSteps) {
|
|
379
|
+
async spreadingActivation(seeds, maxSteps, opts) {
|
|
249
380
|
if (!this.cfg.multiGraphMemoryEnabled) return [];
|
|
250
381
|
const steps = maxSteps ?? this.cfg.maxGraphTraversalSteps;
|
|
251
382
|
const decay = this.cfg.graphActivationDecay;
|
|
383
|
+
const floor = opts?.includeLowConfidence === true ? 0 : clampConfidenceFloor(this.cfg.graphTraversalConfidenceFloor);
|
|
384
|
+
const iterations = clampPageRankIterations(
|
|
385
|
+
this.cfg.graphTraversalPageRankIterations
|
|
386
|
+
);
|
|
252
387
|
try {
|
|
253
388
|
const allEdges = await this.loadEdgesCached();
|
|
254
389
|
const adj = /* @__PURE__ */ new Map();
|
|
255
390
|
for (const edge of allEdges) {
|
|
391
|
+
const conf = readEdgeConfidence(edge);
|
|
392
|
+
if (conf < floor) continue;
|
|
256
393
|
if (!adj.has(edge.from)) adj.set(edge.from, []);
|
|
257
394
|
adj.get(edge.from).push(edge);
|
|
258
395
|
if (edge.type !== "causal") {
|
|
@@ -271,7 +408,9 @@ var GraphIndex = class _GraphIndex {
|
|
|
271
408
|
const edges = adj.get(node) ?? [];
|
|
272
409
|
for (const edge of edges) {
|
|
273
410
|
const neighbor = edge.to === node ? edge.from : edge.to;
|
|
274
|
-
const
|
|
411
|
+
const conf = readEdgeConfidence(edge);
|
|
412
|
+
if (conf < floor) continue;
|
|
413
|
+
const score = edge.weight * conf * Math.pow(decay, hop + 1);
|
|
275
414
|
if (!seedSet.has(neighbor)) {
|
|
276
415
|
const existing = scores.get(neighbor) ?? 0;
|
|
277
416
|
scores.set(neighbor, existing + score);
|
|
@@ -281,7 +420,8 @@ var GraphIndex = class _GraphIndex {
|
|
|
281
420
|
seed: sourceSeed,
|
|
282
421
|
hopDepth: hop + 1,
|
|
283
422
|
decayedWeight: score,
|
|
284
|
-
graphType: edge.type
|
|
423
|
+
graphType: edge.type,
|
|
424
|
+
edgeConfidence: conf
|
|
285
425
|
});
|
|
286
426
|
}
|
|
287
427
|
}
|
|
@@ -291,6 +431,13 @@ var GraphIndex = class _GraphIndex {
|
|
|
291
431
|
}
|
|
292
432
|
}
|
|
293
433
|
}
|
|
434
|
+
if (iterations > 0 && scores.size > 1) {
|
|
435
|
+
applyPageRankRefinement(scores, adj, {
|
|
436
|
+
iterations,
|
|
437
|
+
floor,
|
|
438
|
+
damping: 0.85
|
|
439
|
+
});
|
|
440
|
+
}
|
|
294
441
|
if (this.cfg.graphLateralInhibitionEnabled && scores.size > 1) {
|
|
295
442
|
const inhibited = applyLateralInhibition(scores, {
|
|
296
443
|
beta: this.cfg.graphLateralInhibitionBeta,
|
|
@@ -306,7 +453,8 @@ var GraphIndex = class _GraphIndex {
|
|
|
306
453
|
seed: provenance.get(p)?.seed ?? "",
|
|
307
454
|
hopDepth: provenance.get(p)?.hopDepth ?? 0,
|
|
308
455
|
decayedWeight: provenance.get(p)?.decayedWeight ?? 0,
|
|
309
|
-
graphType: provenance.get(p)?.graphType ?? "entity"
|
|
456
|
+
graphType: provenance.get(p)?.graphType ?? "entity",
|
|
457
|
+
edgeConfidence: provenance.get(p)?.edgeConfidence ?? 1
|
|
310
458
|
})).sort((a, b) => b.score - a.score);
|
|
311
459
|
} catch (err) {
|
|
312
460
|
const { log } = await import("./logger-TNOKCH7X.js");
|
|
@@ -315,6 +463,63 @@ var GraphIndex = class _GraphIndex {
|
|
|
315
463
|
}
|
|
316
464
|
}
|
|
317
465
|
};
|
|
466
|
+
function clampConfidenceFloor(raw) {
|
|
467
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
468
|
+
return DEFAULT_GRAPH_TRAVERSAL_CONFIDENCE_FLOOR;
|
|
469
|
+
}
|
|
470
|
+
if (raw < 0) return 0;
|
|
471
|
+
if (raw > 1) return 1;
|
|
472
|
+
return raw;
|
|
473
|
+
}
|
|
474
|
+
function clampPageRankIterations(raw) {
|
|
475
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return 0;
|
|
476
|
+
if (raw <= 0) return 0;
|
|
477
|
+
return Math.floor(raw);
|
|
478
|
+
}
|
|
479
|
+
function applyPageRankRefinement(scores, adj, opts) {
|
|
480
|
+
const { iterations, floor, damping } = opts;
|
|
481
|
+
if (iterations <= 0 || scores.size === 0) return;
|
|
482
|
+
const safeDamping = Math.min(1, Math.max(0, damping));
|
|
483
|
+
const eligible = (edge, fromNode) => {
|
|
484
|
+
if (readEdgeConfidence(edge) < floor) return false;
|
|
485
|
+
const neighbor = edge.to === fromNode ? edge.from : edge.to;
|
|
486
|
+
return scores.has(neighbor);
|
|
487
|
+
};
|
|
488
|
+
const outboundTotal = /* @__PURE__ */ new Map();
|
|
489
|
+
for (const [node, edges] of adj.entries()) {
|
|
490
|
+
if (!scores.has(node)) continue;
|
|
491
|
+
let sum = 0;
|
|
492
|
+
for (const edge of edges) {
|
|
493
|
+
if (!eligible(edge, node)) continue;
|
|
494
|
+
sum += readEdgeConfidence(edge) * edge.weight;
|
|
495
|
+
}
|
|
496
|
+
if (sum > 0) outboundTotal.set(node, sum);
|
|
497
|
+
}
|
|
498
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
499
|
+
const next = /* @__PURE__ */ new Map();
|
|
500
|
+
for (const [node, score] of scores) {
|
|
501
|
+
next.set(node, (1 - safeDamping) * score);
|
|
502
|
+
}
|
|
503
|
+
for (const [node, score] of scores) {
|
|
504
|
+
const outEdges = adj.get(node);
|
|
505
|
+
const total = outboundTotal.get(node);
|
|
506
|
+
if (!outEdges || outEdges.length === 0 || !total || total <= 0) {
|
|
507
|
+
next.set(node, (next.get(node) ?? 0) + safeDamping * score);
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
for (const edge of outEdges) {
|
|
511
|
+
if (!eligible(edge, node)) continue;
|
|
512
|
+
const conf = readEdgeConfidence(edge);
|
|
513
|
+
const neighbor = edge.to === node ? edge.from : edge.to;
|
|
514
|
+
const flow = safeDamping * score * (conf * edge.weight / total);
|
|
515
|
+
next.set(neighbor, (next.get(neighbor) ?? 0) + flow);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
for (const [node, score] of next) {
|
|
519
|
+
scores.set(node, score);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
318
523
|
function applyLateralInhibition(scores, opts) {
|
|
319
524
|
const { beta, topM } = opts;
|
|
320
525
|
if (beta === 0 || topM === 0) return new Map(scores);
|
|
@@ -334,7 +539,18 @@ function applyLateralInhibition(scores, opts) {
|
|
|
334
539
|
}
|
|
335
540
|
|
|
336
541
|
export {
|
|
542
|
+
DEFAULT_DECAY_WINDOW_MS,
|
|
543
|
+
DEFAULT_DECAY_FLOOR,
|
|
544
|
+
DEFAULT_DECAY_PER_WINDOW,
|
|
545
|
+
readEdgeConfidence,
|
|
546
|
+
decayEdgeConfidence,
|
|
547
|
+
subscribeGraphEvents,
|
|
548
|
+
graphsDir,
|
|
549
|
+
graphFilePath,
|
|
550
|
+
withGraphWriteLock,
|
|
337
551
|
appendEdge,
|
|
552
|
+
readEdgesStrict,
|
|
553
|
+
readAllEdges,
|
|
338
554
|
analyzeGraphHealth,
|
|
339
555
|
GraphIndex
|
|
340
556
|
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateSalt,
|
|
3
|
+
open,
|
|
4
|
+
seal
|
|
5
|
+
} from "./chunk-YGGGUTG3.js";
|
|
6
|
+
|
|
7
|
+
// ../remnic-core/src/secure-store/secure-fs.ts
|
|
8
|
+
import { lstat, mkdir, readFile, readdir, rename, unlink, writeFile } from "fs/promises";
|
|
9
|
+
import path from "path";
|
|
10
|
+
var SecureStoreLockedError = class extends Error {
|
|
11
|
+
constructor(message = "secure-store is locked \u2014 run `remnic secure-store unlock` to decrypt") {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "SecureStoreLockedError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var SecureStoreDecryptError = class extends Error {
|
|
17
|
+
constructor(message = "secure-store decryption failed \u2014 wrong key or tampered ciphertext") {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "SecureStoreDecryptError";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var MAGIC_BYTES = Buffer.from("REMNIC-ENC", "ascii");
|
|
23
|
+
var FILE_FORMAT_VERSION = 1;
|
|
24
|
+
var FILE_FORMAT_FLAGS = 0;
|
|
25
|
+
var MAGIC_HEADER_SIZE = MAGIC_BYTES.length + 2;
|
|
26
|
+
function isEncryptedFile(buf) {
|
|
27
|
+
if (buf.length < MAGIC_HEADER_SIZE) return false;
|
|
28
|
+
const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
|
|
29
|
+
return b.subarray(0, MAGIC_BYTES.length).equals(MAGIC_BYTES);
|
|
30
|
+
}
|
|
31
|
+
function encryptFileBody(plain, key, aad) {
|
|
32
|
+
const plainBuf = typeof plain === "string" ? Buffer.from(plain, "utf8") : plain;
|
|
33
|
+
const salt = generateSalt();
|
|
34
|
+
const envelope = seal(key, salt, plainBuf, aad ? { aad } : {});
|
|
35
|
+
const header = Buffer.alloc(MAGIC_HEADER_SIZE);
|
|
36
|
+
MAGIC_BYTES.copy(header, 0);
|
|
37
|
+
header.writeUInt8(FILE_FORMAT_VERSION, MAGIC_BYTES.length);
|
|
38
|
+
header.writeUInt8(FILE_FORMAT_FLAGS, MAGIC_BYTES.length + 1);
|
|
39
|
+
return Buffer.concat([header, envelope]);
|
|
40
|
+
}
|
|
41
|
+
function decryptFileBody(buf, key, aad) {
|
|
42
|
+
if (!isEncryptedFile(buf)) {
|
|
43
|
+
throw new Error("decryptFileBody: buffer does not start with REMNIC-ENC magic header");
|
|
44
|
+
}
|
|
45
|
+
const version = buf.readUInt8(MAGIC_BYTES.length);
|
|
46
|
+
if (version !== FILE_FORMAT_VERSION) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`decryptFileBody: unsupported file format version ${version} (this build supports ${FILE_FORMAT_VERSION})`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
const flags = buf.readUInt8(MAGIC_BYTES.length + 1);
|
|
52
|
+
if (flags !== FILE_FORMAT_FLAGS) {
|
|
53
|
+
throw new Error(`decryptFileBody: unknown flags byte 0x${flags.toString(16).padStart(2, "0")}`);
|
|
54
|
+
}
|
|
55
|
+
const envelope = buf.subarray(MAGIC_HEADER_SIZE);
|
|
56
|
+
try {
|
|
57
|
+
return open(key, envelope, aad ? { aad } : {});
|
|
58
|
+
} catch (err) {
|
|
59
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
60
|
+
throw new SecureStoreDecryptError(
|
|
61
|
+
`secure-store decryption failed: ${msg}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function filePathAad(filePath, memoryDir) {
|
|
66
|
+
let rel = filePath;
|
|
67
|
+
if (memoryDir && path.isAbsolute(filePath)) {
|
|
68
|
+
rel = path.relative(memoryDir, filePath);
|
|
69
|
+
}
|
|
70
|
+
return Buffer.from(rel, "utf8");
|
|
71
|
+
}
|
|
72
|
+
async function readMaybeEncryptedFile(filePath, key, memoryDir) {
|
|
73
|
+
const buf = await readFile(filePath);
|
|
74
|
+
if (!isEncryptedFile(buf)) {
|
|
75
|
+
return buf.toString("utf8");
|
|
76
|
+
}
|
|
77
|
+
if (key === null) {
|
|
78
|
+
throw new SecureStoreLockedError(
|
|
79
|
+
`secure-store is locked \u2014 cannot read encrypted file at ${filePath}. Run \`remnic secure-store unlock\` to decrypt.`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const aad = filePathAad(filePath, memoryDir);
|
|
83
|
+
const plain = decryptFileBody(buf, key, aad);
|
|
84
|
+
return plain.toString("utf8");
|
|
85
|
+
}
|
|
86
|
+
async function writeMaybeEncryptedFile(filePath, content, key, options = {}, memoryDir) {
|
|
87
|
+
const { mode = 384, atomic = true } = options;
|
|
88
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
89
|
+
let data;
|
|
90
|
+
if (key !== null) {
|
|
91
|
+
const aad = filePathAad(filePath, memoryDir);
|
|
92
|
+
data = encryptFileBody(content, key, aad);
|
|
93
|
+
} else {
|
|
94
|
+
data = content;
|
|
95
|
+
}
|
|
96
|
+
if (atomic) {
|
|
97
|
+
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
98
|
+
try {
|
|
99
|
+
await writeFile(tempPath, data, { mode });
|
|
100
|
+
await rename(tempPath, filePath);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
try {
|
|
103
|
+
await unlink(tempPath);
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
await writeFile(filePath, data, { mode });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function migrateMemoryDirToEncrypted(dir, key, onBeforeEncrypt) {
|
|
113
|
+
const result = { encrypted: 0, skipped: 0, errors: [] };
|
|
114
|
+
const mdFiles = await collectMdFiles(dir);
|
|
115
|
+
for (const filePath of mdFiles) {
|
|
116
|
+
try {
|
|
117
|
+
const buf = await readFile(filePath);
|
|
118
|
+
if (isEncryptedFile(buf)) {
|
|
119
|
+
result.skipped++;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (onBeforeEncrypt) {
|
|
123
|
+
try {
|
|
124
|
+
await onBeforeEncrypt(filePath);
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const content = buf.toString("utf8");
|
|
129
|
+
const aad = filePathAad(filePath, dir);
|
|
130
|
+
const encrypted = encryptFileBody(content, key, aad);
|
|
131
|
+
const tempPath = `${filePath}.enc-tmp-${process.pid}-${Date.now()}`;
|
|
132
|
+
try {
|
|
133
|
+
await writeFile(tempPath, encrypted, { mode: 384 });
|
|
134
|
+
await rename(tempPath, filePath);
|
|
135
|
+
result.encrypted++;
|
|
136
|
+
} catch (writeErr) {
|
|
137
|
+
try {
|
|
138
|
+
const { unlink: unlink2 } = await import("fs/promises");
|
|
139
|
+
await unlink2(tempPath);
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
throw writeErr;
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
result.errors.push({
|
|
146
|
+
filePath,
|
|
147
|
+
error: err instanceof Error ? err.message : String(err)
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
async function collectMdFiles(dir, rootDir = dir) {
|
|
154
|
+
const results = [];
|
|
155
|
+
let names;
|
|
156
|
+
try {
|
|
157
|
+
names = await readdir(dir, { encoding: "utf8" });
|
|
158
|
+
} catch {
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
for (const name of names) {
|
|
162
|
+
if (name.startsWith(".secure-store")) continue;
|
|
163
|
+
const full = path.join(dir, name);
|
|
164
|
+
let isDir = false;
|
|
165
|
+
let isFile = false;
|
|
166
|
+
try {
|
|
167
|
+
const s = await lstat(full);
|
|
168
|
+
if (s.isSymbolicLink()) continue;
|
|
169
|
+
isDir = s.isDirectory();
|
|
170
|
+
isFile = s.isFile();
|
|
171
|
+
} catch {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (isDir) {
|
|
175
|
+
const sub = await collectMdFiles(full, rootDir);
|
|
176
|
+
results.push(...sub);
|
|
177
|
+
} else if (isFile && name.endsWith(".md") && isEncryptableStoragePath(full, rootDir)) {
|
|
178
|
+
results.push(full);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return results;
|
|
182
|
+
}
|
|
183
|
+
function isEncryptableStoragePath(filePath, rootDir) {
|
|
184
|
+
const rel = path.relative(rootDir, filePath);
|
|
185
|
+
if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) return false;
|
|
186
|
+
const normalized = rel.split(path.sep).join("/");
|
|
187
|
+
if (normalized === "profile.md") return true;
|
|
188
|
+
const firstSegment = normalized.split("/", 1)[0];
|
|
189
|
+
return ENCRYPTABLE_STORAGE_ROOTS.has(firstSegment);
|
|
190
|
+
}
|
|
191
|
+
var ENCRYPTABLE_STORAGE_ROOTS = /* @__PURE__ */ new Set([
|
|
192
|
+
"facts",
|
|
193
|
+
"corrections",
|
|
194
|
+
"procedures",
|
|
195
|
+
"reasoning-traces",
|
|
196
|
+
"artifacts",
|
|
197
|
+
"archive"
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
export {
|
|
201
|
+
SecureStoreLockedError,
|
|
202
|
+
SecureStoreDecryptError,
|
|
203
|
+
MAGIC_BYTES,
|
|
204
|
+
FILE_FORMAT_VERSION,
|
|
205
|
+
FILE_FORMAT_FLAGS,
|
|
206
|
+
MAGIC_HEADER_SIZE,
|
|
207
|
+
isEncryptedFile,
|
|
208
|
+
encryptFileBody,
|
|
209
|
+
decryptFileBody,
|
|
210
|
+
filePathAad,
|
|
211
|
+
readMaybeEncryptedFile,
|
|
212
|
+
writeMaybeEncryptedFile,
|
|
213
|
+
migrateMemoryDirToEncrypted
|
|
214
|
+
};
|