@remnic/core 9.3.743 → 9.3.745

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/dist/access-boundary.d.ts +18 -16
  2. package/dist/access-boundary.js +14 -15
  3. package/dist/access-cli.js +92 -33
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +18 -16
  6. package/dist/access-http.js +20 -21
  7. package/dist/access-mcp.d.ts +18 -16
  8. package/dist/access-mcp.js +18 -19
  9. package/dist/access-operations-batch.js +16 -17
  10. package/dist/access-operations.d.ts +18 -16
  11. package/dist/access-operations.js +17 -18
  12. package/dist/access-schema.js +3 -3
  13. package/dist/{access-service-BeW5GOg1.d.ts → access-service-CdY88L86.d.ts} +3 -30
  14. package/dist/access-service.d.ts +8 -6
  15. package/dist/access-service.js +13 -14
  16. package/dist/access-surface-catalog.d.ts +18 -16
  17. package/dist/action-confidence.d.ts +1 -1
  18. package/dist/active-memory-bridge.d.ts +1 -1
  19. package/dist/active-recall.d.ts +1 -1
  20. package/dist/{auto-sync-XHSKKQAH.js → auto-sync-IEBKPGAM.js} +2 -2
  21. package/dist/behavior-learner.d.ts +1 -1
  22. package/dist/behavior-signals.d.ts +1 -1
  23. package/dist/bootstrap.d.ts +7 -4
  24. package/dist/briefing.d.ts +1 -1
  25. package/dist/briefing.js +3 -3
  26. package/dist/buffer-surprise-report.d.ts +1 -1
  27. package/dist/buffer.d.ts +1 -1
  28. package/dist/calibration.d.ts +1 -1
  29. package/dist/capabilities.d.ts +1 -1
  30. package/dist/{catalog-CX0CjOaf.d.ts → catalog-D_ZDxu8Y.d.ts} +1 -1
  31. package/dist/causal-behavior.d.ts +1 -1
  32. package/dist/causal-consolidation.d.ts +1 -1
  33. package/dist/causal-consolidation.js +4 -4
  34. package/dist/causal-trajectory-graph.d.ts +1 -1
  35. package/dist/{chunk-WPLRJ4XM.js → chunk-24YDB5QP.js} +2098 -230
  36. package/dist/chunk-24YDB5QP.js.map +1 -0
  37. package/dist/{chunk-6XG3NQRC.js → chunk-3IJGF6K2.js} +2 -2
  38. package/dist/{chunk-U5WVRMCA.js → chunk-5IN26V6W.js} +2 -2
  39. package/dist/{chunk-XEADGIAA.js → chunk-DIHCFJVV.js} +7 -7
  40. package/dist/{chunk-C7LDPZJJ.js → chunk-EHYB325U.js} +227 -227
  41. package/dist/chunk-EHYB325U.js.map +1 -0
  42. package/dist/{chunk-E7RGJQWP.js → chunk-HIHCB2S6.js} +2 -2
  43. package/dist/{chunk-JFCS6PQU.js → chunk-JHQUIH3W.js} +5 -5
  44. package/dist/{chunk-MXDG6SRH.js → chunk-JRDPT6DA.js} +2 -2
  45. package/dist/{chunk-BYLC4DKI.js → chunk-KMNYXLPL.js} +2 -2
  46. package/dist/{chunk-TQPSOTC7.js → chunk-L4N7SVNN.js} +8 -10
  47. package/dist/{chunk-TQPSOTC7.js.map → chunk-L4N7SVNN.js.map} +1 -1
  48. package/dist/{chunk-DYK27X7V.js → chunk-LMFS64GY.js} +3 -3
  49. package/dist/{chunk-FWXLKH2H.js → chunk-NVYF5OB6.js} +3 -3
  50. package/dist/{chunk-G5FP7VSQ.js → chunk-P4FW3ZU5.js} +58 -60
  51. package/dist/{chunk-G5FP7VSQ.js.map → chunk-P4FW3ZU5.js.map} +1 -1
  52. package/dist/{chunk-56F6EAQ6.js → chunk-Q3X7GUAQ.js} +2 -2
  53. package/dist/{chunk-7N7S2RKF.js → chunk-R2OAN6IP.js} +2 -2
  54. package/dist/{chunk-CHELXDTO.js → chunk-RB6A7OUN.js} +2 -2
  55. package/dist/{chunk-3EA5LIFJ.js → chunk-SIV3JZPP.js} +2 -2
  56. package/dist/{chunk-IP5SANO2.js → chunk-UIKZZ242.js} +2 -2
  57. package/dist/{chunk-UGFPGWJB.js → chunk-URQLO7BX.js} +2 -2
  58. package/dist/{chunk-5UNSGRCR.js → chunk-WK3NK6SP.js} +162 -16
  59. package/dist/chunk-WK3NK6SP.js.map +1 -0
  60. package/dist/{chunk-VE54E6RP.js → chunk-WWVXXSLF.js} +4 -6
  61. package/dist/{chunk-VE54E6RP.js.map → chunk-WWVXXSLF.js.map} +1 -1
  62. package/dist/{chunk-WO22UEM3.js → chunk-XTEWVF3C.js} +2 -2
  63. package/dist/{chunk-VO4VARZL.js → chunk-YQX63P3W.js} +18033 -12571
  64. package/dist/chunk-YQX63P3W.js.map +1 -0
  65. package/dist/{chunk-FCQ7ZYCQ.js → chunk-ZGAJB74V.js} +2 -2
  66. package/dist/{chunk-NBP3YHHS.js → chunk-ZGZNLRKF.js} +2 -2
  67. package/dist/{chunk-C7C5WIIF.js → chunk-ZPZ4VVLT.js} +2 -2
  68. package/dist/{cli-BYad9YYY.d.ts → cli-BNJtn0R0.d.ts} +3 -3
  69. package/dist/cli.d.ts +9 -7
  70. package/dist/cli.js +44 -46
  71. package/dist/compounding/engine.d.ts +1 -1
  72. package/dist/compounding/engine.js +3 -3
  73. package/dist/compounding/preference-consolidator.d.ts +1 -1
  74. package/dist/compression-optimizer.d.ts +1 -1
  75. package/dist/config.d.ts +1 -1
  76. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  77. package/dist/connectors/codex-materialize-runner.js +3 -3
  78. package/dist/connectors/codex-materialize.d.ts +1 -1
  79. package/dist/connectors/index.d.ts +1 -1
  80. package/dist/connectors/index.js +3 -3
  81. package/dist/consolidation-provenance-check.d.ts +1 -1
  82. package/dist/consolidation-undo.d.ts +1 -1
  83. package/dist/contradiction/index.d.ts +1 -1
  84. package/dist/contradiction/index.js +4 -4
  85. package/dist/conversation-index/backend.d.ts +1 -1
  86. package/dist/conversation-index/chunker.d.ts +1 -1
  87. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  88. package/dist/conversation-index/indexer.d.ts +1 -1
  89. package/dist/conversation-index/search.d.ts +1 -1
  90. package/dist/day-summary.d.ts +1 -1
  91. package/dist/delinearize.d.ts +1 -1
  92. package/dist/direct-answer-wiring.d.ts +1 -1
  93. package/dist/direct-answer.d.ts +1 -1
  94. package/dist/embedding-fallback.d.ts +1 -1
  95. package/dist/enrichment/index.d.ts +1 -1
  96. package/dist/entity-retrieval.d.ts +1 -1
  97. package/dist/entity-retrieval.js +3 -3
  98. package/dist/entity-schema.d.ts +1 -1
  99. package/dist/explicit-capture.d.ts +7 -4
  100. package/dist/extraction-faithfulness.d.ts +1 -1
  101. package/dist/extraction-judge-telemetry.d.ts +1 -1
  102. package/dist/extraction-judge-training.d.ts +1 -1
  103. package/dist/extraction-judge.d.ts +1 -1
  104. package/dist/extraction.d.ts +1 -1
  105. package/dist/fallback-llm.d.ts +1 -1
  106. package/dist/graph-dashboard-diff.d.ts +1 -1
  107. package/dist/graph-dashboard-key.d.ts +1 -1
  108. package/dist/graph-dashboard-parser.d.ts +1 -1
  109. package/dist/graph-edge-reinforcement.d.ts +1 -1
  110. package/dist/graph-snapshot.d.ts +1 -1
  111. package/dist/graph.d.ts +1 -1
  112. package/dist/identity-continuity.d.ts +1 -1
  113. package/dist/importance.d.ts +1 -1
  114. package/dist/index.d.ts +13 -11
  115. package/dist/index.js +271 -5362
  116. package/dist/index.js.map +1 -1
  117. package/dist/intent.d.ts +1 -1
  118. package/dist/lcm/engine.d.ts +1 -1
  119. package/dist/lcm/index.d.ts +1 -1
  120. package/dist/lcm/tools.d.ts +1 -1
  121. package/dist/lifecycle.d.ts +1 -1
  122. package/dist/live-connectors-runner.d.ts +1 -1
  123. package/dist/local-llm.d.ts +1 -1
  124. package/dist/local-model-endpoint.d.ts +1 -1
  125. package/dist/maintenance/memory-governance.d.ts +1 -1
  126. package/dist/maintenance/memory-governance.js +3 -3
  127. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  128. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  129. package/dist/mcp-memory-inspector-app.d.ts +17 -15
  130. package/dist/memory-action-policy.d.ts +1 -1
  131. package/dist/memory-cache.d.ts +1 -1
  132. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  133. package/dist/memory-projection-store.d.ts +1 -1
  134. package/dist/memory-provenance.d.ts +1 -1
  135. package/dist/memory-worth-outcomes.d.ts +1 -1
  136. package/dist/models-json.d.ts +1 -1
  137. package/dist/namespaces/migrate.d.ts +2 -2
  138. package/dist/namespaces/migrate.js +4 -4
  139. package/dist/namespaces/principal.d.ts +1 -1
  140. package/dist/namespaces/search.d.ts +1 -1
  141. package/dist/namespaces/storage.d.ts +2 -2
  142. package/dist/namespaces/storage.js +3 -3
  143. package/dist/native-knowledge.d.ts +1 -1
  144. package/dist/operator-toolkit.d.ts +1 -1
  145. package/dist/operator-toolkit.js +10 -11
  146. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  147. package/dist/orchestration/maintenance.d.ts +2 -2
  148. package/dist/orchestration/maintenance.js +5 -5
  149. package/dist/{orchestrator-CupigmWu.d.ts → orchestrator-BBGXLd20.d.ts} +270 -32
  150. package/dist/orchestrator.d.ts +7 -4
  151. package/dist/orchestrator.js +119 -16
  152. package/dist/patterns-cli.d.ts +1 -1
  153. package/dist/policy-runtime.d.ts +1 -1
  154. package/dist/provenance.d.ts +1 -1
  155. package/dist/qmd-recall-cache.d.ts +1 -1
  156. package/dist/qmd.d.ts +1 -1
  157. package/dist/recall-disclosure-escalation.d.ts +1 -1
  158. package/dist/recall-explain-renderer.d.ts +1 -1
  159. package/dist/recall-planner-llm.d.ts +1 -1
  160. package/dist/recall-state.d.ts +1 -1
  161. package/dist/recall-tag-filter.d.ts +1 -1
  162. package/dist/recall-xray-cli.d.ts +1 -1
  163. package/dist/recall-xray-renderer.d.ts +1 -1
  164. package/dist/recall-xray.d.ts +1 -1
  165. package/dist/resolve-auth-token.d.ts +1 -1
  166. package/dist/retrieval-agents.d.ts +1 -1
  167. package/dist/retrieval-tiers.d.ts +1 -1
  168. package/dist/routing/engine.d.ts +1 -1
  169. package/dist/routing/store.d.ts +1 -1
  170. package/dist/search/embed-helper.d.ts +1 -1
  171. package/dist/search/factory.d.ts +1 -1
  172. package/dist/search/index.d.ts +1 -1
  173. package/dist/search/lancedb-backend.d.ts +1 -1
  174. package/dist/search/meilisearch-backend.d.ts +1 -1
  175. package/dist/search/noop-backend.d.ts +1 -1
  176. package/dist/search/orama-backend.d.ts +1 -1
  177. package/dist/search/port.d.ts +1 -1
  178. package/dist/search/remote-backend.d.ts +1 -1
  179. package/dist/{semantic-consolidation-ByzGwC9c.d.ts → semantic-consolidation-D3aSiu9t.d.ts} +1 -1
  180. package/dist/semantic-consolidation.d.ts +2 -2
  181. package/dist/semantic-consolidation.js +4 -4
  182. package/dist/semantic-rule-promotion.js +3 -3
  183. package/dist/semantic-rule-verifier.d.ts +1 -1
  184. package/dist/semantic-rule-verifier.js +3 -3
  185. package/dist/session-observer-bands.d.ts +1 -1
  186. package/dist/session-observer-state.d.ts +1 -1
  187. package/dist/shared-context/manager.d.ts +1 -1
  188. package/dist/signal.d.ts +1 -1
  189. package/dist/storage.d.ts +1 -1
  190. package/dist/storage.js +2 -2
  191. package/dist/summarizer.d.ts +1 -1
  192. package/dist/summary-snapshot.d.ts +1 -1
  193. package/dist/temporal-supersession.d.ts +1 -1
  194. package/dist/temporal-validity.d.ts +1 -1
  195. package/dist/threading.d.ts +1 -1
  196. package/dist/tier-migration.d.ts +1 -1
  197. package/dist/tier-routing.d.ts +1 -1
  198. package/dist/topics.d.ts +1 -1
  199. package/dist/transcript.d.ts +1 -1
  200. package/dist/transfer/backup.js +2 -2
  201. package/dist/transfer/capsule-export.js +2 -2
  202. package/dist/transfer/capsule-import.js +1 -1
  203. package/dist/trust-score-stage.d.ts +1 -1
  204. package/dist/trust-score.d.ts +1 -1
  205. package/dist/{types-5Jik3r4G.d.ts → types-CZm6rx9-.d.ts} +1 -1
  206. package/dist/types.d.ts +1 -1
  207. package/dist/utility-runtime.d.ts +1 -1
  208. package/dist/verified-recall.js +3 -3
  209. package/package.json +2 -2
  210. package/src/index.ts +2 -0
  211. package/src/orchestration/extraction-persist.ts +2785 -0
  212. package/src/orchestration/extraction-run.ts +874 -0
  213. package/src/orchestration/graph-recall-coordinator.ts +471 -0
  214. package/src/orchestrator.ts +200 -3621
  215. package/dist/chunk-5UNSGRCR.js.map +0 -1
  216. package/dist/chunk-A2KKLCOS.js +0 -1889
  217. package/dist/chunk-A2KKLCOS.js.map +0 -1
  218. package/dist/chunk-AVBT5WEW.js +0 -153
  219. package/dist/chunk-AVBT5WEW.js.map +0 -1
  220. package/dist/chunk-C7LDPZJJ.js.map +0 -1
  221. package/dist/chunk-VO4VARZL.js.map +0 -1
  222. package/dist/chunk-WPLRJ4XM.js.map +0 -1
  223. /package/dist/{auto-sync-XHSKKQAH.js.map → auto-sync-IEBKPGAM.js.map} +0 -0
  224. /package/dist/{chunk-6XG3NQRC.js.map → chunk-3IJGF6K2.js.map} +0 -0
  225. /package/dist/{chunk-U5WVRMCA.js.map → chunk-5IN26V6W.js.map} +0 -0
  226. /package/dist/{chunk-XEADGIAA.js.map → chunk-DIHCFJVV.js.map} +0 -0
  227. /package/dist/{chunk-E7RGJQWP.js.map → chunk-HIHCB2S6.js.map} +0 -0
  228. /package/dist/{chunk-JFCS6PQU.js.map → chunk-JHQUIH3W.js.map} +0 -0
  229. /package/dist/{chunk-MXDG6SRH.js.map → chunk-JRDPT6DA.js.map} +0 -0
  230. /package/dist/{chunk-BYLC4DKI.js.map → chunk-KMNYXLPL.js.map} +0 -0
  231. /package/dist/{chunk-DYK27X7V.js.map → chunk-LMFS64GY.js.map} +0 -0
  232. /package/dist/{chunk-FWXLKH2H.js.map → chunk-NVYF5OB6.js.map} +0 -0
  233. /package/dist/{chunk-56F6EAQ6.js.map → chunk-Q3X7GUAQ.js.map} +0 -0
  234. /package/dist/{chunk-7N7S2RKF.js.map → chunk-R2OAN6IP.js.map} +0 -0
  235. /package/dist/{chunk-CHELXDTO.js.map → chunk-RB6A7OUN.js.map} +0 -0
  236. /package/dist/{chunk-3EA5LIFJ.js.map → chunk-SIV3JZPP.js.map} +0 -0
  237. /package/dist/{chunk-IP5SANO2.js.map → chunk-UIKZZ242.js.map} +0 -0
  238. /package/dist/{chunk-UGFPGWJB.js.map → chunk-URQLO7BX.js.map} +0 -0
  239. /package/dist/{chunk-WO22UEM3.js.map → chunk-XTEWVF3C.js.map} +0 -0
  240. /package/dist/{chunk-FCQ7ZYCQ.js.map → chunk-ZGAJB74V.js.map} +0 -0
  241. /package/dist/{chunk-NBP3YHHS.js.map → chunk-ZGZNLRKF.js.map} +0 -0
  242. /package/dist/{chunk-C7C5WIIF.js.map → chunk-ZPZ4VVLT.js.map} +0 -0
@@ -0,0 +1,471 @@
1
+ /**
2
+ * Graph-recall coordinator — extracted from the orchestrator (issue #1526).
3
+ *
4
+ * Owns the graph-expansion subsystem in recall: spreading-activation
5
+ * traversal from seed memories, seed-path resolution relative to
6
+ * per-namespace storage, and the last-graph-recall snapshot persistence.
7
+ * Behavior-preserving move from orchestrator.ts — no logic changes; the
8
+ * orchestrator constructs one instance and keeps thin delegating methods so
9
+ * existing call sites (recallInternal, cold-fallback pipeline) continue to
10
+ * work.
11
+ *
12
+ * Config, storage, graph index, namespace resolution, and QMD result
13
+ * resolution are accessed through getter callbacks (not captured at
14
+ * construction) so that post-construction reassignment of the orchestrator's
15
+ * live fields is honored. This mirrors the RecallRerankCoordinator /
16
+ * ContradictionLinkingCoordinator accessor pattern.
17
+ */
18
+
19
+ import type {
20
+ MemoryFile,
21
+ MemoryIntent,
22
+ PluginConfig,
23
+ QmdSearchResult,
24
+ RecallPlanMode,
25
+ } from "../types.js";
26
+ import type { StorageManager } from "../index.js";
27
+ import type { GraphIndex } from "../graph.js";
28
+ import type { GraphRecallExpandedEntry } from "../recall-state.js";
29
+ import { clampGraphRecallExpandedEntries } from "../recall-state.js";
30
+ import { qmdCollectionPathParts } from "./qmd-result-resolver.js";
31
+ import { log } from "../logger.js";
32
+ import { createHash } from "node:crypto";
33
+ import { mkdir, writeFile } from "node:fs/promises";
34
+ import path from "node:path";
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Types (moved from orchestrator.ts; re-exported by the orchestrator barrel)
38
+ // ---------------------------------------------------------------------------
39
+
40
+ export interface GraphRecallRankedResult {
41
+ path: string;
42
+ score: number;
43
+ docid?: string;
44
+ sourceLabels: string[];
45
+ }
46
+
47
+ export interface GraphRecallShadowComparison {
48
+ baselineCount: number;
49
+ graphCount: number;
50
+ overlapCount: number;
51
+ overlapRatio: number;
52
+ averageOverlapDelta: number;
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Utility functions (moved from orchestrator.ts; re-exported for callers)
57
+ // ---------------------------------------------------------------------------
58
+
59
+ export function mergeGraphExpandedResults(
60
+ primary: QmdSearchResult[],
61
+ expanded: QmdSearchResult[],
62
+ ): QmdSearchResult[] {
63
+ const mergedByPath = new Map<string, QmdSearchResult>();
64
+ for (const item of [...primary, ...expanded]) {
65
+ const prev = mergedByPath.get(item.path);
66
+ if (!prev) {
67
+ mergedByPath.set(item.path, item);
68
+ continue;
69
+ }
70
+ const better = item.score > prev.score ? item : prev;
71
+ const snippet = prev.snippet || item.snippet;
72
+ mergedByPath.set(item.path, { ...better, snippet });
73
+ }
74
+ return Array.from(mergedByPath.values());
75
+ }
76
+
77
+ export function graphPathRelativeToStorage(
78
+ storageDir: string,
79
+ candidatePath: string,
80
+ ): string | null {
81
+ const absolutePath = path.isAbsolute(candidatePath)
82
+ ? candidatePath
83
+ : path.resolve(storageDir, candidatePath);
84
+ const rel = path.relative(storageDir, absolutePath);
85
+ if (!rel || rel === ".") return null;
86
+ if (rel.startsWith("..")) return null;
87
+ return rel.split(path.sep).join("/");
88
+ }
89
+
90
+ function normalizeGraphActivationScore(score: number): number {
91
+ const bounded = Number.isFinite(score) && score > 0 ? score : 0;
92
+ return bounded / (1 + bounded);
93
+ }
94
+
95
+ export function blendGraphExpandedRecallScore(options: {
96
+ graphActivationScore: number;
97
+ seedRecallScore: number;
98
+ activationWeight: number;
99
+ blendMin: number;
100
+ blendMax: number;
101
+ }): number {
102
+ const graphNorm = normalizeGraphActivationScore(options.graphActivationScore);
103
+ const seedScore = Number.isFinite(options.seedRecallScore)
104
+ ? Math.min(1, Math.max(0, options.seedRecallScore))
105
+ : 0;
106
+ const weight = Math.min(1, Math.max(0, options.activationWeight));
107
+ const rawMin = Math.min(1, Math.max(0, options.blendMin));
108
+ const rawMax = Math.min(1, Math.max(0, options.blendMax));
109
+ const minBound = Math.min(rawMin, rawMax);
110
+ const maxBound = Math.max(rawMin, rawMax);
111
+ const blended = graphNorm * weight + seedScore * (1 - weight);
112
+ return Math.max(minBound, Math.min(maxBound, blended));
113
+ }
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // Coordinator
117
+ // ---------------------------------------------------------------------------
118
+
119
+ export class GraphRecallCoordinator {
120
+ private readonly getConfig: () => PluginConfig;
121
+ private readonly getStorage: () => StorageManager;
122
+ private readonly storageFor: (namespace: string) => Promise<StorageManager>;
123
+ private readonly graphIndexFor: (storage: StorageManager) => GraphIndex;
124
+ private readonly namespaceFromPath: (p: string) => string;
125
+ private readonly resolveColdQmdResultForRecall: (
126
+ result: QmdSearchResult,
127
+ fallbackStorage: StorageManager,
128
+ recallNamespaces: readonly string[],
129
+ ) => Promise<{ namespace: string; result: QmdSearchResult } | null>;
130
+ private readonly storageForAbsoluteQmdResultPath: (
131
+ resultPath: string,
132
+ fallbackStorage: StorageManager,
133
+ recallNamespaces: readonly string[],
134
+ ) => Promise<{ storage: StorageManager; dir: string; namespace: string } | null>;
135
+ private readonly readQmdResultMemory: (
136
+ resultPath: string,
137
+ fallbackStorage: StorageManager,
138
+ recallNamespaces: readonly string[],
139
+ ) => Promise<MemoryFile | null>;
140
+
141
+ constructor(options: {
142
+ getConfig: () => PluginConfig;
143
+ getStorage: () => StorageManager;
144
+ storageFor: (namespace: string) => Promise<StorageManager>;
145
+ graphIndexFor: (storage: StorageManager) => GraphIndex;
146
+ namespaceFromPath: (p: string) => string;
147
+ resolveColdQmdResultForRecall: (
148
+ result: QmdSearchResult,
149
+ fallbackStorage: StorageManager,
150
+ recallNamespaces: readonly string[],
151
+ ) => Promise<{ namespace: string; result: QmdSearchResult } | null>;
152
+ storageForAbsoluteQmdResultPath: (
153
+ resultPath: string,
154
+ fallbackStorage: StorageManager,
155
+ recallNamespaces: readonly string[],
156
+ ) => Promise<{ storage: StorageManager; dir: string; namespace: string } | null>;
157
+ readQmdResultMemory: (
158
+ resultPath: string,
159
+ fallbackStorage: StorageManager,
160
+ recallNamespaces: readonly string[],
161
+ ) => Promise<MemoryFile | null>;
162
+ }) {
163
+ this.getConfig = options.getConfig;
164
+ this.getStorage = options.getStorage;
165
+ this.storageFor = options.storageFor;
166
+ this.graphIndexFor = options.graphIndexFor;
167
+ this.namespaceFromPath = options.namespaceFromPath;
168
+ this.resolveColdQmdResultForRecall = options.resolveColdQmdResultForRecall;
169
+ this.storageForAbsoluteQmdResultPath = options.storageForAbsoluteQmdResultPath;
170
+ this.readQmdResultMemory = options.readQmdResultMemory;
171
+ }
172
+
173
+ async expandResultsViaGraph(options: {
174
+ memoryResults: QmdSearchResult[];
175
+ recallNamespaces: string[];
176
+ recallResultLimit: number;
177
+ deadlineAtMs?: number | null;
178
+ /** Issue #681 — when true, bypass graphTraversalConfidenceFloor. */
179
+ includeLowConfidence?: boolean;
180
+ }): Promise<{
181
+ merged: QmdSearchResult[];
182
+ seedPaths: string[];
183
+ expandedPaths: GraphRecallExpandedEntry[];
184
+ seedResults: QmdSearchResult[];
185
+ }> {
186
+ const config = this.getConfig();
187
+ const deadlineExpired = (): boolean =>
188
+ typeof options.deadlineAtMs === "number" &&
189
+ Date.now() >= options.deadlineAtMs;
190
+ const byNamespace = new Map<string, QmdSearchResult[]>();
191
+ const addResultForNamespace = (
192
+ namespace: string,
193
+ result: QmdSearchResult,
194
+ ): void => {
195
+ const existing = byNamespace.get(namespace);
196
+ if (existing) {
197
+ existing.push(result);
198
+ } else {
199
+ byNamespace.set(namespace, [result]);
200
+ }
201
+ };
202
+ const resolvedAmbiguousSeeds = new Map<
203
+ string,
204
+ { namespace: string; result: QmdSearchResult } | null
205
+ >();
206
+ const resolveAmbiguousSeedOwner = async (
207
+ result: QmdSearchResult,
208
+ parts: { collection: string; relativePath: string } | null,
209
+ ): Promise<{ namespace: string; result: QmdSearchResult } | null> => {
210
+ const cached = resolvedAmbiguousSeeds.get(result.path);
211
+ if (cached !== undefined) return cached;
212
+ if (deadlineExpired()) {
213
+ resolvedAmbiguousSeeds.set(result.path, null);
214
+ return null;
215
+ }
216
+
217
+ let resolvedPath = result.path;
218
+ let resolvedResult = result;
219
+ if (parts) {
220
+ const resolvedCold = await this.resolveColdQmdResultForRecall(
221
+ result,
222
+ this.getStorage(),
223
+ options.recallNamespaces,
224
+ );
225
+ if (!resolvedCold || deadlineExpired()) {
226
+ resolvedAmbiguousSeeds.set(result.path, null);
227
+ return null;
228
+ }
229
+ resolvedPath = resolvedCold.result.path;
230
+ resolvedResult = resolvedCold.result;
231
+ }
232
+
233
+ if (!path.isAbsolute(resolvedPath)) {
234
+ resolvedAmbiguousSeeds.set(result.path, null);
235
+ return null;
236
+ }
237
+ const ownerStorage = await this.storageForAbsoluteQmdResultPath(
238
+ resolvedPath,
239
+ this.getStorage(),
240
+ options.recallNamespaces,
241
+ );
242
+ const ownerNamespace = ownerStorage?.namespace ?? null;
243
+ const resolved =
244
+ ownerNamespace && options.recallNamespaces.includes(ownerNamespace)
245
+ ? { namespace: ownerNamespace, result: resolvedResult }
246
+ : null;
247
+ resolvedAmbiguousSeeds.set(result.path, resolved);
248
+ return resolved;
249
+ };
250
+ const coldCollection = config.qmdColdCollection ?? "openclaw-engram-cold";
251
+ for (const result of options.memoryResults) {
252
+ if (deadlineExpired()) break;
253
+ const parts = qmdCollectionPathParts(result.path);
254
+ if (parts?.collection === coldCollection) {
255
+ const resolved = await resolveAmbiguousSeedOwner(result, parts);
256
+ if (resolved) {
257
+ addResultForNamespace(resolved.namespace, resolved.result);
258
+ }
259
+ continue;
260
+ }
261
+ if (path.isAbsolute(result.path)) {
262
+ const resolved = await resolveAmbiguousSeedOwner(result, null);
263
+ if (resolved) {
264
+ addResultForNamespace(resolved.namespace, resolved.result);
265
+ }
266
+ continue;
267
+ }
268
+ const ns = this.namespaceFromPath(result.path);
269
+ if (!options.recallNamespaces.includes(ns)) continue;
270
+ addResultForNamespace(ns, result);
271
+ }
272
+
273
+ const perNamespaceSeedCap = Math.max(3, options.recallResultLimit);
274
+ const perNamespaceExpandedCap = Math.max(8, options.recallResultLimit * 2);
275
+ const seedPaths: string[] = [];
276
+ const seedResults: QmdSearchResult[] = [];
277
+ const expandedPaths: GraphRecallExpandedEntry[] = [];
278
+ const expandedResults: QmdSearchResult[] = [];
279
+
280
+ for (const [namespace, nsResults] of byNamespace.entries()) {
281
+ if (deadlineExpired()) break;
282
+ const storage = await this.storageFor(namespace);
283
+ const seedCandidates = nsResults.slice(0, perNamespaceSeedCap);
284
+ seedResults.push(...seedCandidates);
285
+ const seedRelativePaths =
286
+ typeof options.deadlineAtMs === "number"
287
+ ? await this.graphSeedPathsWithinDeadline(
288
+ storage,
289
+ seedCandidates,
290
+ options.deadlineAtMs,
291
+ [namespace],
292
+ )
293
+ : (
294
+ await Promise.all(
295
+ seedCandidates.map((result) =>
296
+ this.graphSeedPathRelativeToStorage(storage, result, [
297
+ namespace,
298
+ ]),
299
+ ),
300
+ )
301
+ ).filter(
302
+ (value): value is string =>
303
+ typeof value === "string" && value.length > 0,
304
+ );
305
+ if (deadlineExpired()) break;
306
+ if (seedRelativePaths.length === 0) continue;
307
+
308
+ const seedRecallScore = seedCandidates.reduce(
309
+ (max, item) => Math.max(max, item.score),
310
+ 0,
311
+ );
312
+ seedPaths.push(
313
+ ...seedRelativePaths.map((rel) => path.join(storage.dir, rel)),
314
+ );
315
+ const seedSet = new Set(seedRelativePaths);
316
+ const expanded = await this.graphIndexFor(storage).spreadingActivation(
317
+ seedRelativePaths,
318
+ config.maxGraphTraversalSteps,
319
+ {
320
+ ...(options.includeLowConfidence === true ? { includeLowConfidence: true } : {}),
321
+ ...(typeof options.deadlineAtMs === "number"
322
+ ? { deadlineAtMs: options.deadlineAtMs }
323
+ : {}),
324
+ },
325
+ );
326
+ if (expanded.length === 0) continue;
327
+ if (deadlineExpired()) break;
328
+
329
+ for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
330
+ if (deadlineExpired()) break;
331
+ if (seedSet.has(candidate.path)) continue;
332
+ const memoryPath = path.resolve(storage.dir, candidate.path);
333
+ const memory = await storage.readMemoryByPath(memoryPath);
334
+ if (deadlineExpired()) break;
335
+ if (!memory) continue;
336
+ if (/(?:^|[\\/])artifacts(?:[\\/]|$)/i.test(memory.path)) continue;
337
+ if (memory.frontmatter.status && memory.frontmatter.status !== "active")
338
+ continue;
339
+
340
+ const snippet = memory.content.slice(0, 400);
341
+ const score = blendGraphExpandedRecallScore({
342
+ graphActivationScore: candidate.score,
343
+ seedRecallScore,
344
+ activationWeight: config.graphExpansionActivationWeight,
345
+ blendMin: config.graphExpansionBlendMin,
346
+ blendMax: config.graphExpansionBlendMax,
347
+ });
348
+ expandedResults.push({
349
+ docid: memory.frontmatter.id,
350
+ path: memory.path,
351
+ snippet,
352
+ score,
353
+ });
354
+ expandedPaths.push({
355
+ path: memory.path,
356
+ score,
357
+ namespace,
358
+ seed: path.resolve(storage.dir, candidate.seed),
359
+ hopDepth: candidate.hopDepth,
360
+ decayedWeight: candidate.decayedWeight,
361
+ graphType: candidate.graphType,
362
+ // Issue #681 PR 3/3 — surface the per-edge confidence used for
363
+ // PageRank weighting / floor pruning so downstream observability
364
+ // (recall_xray, memory_graph_explain) can attribute ranking and
365
+ // pruning decisions to specific edges.
366
+ edgeConfidence: candidate.edgeConfidence,
367
+ });
368
+ }
369
+ }
370
+
371
+ return {
372
+ merged: mergeGraphExpandedResults(options.memoryResults, expandedResults),
373
+ seedPaths,
374
+ expandedPaths,
375
+ seedResults,
376
+ };
377
+ }
378
+
379
+ async recordLastGraphRecallSnapshot(options: {
380
+ storage: StorageManager;
381
+ prompt: string;
382
+ recallMode: RecallPlanMode;
383
+ recallNamespaces: string[];
384
+ seedPaths: string[];
385
+ expandedPaths: GraphRecallExpandedEntry[];
386
+ status: "completed" | "skipped" | "aborted";
387
+ reason?: string;
388
+ shadowMode?: boolean;
389
+ queryIntent: MemoryIntent;
390
+ seedResults?: GraphRecallRankedResult[];
391
+ finalResults?: GraphRecallRankedResult[];
392
+ shadowComparison?: GraphRecallShadowComparison;
393
+ }): Promise<void> {
394
+ try {
395
+ const snapshotPath = path.join(
396
+ options.storage.dir,
397
+ "state",
398
+ "last_graph_recall.json",
399
+ );
400
+ await mkdir(path.dirname(snapshotPath), { recursive: true });
401
+ const now = new Date().toISOString();
402
+ const totalSeedCount = options.seedPaths.length;
403
+ const totalExpandedCount = options.expandedPaths.length;
404
+ const seeds = options.seedPaths.slice(0, 64);
405
+ const expanded = clampGraphRecallExpandedEntries(
406
+ options.expandedPaths,
407
+ 64,
408
+ );
409
+ const payload = {
410
+ recordedAt: now,
411
+ mode: options.recallMode,
412
+ queryHash: createHash("sha256").update(options.prompt).digest("hex"),
413
+ queryLength: options.prompt.length,
414
+ namespaces: options.recallNamespaces,
415
+ seedCount: totalSeedCount,
416
+ expandedCount: totalExpandedCount,
417
+ seeds,
418
+ expanded,
419
+ status: options.status,
420
+ reason: options.reason,
421
+ shadowMode: options.shadowMode === true,
422
+ queryIntent: options.queryIntent,
423
+ seedResults: (options.seedResults ?? []).slice(0, 64),
424
+ finalResults: (options.finalResults ?? []).slice(0, 64),
425
+ shadowComparison: options.shadowComparison,
426
+ };
427
+ await writeFile(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
428
+ } catch (err) {
429
+ log.debug(`last graph recall write failed: ${err}`);
430
+ }
431
+ }
432
+
433
+ private async graphSeedPathRelativeToStorage(
434
+ storage: StorageManager,
435
+ result: QmdSearchResult,
436
+ recallNamespaces: readonly string[] = [],
437
+ ): Promise<string | null> {
438
+ const parts = qmdCollectionPathParts(result.path);
439
+ if (parts) {
440
+ const memory = await this.readQmdResultMemory(
441
+ result.path,
442
+ storage,
443
+ recallNamespaces,
444
+ );
445
+ return memory
446
+ ? graphPathRelativeToStorage(storage.dir, memory.path)
447
+ : null;
448
+ }
449
+ return graphPathRelativeToStorage(storage.dir, result.path);
450
+ }
451
+
452
+ private async graphSeedPathsWithinDeadline(
453
+ storage: StorageManager,
454
+ results: QmdSearchResult[],
455
+ deadlineAtMs: number,
456
+ recallNamespaces: readonly string[] = [],
457
+ ): Promise<string[]> {
458
+ const resolved: string[] = [];
459
+ for (const result of results) {
460
+ if (Date.now() >= deadlineAtMs) break;
461
+ const seedPath = await this.graphSeedPathRelativeToStorage(
462
+ storage,
463
+ result,
464
+ recallNamespaces,
465
+ );
466
+ if (Date.now() >= deadlineAtMs) break;
467
+ if (seedPath) resolved.push(seedPath);
468
+ }
469
+ return resolved;
470
+ }
471
+ }