@remnic/plugin-openclaw 1.0.10 → 1.0.12
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-GBM3WPAM.js +33 -0
- package/dist/capsule-crypto-K3IRTKRH.js +17 -0
- package/dist/capsule-export-IXVERCQG.js +17 -0
- package/dist/capsule-import-IA6VIOPQ.js +16 -0
- package/dist/capsule-merge-IWOQ34KL.js +189 -0
- package/dist/{causal-chain-OKDZSDEB.js → causal-chain-WYN5QOPS.js} +3 -2
- package/dist/{causal-consolidation-5BEXLQV5.js → causal-consolidation-YI53C2AO.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-4G2XCSD2.js +186 -0
- package/dist/chunk-4LYQ4ONL.js +185 -0
- package/dist/chunk-6F6EKSVP.js +453 -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-JJSNPSCD.js → chunk-7UZNLMW5.js} +652 -174
- package/dist/chunk-B52XADV3.js +244 -0
- package/dist/chunk-BU5KJVWF.js +78 -0
- package/dist/chunk-CDAZGIGT.js +720 -0
- package/dist/chunk-CXM7EBAO.js +289 -0
- package/dist/{chunk-HCFFXBLV.js → chunk-EXDYWXMB.js} +6 -1861
- package/dist/chunk-FGTYFLL5.js +274 -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-L6I4MQKO.js +227 -0
- package/dist/chunk-LLUROTZJ.js +328 -0
- package/dist/chunk-MBIFE6SA.js +250 -0
- package/dist/chunk-NKVIN6RD.js +118 -0
- package/dist/chunk-OAE7AQ6R.js +1832 -0
- package/dist/chunk-OEI7GLV2.js +17 -0
- package/dist/chunk-RKR6PTPA.js +308 -0
- 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-S2ISS4AH.js → chunk-TILAJIJR.js} +10 -10
- package/dist/chunk-TLVIQLB4.js +874 -0
- package/dist/{chunk-YHH3SXKD.js → chunk-WPINX4MF.js} +1 -59
- package/dist/chunk-YGGGUTG3.js +125 -0
- package/dist/cipher-VHAFCG7Z.js +27 -0
- package/dist/dreams-ledger-3I52ISYR.js +285 -0
- package/dist/{engine-65C2J63X.js → engine-BIYI3P4J.js} +7 -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 +10654 -3067
- package/dist/kdf-H5B23ZM2.js +25 -0
- package/dist/memory-governance-SJ5DGRB3.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-A4NGCNXV.js +155 -0
- package/dist/state-PVISYXRH.js +7 -0
- package/dist/state-store-LP5BO6SF.js +15 -0
- package/dist/{storage-DM4ZGOCN.js → storage-PTQ2H2YJ.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-R4DO7AKM.js +30 -0
- package/openclaw.plugin.json +519 -4
- package/package.json +2 -2
|
@@ -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,186 @@
|
|
|
1
|
+
// ../remnic-core/src/lifecycle.ts
|
|
2
|
+
var DEFAULT_POLICY = {
|
|
3
|
+
promoteHeatThreshold: 0.55,
|
|
4
|
+
staleDecayThreshold: 0.65,
|
|
5
|
+
archiveDecayThreshold: 0.85,
|
|
6
|
+
protectedCategories: ["decision", "principle", "commitment", "preference", "procedure"]
|
|
7
|
+
};
|
|
8
|
+
function clamp01(value) {
|
|
9
|
+
if (!Number.isFinite(value)) return 0;
|
|
10
|
+
if (value < 0) return 0;
|
|
11
|
+
if (value > 1) return 1;
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function clampLifecycleThreshold(value) {
|
|
15
|
+
return clamp01(value);
|
|
16
|
+
}
|
|
17
|
+
function parseIsoMs(value) {
|
|
18
|
+
if (!value) return null;
|
|
19
|
+
const ms = Date.parse(value);
|
|
20
|
+
return Number.isFinite(ms) ? ms : null;
|
|
21
|
+
}
|
|
22
|
+
function daysSince(value, nowMs) {
|
|
23
|
+
const ts = parseIsoMs(value);
|
|
24
|
+
if (ts === null) return 365;
|
|
25
|
+
return Math.max(0, (nowMs - ts) / 864e5);
|
|
26
|
+
}
|
|
27
|
+
function confidenceTierWeight(frontmatter) {
|
|
28
|
+
switch (frontmatter.confidenceTier) {
|
|
29
|
+
case "explicit":
|
|
30
|
+
return 1;
|
|
31
|
+
case "implied":
|
|
32
|
+
return 0.8;
|
|
33
|
+
case "inferred":
|
|
34
|
+
return 0.6;
|
|
35
|
+
case "speculative":
|
|
36
|
+
return 0.35;
|
|
37
|
+
default:
|
|
38
|
+
return clamp01(frontmatter.confidence ?? 0.5);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function accessWeight(accessCount) {
|
|
42
|
+
const raw = accessCount ?? 0;
|
|
43
|
+
if (raw <= 0) return 0;
|
|
44
|
+
return clamp01(Math.log1p(raw) / Math.log1p(20));
|
|
45
|
+
}
|
|
46
|
+
function recencyWeight(frontmatter, nowMs) {
|
|
47
|
+
const lastTouch = frontmatter.lastAccessed ?? frontmatter.updated ?? frontmatter.created;
|
|
48
|
+
const ageDays = daysSince(lastTouch, nowMs);
|
|
49
|
+
return clamp01(1 - ageDays / 90);
|
|
50
|
+
}
|
|
51
|
+
function feedbackWeight(signals) {
|
|
52
|
+
const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);
|
|
53
|
+
return clamp01((raw + 1) / 2);
|
|
54
|
+
}
|
|
55
|
+
function boundedFeedbackScore(signals) {
|
|
56
|
+
const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);
|
|
57
|
+
if (!Number.isFinite(raw)) return 0;
|
|
58
|
+
if (raw < -1) return -1;
|
|
59
|
+
if (raw > 1) return 1;
|
|
60
|
+
return raw;
|
|
61
|
+
}
|
|
62
|
+
function isProtectedMemory(frontmatter, policy) {
|
|
63
|
+
return frontmatter.policyClass === "protected" || policy.protectedCategories.includes(frontmatter.category);
|
|
64
|
+
}
|
|
65
|
+
function resolveLifecycleState(frontmatter) {
|
|
66
|
+
if (frontmatter.status === "archived") return "archived";
|
|
67
|
+
return frontmatter.lifecycleState ?? "candidate";
|
|
68
|
+
}
|
|
69
|
+
function computeHeat(memory, now, signals) {
|
|
70
|
+
const frontmatter = memory.frontmatter;
|
|
71
|
+
if (frontmatter.status === "archived") return 0;
|
|
72
|
+
const inputs = computeLifecycleValueInputs(memory, now, signals);
|
|
73
|
+
const causalBoost = clamp01(signals?.causalImpactScore ?? 0) * 0.05;
|
|
74
|
+
const score = inputs.confidence * 0.25 + inputs.access * 0.3 + inputs.recency * 0.2 + inputs.importance * 0.15 + inputs.feedback * 0.1 + causalBoost - inputs.disputedPenalty;
|
|
75
|
+
return clamp01(score);
|
|
76
|
+
}
|
|
77
|
+
function computeLifecycleValueInputs(memory, now, signals) {
|
|
78
|
+
const frontmatter = memory.frontmatter;
|
|
79
|
+
const nowMs = now.getTime();
|
|
80
|
+
return {
|
|
81
|
+
confidence: confidenceTierWeight(frontmatter),
|
|
82
|
+
access: accessWeight(frontmatter.accessCount),
|
|
83
|
+
recency: recencyWeight(frontmatter, nowMs),
|
|
84
|
+
importance: clamp01(frontmatter.importance?.score ?? 0.5),
|
|
85
|
+
feedback: feedbackWeight(signals),
|
|
86
|
+
disputedPenalty: frontmatter.verificationState === "disputed" ? 0.2 : 0
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function computeDecay(memory, now, signals) {
|
|
90
|
+
const frontmatter = memory.frontmatter;
|
|
91
|
+
if (frontmatter.status === "archived") return 1;
|
|
92
|
+
const nowMs = now.getTime();
|
|
93
|
+
const ageDays = daysSince(frontmatter.updated ?? frontmatter.created, nowMs);
|
|
94
|
+
const staleAccessDays = daysSince(frontmatter.lastAccessed, nowMs);
|
|
95
|
+
const ageRisk = clamp01(ageDays / 180);
|
|
96
|
+
const staleAccessRisk = clamp01(staleAccessDays / 120);
|
|
97
|
+
const confidenceRisk = 1 - confidenceTierWeight(frontmatter);
|
|
98
|
+
const feedbackRisk = clamp01((boundedFeedbackScore(signals) * -1 + 1) / 2);
|
|
99
|
+
const heat = computeHeat(memory, now, signals);
|
|
100
|
+
const score = ageRisk * 0.3 + staleAccessRisk * 0.25 + confidenceRisk * 0.2 + feedbackRisk * 0.1 + (1 - heat) * 0.15;
|
|
101
|
+
return clamp01(score);
|
|
102
|
+
}
|
|
103
|
+
function toTerminalDisputedState(currentState) {
|
|
104
|
+
if (currentState === "archived") return "archived";
|
|
105
|
+
return "stale";
|
|
106
|
+
}
|
|
107
|
+
function isActiveEligible(verificationState) {
|
|
108
|
+
return verificationState === "user_confirmed" || verificationState === "system_inferred";
|
|
109
|
+
}
|
|
110
|
+
function decideLifecycleTransition(memory, policy, now, signals) {
|
|
111
|
+
const mergedPolicy = { ...DEFAULT_POLICY, ...policy };
|
|
112
|
+
const frontmatter = memory.frontmatter;
|
|
113
|
+
const currentState = resolveLifecycleState(frontmatter);
|
|
114
|
+
const heatScore = computeHeat(memory, now, signals);
|
|
115
|
+
const decayScore = computeDecay(memory, now, signals);
|
|
116
|
+
const protectedMemory = isProtectedMemory(frontmatter, mergedPolicy);
|
|
117
|
+
if (currentState === "archived") {
|
|
118
|
+
return {
|
|
119
|
+
currentState,
|
|
120
|
+
nextState: "archived",
|
|
121
|
+
heatScore,
|
|
122
|
+
decayScore,
|
|
123
|
+
changed: false,
|
|
124
|
+
reason: "archived_is_terminal"
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (frontmatter.verificationState === "disputed") {
|
|
128
|
+
const nextState = toTerminalDisputedState(currentState);
|
|
129
|
+
return {
|
|
130
|
+
currentState,
|
|
131
|
+
nextState,
|
|
132
|
+
heatScore,
|
|
133
|
+
decayScore,
|
|
134
|
+
changed: nextState !== currentState,
|
|
135
|
+
reason: "disputed_memories_do_not_promote_to_active"
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (decayScore >= mergedPolicy.archiveDecayThreshold && !protectedMemory) {
|
|
139
|
+
return {
|
|
140
|
+
currentState,
|
|
141
|
+
nextState: "archived",
|
|
142
|
+
heatScore,
|
|
143
|
+
decayScore,
|
|
144
|
+
changed: true,
|
|
145
|
+
reason: "decay_exceeded_archive_threshold"
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (decayScore >= mergedPolicy.staleDecayThreshold) {
|
|
149
|
+
return {
|
|
150
|
+
currentState,
|
|
151
|
+
nextState: "stale",
|
|
152
|
+
heatScore,
|
|
153
|
+
decayScore,
|
|
154
|
+
changed: currentState !== "stale",
|
|
155
|
+
reason: "decay_exceeded_stale_threshold"
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (heatScore >= mergedPolicy.promoteHeatThreshold) {
|
|
159
|
+
const nextState = isActiveEligible(frontmatter.verificationState) ? "active" : "validated";
|
|
160
|
+
return {
|
|
161
|
+
currentState,
|
|
162
|
+
nextState,
|
|
163
|
+
heatScore,
|
|
164
|
+
decayScore,
|
|
165
|
+
changed: currentState !== nextState,
|
|
166
|
+
reason: "heat_exceeded_promote_threshold"
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
currentState,
|
|
171
|
+
nextState: currentState,
|
|
172
|
+
heatScore,
|
|
173
|
+
decayScore,
|
|
174
|
+
changed: false,
|
|
175
|
+
reason: "no_transition"
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export {
|
|
180
|
+
clamp01,
|
|
181
|
+
clampLifecycleThreshold,
|
|
182
|
+
daysSince,
|
|
183
|
+
resolveLifecycleState,
|
|
184
|
+
computeLifecycleValueInputs,
|
|
185
|
+
decideLifecycleTransition
|
|
186
|
+
};
|