@remnic/core 9.3.677 → 9.3.678
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 +7 -7
- package/dist/access-http.js +6 -6
- package/dist/access-mcp.js +5 -5
- package/dist/access-service.js +4 -4
- package/dist/capabilities.d.ts +29 -1
- package/dist/capabilities.js +5 -3
- 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-7UTCHQTB.js → chunk-2AP4QJX5.js} +3 -3
- package/dist/{chunk-Y56J7CXW.js → chunk-2LDBXPLB.js} +10 -5
- package/dist/chunk-2LDBXPLB.js.map +1 -0
- package/dist/{chunk-CZMLLVU2.js → chunk-4PPMUNV5.js} +3 -3
- package/dist/{chunk-YEQBJXVO.js → chunk-7AAKSHDG.js} +37 -22
- package/dist/chunk-7AAKSHDG.js.map +1 -0
- package/dist/{chunk-RP64QP7G.js → chunk-K2JYO6QV.js} +3 -3
- package/dist/{chunk-RI5XBIZ6.js → chunk-PBH4JUAB.js} +14 -2
- package/dist/{chunk-RI5XBIZ6.js.map → chunk-PBH4JUAB.js.map} +1 -1
- package/dist/{chunk-Z56KDLDK.js → chunk-PCGCQTU6.js} +7 -7
- package/dist/{chunk-B6IUW76R.js → chunk-Q2H5U37U.js} +1 -1
- package/dist/{chunk-WXGTC424.js → chunk-RC3AFF6Z.js} +1 -1
- package/dist/{chunk-HQCGRSRU.js → chunk-SECQS4G4.js} +2 -2
- package/dist/{chunk-2DKXY243.js → chunk-UNZLU2MX.js} +3 -3
- package/dist/{chunk-NE566K4E.js → chunk-YXWAILM4.js} +2 -2
- package/dist/cli.js +8 -8
- package/dist/compounding/engine.js +1 -1
- package/dist/{graph-edge-decay-PUFNHOBS.js → graph-edge-decay-KSVJGCZW.js} +2 -2
- package/dist/graph-snapshot.js +2 -2
- package/dist/graph.d.ts +11 -0
- package/dist/graph.js +1 -1
- package/dist/index.js +11 -11
- package/dist/operator-toolkit.js +2 -2
- package/dist/orchestrator.js +5 -5
- package/package.json +2 -2
- package/src/capabilities.test.ts +173 -0
- package/src/capabilities.ts +61 -0
- package/src/graph.ts +20 -4
- package/src/orchestrator.ts +35 -11
- package/dist/chunk-Y56J7CXW.js.map +0 -1
- package/dist/chunk-YEQBJXVO.js.map +0 -1
- /package/dist/{chunk-7UTCHQTB.js.map → chunk-2AP4QJX5.js.map} +0 -0
- /package/dist/{chunk-CZMLLVU2.js.map → chunk-4PPMUNV5.js.map} +0 -0
- /package/dist/{chunk-RP64QP7G.js.map → chunk-K2JYO6QV.js.map} +0 -0
- /package/dist/{chunk-Z56KDLDK.js.map → chunk-PCGCQTU6.js.map} +0 -0
- /package/dist/{chunk-B6IUW76R.js.map → chunk-Q2H5U37U.js.map} +0 -0
- /package/dist/{chunk-WXGTC424.js.map → chunk-RC3AFF6Z.js.map} +0 -0
- /package/dist/{chunk-HQCGRSRU.js.map → chunk-SECQS4G4.js.map} +0 -0
- /package/dist/{chunk-2DKXY243.js.map → chunk-UNZLU2MX.js.map} +0 -0
- /package/dist/{chunk-NE566K4E.js.map → chunk-YXWAILM4.js.map} +0 -0
- /package/dist/{graph-edge-decay-PUFNHOBS.js.map → graph-edge-decay-KSVJGCZW.js.map} +0 -0
package/src/capabilities.test.ts
CHANGED
|
@@ -95,3 +95,176 @@ test("resolveCapabilities returns a frozen object", () => {
|
|
|
95
95
|
const caps = resolveCapabilities(parseConfig({}));
|
|
96
96
|
assert.equal(Object.isFrozen(caps), true, "CapabilitySet must be frozen");
|
|
97
97
|
});
|
|
98
|
+
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// GraphConstructionCapabilitySet — gate-parity tests (issue #1566 Cluster A).
|
|
101
|
+
//
|
|
102
|
+
// Same invariant as the recall CapabilitySet tests above: every caps field
|
|
103
|
+
// must project from its `<field>Enabled` config flag, so a future edit to
|
|
104
|
+
// `resolveGraphConstructionCapabilities` that maps a field to the wrong flag
|
|
105
|
+
// (or drops the `!== false` default for the optional session-adjacency flag)
|
|
106
|
+
// fails loudly here.
|
|
107
|
+
//
|
|
108
|
+
// Parity contract: a caps-resolved run and a config-derived run MUST produce
|
|
109
|
+
// identical boolean values for every gate — on AND off. These tests are the
|
|
110
|
+
// executable proof of that contract.
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
import {
|
|
114
|
+
resolveGraphConstructionCapabilities,
|
|
115
|
+
type GraphConstructionCapabilitySet,
|
|
116
|
+
} from "./capabilities.js";
|
|
117
|
+
|
|
118
|
+
const GRAPH_FIELD_TO_FLAG: Record<keyof GraphConstructionCapabilitySet, string> = {
|
|
119
|
+
entityGraph: "entityGraphEnabled",
|
|
120
|
+
timeGraph: "timeGraphEnabled",
|
|
121
|
+
causalGraph: "causalGraphEnabled",
|
|
122
|
+
multiGraphMemory: "multiGraphMemoryEnabled",
|
|
123
|
+
graphWriteSessionAdjacency: "graphWriteSessionAdjacencyEnabled",
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const GRAPH_FIELDS = Object.keys(GRAPH_FIELD_TO_FLAG) as Array<
|
|
127
|
+
keyof GraphConstructionCapabilitySet
|
|
128
|
+
>;
|
|
129
|
+
|
|
130
|
+
test("resolveGraphConstructionCapabilities projects every field from its flag (true variant)", () => {
|
|
131
|
+
const overrides: Record<string, boolean> = {};
|
|
132
|
+
for (const flag of Object.values(GRAPH_FIELD_TO_FLAG)) overrides[flag] = true;
|
|
133
|
+
const config = parseConfig(overrides);
|
|
134
|
+
const graphCaps = resolveGraphConstructionCapabilities(config);
|
|
135
|
+
|
|
136
|
+
for (const field of GRAPH_FIELDS) {
|
|
137
|
+
const flag = GRAPH_FIELD_TO_FLAG[field];
|
|
138
|
+
assert.equal(
|
|
139
|
+
graphCaps[field],
|
|
140
|
+
(config as unknown as Record<string, boolean>)[flag],
|
|
141
|
+
`graphCaps.${field} must equal config.${flag} (true variant)`,
|
|
142
|
+
);
|
|
143
|
+
assert.equal(graphCaps[field], true, `graphCaps.${field} should be true here`);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("resolveGraphConstructionCapabilities projects every field from its flag (false variant)", () => {
|
|
148
|
+
const overrides: Record<string, boolean> = {};
|
|
149
|
+
for (const flag of Object.values(GRAPH_FIELD_TO_FLAG)) overrides[flag] = false;
|
|
150
|
+
const config = parseConfig(overrides);
|
|
151
|
+
const graphCaps = resolveGraphConstructionCapabilities(config);
|
|
152
|
+
|
|
153
|
+
for (const field of GRAPH_FIELDS) {
|
|
154
|
+
const flag = GRAPH_FIELD_TO_FLAG[field];
|
|
155
|
+
assert.equal(
|
|
156
|
+
graphCaps[field],
|
|
157
|
+
(config as unknown as Record<string, boolean>)[flag],
|
|
158
|
+
`graphCaps.${field} must equal config.${flag} (false variant)`,
|
|
159
|
+
);
|
|
160
|
+
assert.equal(graphCaps[field], false, `graphCaps.${field} should be false here`);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("resolveGraphConstructionCapabilities preserves graphWriteSessionAdjacency default-on when undefined", () => {
|
|
165
|
+
// graphWriteSessionAdjacencyEnabled is optional — `!== false` means
|
|
166
|
+
// default-ON. This is the exact semantics the migrated call site used
|
|
167
|
+
// (orchestrator.ts buildGraphEdge), and the parity test below verifies
|
|
168
|
+
// the caps resolver does not drift from it.
|
|
169
|
+
const config = parseConfig({});
|
|
170
|
+
const graphCaps = resolveGraphConstructionCapabilities(config);
|
|
171
|
+
|
|
172
|
+
assert.equal(
|
|
173
|
+
graphCaps.graphWriteSessionAdjacency,
|
|
174
|
+
config.graphWriteSessionAdjacencyEnabled !== false,
|
|
175
|
+
"graphWriteSessionAdjacency must be default-on unless explicitly false",
|
|
176
|
+
);
|
|
177
|
+
assert.equal(
|
|
178
|
+
graphCaps.graphWriteSessionAdjacency,
|
|
179
|
+
true,
|
|
180
|
+
"with no explicit override, graphWriteSessionAdjacency should be true",
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// Explicit-false must propagate (not get swallowed by the default).
|
|
184
|
+
const disabled = parseConfig({ graphWriteSessionAdjacencyEnabled: false });
|
|
185
|
+
assert.equal(
|
|
186
|
+
resolveGraphConstructionCapabilities(disabled).graphWriteSessionAdjacency,
|
|
187
|
+
false,
|
|
188
|
+
"explicit false must disable graphWriteSessionAdjacency",
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("resolveGraphConstructionCapabilities returns a frozen object", () => {
|
|
193
|
+
const graphCaps = resolveGraphConstructionCapabilities(parseConfig({}));
|
|
194
|
+
assert.equal(Object.isFrozen(graphCaps), true, "GraphConstructionCapabilitySet must be frozen");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("resolveGraphConstructionCapabilities matches pre-migration config reads (parity contract)", () => {
|
|
198
|
+
// This is the core parity test: for EVERY combination of the 5 cluster-A
|
|
199
|
+
// flags, the caps-resolved values MUST be identical to what the old
|
|
200
|
+
// scattered `this.config.<flag>Enabled` reads would have produced.
|
|
201
|
+
//
|
|
202
|
+
// We exercise representative combinations rather than the full 2^5 space
|
|
203
|
+
// (32 cases) to keep the test cheap, but we cover:
|
|
204
|
+
// - all-off (the "no graphs" baseline)
|
|
205
|
+
// - all-on (the "full graph" mode)
|
|
206
|
+
// - only entity graph
|
|
207
|
+
// - multiGraph off but others on (the recall-without-write split)
|
|
208
|
+
// - session-adjacency undefined vs explicit-false
|
|
209
|
+
const cases: Record<string, Record<string, boolean | undefined>> = {
|
|
210
|
+
"all-off": {
|
|
211
|
+
entityGraphEnabled: false,
|
|
212
|
+
timeGraphEnabled: false,
|
|
213
|
+
causalGraphEnabled: false,
|
|
214
|
+
multiGraphMemoryEnabled: false,
|
|
215
|
+
graphWriteSessionAdjacencyEnabled: false,
|
|
216
|
+
},
|
|
217
|
+
"all-on": {
|
|
218
|
+
entityGraphEnabled: true,
|
|
219
|
+
timeGraphEnabled: true,
|
|
220
|
+
causalGraphEnabled: true,
|
|
221
|
+
multiGraphMemoryEnabled: true,
|
|
222
|
+
graphWriteSessionAdjacencyEnabled: true,
|
|
223
|
+
},
|
|
224
|
+
"entity-only": {
|
|
225
|
+
entityGraphEnabled: true,
|
|
226
|
+
timeGraphEnabled: false,
|
|
227
|
+
causalGraphEnabled: false,
|
|
228
|
+
multiGraphMemoryEnabled: true,
|
|
229
|
+
graphWriteSessionAdjacencyEnabled: true,
|
|
230
|
+
},
|
|
231
|
+
"multigraph-off": {
|
|
232
|
+
entityGraphEnabled: true,
|
|
233
|
+
timeGraphEnabled: true,
|
|
234
|
+
causalGraphEnabled: true,
|
|
235
|
+
multiGraphMemoryEnabled: false,
|
|
236
|
+
graphWriteSessionAdjacencyEnabled: true,
|
|
237
|
+
},
|
|
238
|
+
"session-adj-undefined": {
|
|
239
|
+
entityGraphEnabled: true,
|
|
240
|
+
timeGraphEnabled: true,
|
|
241
|
+
causalGraphEnabled: true,
|
|
242
|
+
multiGraphMemoryEnabled: true,
|
|
243
|
+
// graphWriteSessionAdjacencyEnabled deliberately omitted
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
for (const [label, overrides] of Object.entries(cases)) {
|
|
248
|
+
const config = parseConfig(overrides);
|
|
249
|
+
const graphCaps = resolveGraphConstructionCapabilities(config);
|
|
250
|
+
|
|
251
|
+
// Parity: each caps field must equal the pre-migration config read.
|
|
252
|
+
// entityGraph/timeGraph/causalGraph/multiGraphMemory used bare reads:
|
|
253
|
+
// config.entityGraphEnabled → graphCaps.entityGraph
|
|
254
|
+
// graphWriteSessionAdjacency used `!== false`:
|
|
255
|
+
// config.graphWriteSessionAdjacencyEnabled !== false → graphCaps.graphWriteSessionAdjacency
|
|
256
|
+
assert.equal(graphCaps.entityGraph, config.entityGraphEnabled, `[${label}] entityGraph parity`);
|
|
257
|
+
assert.equal(graphCaps.timeGraph, config.timeGraphEnabled, `[${label}] timeGraph parity`);
|
|
258
|
+
assert.equal(graphCaps.causalGraph, config.causalGraphEnabled, `[${label}] causalGraph parity`);
|
|
259
|
+
assert.equal(
|
|
260
|
+
graphCaps.multiGraphMemory,
|
|
261
|
+
config.multiGraphMemoryEnabled,
|
|
262
|
+
`[${label}] multiGraphMemory parity`,
|
|
263
|
+
);
|
|
264
|
+
assert.equal(
|
|
265
|
+
graphCaps.graphWriteSessionAdjacency,
|
|
266
|
+
config.graphWriteSessionAdjacencyEnabled !== false,
|
|
267
|
+
`[${label}] graphWriteSessionAdjacency parity (must match !== false)`,
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
});
|
package/src/capabilities.ts
CHANGED
|
@@ -84,3 +84,64 @@ export function resolveCapabilities(config: PluginConfig): CapabilitySet {
|
|
|
84
84
|
graphExpandedIntent: config.graphExpandedIntentEnabled === true,
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
+
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Graph-construction capability set (issue #1566 Cluster A).
|
|
90
|
+
//
|
|
91
|
+
// The recall CapabilitySet above covers flags whose EVERY read site lives on
|
|
92
|
+
// the recall call chain. The five flags below are read on graph-construction,
|
|
93
|
+
// write/extraction, AND recall paths — so they cannot join the recall set
|
|
94
|
+
// without leaving some sites on `caps.` and others on `config.` (the exact
|
|
95
|
+
// divergence #1523 forbade). They get their own projection, resolved at graph
|
|
96
|
+
// build/write entry (and alongside `caps` when recall reads them).
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Frozen projection of graph-construction feature gates.
|
|
101
|
+
*
|
|
102
|
+
* Every field is `readonly boolean`. Composition (including default-when-
|
|
103
|
+
* undefined semantics for the optional `graphWriteSessionAdjacencyEnabled`)
|
|
104
|
+
* lives ONLY in {@link resolveGraphConstructionCapabilities}.
|
|
105
|
+
*/
|
|
106
|
+
export interface GraphConstructionCapabilitySet {
|
|
107
|
+
/** `entityGraphEnabled` — maintain entity co-reference graph edges. */
|
|
108
|
+
readonly entityGraph: boolean;
|
|
109
|
+
/** `timeGraphEnabled` — maintain thread-adjacency graph edges. */
|
|
110
|
+
readonly timeGraph: boolean;
|
|
111
|
+
/** `causalGraphEnabled` — maintain causal-language graph edges. */
|
|
112
|
+
readonly causalGraph: boolean;
|
|
113
|
+
/** `multiGraphMemoryEnabled` — master switch for multi-graph writes/traversal. */
|
|
114
|
+
readonly multiGraphMemory: boolean;
|
|
115
|
+
/** `graphWriteSessionAdjacencyEnabled` — session-adjacency fallback for time edges (default-on). */
|
|
116
|
+
readonly graphWriteSessionAdjacency: boolean;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Resolve the {@link GraphConstructionCapabilitySet} from parsed config.
|
|
121
|
+
*
|
|
122
|
+
* Call this ONCE at graph build/write entry (extraction, recall graph
|
|
123
|
+
* expansion, graph-health/repair) and thread the result down — exactly the
|
|
124
|
+
* pattern {@link resolveCapabilities} established for recall.
|
|
125
|
+
*/
|
|
126
|
+
export type GraphConstructionConfigProjection = Pick<
|
|
127
|
+
PluginConfig,
|
|
128
|
+
| "entityGraphEnabled"
|
|
129
|
+
| "timeGraphEnabled"
|
|
130
|
+
| "causalGraphEnabled"
|
|
131
|
+
| "multiGraphMemoryEnabled"
|
|
132
|
+
| "graphWriteSessionAdjacencyEnabled"
|
|
133
|
+
>;
|
|
134
|
+
|
|
135
|
+
export function resolveGraphConstructionCapabilities(
|
|
136
|
+
config: GraphConstructionConfigProjection,
|
|
137
|
+
): GraphConstructionCapabilitySet {
|
|
138
|
+
return Object.freeze({
|
|
139
|
+
entityGraph: config.entityGraphEnabled,
|
|
140
|
+
timeGraph: config.timeGraphEnabled,
|
|
141
|
+
causalGraph: config.causalGraphEnabled,
|
|
142
|
+
multiGraphMemory: config.multiGraphMemoryEnabled,
|
|
143
|
+
// Optional flag: preserve the exact default-when-undefined semantics the
|
|
144
|
+
// migrated call site used (`!== false` = default-on).
|
|
145
|
+
graphWriteSessionAdjacency: config.graphWriteSessionAdjacencyEnabled !== false,
|
|
146
|
+
});
|
|
147
|
+
}
|
package/src/graph.ts
CHANGED
|
@@ -424,13 +424,29 @@ export class GraphIndex {
|
|
|
424
424
|
recentInThread?: string[];
|
|
425
425
|
entitySiblings?: string[];
|
|
426
426
|
causalPredecessor?: string;
|
|
427
|
+
/**
|
|
428
|
+
* Optional frozen gate overrides (from GraphConstructionCapabilitySet).
|
|
429
|
+
* When provided, these take precedence over `this.cfg` so the caller's
|
|
430
|
+
* operation-scoped snapshot is the single source of truth (#1566).
|
|
431
|
+
*/
|
|
432
|
+
graphCapsOverride?: {
|
|
433
|
+
entityGraph: boolean;
|
|
434
|
+
timeGraph: boolean;
|
|
435
|
+
causalGraph: boolean;
|
|
436
|
+
multiGraphMemory: boolean;
|
|
437
|
+
};
|
|
427
438
|
}): Promise<void> {
|
|
428
|
-
|
|
439
|
+
const g = opts.graphCapsOverride;
|
|
440
|
+
const multiGraphOn = g ? g.multiGraphMemory : this.cfg.multiGraphMemoryEnabled;
|
|
441
|
+
if (!multiGraphOn) return;
|
|
442
|
+
const entityOn = g ? g.entityGraph : this.cfg.entityGraphEnabled;
|
|
443
|
+
const timeOn = g ? g.timeGraph : this.cfg.timeGraphEnabled;
|
|
444
|
+
const causalOn = g ? g.causalGraph : this.cfg.causalGraphEnabled;
|
|
429
445
|
const ts = new Date().toISOString();
|
|
430
446
|
|
|
431
447
|
try {
|
|
432
448
|
// Entity graph
|
|
433
|
-
if (
|
|
449
|
+
if (entityOn && opts.entityRef && opts.entitySiblings?.length) {
|
|
434
450
|
const siblings = opts.entitySiblings.slice(0, this.cfg.maxEntityGraphEdgesPerMemory);
|
|
435
451
|
for (const sibling of siblings) {
|
|
436
452
|
await appendEdge(this.memoryDir, {
|
|
@@ -445,7 +461,7 @@ export class GraphIndex {
|
|
|
445
461
|
}
|
|
446
462
|
|
|
447
463
|
// Time graph — link to most recent memory in same thread
|
|
448
|
-
if (
|
|
464
|
+
if (timeOn && opts.threadId && opts.recentInThread?.length) {
|
|
449
465
|
const predecessor = opts.recentInThread[opts.recentInThread.length - 1];
|
|
450
466
|
if (predecessor && predecessor !== opts.memoryPath) {
|
|
451
467
|
await appendEdge(this.memoryDir, {
|
|
@@ -460,7 +476,7 @@ export class GraphIndex {
|
|
|
460
476
|
}
|
|
461
477
|
|
|
462
478
|
// Causal graph
|
|
463
|
-
if (
|
|
479
|
+
if (causalOn && opts.causalPredecessor) {
|
|
464
480
|
const phrase = detectCausalPhrase(opts.content);
|
|
465
481
|
if (phrase) {
|
|
466
482
|
await appendEdge(this.memoryDir, {
|
package/src/orchestrator.ts
CHANGED
|
@@ -245,7 +245,12 @@ import {
|
|
|
245
245
|
type TrustZoneSearchResult,
|
|
246
246
|
} from "./trust-zones.js";
|
|
247
247
|
import { tryDirectAnswer, type DirectAnswerSources } from "./direct-answer-wiring.js";
|
|
248
|
-
import {
|
|
248
|
+
import {
|
|
249
|
+
resolveCapabilities,
|
|
250
|
+
resolveGraphConstructionCapabilities,
|
|
251
|
+
type CapabilitySet,
|
|
252
|
+
type GraphConstructionCapabilitySet,
|
|
253
|
+
} from "./capabilities.js";
|
|
249
254
|
import { DEFAULT_TAXONOMY } from "./taxonomy/index.js";
|
|
250
255
|
import {
|
|
251
256
|
searchHarmonicRetrieval,
|
|
@@ -5756,6 +5761,7 @@ export class Orchestrator {
|
|
|
5756
5761
|
// entry, and thread the frozen set down (issue #1523). Never re-read the
|
|
5757
5762
|
// migrated flags off `this.config` mid-operation.
|
|
5758
5763
|
const caps = resolveCapabilities(this.config);
|
|
5764
|
+
const graphCaps = resolveGraphConstructionCapabilities(this.config); // #1566 Cluster A
|
|
5759
5765
|
const abortController = new AbortController();
|
|
5760
5766
|
const onAbort = () => {
|
|
5761
5767
|
abortController.abort();
|
|
@@ -5832,7 +5838,7 @@ export class Orchestrator {
|
|
|
5832
5838
|
const recallPromise = this.recallInternal(prompt, sessionKey, {
|
|
5833
5839
|
...options,
|
|
5834
5840
|
abortSignal: abortController.signal,
|
|
5835
|
-
}, caps);
|
|
5841
|
+
}, caps, graphCaps);
|
|
5836
5842
|
const RECALL_TIMEOUT_MS = this.config.recallOuterTimeoutMs ?? 75_000;
|
|
5837
5843
|
if (RECALL_TIMEOUT_MS <= 0) {
|
|
5838
5844
|
return await recallPromise;
|
|
@@ -7279,6 +7285,7 @@ export class Orchestrator {
|
|
|
7279
7285
|
sessionKey?: string,
|
|
7280
7286
|
options: RecallInvocationOptions = {},
|
|
7281
7287
|
caps: CapabilitySet = resolveCapabilities(this.config),
|
|
7288
|
+
graphCaps: GraphConstructionCapabilitySet = resolveGraphConstructionCapabilities(this.config),
|
|
7282
7289
|
): Promise<string> {
|
|
7283
7290
|
const recallStart = Date.now();
|
|
7284
7291
|
// Backend degradations observed by this recall's QMD searches (#1536):
|
|
@@ -7440,7 +7447,7 @@ export class Orchestrator {
|
|
|
7440
7447
|
const recallModeDecisionOptions = {
|
|
7441
7448
|
plannerEnabled: caps.recallPlanner,
|
|
7442
7449
|
graphRecallEnabled: caps.graphRecall,
|
|
7443
|
-
multiGraphMemoryEnabled:
|
|
7450
|
+
multiGraphMemoryEnabled: graphCaps.multiGraphMemory,
|
|
7444
7451
|
graphExpandedIntentEnabled: caps.graphExpandedIntent,
|
|
7445
7452
|
prompt,
|
|
7446
7453
|
};
|
|
@@ -7678,7 +7685,7 @@ export class Orchestrator {
|
|
|
7678
7685
|
shadowMode: graphDecisionShadowMode,
|
|
7679
7686
|
qmdAvailable,
|
|
7680
7687
|
graphRecallEnabled: caps.graphRecall,
|
|
7681
|
-
multiGraphMemoryEnabled:
|
|
7688
|
+
multiGraphMemoryEnabled: graphCaps.multiGraphMemory,
|
|
7682
7689
|
},
|
|
7683
7690
|
});
|
|
7684
7691
|
|
|
@@ -10898,7 +10905,7 @@ export class Orchestrator {
|
|
|
10898
10905
|
);
|
|
10899
10906
|
|
|
10900
10907
|
const isFullModeGraphAssist =
|
|
10901
|
-
|
|
10908
|
+
graphCaps.multiGraphMemory &&
|
|
10902
10909
|
caps.graphAssistInFullMode &&
|
|
10903
10910
|
recallMode === "full" &&
|
|
10904
10911
|
memoryResults.length >=
|
|
@@ -11321,6 +11328,7 @@ export class Orchestrator {
|
|
|
11321
11328
|
recallResultLimit,
|
|
11322
11329
|
recallMode,
|
|
11323
11330
|
caps,
|
|
11331
|
+
graphCaps,
|
|
11324
11332
|
queryAwarePrefilter,
|
|
11325
11333
|
abortSignal: options.abortSignal,
|
|
11326
11334
|
onDegradation: (degradation) => {
|
|
@@ -11548,6 +11556,7 @@ export class Orchestrator {
|
|
|
11548
11556
|
recallResultLimit,
|
|
11549
11557
|
recallMode,
|
|
11550
11558
|
caps,
|
|
11559
|
+
graphCaps,
|
|
11551
11560
|
queryAwarePrefilter,
|
|
11552
11561
|
abortSignal: options.abortSignal,
|
|
11553
11562
|
onDegradation: (degradation) => {
|
|
@@ -11662,6 +11671,7 @@ export class Orchestrator {
|
|
|
11662
11671
|
recallResultLimit,
|
|
11663
11672
|
recallMode,
|
|
11664
11673
|
caps,
|
|
11674
|
+
graphCaps,
|
|
11665
11675
|
queryAwarePrefilter,
|
|
11666
11676
|
abortSignal: options.abortSignal,
|
|
11667
11677
|
onDegradation: (degradation) => {
|
|
@@ -11710,6 +11720,7 @@ export class Orchestrator {
|
|
|
11710
11720
|
recallResultLimit,
|
|
11711
11721
|
recallMode,
|
|
11712
11722
|
caps,
|
|
11723
|
+
graphCaps,
|
|
11713
11724
|
queryAwarePrefilter,
|
|
11714
11725
|
abortSignal: options.abortSignal,
|
|
11715
11726
|
onDegradation: (degradation) => {
|
|
@@ -13926,6 +13937,7 @@ export class Orchestrator {
|
|
|
13926
13937
|
sourceContext?: { sessionKey?: string; principal?: string; validAt?: string },
|
|
13927
13938
|
baseNamespace?: string,
|
|
13928
13939
|
scopeProfileWritePlan?: ResolvedScopeProfilePlan | null,
|
|
13940
|
+
graphCaps: GraphConstructionCapabilitySet = resolveGraphConstructionCapabilities(this.config),
|
|
13929
13941
|
): Promise<string[]> {
|
|
13930
13942
|
// Inline source attribution (issue #369). When enabled, every extracted
|
|
13931
13943
|
// fact is rewritten to carry a compact provenance tag inside its body so
|
|
@@ -14587,7 +14599,7 @@ export class Orchestrator {
|
|
|
14587
14599
|
allMemsForGraph: null,
|
|
14588
14600
|
memoryPathById: new Map<string, string>(),
|
|
14589
14601
|
};
|
|
14590
|
-
if (
|
|
14602
|
+
if (graphCaps.multiGraphMemory) {
|
|
14591
14603
|
try {
|
|
14592
14604
|
created.allMemsForGraph = await targetStorage.readAllMemories();
|
|
14593
14605
|
for (const [id, relPath] of buildMemoryPathById(
|
|
@@ -14604,7 +14616,7 @@ export class Orchestrator {
|
|
|
14604
14616
|
return created;
|
|
14605
14617
|
};
|
|
14606
14618
|
let threadEpisodeIdsForGraph: string[] | undefined;
|
|
14607
|
-
if (
|
|
14619
|
+
if (graphCaps.multiGraphMemory && threadIdForExtraction) {
|
|
14608
14620
|
try {
|
|
14609
14621
|
const thread = await this.threading.loadThread(threadIdForExtraction);
|
|
14610
14622
|
threadEpisodeIdsForGraph = thread?.episodeIds
|
|
@@ -15444,7 +15456,7 @@ export class Orchestrator {
|
|
|
15444
15456
|
});
|
|
15445
15457
|
}
|
|
15446
15458
|
// v8.2: graph edge building for chunked memories
|
|
15447
|
-
if (
|
|
15459
|
+
if (graphCaps.multiGraphMemory) {
|
|
15448
15460
|
try {
|
|
15449
15461
|
const graphContext = await ensureGraphContext(targetStorage);
|
|
15450
15462
|
const entityRef =
|
|
@@ -15477,6 +15489,7 @@ export class Orchestrator {
|
|
|
15477
15489
|
threadIdForExtraction ?? undefined,
|
|
15478
15490
|
threadEpisodeIdsForGraph,
|
|
15479
15491
|
graphContext.previousPersistedRelPath,
|
|
15492
|
+
graphCaps,
|
|
15480
15493
|
);
|
|
15481
15494
|
graphContext.previousPersistedRelPath = parentRelPath;
|
|
15482
15495
|
} catch {
|
|
@@ -15636,7 +15649,7 @@ export class Orchestrator {
|
|
|
15636
15649
|
source: extractionWriteSource,
|
|
15637
15650
|
});
|
|
15638
15651
|
// v8.2: graph edge building (fail-open — errors caught inside GraphIndex)
|
|
15639
|
-
if (
|
|
15652
|
+
if (graphCaps.multiGraphMemory) {
|
|
15640
15653
|
try {
|
|
15641
15654
|
const graphContext = await ensureGraphContext(targetStorage);
|
|
15642
15655
|
const entityRef =
|
|
@@ -15669,6 +15682,7 @@ export class Orchestrator {
|
|
|
15669
15682
|
threadIdForExtraction ?? undefined,
|
|
15670
15683
|
threadEpisodeIdsForGraph,
|
|
15671
15684
|
graphContext.previousPersistedRelPath,
|
|
15685
|
+
graphCaps,
|
|
15672
15686
|
);
|
|
15673
15687
|
graphContext.previousPersistedRelPath = memoryRelPath;
|
|
15674
15688
|
} catch {
|
|
@@ -15940,6 +15954,7 @@ export class Orchestrator {
|
|
|
15940
15954
|
threadIdForEdge: string | undefined,
|
|
15941
15955
|
threadEpisodeIdsForGraph: string[] | undefined,
|
|
15942
15956
|
fallbackCausalPredecessor: string | undefined,
|
|
15957
|
+
graphCaps: GraphConstructionCapabilitySet = resolveGraphConstructionCapabilities(this.config),
|
|
15943
15958
|
): Promise<void> {
|
|
15944
15959
|
// Entity siblings: other memories sharing the same entityRef
|
|
15945
15960
|
const entitySiblings: string[] = [];
|
|
@@ -15976,7 +15991,7 @@ export class Orchestrator {
|
|
|
15976
15991
|
}
|
|
15977
15992
|
if (
|
|
15978
15993
|
recentInThread.length === 0 &&
|
|
15979
|
-
|
|
15994
|
+
graphCaps.graphWriteSessionAdjacency &&
|
|
15980
15995
|
fallbackCausalPredecessor &&
|
|
15981
15996
|
fallbackCausalPredecessor !== memoryRelPath
|
|
15982
15997
|
) {
|
|
@@ -15993,6 +16008,12 @@ export class Orchestrator {
|
|
|
15993
16008
|
recentInThread,
|
|
15994
16009
|
entitySiblings,
|
|
15995
16010
|
causalPredecessor,
|
|
16011
|
+
graphCapsOverride: {
|
|
16012
|
+
entityGraph: graphCaps.entityGraph,
|
|
16013
|
+
timeGraph: graphCaps.timeGraph,
|
|
16014
|
+
causalGraph: graphCaps.causalGraph,
|
|
16015
|
+
multiGraphMemory: graphCaps.multiGraphMemory,
|
|
16016
|
+
},
|
|
15996
16017
|
});
|
|
15997
16018
|
}
|
|
15998
16019
|
|
|
@@ -18608,6 +18629,8 @@ export class Orchestrator {
|
|
|
18608
18629
|
* equivalent config-derived set — behavior-preserving.
|
|
18609
18630
|
*/
|
|
18610
18631
|
caps?: CapabilitySet;
|
|
18632
|
+
/** Graph-construction gates resolved at recall entry (#1566 Cluster A). */
|
|
18633
|
+
graphCaps?: GraphConstructionCapabilitySet;
|
|
18611
18634
|
queryAwarePrefilter?: QueryAwarePrefilter;
|
|
18612
18635
|
abortSignal?: AbortSignal;
|
|
18613
18636
|
/** Backend degradation observer — cold-tier QMD must report like hot (#1536). */
|
|
@@ -18630,6 +18653,7 @@ export class Orchestrator {
|
|
|
18630
18653
|
// Prefer the threaded set; fall back to a config-derived set so direct
|
|
18631
18654
|
// callers (unit tests) behave identically to the recall pipeline (#1523).
|
|
18632
18655
|
const caps = options.caps ?? resolveCapabilities(this.config);
|
|
18656
|
+
const graphCaps = options.graphCaps ?? resolveGraphConstructionCapabilities(this.config);
|
|
18633
18657
|
if (options.queryAwarePrefilter?.candidatePaths?.size === 0) {
|
|
18634
18658
|
if (options.xrayPoolSizeSink) options.xrayPoolSizeSink.size = 0;
|
|
18635
18659
|
return [];
|
|
@@ -18844,7 +18868,7 @@ export class Orchestrator {
|
|
|
18844
18868
|
|
|
18845
18869
|
const isFullModeGraphAssist =
|
|
18846
18870
|
this.config.qmdTierParityGraphEnabled &&
|
|
18847
|
-
|
|
18871
|
+
graphCaps.multiGraphMemory &&
|
|
18848
18872
|
caps.graphAssistInFullMode &&
|
|
18849
18873
|
options.recallMode === "full" &&
|
|
18850
18874
|
results.length >= Math.max(1, this.config.graphAssistMinSeedResults ?? 3);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/graph.ts"],"sourcesContent":["/**\n * Multi-Graph Memory (MAGMA/SYNAPSE-inspired, v8.2)\n *\n * Maintains three typed edge graphs:\n * entity.jsonl — memories sharing a named entity (entityRef)\n * time.jsonl — consecutive memories in the same thread/session\n * causal.jsonl — memories linked by causal language heuristics\n *\n * Stored under `<memoryDir>/state/graphs/`.\n * All writes are fail-open: errors are caught/logged, never thrown.\n */\n\nimport { appendFile, mkdir, readFile } from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { readEdgeConfidence } from \"./graph-edge-reinforcement.js\";\nimport { emitGraphEvent } from \"./graph-events.js\";\n\nexport type GraphType = \"entity\" | \"time\" | \"causal\";\n\nexport interface GraphEdge {\n from: string; // relative memory path (e.g. \"facts/2026-02-22/abc.md\")\n to: string; // relative memory path\n type: GraphType;\n weight: number; // 1.0 default, decay applied during traversal\n label: string; // entity name, threadId, or matched causal phrase\n ts: string; // ISO timestamp of edge creation\n\n // Issue #681 — edge confidence + reinforcement (PR 1/3: schema + primitive only).\n // Both fields are optional so existing edges without confidence still validate.\n // Treat a missing `confidence` as 1.0 (legacy behavior) at read sites.\n // PR 2/3 wires the maintenance decay job; PR 3/3 weights PageRank traversal by confidence.\n confidence?: number; // [0, 1]; missing = 1.0\n lastReinforcedAt?: string; // ISO timestamp of most recent reinforcement\n}\n\nexport interface GraphConfig {\n multiGraphMemoryEnabled: boolean;\n entityGraphEnabled: boolean;\n timeGraphEnabled: boolean;\n causalGraphEnabled: boolean;\n maxGraphTraversalSteps: number;\n graphActivationDecay: number;\n maxEntityGraphEdgesPerMemory: number;\n graphLateralInhibitionEnabled: boolean;\n graphLateralInhibitionBeta: number;\n graphLateralInhibitionTopM: number;\n /**\n * Issue #681 PR 3/3 — minimum edge confidence required for traversal.\n * Edges with confidence below this floor are pruned. Legacy edges\n * (no `confidence` field) are treated as 1.0 and always pass.\n * Range `[0, 1]`. Default 0.2.\n */\n graphTraversalConfidenceFloor: number;\n /**\n * Issue #681 PR 3/3 — number of PageRank-style refinement iterations\n * applied on top of BFS activation. Set to 0 to disable refinement\n * and return raw BFS scores. Default 8.\n */\n graphTraversalPageRankIterations: number;\n}\n\n/** Default minimum edge confidence required for traversal (issue #681 PR 3/3). */\nexport const DEFAULT_GRAPH_TRAVERSAL_CONFIDENCE_FLOOR = 0.2;\n/** Default PageRank-style refinement iteration count (issue #681 PR 3/3). */\nexport const DEFAULT_GRAPH_TRAVERSAL_PAGERANK_ITERATIONS = 8;\n\n// Causal signal phrases — order matters (most specific first)\nexport const CAUSAL_PHRASES = [\"as a result\", \"led to\", \"because of\", \"therefore\", \"caused\", \"because\"];\n\nexport function graphsDir(memoryDir: string): string {\n return path.join(memoryDir, \"state\", \"graphs\");\n}\n\nexport function graphFilePath(memoryDir: string, type: GraphType): string {\n return path.join(graphsDir(memoryDir), `${type}.jsonl`);\n}\n\nexport async function ensureGraphsDir(memoryDir: string): Promise<void> {\n await mkdir(graphsDir(memoryDir), { recursive: true });\n}\n\n// ---------------------------------------------------------------------------\n// Per-graph-file write lock (gotcha #40 promise-chain pattern).\n//\n// Both the append path (`appendEdge`) and the rewrite path used by the\n// decay maintenance job must serialize on the same lock keyed by the\n// JSONL file path. Without this, an extraction can append a new edge\n// between the decay job's read-snapshot and rewrite, silently dropping\n// the appended edge during active traffic (issue #729 / Codex P1).\n// ---------------------------------------------------------------------------\nconst graphWriteLocks = new Map<string, Promise<void>>();\n\n/**\n * Run `fn` while holding the write lock for the given graph JSONL file.\n *\n * The lock is keyed by absolute file path so concurrent writes to\n * different graph types proceed independently. The chain recovers from\n * rejection (gotcha #40) so a single I/O failure does not poison all\n * future writers, but the original error is still surfaced to the\n * caller of `withGraphWriteLock`.\n */\nexport function withGraphWriteLock<T>(filePath: string, fn: () => Promise<T>): Promise<T> {\n const prev = graphWriteLocks.get(filePath) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n graphWriteLocks.set(\n filePath,\n next.then(\n () => undefined,\n () => undefined\n )\n );\n return next;\n}\n\nexport async function appendEdge(memoryDir: string, edge: GraphEdge): Promise<void> {\n await ensureGraphsDir(memoryDir);\n const filePath = graphFilePath(memoryDir, edge.type);\n const line = `${JSON.stringify(edge)}\\n`;\n await withGraphWriteLock(filePath, async () => {\n await appendFile(filePath, line, \"utf8\");\n });\n // Emit edge-added event for SSE subscribers (issue #691 PR 5/5).\n // Fail-open: emitGraphEvent catches listener errors so a bad SSE client\n // can never surface into the extraction pipeline.\n emitGraphEvent(memoryDir, \"edge-added\", {\n source: edge.from,\n target: edge.to,\n kind: edge.type,\n weight: edge.weight,\n label: edge.label,\n confidence: typeof edge.confidence === \"number\" ? edge.confidence : 1.0,\n });\n}\n\nfunction isNodeError(err: unknown): err is NodeJS.ErrnoException {\n return typeof err === \"object\" && err !== null && \"code\" in err;\n}\n\nfunction parseEdgesJsonl(raw: string, expectedType: GraphType): GraphEdge[] {\n const edges: GraphEdge[] = [];\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const parsed = JSON.parse(trimmed) as unknown;\n if (isValidGraphEdge(parsed, expectedType)) {\n edges.push(parsed);\n }\n } catch {\n // skip corrupt lines — fail-open for partial JSONL recovery\n }\n }\n return edges;\n}\n\n/**\n * Read all edges of a given type from the JSONL file.\n * Returns [] if the file doesn't exist or any read error occurs (fail-open).\n *\n * Production traversal callers (recall/PageRank) depend on this fail-open\n * posture so a temporarily missing or unreadable graph file never blocks\n * a recall. Maintenance jobs that need to distinguish ENOENT from real\n * I/O failures must use {@link readEdgesStrict} instead.\n */\nexport async function readEdges(memoryDir: string, type: GraphType): Promise<GraphEdge[]> {\n const filePath = graphFilePath(memoryDir, type);\n try {\n const raw = await readFile(filePath, \"utf8\");\n return parseEdgesJsonl(raw, type);\n } catch {\n return [];\n }\n}\n\n/**\n * Same as {@link readEdges} but only swallows `ENOENT`; all other read\n * errors (`EACCES`, `EIO`, …) are propagated. Used by the graph-edge\n * decay maintenance job so I/O outages surface as a failed run instead\n * of being silently reported as \"no edges to decay\" (issue #729 /\n * Codex P1, line 120).\n */\nexport async function readEdgesStrict(memoryDir: string, type: GraphType): Promise<GraphEdge[]> {\n const filePath = graphFilePath(memoryDir, type);\n try {\n const raw = await readFile(filePath, \"utf8\");\n return parseEdgesJsonl(raw, type);\n } catch (err) {\n if (isNodeError(err) && err.code === \"ENOENT\") {\n return [];\n }\n throw err;\n }\n}\n\n/**\n * Read edges from all enabled graph types.\n */\nexport async function readAllEdges(\n memoryDir: string,\n config: Pick<GraphConfig, \"entityGraphEnabled\" | \"timeGraphEnabled\" | \"causalGraphEnabled\">\n): Promise<GraphEdge[]> {\n const parts: GraphEdge[][] = await Promise.all([\n config.entityGraphEnabled ? readEdges(memoryDir, \"entity\") : Promise.resolve([]),\n config.timeGraphEnabled ? readEdges(memoryDir, \"time\") : Promise.resolve([]),\n config.causalGraphEnabled ? readEdges(memoryDir, \"causal\") : Promise.resolve([]),\n ]);\n return parts.flat();\n}\n\nexport interface GraphHealthFileStats {\n type: GraphType;\n filePath: string;\n exists: boolean;\n totalLines: number;\n validEdges: number;\n corruptLines: number;\n uniqueNodes: number;\n}\n\nexport interface GraphHealthReport {\n generatedAt: string;\n enabledTypes: GraphType[];\n totals: {\n totalLines: number;\n validEdges: number;\n corruptLines: number;\n uniqueNodes: number;\n };\n files: GraphHealthFileStats[];\n repairGuidance?: string[];\n}\n\nfunction isValidGraphEdge(raw: unknown, expectedType: GraphType): raw is GraphEdge {\n if (!raw || typeof raw !== \"object\") return false;\n const edge = raw as Record<string, unknown>;\n return (\n edge.type === expectedType &&\n typeof edge.from === \"string\" &&\n edge.from.length > 0 &&\n typeof edge.to === \"string\" &&\n edge.to.length > 0 &&\n typeof edge.weight === \"number\" &&\n Number.isFinite(edge.weight) &&\n typeof edge.label === \"string\" &&\n typeof edge.ts === \"string\"\n );\n}\n\nexport async function analyzeGraphHealth(\n memoryDir: string,\n options?: {\n entityGraphEnabled?: boolean;\n timeGraphEnabled?: boolean;\n causalGraphEnabled?: boolean;\n includeRepairGuidance?: boolean;\n }\n): Promise<GraphHealthReport> {\n const enabledTypes: GraphType[] = [];\n if (options?.entityGraphEnabled !== false) enabledTypes.push(\"entity\");\n if (options?.timeGraphEnabled !== false) enabledTypes.push(\"time\");\n if (options?.causalGraphEnabled !== false) enabledTypes.push(\"causal\");\n\n const files: GraphHealthFileStats[] = [];\n const globalNodes = new Set<string>();\n\n for (const type of enabledTypes) {\n const filePath = graphFilePath(memoryDir, type);\n let exists = true;\n let totalLines = 0;\n let validEdges = 0;\n let corruptLines = 0;\n const nodes = new Set<string>();\n\n try {\n const raw = await readFile(filePath, \"utf8\");\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n totalLines += 1;\n try {\n const parsed = JSON.parse(trimmed) as unknown;\n if (!isValidGraphEdge(parsed, type)) {\n corruptLines += 1;\n continue;\n }\n validEdges += 1;\n nodes.add(parsed.from);\n nodes.add(parsed.to);\n globalNodes.add(parsed.from);\n globalNodes.add(parsed.to);\n } catch {\n corruptLines += 1;\n }\n }\n } catch {\n exists = false;\n }\n\n files.push({\n type,\n filePath,\n exists,\n totalLines,\n validEdges,\n corruptLines,\n uniqueNodes: nodes.size,\n });\n }\n\n const totals = files.reduce(\n (acc, item) => {\n acc.totalLines += item.totalLines;\n acc.validEdges += item.validEdges;\n acc.corruptLines += item.corruptLines;\n return acc;\n },\n {\n totalLines: 0,\n validEdges: 0,\n corruptLines: 0,\n uniqueNodes: globalNodes.size,\n }\n );\n totals.uniqueNodes = globalNodes.size;\n\n const report: GraphHealthReport = {\n generatedAt: new Date().toISOString(),\n enabledTypes,\n totals,\n files,\n };\n\n if (options?.includeRepairGuidance === true) {\n const guidance: string[] = [];\n if (totals.corruptLines > 0) {\n guidance.push(\n \"Corrupt graph lines detected: back up memory/state/graphs, then rebuild graphs from clean memory replay/extraction runs.\"\n );\n }\n if (totals.validEdges === 0) {\n guidance.push(\n \"No valid edges detected yet: run normal extraction traffic (or replay ingestion) to seed graph files.\"\n );\n }\n if (guidance.length > 0) report.repairGuidance = guidance;\n }\n\n return report;\n}\n\n/**\n * Detect causal signal phrases in text. Returns the first matched phrase, or null.\n */\nexport function detectCausalPhrase(text: string): string | null {\n const lower = text.toLowerCase();\n for (const phrase of CAUSAL_PHRASES) {\n if (lower.includes(phrase)) return phrase;\n }\n return null;\n}\n\n/**\n * GraphIndex — builds and updates the three memory graphs.\n *\n * Usage (orchestrator):\n * this.graphIndex = new GraphIndex(config.memoryDir, config);\n *\n * // After each memory write:\n * await this.graphIndex.onMemoryWritten(memoryPath, frontmatter, threadId, recentInThread);\n */\nexport class GraphIndex {\n private readonly memoryDir: string;\n private readonly cfg: GraphConfig;\n\n // Cache for readAllEdges() result. With 30k+ entity edges (6 MB JSONL) the\n // file read + JSON parse takes 2-4 s per call. This instance-level cache\n // eliminates that overhead on every spreadingActivation() call; it is\n // invalidated (set to null) in onMemoryWritten() so new edges appear promptly.\n private edgeCache: { allEdges: GraphEdge[]; loadedAt: number } | null = null;\n private static readonly EDGE_CACHE_TTL_MS = 300_000; // 5 minutes\n\n constructor(memoryDir: string, cfg: GraphConfig) {\n this.memoryDir = memoryDir;\n this.cfg = cfg;\n }\n\n /** Clear the edge cache so the next spreadingActivation() re-reads from disk.\n * Call after any code path that appends edges outside of onMemoryWritten(). */\n invalidateEdgeCache(): void {\n this.edgeCache = null;\n }\n\n private async loadEdgesCached(): Promise<GraphEdge[]> {\n if (this.edgeCache && Date.now() - this.edgeCache.loadedAt < GraphIndex.EDGE_CACHE_TTL_MS) {\n return this.edgeCache.allEdges;\n }\n const allEdges = await readAllEdges(this.memoryDir, {\n entityGraphEnabled: this.cfg.entityGraphEnabled,\n timeGraphEnabled: this.cfg.timeGraphEnabled,\n causalGraphEnabled: this.cfg.causalGraphEnabled,\n });\n this.edgeCache = { allEdges, loadedAt: Date.now() };\n return allEdges;\n }\n\n /**\n * Called after a memory is written to disk.\n *\n * @param memoryPath - relative path from memoryDir (e.g. \"facts/2026-02-22/abc.md\")\n * @param entityRef - entityRef frontmatter field (if any)\n * @param content - full memory text (for causal detection)\n * @param created - ISO timestamp of this memory\n * @param threadId - current thread ID (for time graph)\n * @param recentInThread - paths of the N most-recent memories in this thread (for time graph)\n * @param entitySiblings - paths of other memories that share the same entityRef (for entity graph)\n */\n async onMemoryWritten(opts: {\n memoryPath: string;\n entityRef?: string;\n content: string;\n created: string;\n threadId?: string;\n recentInThread?: string[];\n entitySiblings?: string[];\n causalPredecessor?: string;\n }): Promise<void> {\n if (!this.cfg.multiGraphMemoryEnabled) return;\n const ts = new Date().toISOString();\n\n try {\n // Entity graph\n if (this.cfg.entityGraphEnabled && opts.entityRef && opts.entitySiblings?.length) {\n const siblings = opts.entitySiblings.slice(0, this.cfg.maxEntityGraphEdgesPerMemory);\n for (const sibling of siblings) {\n await appendEdge(this.memoryDir, {\n from: opts.memoryPath,\n to: sibling,\n type: \"entity\",\n weight: 1.0,\n label: opts.entityRef,\n ts,\n });\n }\n }\n\n // Time graph — link to most recent memory in same thread\n if (this.cfg.timeGraphEnabled && opts.threadId && opts.recentInThread?.length) {\n const predecessor = opts.recentInThread[opts.recentInThread.length - 1];\n if (predecessor && predecessor !== opts.memoryPath) {\n await appendEdge(this.memoryDir, {\n from: predecessor,\n to: opts.memoryPath,\n type: \"time\",\n weight: 1.0,\n label: opts.threadId,\n ts,\n });\n }\n }\n\n // Causal graph\n if (this.cfg.causalGraphEnabled && opts.causalPredecessor) {\n const phrase = detectCausalPhrase(opts.content);\n if (phrase) {\n await appendEdge(this.memoryDir, {\n from: opts.causalPredecessor,\n to: opts.memoryPath,\n type: \"causal\",\n weight: 1.0,\n label: phrase,\n ts,\n });\n }\n }\n } catch (err) {\n // Fail-open: graph write errors must never surface to caller\n const { log } = await import(\"./logger.js\");\n log.warn(`[graph] onMemoryWritten error: ${err}`);\n } finally {\n // Invalidate edge cache so spreadingActivation() picks up new edges.\n // In `finally` so the cache is cleared even on partial write failure.\n this.edgeCache = null;\n }\n }\n\n /**\n * Spreading activation BFS (SYNAPSE-inspired).\n *\n * Starting from `seeds`, traverse the combined graph for up to `maxSteps` hops.\n * Each candidate gets an activation score = edge.weight × edgeConfidence × decay^hop.\n *\n * Issue #681 PR 3/3 — confidence-aware traversal:\n * - Each edge's `weight` is multiplied by its `confidence` (legacy edges\n * missing `confidence` are treated as 1.0, preserving prior behavior).\n * - Edges with `confidence < graphTraversalConfidenceFloor` are pruned and\n * contribute neither activation nor downstream neighbors.\n * - When `graphTraversalPageRankIterations > 0`, an additional PageRank-\n * style refinement pass redistributes activation along confidence-weighted\n * edges, sharpening the ranking among multi-hop candidates.\n * - Per-result provenance includes the highest-confidence edge that landed\n * on each candidate, so the X-ray surface can attribute pruning and\n * ranking decisions back to specific edges.\n *\n * @param seeds - initial memory paths to expand from (e.g. QMD top results)\n * @param maxSteps - max BFS hops (from config: maxGraphTraversalSteps)\n * @returns Array of {path, score, edgeConfidence, ...} sorted descending, not including seed paths\n */\n async spreadingActivation(\n seeds: string[],\n maxSteps?: number,\n opts?: {\n /**\n * Issue #681 — when `true`, bypasses the configured\n * `graphTraversalConfidenceFloor` and includes low-confidence\n * edges in traversal. Equivalent to forcing the floor to `0`.\n * Default `false` (floor from config is applied).\n */\n includeLowConfidence?: boolean;\n /** Absolute deadline in ms-since-epoch for post-retrieval assembly. */\n deadlineAtMs?: number;\n }\n ): Promise<\n Array<{\n path: string;\n score: number;\n seed: string;\n hopDepth: number;\n decayedWeight: number;\n graphType: \"entity\" | \"time\" | \"causal\";\n /**\n * Confidence of the edge that produced this candidate's recorded\n * provenance (the strongest edge along the chosen entry path).\n * In `[0, 1]`. Legacy edges without `confidence` surface as 1.0.\n */\n edgeConfidence: number;\n }>\n > {\n if (!this.cfg.multiGraphMemoryEnabled) return [];\n const steps = maxSteps ?? this.cfg.maxGraphTraversalSteps;\n const decay = this.cfg.graphActivationDecay;\n // When `includeLowConfidence` is set, use floor=0 so all edges\n // participate in traversal regardless of their decay state.\n // Otherwise clamp the configured floor into [0, 1] so misconfiguration\n // cannot (a) admit edges with negative confidence or (b) reject every\n // edge.\n const floor =\n opts?.includeLowConfidence === true ? 0 : clampConfidenceFloor(this.cfg.graphTraversalConfidenceFloor);\n const iterations = clampPageRankIterations(this.cfg.graphTraversalPageRankIterations);\n const deadlineAtMs = opts?.deadlineAtMs;\n const deadlineExpired = (): boolean =>\n typeof deadlineAtMs === \"number\" && Date.now() >= deadlineAtMs;\n\n try {\n if (deadlineExpired()) return [];\n const allEdges = await this.loadEdgesCached();\n if (deadlineExpired()) return [];\n\n // Build adjacency index: from → edges, to → edges (bidirectional for entity/time, directional for causal).\n // Edges below the confidence floor are pruned at index time so neither\n // direct activation nor downstream BFS expansion can re-introduce them.\n const adj = new Map<string, GraphEdge[]>();\n for (let i = 0; i < allEdges.length; i += 1) {\n if ((i & 1023) === 0 && deadlineExpired()) return [];\n const edge = allEdges[i];\n const conf = readEdgeConfidence(edge);\n if (conf < floor) continue;\n if (!adj.has(edge.from)) adj.set(edge.from, []);\n adj.get(edge.from)!.push(edge);\n // Entity and time edges are bidirectional\n if (edge.type !== \"causal\") {\n if (!adj.has(edge.to)) adj.set(edge.to, []);\n adj.get(edge.to)!.push({ ...edge, from: edge.to, to: edge.from });\n }\n }\n\n const seedSet = new Set(seeds);\n const scores = new Map<string, number>(); // candidate path → accumulated activation score\n const provenance = new Map<\n string,\n {\n seed: string;\n hopDepth: number;\n decayedWeight: number;\n graphType: \"entity\" | \"time\" | \"causal\";\n edgeConfidence: number;\n }\n >();\n let frontier = new Map<string, { node: string; seed: string; activation: number }>();\n const reachedBySeed = new Map<string, Set<string>>();\n for (const seed of seeds) {\n frontier.set(`${seed}\\0${seed}`, { node: seed, seed, activation: 1 });\n reachedBySeed.set(seed, new Set([seed]));\n }\n const finalizeScores = () =>\n Array.from(scores.entries())\n .map(([p, score]) => ({\n path: p,\n score,\n seed: provenance.get(p)?.seed ?? \"\",\n hopDepth: provenance.get(p)?.hopDepth ?? 0,\n decayedWeight: provenance.get(p)?.decayedWeight ?? 0,\n graphType: provenance.get(p)?.graphType ?? \"entity\",\n edgeConfidence: provenance.get(p)?.edgeConfidence ?? 1,\n }))\n .sort((a, b) => b.score - a.score);\n\n for (let hop = 0; hop < steps && frontier.size > 0; hop++) {\n if (deadlineExpired()) return finalizeScores();\n const nextFrontier = new Map<string, { node: string; seed: string; activation: number }>();\n\n for (const { node, seed: sourceSeed, activation } of frontier.values()) {\n const edges = adj.get(node) ?? [];\n for (let i = 0; i < edges.length; i += 1) {\n if ((i & 1023) === 0 && deadlineExpired()) return finalizeScores();\n const edge = edges[i];\n const neighbor = edge.to === node ? edge.from : edge.to;\n const conf = readEdgeConfidence(edge);\n // Defense in depth: the adjacency build already drops sub-floor\n // edges, but if a synthesized reverse edge ever bypassed that\n // path, this guard keeps spreading activation honest.\n if (conf < floor) continue;\n const score = activation * edge.weight * conf * decay;\n const reachedForSeed = reachedBySeed.get(sourceSeed);\n if (reachedForSeed?.has(neighbor)) {\n continue;\n }\n\n if (!seedSet.has(neighbor)) {\n const existing = scores.get(neighbor) ?? 0;\n scores.set(neighbor, existing + score);\n\n const prev = provenance.get(neighbor);\n if (!prev || hop + 1 < prev.hopDepth || (hop + 1 === prev.hopDepth && score > prev.decayedWeight)) {\n provenance.set(neighbor, {\n seed: sourceSeed,\n hopDepth: hop + 1,\n decayedWeight: score,\n graphType: edge.type,\n edgeConfidence: conf,\n });\n }\n\n if (hop + 1 < steps) {\n const frontierKey = `${sourceSeed}\\0${neighbor}`;\n const existingFrontier = nextFrontier.get(frontierKey);\n if (existingFrontier) {\n existingFrontier.activation += score;\n } else {\n nextFrontier.set(frontierKey, {\n node: neighbor,\n seed: sourceSeed,\n activation: score,\n });\n }\n }\n }\n }\n }\n\n for (const { node, seed } of nextFrontier.values()) {\n reachedBySeed.get(seed)?.add(node);\n }\n frontier = nextFrontier;\n }\n\n // Issue #681 PR 3/3 — optional PageRank-style refinement.\n // Redistributes a node's accumulated activation along its outgoing\n // edges, weighted by edge confidence. Damping is fixed at the\n // canonical 0.85 so the ranking stays comparable across queries;\n // the `iterations` knob bounds compute, not behavior shape.\n if (!deadlineExpired() && iterations > 0 && scores.size > 1) {\n applyPageRankRefinement(scores, adj, {\n iterations,\n floor,\n damping: 0.85,\n deadlineAtMs,\n });\n }\n\n // Apply lateral inhibition if enabled (Synapse-inspired competitive suppression)\n if (deadlineExpired()) return finalizeScores();\n if (this.cfg.graphLateralInhibitionEnabled && scores.size > 1) {\n const inhibited = applyLateralInhibition(scores, {\n beta: this.cfg.graphLateralInhibitionBeta,\n topM: this.cfg.graphLateralInhibitionTopM,\n });\n for (const [k, v] of inhibited) {\n scores.set(k, v);\n }\n }\n\n return finalizeScores();\n } catch (err) {\n const { log } = await import(\"./logger.js\");\n log.warn(`[graph] spreadingActivation error: ${err}`);\n return [];\n }\n }\n}\n\n/**\n * Clamp `graphTraversalConfidenceFloor` into the legal range `[0, 1]`.\n * Non-finite or non-numeric values fall back to the documented default\n * so misconfiguration cannot silently disable the floor or reject every edge.\n *\n * Exported for tests; call sites in `spreadingActivation` use it to make\n * the contract explicit at every boundary.\n */\nexport function clampConfidenceFloor(raw: unknown): number {\n if (typeof raw !== \"number\" || !Number.isFinite(raw)) {\n return DEFAULT_GRAPH_TRAVERSAL_CONFIDENCE_FLOOR;\n }\n if (raw < 0) return 0;\n if (raw > 1) return 1;\n return raw;\n}\n\n/**\n * Clamp `graphTraversalPageRankIterations` into a non-negative integer.\n * Negative or non-finite values fall back to 0 (disable refinement) so\n * misconfiguration cannot stall recall in an unbounded loop.\n */\nexport function clampPageRankIterations(raw: unknown): number {\n if (typeof raw !== \"number\" || !Number.isFinite(raw)) return 0;\n if (raw <= 0) return 0;\n return Math.floor(raw);\n}\n\n/**\n * PageRank-style refinement on top of the BFS activation map.\n *\n * Each iteration redistributes a fraction of every node's score along\n * its outgoing edges, scaled by edge confidence. Confidence below\n * `floor` is filtered out before redistribution, mirroring the BFS\n * pruning rule. Mutates `scores` in place.\n *\n * Exported for tests; in production, call sites pass the same adjacency\n * map already used by BFS so behavior stays consistent.\n */\nexport function applyPageRankRefinement(\n scores: Map<string, number>,\n adj: Map<string, GraphEdge[]>,\n opts: { iterations: number; floor: number; damping: number; deadlineAtMs?: number }\n): void {\n const { iterations, floor, damping, deadlineAtMs } = opts;\n if (iterations <= 0 || scores.size === 0) return;\n const safeDamping = Math.min(1, Math.max(0, damping));\n const deadlineExpired = (): boolean =>\n typeof deadlineAtMs === \"number\" && Date.now() >= deadlineAtMs;\n\n // Pre-compute confidence-weighted out-edge totals for normalization.\n // Done once per refinement, not per iteration, since adjacency is\n // immutable inside the loop.\n //\n // Codex P1 (#735): the denominator MUST be computed over the same\n // eligible-neighbor set the iteration redistributes into — i.e.\n // edges whose neighbor is in `scores`. Counting edges-to-seeds (or\n // edges-to-unseen-nodes) in the denominator while dropping their\n // flow during iteration leaks `safeDamping × score` every pass and\n // collapses leaf candidates' scores instead of just re-ranking them.\n const eligible = (edge: GraphEdge, fromNode: string): boolean => {\n if (readEdgeConfidence(edge) < floor) return false;\n const neighbor = edge.to === fromNode ? edge.from : edge.to;\n return scores.has(neighbor);\n };\n const outboundTotal = new Map<string, number>();\n for (const [node, edges] of adj.entries()) {\n if (deadlineExpired()) return;\n if (!scores.has(node)) continue; // only candidate nodes redistribute\n let sum = 0;\n for (const edge of edges) {\n if (!eligible(edge, node)) continue;\n sum += readEdgeConfidence(edge) * edge.weight;\n }\n if (sum > 0) outboundTotal.set(node, sum);\n }\n\n for (let i = 0; i < iterations; i += 1) {\n if (deadlineExpired()) return;\n const next = new Map<string, number>();\n // Teleport / damping floor: every node retains `(1 - damping) * score`\n // of its current activation so dangling nodes do not bleed to zero.\n for (const [node, score] of scores) {\n next.set(node, (1 - safeDamping) * score);\n }\n for (const [node, score] of scores) {\n const outEdges = adj.get(node);\n const total = outboundTotal.get(node);\n // Dangling-node fallback: when a candidate has zero eligible\n // outflow (no in-scores neighbors above the floor), the\n // `safeDamping × score` portion would otherwise evaporate. Keep\n // it on `node` so total mass is conserved and the score reflects\n // the candidate's standing rather than its in-degree topology.\n if (!outEdges || outEdges.length === 0 || !total || total <= 0) {\n next.set(node, (next.get(node) ?? 0) + safeDamping * score);\n continue;\n }\n for (let edgeIndex = 0; edgeIndex < outEdges.length; edgeIndex += 1) {\n if ((edgeIndex & 1023) === 0 && deadlineExpired()) return;\n const edge = outEdges[edgeIndex];\n if (!eligible(edge, node)) continue;\n const conf = readEdgeConfidence(edge);\n const neighbor = edge.to === node ? edge.from : edge.to;\n const flow = safeDamping * score * ((conf * edge.weight) / total);\n next.set(neighbor, (next.get(neighbor) ?? 0) + flow);\n }\n }\n for (const [node, score] of next) {\n scores.set(node, score);\n }\n }\n}\n\n/**\n * Lateral inhibition (Synapse-inspired).\n *\n * For each node, the top-M higher-activation competitors exert inhibition\n * proportional to their activation difference. Output is clamped to [0, ∞).\n *\n * No sigmoid is applied here — downstream `normalizeGraphActivationScore`\n * already applies x/(1+x) soft squash, so adding a sigmoid would double-\n * normalize and cap graph influence at ~50%.\n *\n * Formula: u_hat_i = max(0, u_i - beta * sum_{k in top-M where u_k > u_i}(u_k - u_i))\n *\n * When beta=0 or topM=0, returns original scores unchanged (no-op).\n */\nexport function applyLateralInhibition(\n scores: Map<string, number>,\n opts: { beta: number; topM: number }\n): Map<string, number> {\n const { beta, topM } = opts;\n if (beta === 0 || topM === 0) return new Map(scores);\n\n const sorted = Array.from(scores.entries()).sort((a, b) => b[1] - a[1]);\n const topCompetitors = sorted.slice(0, topM);\n\n const result = new Map<string, number>();\n for (const [node, u] of scores) {\n let inhibition = 0;\n for (const [, uK] of topCompetitors) {\n if (uK > u) {\n inhibition += uK - u;\n }\n }\n result.set(node, Math.max(0, u - beta * inhibition));\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
|