@remnic/core 9.3.601 → 9.3.602
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/access-cli.js +6 -6
- package/dist/access-http.js +6 -6
- package/dist/access-mcp.js +5 -5
- package/dist/access-service.js +4 -4
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +2 -2
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +1 -1
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-ELKI4BB6.js → chunk-2ESBDLT5.js} +3 -3
- package/dist/{chunk-WZA5Y6AC.js → chunk-2QANQKSQ.js} +3 -3
- package/dist/{chunk-BDCCWRHR.js → chunk-5RPTH6AU.js} +3 -3
- package/dist/{chunk-JKV57BTN.js → chunk-7V2SGZ3H.js} +2 -2
- package/dist/{chunk-D4KJ74JJ.js → chunk-EWC6W6AB.js} +2 -2
- package/dist/{chunk-V67GWXM2.js → chunk-IP73YCZP.js} +1 -1
- package/dist/{chunk-KDUVQU6Y.js → chunk-MTGOAU7A.js} +4 -4
- package/dist/{chunk-65JSA4MP.js → chunk-RUYYYLDT.js} +7 -7
- package/dist/{chunk-CL3MWNNQ.js → chunk-TH67Q46T.js} +3 -3
- package/dist/{chunk-ZZYF3BUL.js → chunk-TQOU3VAT.js} +1 -1
- package/dist/{chunk-4JRRISLU.js → chunk-XL7FK7PJ.js} +61 -43
- package/dist/chunk-XL7FK7PJ.js.map +1 -0
- package/dist/cli.js +8 -8
- package/dist/compounding/engine.js +1 -1
- package/dist/{graph-edge-decay-MUP5J7CC.js → graph-edge-decay-5ZOK7ZNO.js} +2 -2
- package/dist/graph-snapshot.js +2 -2
- package/dist/graph.js +1 -1
- package/dist/index.js +10 -10
- package/dist/operator-toolkit.js +2 -2
- package/dist/orchestrator.js +4 -4
- package/dist/schemas.d.ts +22 -22
- package/dist/transfer/types.d.ts +12 -12
- package/package.json +1 -1
- package/src/graph.test.ts +76 -11
- package/src/graph.ts +101 -88
- package/dist/chunk-4JRRISLU.js.map +0 -1
- /package/dist/{chunk-ELKI4BB6.js.map → chunk-2ESBDLT5.js.map} +0 -0
- /package/dist/{chunk-WZA5Y6AC.js.map → chunk-2QANQKSQ.js.map} +0 -0
- /package/dist/{chunk-BDCCWRHR.js.map → chunk-5RPTH6AU.js.map} +0 -0
- /package/dist/{chunk-JKV57BTN.js.map → chunk-7V2SGZ3H.js.map} +0 -0
- /package/dist/{chunk-D4KJ74JJ.js.map → chunk-EWC6W6AB.js.map} +0 -0
- /package/dist/{chunk-V67GWXM2.js.map → chunk-IP73YCZP.js.map} +0 -0
- /package/dist/{chunk-KDUVQU6Y.js.map → chunk-MTGOAU7A.js.map} +0 -0
- /package/dist/{chunk-65JSA4MP.js.map → chunk-RUYYYLDT.js.map} +0 -0
- /package/dist/{chunk-CL3MWNNQ.js.map → chunk-TH67Q46T.js.map} +0 -0
- /package/dist/{chunk-ZZYF3BUL.js.map → chunk-TQOU3VAT.js.map} +0 -0
- /package/dist/{graph-edge-decay-MUP5J7CC.js.map → graph-edge-decay-5ZOK7ZNO.js.map} +0 -0
package/dist/transfer/types.d.ts
CHANGED
|
@@ -313,13 +313,13 @@ declare const CapsuleBlockSchema: z.ZodObject<{
|
|
|
313
313
|
peerProfiles: boolean;
|
|
314
314
|
}>;
|
|
315
315
|
}, "strip", z.ZodTypeAny, {
|
|
316
|
-
schemaVersion: string;
|
|
317
316
|
includes: {
|
|
318
317
|
procedural: boolean;
|
|
319
318
|
taxonomy: boolean;
|
|
320
319
|
identityAnchors: boolean;
|
|
321
320
|
peerProfiles: boolean;
|
|
322
321
|
};
|
|
322
|
+
schemaVersion: string;
|
|
323
323
|
id: string;
|
|
324
324
|
description: string;
|
|
325
325
|
version: string;
|
|
@@ -334,13 +334,13 @@ declare const CapsuleBlockSchema: z.ZodObject<{
|
|
|
334
334
|
directAnswerEnabled: boolean;
|
|
335
335
|
};
|
|
336
336
|
}, {
|
|
337
|
-
schemaVersion: string;
|
|
338
337
|
includes: {
|
|
339
338
|
procedural: boolean;
|
|
340
339
|
taxonomy: boolean;
|
|
341
340
|
identityAnchors: boolean;
|
|
342
341
|
peerProfiles: boolean;
|
|
343
342
|
};
|
|
343
|
+
schemaVersion: string;
|
|
344
344
|
id: string;
|
|
345
345
|
description: string;
|
|
346
346
|
version: string;
|
|
@@ -464,13 +464,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
|
|
|
464
464
|
peerProfiles: boolean;
|
|
465
465
|
}>;
|
|
466
466
|
}, "strip", z.ZodTypeAny, {
|
|
467
|
-
schemaVersion: string;
|
|
468
467
|
includes: {
|
|
469
468
|
procedural: boolean;
|
|
470
469
|
taxonomy: boolean;
|
|
471
470
|
identityAnchors: boolean;
|
|
472
471
|
peerProfiles: boolean;
|
|
473
472
|
};
|
|
473
|
+
schemaVersion: string;
|
|
474
474
|
id: string;
|
|
475
475
|
description: string;
|
|
476
476
|
version: string;
|
|
@@ -485,13 +485,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
|
|
|
485
485
|
directAnswerEnabled: boolean;
|
|
486
486
|
};
|
|
487
487
|
}, {
|
|
488
|
-
schemaVersion: string;
|
|
489
488
|
includes: {
|
|
490
489
|
procedural: boolean;
|
|
491
490
|
taxonomy: boolean;
|
|
492
491
|
identityAnchors: boolean;
|
|
493
492
|
peerProfiles: boolean;
|
|
494
493
|
};
|
|
494
|
+
schemaVersion: string;
|
|
495
495
|
id: string;
|
|
496
496
|
description: string;
|
|
497
497
|
version: string;
|
|
@@ -518,13 +518,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
|
|
|
518
518
|
pluginVersion: string;
|
|
519
519
|
includesTranscripts: boolean;
|
|
520
520
|
capsule: {
|
|
521
|
-
schemaVersion: string;
|
|
522
521
|
includes: {
|
|
523
522
|
procedural: boolean;
|
|
524
523
|
taxonomy: boolean;
|
|
525
524
|
identityAnchors: boolean;
|
|
526
525
|
peerProfiles: boolean;
|
|
527
526
|
};
|
|
527
|
+
schemaVersion: string;
|
|
528
528
|
id: string;
|
|
529
529
|
description: string;
|
|
530
530
|
version: string;
|
|
@@ -551,13 +551,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
|
|
|
551
551
|
pluginVersion: string;
|
|
552
552
|
includesTranscripts: boolean;
|
|
553
553
|
capsule: {
|
|
554
|
-
schemaVersion: string;
|
|
555
554
|
includes: {
|
|
556
555
|
procedural: boolean;
|
|
557
556
|
taxonomy: boolean;
|
|
558
557
|
identityAnchors: boolean;
|
|
559
558
|
peerProfiles: boolean;
|
|
560
559
|
};
|
|
560
|
+
schemaVersion: string;
|
|
561
561
|
id: string;
|
|
562
562
|
description: string;
|
|
563
563
|
version: string;
|
|
@@ -683,13 +683,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
683
683
|
peerProfiles: boolean;
|
|
684
684
|
}>;
|
|
685
685
|
}, "strip", z.ZodTypeAny, {
|
|
686
|
-
schemaVersion: string;
|
|
687
686
|
includes: {
|
|
688
687
|
procedural: boolean;
|
|
689
688
|
taxonomy: boolean;
|
|
690
689
|
identityAnchors: boolean;
|
|
691
690
|
peerProfiles: boolean;
|
|
692
691
|
};
|
|
692
|
+
schemaVersion: string;
|
|
693
693
|
id: string;
|
|
694
694
|
description: string;
|
|
695
695
|
version: string;
|
|
@@ -704,13 +704,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
704
704
|
directAnswerEnabled: boolean;
|
|
705
705
|
};
|
|
706
706
|
}, {
|
|
707
|
-
schemaVersion: string;
|
|
708
707
|
includes: {
|
|
709
708
|
procedural: boolean;
|
|
710
709
|
taxonomy: boolean;
|
|
711
710
|
identityAnchors: boolean;
|
|
712
711
|
peerProfiles: boolean;
|
|
713
712
|
};
|
|
713
|
+
schemaVersion: string;
|
|
714
714
|
id: string;
|
|
715
715
|
description: string;
|
|
716
716
|
version: string;
|
|
@@ -737,13 +737,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
737
737
|
pluginVersion: string;
|
|
738
738
|
includesTranscripts: boolean;
|
|
739
739
|
capsule: {
|
|
740
|
-
schemaVersion: string;
|
|
741
740
|
includes: {
|
|
742
741
|
procedural: boolean;
|
|
743
742
|
taxonomy: boolean;
|
|
744
743
|
identityAnchors: boolean;
|
|
745
744
|
peerProfiles: boolean;
|
|
746
745
|
};
|
|
746
|
+
schemaVersion: string;
|
|
747
747
|
id: string;
|
|
748
748
|
description: string;
|
|
749
749
|
version: string;
|
|
@@ -770,13 +770,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
770
770
|
pluginVersion: string;
|
|
771
771
|
includesTranscripts: boolean;
|
|
772
772
|
capsule: {
|
|
773
|
-
schemaVersion: string;
|
|
774
773
|
includes: {
|
|
775
774
|
procedural: boolean;
|
|
776
775
|
taxonomy: boolean;
|
|
777
776
|
identityAnchors: boolean;
|
|
778
777
|
peerProfiles: boolean;
|
|
779
778
|
};
|
|
779
|
+
schemaVersion: string;
|
|
780
780
|
id: string;
|
|
781
781
|
description: string;
|
|
782
782
|
version: string;
|
|
@@ -815,13 +815,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
815
815
|
pluginVersion: string;
|
|
816
816
|
includesTranscripts: boolean;
|
|
817
817
|
capsule: {
|
|
818
|
-
schemaVersion: string;
|
|
819
818
|
includes: {
|
|
820
819
|
procedural: boolean;
|
|
821
820
|
taxonomy: boolean;
|
|
822
821
|
identityAnchors: boolean;
|
|
823
822
|
peerProfiles: boolean;
|
|
824
823
|
};
|
|
824
|
+
schemaVersion: string;
|
|
825
825
|
id: string;
|
|
826
826
|
description: string;
|
|
827
827
|
version: string;
|
|
@@ -854,13 +854,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
854
854
|
pluginVersion: string;
|
|
855
855
|
includesTranscripts: boolean;
|
|
856
856
|
capsule: {
|
|
857
|
-
schemaVersion: string;
|
|
858
857
|
includes: {
|
|
859
858
|
procedural: boolean;
|
|
860
859
|
taxonomy: boolean;
|
|
861
860
|
identityAnchors: boolean;
|
|
862
861
|
peerProfiles: boolean;
|
|
863
862
|
};
|
|
863
|
+
schemaVersion: string;
|
|
864
864
|
id: string;
|
|
865
865
|
description: string;
|
|
866
866
|
version: string;
|
package/package.json
CHANGED
package/src/graph.test.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
1
|
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import
|
|
5
|
+
import test from "node:test";
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
GraphIndex,
|
|
9
|
-
graphFilePath,
|
|
10
|
-
readEdges,
|
|
11
|
-
type GraphConfig,
|
|
12
|
-
} from "./graph.js";
|
|
7
|
+
import { type GraphConfig, type GraphEdge, GraphIndex, graphFilePath, readEdges } from "./graph.js";
|
|
13
8
|
|
|
14
9
|
function makeGraphConfig(): GraphConfig {
|
|
15
10
|
return {
|
|
@@ -53,17 +48,87 @@ test("graph reads skip malformed JSON edge objects before traversal", async () =
|
|
|
53
48
|
}),
|
|
54
49
|
"",
|
|
55
50
|
].join("\n"),
|
|
56
|
-
"utf-8"
|
|
51
|
+
"utf-8"
|
|
57
52
|
);
|
|
58
53
|
|
|
59
54
|
const edges = await readEdges(memoryDir, "entity");
|
|
60
|
-
assert.deepEqual(
|
|
55
|
+
assert.deepEqual(
|
|
56
|
+
edges.map((edge) => edge.to),
|
|
57
|
+
["c"]
|
|
58
|
+
);
|
|
61
59
|
|
|
62
60
|
const graph = new GraphIndex(memoryDir, makeGraphConfig());
|
|
63
61
|
const activated = await graph.spreadingActivation(["a"]);
|
|
64
|
-
assert.deepEqual(
|
|
62
|
+
assert.deepEqual(
|
|
63
|
+
activated.map((candidate) => candidate.path),
|
|
64
|
+
["c"]
|
|
65
|
+
);
|
|
65
66
|
assert.equal(Number.isFinite(activated[0]?.score), true);
|
|
66
67
|
} finally {
|
|
67
68
|
await rm(memoryDir, { recursive: true, force: true });
|
|
68
69
|
}
|
|
69
70
|
});
|
|
71
|
+
|
|
72
|
+
test("spreadingActivation propagates accumulated activation from multiple seeds", async () => {
|
|
73
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-multi-seed-"));
|
|
74
|
+
try {
|
|
75
|
+
await writeGraphEdges(memoryDir, [
|
|
76
|
+
makeEdge("seed-a", "shared"),
|
|
77
|
+
makeEdge("seed-b", "shared"),
|
|
78
|
+
makeEdge("shared", "downstream"),
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
const graph = new GraphIndex(memoryDir, makeGraphConfig());
|
|
82
|
+
const activated = await graph.spreadingActivation(["seed-a", "seed-b"]);
|
|
83
|
+
const scores = new Map(activated.map((candidate) => [candidate.path, candidate.score]));
|
|
84
|
+
|
|
85
|
+
assert.equal(scores.get("shared"), 1);
|
|
86
|
+
assert.equal(scores.get("downstream"), 0.5);
|
|
87
|
+
} finally {
|
|
88
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("spreadingActivation propagates same-depth alternate path activation", async () => {
|
|
93
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-alt-path-"));
|
|
94
|
+
try {
|
|
95
|
+
await writeGraphEdges(memoryDir, [
|
|
96
|
+
makeEdge("seed", "left"),
|
|
97
|
+
makeEdge("seed", "right"),
|
|
98
|
+
makeEdge("left", "shared"),
|
|
99
|
+
makeEdge("right", "shared"),
|
|
100
|
+
makeEdge("shared", "downstream"),
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
const graph = new GraphIndex(memoryDir, {
|
|
104
|
+
...makeGraphConfig(),
|
|
105
|
+
maxGraphTraversalSteps: 3,
|
|
106
|
+
});
|
|
107
|
+
const activated = await graph.spreadingActivation(["seed"]);
|
|
108
|
+
const scores = new Map(activated.map((candidate) => [candidate.path, candidate.score]));
|
|
109
|
+
|
|
110
|
+
assert.equal(scores.get("left"), 0.5);
|
|
111
|
+
assert.equal(scores.get("right"), 0.5);
|
|
112
|
+
assert.equal(scores.get("shared"), 0.5);
|
|
113
|
+
assert.equal(scores.get("downstream"), 0.25);
|
|
114
|
+
} finally {
|
|
115
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
function makeEdge(from: string, to: string): GraphEdge {
|
|
120
|
+
return {
|
|
121
|
+
from,
|
|
122
|
+
to,
|
|
123
|
+
type: "entity",
|
|
124
|
+
weight: 1,
|
|
125
|
+
label: "test",
|
|
126
|
+
ts: "2026-01-01T00:00:00.000Z",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function writeGraphEdges(memoryDir: string, edges: GraphEdge[]): Promise<void> {
|
|
131
|
+
const filePath = graphFilePath(memoryDir, "entity");
|
|
132
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
133
|
+
await writeFile(filePath, `${edges.map((edge) => JSON.stringify(edge)).join("\n")}\n`, "utf-8");
|
|
134
|
+
}
|
package/src/graph.ts
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* All writes are fail-open: errors are caught/logged, never thrown.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
14
|
-
import * as path from "path";
|
|
13
|
+
import { appendFile, mkdir, readFile } from "node:fs/promises";
|
|
14
|
+
import * as path from "node:path";
|
|
15
15
|
|
|
16
16
|
import { readEdgeConfidence } from "./graph-edge-reinforcement.js";
|
|
17
17
|
import { emitGraphEvent } from "./graph-events.js";
|
|
@@ -66,14 +66,7 @@ export const DEFAULT_GRAPH_TRAVERSAL_CONFIDENCE_FLOOR = 0.2;
|
|
|
66
66
|
export const DEFAULT_GRAPH_TRAVERSAL_PAGERANK_ITERATIONS = 8;
|
|
67
67
|
|
|
68
68
|
// Causal signal phrases — order matters (most specific first)
|
|
69
|
-
export const CAUSAL_PHRASES = [
|
|
70
|
-
"as a result",
|
|
71
|
-
"led to",
|
|
72
|
-
"because of",
|
|
73
|
-
"therefore",
|
|
74
|
-
"caused",
|
|
75
|
-
"because",
|
|
76
|
-
];
|
|
69
|
+
export const CAUSAL_PHRASES = ["as a result", "led to", "because of", "therefore", "caused", "because"];
|
|
77
70
|
|
|
78
71
|
export function graphsDir(memoryDir: string): string {
|
|
79
72
|
return path.join(memoryDir, "state", "graphs");
|
|
@@ -114,8 +107,8 @@ export function withGraphWriteLock<T>(filePath: string, fn: () => Promise<T>): P
|
|
|
114
107
|
filePath,
|
|
115
108
|
next.then(
|
|
116
109
|
() => undefined,
|
|
117
|
-
() => undefined
|
|
118
|
-
)
|
|
110
|
+
() => undefined
|
|
111
|
+
)
|
|
119
112
|
);
|
|
120
113
|
return next;
|
|
121
114
|
}
|
|
@@ -123,7 +116,7 @@ export function withGraphWriteLock<T>(filePath: string, fn: () => Promise<T>): P
|
|
|
123
116
|
export async function appendEdge(memoryDir: string, edge: GraphEdge): Promise<void> {
|
|
124
117
|
await ensureGraphsDir(memoryDir);
|
|
125
118
|
const filePath = graphFilePath(memoryDir, edge.type);
|
|
126
|
-
const line = JSON.stringify(edge)
|
|
119
|
+
const line = `${JSON.stringify(edge)}\n`;
|
|
127
120
|
await withGraphWriteLock(filePath, async () => {
|
|
128
121
|
await appendFile(filePath, line, "utf8");
|
|
129
122
|
});
|
|
@@ -205,7 +198,7 @@ export async function readEdgesStrict(memoryDir: string, type: GraphType): Promi
|
|
|
205
198
|
*/
|
|
206
199
|
export async function readAllEdges(
|
|
207
200
|
memoryDir: string,
|
|
208
|
-
config: Pick<GraphConfig, "entityGraphEnabled" | "timeGraphEnabled" | "causalGraphEnabled"
|
|
201
|
+
config: Pick<GraphConfig, "entityGraphEnabled" | "timeGraphEnabled" | "causalGraphEnabled">
|
|
209
202
|
): Promise<GraphEdge[]> {
|
|
210
203
|
const parts: GraphEdge[][] = await Promise.all([
|
|
211
204
|
config.entityGraphEnabled ? readEdges(memoryDir, "entity") : Promise.resolve([]),
|
|
@@ -243,9 +236,12 @@ function isValidGraphEdge(raw: unknown, expectedType: GraphType): raw is GraphEd
|
|
|
243
236
|
const edge = raw as Record<string, unknown>;
|
|
244
237
|
return (
|
|
245
238
|
edge.type === expectedType &&
|
|
246
|
-
typeof edge.from === "string" &&
|
|
247
|
-
|
|
248
|
-
typeof edge.
|
|
239
|
+
typeof edge.from === "string" &&
|
|
240
|
+
edge.from.length > 0 &&
|
|
241
|
+
typeof edge.to === "string" &&
|
|
242
|
+
edge.to.length > 0 &&
|
|
243
|
+
typeof edge.weight === "number" &&
|
|
244
|
+
Number.isFinite(edge.weight) &&
|
|
249
245
|
typeof edge.label === "string" &&
|
|
250
246
|
typeof edge.ts === "string"
|
|
251
247
|
);
|
|
@@ -258,7 +254,7 @@ export async function analyzeGraphHealth(
|
|
|
258
254
|
timeGraphEnabled?: boolean;
|
|
259
255
|
causalGraphEnabled?: boolean;
|
|
260
256
|
includeRepairGuidance?: boolean;
|
|
261
|
-
}
|
|
257
|
+
}
|
|
262
258
|
): Promise<GraphHealthReport> {
|
|
263
259
|
const enabledTypes: GraphType[] = [];
|
|
264
260
|
if (options?.entityGraphEnabled !== false) enabledTypes.push("entity");
|
|
@@ -324,7 +320,7 @@ export async function analyzeGraphHealth(
|
|
|
324
320
|
validEdges: 0,
|
|
325
321
|
corruptLines: 0,
|
|
326
322
|
uniqueNodes: globalNodes.size,
|
|
327
|
-
}
|
|
323
|
+
}
|
|
328
324
|
);
|
|
329
325
|
totals.uniqueNodes = globalNodes.size;
|
|
330
326
|
|
|
@@ -338,10 +334,14 @@ export async function analyzeGraphHealth(
|
|
|
338
334
|
if (options?.includeRepairGuidance === true) {
|
|
339
335
|
const guidance: string[] = [];
|
|
340
336
|
if (totals.corruptLines > 0) {
|
|
341
|
-
guidance.push(
|
|
337
|
+
guidance.push(
|
|
338
|
+
"Corrupt graph lines detected: back up memory/state/graphs, then rebuild graphs from clean memory replay/extraction runs."
|
|
339
|
+
);
|
|
342
340
|
}
|
|
343
341
|
if (totals.validEdges === 0) {
|
|
344
|
-
guidance.push(
|
|
342
|
+
guidance.push(
|
|
343
|
+
"No valid edges detected yet: run normal extraction traffic (or replay ingestion) to seed graph files."
|
|
344
|
+
);
|
|
345
345
|
}
|
|
346
346
|
if (guidance.length > 0) report.repairGuidance = guidance;
|
|
347
347
|
}
|
|
@@ -392,10 +392,7 @@ export class GraphIndex {
|
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
private async loadEdgesCached(): Promise<GraphEdge[]> {
|
|
395
|
-
if (
|
|
396
|
-
this.edgeCache &&
|
|
397
|
-
Date.now() - this.edgeCache.loadedAt < GraphIndex.EDGE_CACHE_TTL_MS
|
|
398
|
-
) {
|
|
395
|
+
if (this.edgeCache && Date.now() - this.edgeCache.loadedAt < GraphIndex.EDGE_CACHE_TTL_MS) {
|
|
399
396
|
return this.edgeCache.allEdges;
|
|
400
397
|
}
|
|
401
398
|
const allEdges = await readAllEdges(this.memoryDir, {
|
|
@@ -520,21 +517,23 @@ export class GraphIndex {
|
|
|
520
517
|
* Default `false` (floor from config is applied).
|
|
521
518
|
*/
|
|
522
519
|
includeLowConfidence?: boolean;
|
|
523
|
-
}
|
|
524
|
-
): Promise<
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
520
|
+
}
|
|
521
|
+
): Promise<
|
|
522
|
+
Array<{
|
|
523
|
+
path: string;
|
|
524
|
+
score: number;
|
|
525
|
+
seed: string;
|
|
526
|
+
hopDepth: number;
|
|
527
|
+
decayedWeight: number;
|
|
528
|
+
graphType: "entity" | "time" | "causal";
|
|
529
|
+
/**
|
|
530
|
+
* Confidence of the edge that produced this candidate's recorded
|
|
531
|
+
* provenance (the strongest edge along the chosen entry path).
|
|
532
|
+
* In `[0, 1]`. Legacy edges without `confidence` surface as 1.0.
|
|
533
|
+
*/
|
|
534
|
+
edgeConfidence: number;
|
|
535
|
+
}>
|
|
536
|
+
> {
|
|
538
537
|
if (!this.cfg.multiGraphMemoryEnabled) return [];
|
|
539
538
|
const steps = maxSteps ?? this.cfg.maxGraphTraversalSteps;
|
|
540
539
|
const decay = this.cfg.graphActivationDecay;
|
|
@@ -543,12 +542,9 @@ export class GraphIndex {
|
|
|
543
542
|
// Otherwise clamp the configured floor into [0, 1] so misconfiguration
|
|
544
543
|
// cannot (a) admit edges with negative confidence or (b) reject every
|
|
545
544
|
// edge.
|
|
546
|
-
const floor =
|
|
547
|
-
? 0
|
|
548
|
-
|
|
549
|
-
const iterations = clampPageRankIterations(
|
|
550
|
-
this.cfg.graphTraversalPageRankIterations,
|
|
551
|
-
);
|
|
545
|
+
const floor =
|
|
546
|
+
opts?.includeLowConfidence === true ? 0 : clampConfidenceFloor(this.cfg.graphTraversalConfidenceFloor);
|
|
547
|
+
const iterations = clampPageRankIterations(this.cfg.graphTraversalPageRankIterations);
|
|
552
548
|
|
|
553
549
|
try {
|
|
554
550
|
const allEdges = await this.loadEdgesCached();
|
|
@@ -581,50 +577,67 @@ export class GraphIndex {
|
|
|
581
577
|
edgeConfidence: number;
|
|
582
578
|
}
|
|
583
579
|
>();
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
for (const
|
|
595
|
-
const
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
const prev = provenance.get(neighbor);
|
|
608
|
-
if (
|
|
609
|
-
!prev ||
|
|
610
|
-
hop + 1 < prev.hopDepth ||
|
|
611
|
-
(hop + 1 === prev.hopDepth && score > prev.decayedWeight)
|
|
612
|
-
) {
|
|
613
|
-
provenance.set(neighbor, {
|
|
614
|
-
seed: sourceSeed,
|
|
615
|
-
hopDepth: hop + 1,
|
|
616
|
-
decayedWeight: score,
|
|
617
|
-
graphType: edge.type,
|
|
618
|
-
edgeConfidence: conf,
|
|
619
|
-
});
|
|
580
|
+
let frontier = new Map<string, { node: string; seed: string; activation: number }>();
|
|
581
|
+
const reachedBySeed = new Map<string, Set<string>>();
|
|
582
|
+
for (const seed of seeds) {
|
|
583
|
+
frontier.set(`${seed}\0${seed}`, { node: seed, seed, activation: 1 });
|
|
584
|
+
reachedBySeed.set(seed, new Set([seed]));
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
for (let hop = 0; hop < steps && frontier.size > 0; hop++) {
|
|
588
|
+
const nextFrontier = new Map<string, { node: string; seed: string; activation: number }>();
|
|
589
|
+
|
|
590
|
+
for (const { node, seed: sourceSeed, activation } of frontier.values()) {
|
|
591
|
+
const edges = adj.get(node) ?? [];
|
|
592
|
+
for (const edge of edges) {
|
|
593
|
+
const neighbor = edge.to === node ? edge.from : edge.to;
|
|
594
|
+
const conf = readEdgeConfidence(edge);
|
|
595
|
+
// Defense in depth: the adjacency build already drops sub-floor
|
|
596
|
+
// edges, but if a synthesized reverse edge ever bypassed that
|
|
597
|
+
// path, this guard keeps spreading activation honest.
|
|
598
|
+
if (conf < floor) continue;
|
|
599
|
+
const score = activation * edge.weight * conf * decay;
|
|
600
|
+
const reachedForSeed = reachedBySeed.get(sourceSeed);
|
|
601
|
+
if (reachedForSeed?.has(neighbor)) {
|
|
602
|
+
continue;
|
|
620
603
|
}
|
|
621
|
-
}
|
|
622
604
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
605
|
+
if (!seedSet.has(neighbor)) {
|
|
606
|
+
const existing = scores.get(neighbor) ?? 0;
|
|
607
|
+
scores.set(neighbor, existing + score);
|
|
608
|
+
|
|
609
|
+
const prev = provenance.get(neighbor);
|
|
610
|
+
if (!prev || hop + 1 < prev.hopDepth || (hop + 1 === prev.hopDepth && score > prev.decayedWeight)) {
|
|
611
|
+
provenance.set(neighbor, {
|
|
612
|
+
seed: sourceSeed,
|
|
613
|
+
hopDepth: hop + 1,
|
|
614
|
+
decayedWeight: score,
|
|
615
|
+
graphType: edge.type,
|
|
616
|
+
edgeConfidence: conf,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (hop + 1 < steps) {
|
|
621
|
+
const frontierKey = `${sourceSeed}\0${neighbor}`;
|
|
622
|
+
const existingFrontier = nextFrontier.get(frontierKey);
|
|
623
|
+
if (existingFrontier) {
|
|
624
|
+
existingFrontier.activation += score;
|
|
625
|
+
} else {
|
|
626
|
+
nextFrontier.set(frontierKey, {
|
|
627
|
+
node: neighbor,
|
|
628
|
+
seed: sourceSeed,
|
|
629
|
+
activation: score,
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
626
634
|
}
|
|
627
635
|
}
|
|
636
|
+
|
|
637
|
+
for (const { node, seed } of nextFrontier.values()) {
|
|
638
|
+
reachedBySeed.get(seed)?.add(node);
|
|
639
|
+
}
|
|
640
|
+
frontier = nextFrontier;
|
|
628
641
|
}
|
|
629
642
|
|
|
630
643
|
// Issue #681 PR 3/3 — optional PageRank-style refinement.
|
|
@@ -712,7 +725,7 @@ export function clampPageRankIterations(raw: unknown): number {
|
|
|
712
725
|
export function applyPageRankRefinement(
|
|
713
726
|
scores: Map<string, number>,
|
|
714
727
|
adj: Map<string, GraphEdge[]>,
|
|
715
|
-
opts: { iterations: number; floor: number; damping: number }
|
|
728
|
+
opts: { iterations: number; floor: number; damping: number }
|
|
716
729
|
): void {
|
|
717
730
|
const { iterations, floor, damping } = opts;
|
|
718
731
|
if (iterations <= 0 || scores.size === 0) return;
|
|
@@ -793,7 +806,7 @@ export function applyPageRankRefinement(
|
|
|
793
806
|
*/
|
|
794
807
|
export function applyLateralInhibition(
|
|
795
808
|
scores: Map<string, number>,
|
|
796
|
-
opts: { beta: number; topM: number }
|
|
809
|
+
opts: { beta: number; topM: number }
|
|
797
810
|
): Map<string, number> {
|
|
798
811
|
const { beta, topM } = opts;
|
|
799
812
|
if (beta === 0 || topM === 0) return new Map(scores);
|