noteconnection 1.6.8 → 1.8.0

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 (212) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +258 -64
  3. package/dist/src/agent_workspace.contract.parity.test.js +475 -0
  4. package/dist/src/agent_workspace.frontend.test.js +9989 -0
  5. package/dist/src/agent_workspace.locale.contract.test.js +95 -0
  6. package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
  7. package/dist/src/copy.assets.contract.test.js +130 -0
  8. package/dist/src/core/PathBridge.js +28 -3
  9. package/dist/src/export/WorkspaceExportBundle.js +1130 -0
  10. package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
  11. package/dist/src/export/index.js +18 -0
  12. package/dist/src/export/types.js +2 -0
  13. package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
  14. package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
  15. package/dist/src/foundation.release.evidence.contract.test.js +406 -0
  16. package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
  17. package/dist/src/frontend/README.md +258 -64
  18. package/dist/src/frontend/agent_workspace.js +5353 -0
  19. package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
  20. package/dist/src/frontend/analysis.js +31 -4
  21. package/dist/src/frontend/app.js +3597 -141
  22. package/dist/src/frontend/focus_mode_interactions.js +33 -0
  23. package/dist/src/frontend/godot_future_path_renderer.js +540 -0
  24. package/dist/src/frontend/godot_tree_interactions.js +178 -0
  25. package/dist/src/frontend/graph_state.mjs +105 -0
  26. package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
  27. package/dist/src/frontend/i18n.mjs +186 -0
  28. package/dist/src/frontend/index.html +641 -266
  29. package/dist/src/frontend/layout_gpu.js +12 -4
  30. package/dist/src/frontend/locales/en.json +721 -5
  31. package/dist/src/frontend/locales/zh.json +721 -5
  32. package/dist/src/frontend/main.mjs +60 -0
  33. package/dist/src/frontend/markdown_runtime.js +827 -0
  34. package/dist/src/frontend/notemd.css +49 -0
  35. package/dist/src/frontend/notemd.html +7 -1
  36. package/dist/src/frontend/notemd.js +64 -0
  37. package/dist/src/frontend/path.html +107 -0
  38. package/dist/src/frontend/path_app.js +2189 -150
  39. package/dist/src/frontend/path_layout.mjs +143 -0
  40. package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
  41. package/dist/src/frontend/path_modules_bridge.js +486 -0
  42. package/dist/src/frontend/path_state.mjs +118 -0
  43. package/dist/src/frontend/path_styles.css +146 -0
  44. package/dist/src/frontend/path_worker_bridge.mjs +85 -0
  45. package/dist/src/frontend/reader.js +522 -27
  46. package/dist/src/frontend/runtime_bridge.js +67 -54
  47. package/dist/src/frontend/runtime_bridge.mjs +279 -0
  48. package/dist/src/frontend/settings.js +130 -12
  49. package/dist/src/frontend/simulationWorker.js +241 -6
  50. package/dist/src/frontend/source_manager.js +190 -21
  51. package/dist/src/frontend/styles.css +2853 -72
  52. package/dist/src/frontend/workbench_state.mjs +101 -0
  53. package/dist/src/frontend/workspace_panes.js +10168 -0
  54. package/dist/src/frontend.locale.contract.test.js +62 -0
  55. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  56. package/dist/src/indexing/IndexLifecycle.js +195 -0
  57. package/dist/src/indexing/IndexLifecycle.test.js +49 -0
  58. package/dist/src/indexing/SegmentBuilder.js +64 -0
  59. package/dist/src/indexing/UnitBuilder.js +48 -0
  60. package/dist/src/indexing/types.js +2 -0
  61. package/dist/src/knowledge.api.contract.test.js +170 -0
  62. package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
  64. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  65. package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
  68. package/dist/src/learning/answerReleaseReview.js +4319 -0
  69. package/dist/src/learning/answerReleaseReview.test.js +2888 -0
  70. package/dist/src/learning/api.js +2 -0
  71. package/dist/src/learning/conversationComposer.js +1480 -0
  72. package/dist/src/learning/conversationComposer.test.js +1817 -0
  73. package/dist/src/learning/domains/ConversationManager.js +53 -0
  74. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  75. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  76. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  77. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  78. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  79. package/dist/src/learning/domains/TutorRouter.js +313 -0
  80. package/dist/src/learning/domains/index.js +33 -0
  81. package/dist/src/learning/domains/types.js +7 -0
  82. package/dist/src/learning/errors.js +29 -0
  83. package/dist/src/learning/evidenceContextAssembler.js +1176 -0
  84. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  85. package/dist/src/learning/graphContextAssembler.js +870 -0
  86. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  87. package/dist/src/learning/index.js +28 -0
  88. package/dist/src/learning/queryBackend.js +1898 -0
  89. package/dist/src/learning/queryBackend.test.js +955 -0
  90. package/dist/src/learning/ragContextPack.js +257 -0
  91. package/dist/src/learning/ragContextPack.test.js +160 -0
  92. package/dist/src/learning/ragPublicText.js +38 -0
  93. package/dist/src/learning/ragSufficiencyJudge.js +161 -0
  94. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  95. package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
  96. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
  97. package/dist/src/learning/requestNormalization.js +198 -0
  98. package/dist/src/learning/runtimeCapability.js +4677 -0
  99. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  100. package/dist/src/learning/store.js +1240 -0
  101. package/dist/src/learning/store.test.js +1126 -0
  102. package/dist/src/learning/tutorAdapter.js +2 -0
  103. package/dist/src/learning/types.js +2 -0
  104. package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
  105. package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
  106. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  107. package/dist/src/license.policy.contract.test.js +66 -0
  108. package/dist/src/memory/MemoryGovernance.js +74 -0
  109. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  110. package/dist/src/memory/types.js +2 -0
  111. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  112. package/dist/src/middleware/auth.js +17 -0
  113. package/dist/src/middleware/body-parser.js +45 -0
  114. package/dist/src/middleware/cors.js +44 -0
  115. package/dist/src/middleware/index.js +21 -0
  116. package/dist/src/middleware/request-trace.js +96 -0
  117. package/dist/src/notemd/AppConfigToml.js +6 -4
  118. package/dist/src/notemd/MermaidProcessor.js +400 -50
  119. package/dist/src/notemd/NotemdService.js +498 -13
  120. package/dist/src/notemd/PromptManager.js +15 -0
  121. package/dist/src/notemd/cli/commands.js +357 -0
  122. package/dist/src/notemd/cli/dispatcher.js +225 -0
  123. package/dist/src/notemd/cli/index.js +169 -0
  124. package/dist/src/notemd/cli/parser.js +68 -0
  125. package/dist/src/notemd/cli/types.js +2 -0
  126. package/dist/src/notemd/constants.js +43 -0
  127. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  128. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  129. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  130. package/dist/src/notemd/diagram/intent.js +95 -0
  131. package/dist/src/notemd/diagram/planner.js +71 -0
  132. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  133. package/dist/src/notemd/diagram/types.js +18 -0
  134. package/dist/src/notemd/index.js +26 -0
  135. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  136. package/dist/src/notemd/operations/cliContracts.js +17 -0
  137. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  138. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  139. package/dist/src/notemd/operations/registry.js +991 -0
  140. package/dist/src/notemd/operations/types.js +2 -0
  141. package/dist/src/notemd/providerDiagnostics.js +220 -0
  142. package/dist/src/notemd/providerProfiles.js +42 -0
  143. package/dist/src/notemd/providerTemplates.js +231 -0
  144. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  145. package/dist/src/notemd/search/SearchManager.js +13 -0
  146. package/dist/src/notemd/search/SearchProvider.js +2 -0
  147. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  148. package/dist/src/notemd.agent.manifest.test.js +85 -0
  149. package/dist/src/notemd.api.contract.test.js +14 -1
  150. package/dist/src/notemd.app_config_toml.test.js +2 -1
  151. package/dist/src/notemd.batch.workflow.test.js +117 -0
  152. package/dist/src/notemd.cli.e2e.test.js +136 -0
  153. package/dist/src/notemd.core.test.js +51 -0
  154. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  155. package/dist/src/notemd.providerTemplates.test.js +34 -0
  156. package/dist/src/notemd.server.integration.test.js +143 -35
  157. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  158. package/dist/src/pathbridge.handshake.contract.test.js +16 -2
  159. package/dist/src/pathmode.background.contract.test.js +69 -0
  160. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  161. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  162. package/dist/src/platform/ExportProfile.js +58 -0
  163. package/dist/src/platform/PlatformCapabilities.js +45 -0
  164. package/dist/src/platform/PlatformCapabilities.test.js +30 -0
  165. package/dist/src/platform/RenderMaterializer.js +33 -0
  166. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  167. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  168. package/dist/src/reader_renderer.js +404 -3
  169. package/dist/src/reader_renderer.test.js +87 -0
  170. package/dist/src/release.godot.mirror.contract.test.js +73 -0
  171. package/dist/src/resources/ResourceRegistry.js +223 -0
  172. package/dist/src/resources/ResourceRegistry.test.js +61 -0
  173. package/dist/src/resources/types.js +2 -0
  174. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  175. package/dist/src/routes/data.js +267 -0
  176. package/dist/src/routes/diagnostics.js +51 -0
  177. package/dist/src/routes/index.js +23 -0
  178. package/dist/src/routes/knowledge.js +968 -0
  179. package/dist/src/routes/markdown.js +287 -0
  180. package/dist/src/routes/notemd.js +565 -0
  181. package/dist/src/routes/registry.contract.test.js +130 -0
  182. package/dist/src/routes/render.js +285 -0
  183. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  184. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  185. package/dist/src/routes/settings.js +6 -0
  186. package/dist/src/routes/staticFiles.js +94 -0
  187. package/dist/src/routes/types.js +2 -0
  188. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  189. package/dist/src/server.js +11090 -1549
  190. package/dist/src/server.migration.test.js +193 -21
  191. package/dist/src/server.port.fallback.contract.test.js +63 -0
  192. package/dist/src/session/SessionStateStore.js +81 -0
  193. package/dist/src/session/SessionStateStore.test.js +58 -0
  194. package/dist/src/session/types.js +2 -0
  195. package/dist/src/settings.runtime.contract.test.js +50 -0
  196. package/dist/src/shared/types.contract.test.js +107 -0
  197. package/dist/src/shared/types.js +22 -0
  198. package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
  199. package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
  200. package/dist/src/source_manager.loadflow.test.js +46 -0
  201. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  202. package/dist/src/tauri.frontend.build.contract.test.js +60 -0
  203. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  204. package/dist/src/utils/RuntimePaths.js +4 -13
  205. package/dist/src/utils/platform.js +153 -0
  206. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  207. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  208. package/dist/src/workflows/types.js +2 -0
  209. package/dist/src/workspace/WorkspaceRegistry.js +122 -0
  210. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  211. package/dist/src/workspace/types.js +2 -0
  212. package/package.json +61 -10
@@ -0,0 +1,870 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assembleAgentConversationGraphContext = assembleAgentConversationGraphContext;
4
+ exports.buildAgentConversationGraphContextFromKnowledgePoints = buildAgentConversationGraphContextFromKnowledgePoints;
5
+ const store_1 = require("./store");
6
+ function normalizeGraphComparableTitle(value) {
7
+ return normalizeWhitespace(String(value || '').toLowerCase());
8
+ }
9
+ function buildAnchorNodeExclusion(anchorPoint, graphContext) {
10
+ const atomIds = new Set([
11
+ graphContext.anchorAtomId,
12
+ ...pointAtomIds(anchorPoint),
13
+ ].map((atomId) => String(atomId || '').trim()).filter(Boolean));
14
+ const titles = new Set([
15
+ graphContext.anchorTitle,
16
+ anchorPoint.title,
17
+ ].map(normalizeGraphComparableTitle).filter(Boolean));
18
+ return { atomIds, titles };
19
+ }
20
+ function isAnchorEquivalentNode(atomId, title, exclusion) {
21
+ const normalizedAtomId = String(atomId || '').trim();
22
+ if (normalizedAtomId && exclusion.atomIds.has(normalizedAtomId)) {
23
+ return true;
24
+ }
25
+ const normalizedTitle = normalizeGraphComparableTitle(title);
26
+ return Boolean(normalizedTitle && exclusion.titles.has(normalizedTitle));
27
+ }
28
+ function relationEdgeKey(edge) {
29
+ const edgeId = String(edge.edgeId || edge.id || '').trim();
30
+ if (edgeId) {
31
+ return `id:${edgeId}`;
32
+ }
33
+ return [
34
+ String(edge.sourceAtomId || '').trim(),
35
+ String(edge.targetAtomId || '').trim(),
36
+ String(edge.relationKind || '').trim(),
37
+ String(edge.provenance || '').trim(),
38
+ ].join('|');
39
+ }
40
+ async function queryAdjacentEdgesForAnchorAtoms(opsStore, anchorAtomIds, direction, edgeLimit) {
41
+ const edgesByKey = new Map();
42
+ const normalizedAnchorAtomIds = Array.from(new Set(anchorAtomIds
43
+ .map((atomId) => String(atomId || '').trim())
44
+ .filter(Boolean)));
45
+ for (const atomId of normalizedAnchorAtomIds) {
46
+ const edges = await opsStore.queryEdges({
47
+ ...(direction === 'predecessor' ? { toNodeId: atomId } : { fromNodeId: atomId }),
48
+ ...(Number.isFinite(Number(edgeLimit)) && Number(edgeLimit) > 0
49
+ ? { limit: Math.max(1, Math.floor(Number(edgeLimit))) }
50
+ : {}),
51
+ });
52
+ edges.forEach((edge) => {
53
+ edgesByKey.set(relationEdgeKey(edge), edge);
54
+ });
55
+ }
56
+ return Array.from(edgesByKey.values());
57
+ }
58
+ async function countUsableAdjacentNodes(opsStore, edges, direction, exclusion, titleCache) {
59
+ const seenNodeKeys = new Set();
60
+ for (const edge of edges) {
61
+ const relatedAtomId = direction === 'predecessor'
62
+ ? String(edge.sourceAtomId || '').trim()
63
+ : String(edge.targetAtomId || '').trim();
64
+ if (!relatedAtomId || exclusion.atomIds.has(relatedAtomId)) {
65
+ continue;
66
+ }
67
+ const title = await resolveNodeTitle(opsStore, relatedAtomId, titleCache);
68
+ if (!title || isAnchorEquivalentNode(relatedAtomId, title, exclusion)) {
69
+ continue;
70
+ }
71
+ seenNodeKeys.add(relatedAtomId + '|' + normalizeGraphComparableTitle(title));
72
+ }
73
+ return seenNodeKeys.size;
74
+ }
75
+ function normalizeWhitespace(value) {
76
+ return String(value || '').replace(/\s+/g, ' ').trim();
77
+ }
78
+ function tokenize(value) {
79
+ return Array.from(new Set(normalizeWhitespace(String(value || '').toLowerCase())
80
+ .replace(/[^\p{L}\p{N}\s_-]+/gu, ' ')
81
+ .split(/\s+/)
82
+ .map((token) => token.trim())
83
+ .filter((token) => token.length >= 2)));
84
+ }
85
+ function computeJaccard(left, right) {
86
+ if (left.length <= 0 || right.length <= 0) {
87
+ return 0;
88
+ }
89
+ const leftSet = new Set(left);
90
+ const rightSet = new Set(right);
91
+ let intersection = 0;
92
+ leftSet.forEach((token) => {
93
+ if (rightSet.has(token)) {
94
+ intersection += 1;
95
+ }
96
+ });
97
+ const union = leftSet.size + rightSet.size - intersection;
98
+ if (union <= 0) {
99
+ return 0;
100
+ }
101
+ return intersection / union;
102
+ }
103
+ function classifyConversationIntent(message) {
104
+ const normalized = normalizeWhitespace(String(message || '').toLowerCase());
105
+ if (!normalized) {
106
+ return 'generic';
107
+ }
108
+ if (normalized.includes('compare')
109
+ || normalized.includes('difference')
110
+ || normalized.includes('vs')
111
+ || normalized.includes('区分')
112
+ || normalized.includes('对比')) {
113
+ return 'compare';
114
+ }
115
+ if (normalized.includes('how to')
116
+ || normalized.includes('how do')
117
+ || normalized.includes('steps')
118
+ || normalized.includes('plan')
119
+ || normalized.includes('如何')
120
+ || normalized.includes('怎么')
121
+ || normalized.includes('步骤')
122
+ || normalized.includes('方案')) {
123
+ return 'how_to';
124
+ }
125
+ if (normalized.includes('what is')
126
+ || normalized.includes('why')
127
+ || normalized.includes('explain')
128
+ || normalized.includes('解释')
129
+ || normalized.includes('什么是')
130
+ || normalized.includes('为什么')) {
131
+ return 'explain';
132
+ }
133
+ return 'generic';
134
+ }
135
+ function clampInteger(value, minValue, maxValue) {
136
+ if (!Number.isFinite(value)) {
137
+ return minValue;
138
+ }
139
+ return Math.max(minValue, Math.min(maxValue, Math.floor(value)));
140
+ }
141
+ function resolveBudget(intent, budget) {
142
+ const defaultSupportNodes = intent === 'compare' || intent === 'how_to' ? 3 : 2;
143
+ const defaultSuccessors = intent === 'how_to' ? 3 : intent === 'compare' ? 2 : 1;
144
+ return {
145
+ maxSupportNodes: clampInteger(Number(budget?.maxSupportNodes ?? defaultSupportNodes), 1, 6),
146
+ maxConnectionPaths: clampInteger(Number(budget?.maxConnectionPaths ?? defaultSupportNodes), 1, 6),
147
+ maxPathDepth: clampInteger(Number(budget?.maxPathDepth ?? 6), 2, 8),
148
+ maxPredecessors: clampInteger(Number(budget?.maxPredecessors ?? 3), 0, 6),
149
+ maxSuccessors: clampInteger(Number(budget?.maxSuccessors ?? defaultSuccessors), 0, 6),
150
+ };
151
+ }
152
+ function pointAtomIds(point) {
153
+ const groupedAtomIds = Array.isArray(point.atomIds) && point.atomIds.length > 0
154
+ ? point.atomIds
155
+ : [point.atomId];
156
+ return groupedAtomIds
157
+ .map((atomId) => String(atomId || '').trim())
158
+ .filter(Boolean);
159
+ }
160
+ function pointTitleMentionScore(message, point) {
161
+ const normalizedMessage = normalizeWhitespace(String(message || '').toLowerCase());
162
+ const normalizedTitle = normalizeWhitespace(String(point.title || '').toLowerCase());
163
+ if (!normalizedMessage || !normalizedTitle) {
164
+ return 0;
165
+ }
166
+ if (normalizedMessage.includes(normalizedTitle)) {
167
+ return 0.24;
168
+ }
169
+ return 0;
170
+ }
171
+ function selectAnchorPoint(message, knowledgePoints) {
172
+ const queryTokens = tokenize(message);
173
+ const ranked = knowledgePoints.map((point, index) => {
174
+ const titleTokens = tokenize(point.title);
175
+ const evidenceTokens = tokenize(`${point.summary || ''} ${point.evidenceSnippet || ''}`);
176
+ const titleMentionScore = pointTitleMentionScore(message, point);
177
+ const titleOverlapScore = computeJaccard(queryTokens, titleTokens) * 0.18;
178
+ const evidenceOverlapScore = computeJaccard(queryTokens, evidenceTokens) * 0.08;
179
+ const relationDensityScore = Math.min(Array.isArray(point.relationPath) ? point.relationPath.length : 0, 4) * 0.02;
180
+ const temporalPenalty = point.temporalValidity?.isValid === false ? 0.18 : 0;
181
+ const totalScore = Number((Number(point.score || 0)
182
+ + titleMentionScore
183
+ + titleOverlapScore
184
+ + evidenceOverlapScore
185
+ + relationDensityScore
186
+ - temporalPenalty).toFixed(6));
187
+ const reason = titleMentionScore > 0
188
+ ? 'title_mention'
189
+ : titleOverlapScore > evidenceOverlapScore
190
+ ? 'title_overlap'
191
+ : temporalPenalty > 0
192
+ ? 'retrieval_score_with_temporal_penalty'
193
+ : 'retrieval_score';
194
+ return {
195
+ point,
196
+ index,
197
+ totalScore,
198
+ reason,
199
+ };
200
+ });
201
+ ranked.sort((left, right) => (right.totalScore - left.totalScore
202
+ || left.index - right.index));
203
+ const best = ranked[0];
204
+ return {
205
+ point: best.point,
206
+ index: best.index,
207
+ reason: best.reason,
208
+ };
209
+ }
210
+ function isDirectlyLinkedToAnchor(point, anchorAtomIds) {
211
+ if (Array.isArray(point.relationPathAtomIds)) {
212
+ for (const atomId of point.relationPathAtomIds) {
213
+ if (anchorAtomIds.has(String(atomId || '').trim())) {
214
+ return true;
215
+ }
216
+ }
217
+ }
218
+ if (Array.isArray(point.relationPath)) {
219
+ for (const edge of point.relationPath) {
220
+ const sourceAtomId = String(edge?.sourceAtomId || '').trim();
221
+ const targetAtomId = String(edge?.targetAtomId || '').trim();
222
+ if (anchorAtomIds.has(sourceAtomId) || anchorAtomIds.has(targetAtomId)) {
223
+ return true;
224
+ }
225
+ }
226
+ }
227
+ return false;
228
+ }
229
+ function rankSupportPoints(message, intent, anchorPoint, knowledgePoints, budget) {
230
+ const queryTokens = tokenize(message);
231
+ const anchorAtomIdSet = new Set(pointAtomIds(anchorPoint));
232
+ const ranked = knowledgePoints
233
+ .filter((point) => point !== anchorPoint)
234
+ .map((point, index) => {
235
+ const titleTokens = tokenize(point.title);
236
+ const evidenceTokens = tokenize(`${point.summary || ''} ${point.evidenceSnippet || ''}`);
237
+ const directLinkScore = isDirectlyLinkedToAnchor(point, anchorAtomIdSet) ? 0.18 : 0;
238
+ const titleOverlapScore = computeJaccard(queryTokens, titleTokens) * 0.08;
239
+ const evidenceOverlapScore = computeJaccard(queryTokens, evidenceTokens) * 0.05;
240
+ const compareBonus = intent === 'compare' ? 0.04 : 0;
241
+ const temporalPenalty = point.temporalValidity?.isValid === false ? 0.12 : 0;
242
+ const totalScore = Number((Number(point.score || 0)
243
+ + directLinkScore
244
+ + titleOverlapScore
245
+ + evidenceOverlapScore
246
+ + compareBonus
247
+ - temporalPenalty).toFixed(6));
248
+ return {
249
+ point,
250
+ index,
251
+ totalScore,
252
+ };
253
+ });
254
+ ranked.sort((left, right) => (right.totalScore - left.totalScore
255
+ || left.index - right.index));
256
+ return ranked
257
+ .slice(0, budget.maxSupportNodes)
258
+ .map((entry) => entry.point);
259
+ }
260
+ function buildBaseGraphContext(knowledgePoints) {
261
+ const anchorPoint = knowledgePoints[0];
262
+ if (!anchorPoint) {
263
+ return null;
264
+ }
265
+ const relationPath = knowledgePoints.flatMap((point) => (Array.isArray(point.relationPath)
266
+ ? point.relationPath
267
+ : []));
268
+ const relationKinds = Array.from(new Set(relationPath
269
+ .map((edge) => edge.relationKind)
270
+ .filter(Boolean)));
271
+ const relationSummaryMap = new Map();
272
+ const anchorAtomId = String(anchorPoint.atomId || '').trim();
273
+ const atomToKnowledgePoint = new Map();
274
+ const knowledgePointAtomIds = new Set(knowledgePoints
275
+ .flatMap((point) => pointAtomIds(point)));
276
+ knowledgePoints.forEach((point) => {
277
+ const pointTitle = normalizeWhitespace(String(point.title || '').trim());
278
+ pointAtomIds(point).forEach((atomId) => {
279
+ atomToKnowledgePoint.set(atomId, {
280
+ pointAtomId: String(point.atomId || '').trim(),
281
+ title: pointTitle || atomId,
282
+ });
283
+ });
284
+ });
285
+ const knowledgePointRelationMap = new Map();
286
+ relationPath.forEach((edge) => {
287
+ if (!edge || !edge.relationKind) {
288
+ return;
289
+ }
290
+ const edgeId = String(edge.edgeId || edge.id || '').trim();
291
+ const summary = relationSummaryMap.get(edge.relationKind) || {
292
+ edgeIds: new Set(),
293
+ sourceAtomIds: new Set(),
294
+ targetAtomIds: new Set(),
295
+ confidenceValues: [],
296
+ };
297
+ if (edgeId) {
298
+ summary.edgeIds.add(edgeId);
299
+ }
300
+ const sourceAtomId = String(edge.sourceAtomId || '').trim();
301
+ const targetAtomId = String(edge.targetAtomId || '').trim();
302
+ if (sourceAtomId) {
303
+ summary.sourceAtomIds.add(sourceAtomId);
304
+ }
305
+ const targetAtomIds = [
306
+ sourceAtomId,
307
+ targetAtomId,
308
+ ]
309
+ .filter(Boolean)
310
+ .filter((atomId) => atomId !== anchorAtomId && !knowledgePointAtomIds.has(atomId));
311
+ targetAtomIds.forEach((atomId) => summary.targetAtomIds.add(atomId));
312
+ if (Number.isFinite(Number(edge.confidence))) {
313
+ summary.confidenceValues.push(Number(edge.confidence));
314
+ }
315
+ if (sourceAtomId && targetAtomId) {
316
+ const sourcePoint = atomToKnowledgePoint.get(sourceAtomId);
317
+ const targetPoint = atomToKnowledgePoint.get(targetAtomId);
318
+ if (sourcePoint
319
+ && targetPoint
320
+ && sourcePoint.pointAtomId
321
+ && targetPoint.pointAtomId
322
+ && sourcePoint.pointAtomId !== targetPoint.pointAtomId) {
323
+ const relationKey = [
324
+ edgeId,
325
+ sourcePoint.pointAtomId,
326
+ targetPoint.pointAtomId,
327
+ edge.relationKind,
328
+ ].join('|');
329
+ knowledgePointRelationMap.set(relationKey, {
330
+ edgeId,
331
+ relationKind: edge.relationKind,
332
+ sourceAtomId: sourcePoint.pointAtomId,
333
+ sourceTitle: sourcePoint.title,
334
+ targetAtomId: targetPoint.pointAtomId,
335
+ targetTitle: targetPoint.title,
336
+ confidence: Number(Number(edge.confidence || 0).toFixed(4)),
337
+ });
338
+ }
339
+ }
340
+ relationSummaryMap.set(edge.relationKind, summary);
341
+ });
342
+ const relationSummaries = Array.from(relationSummaryMap.entries()).map(([relationKind, summary]) => ({
343
+ relationKind,
344
+ edgeIds: Array.from(summary.edgeIds.values()),
345
+ sourceAtomIds: Array.from(summary.sourceAtomIds.values()),
346
+ targetAtomIds: Array.from(summary.targetAtomIds.values()),
347
+ averageConfidence: summary.confidenceValues.length > 0
348
+ ? Number((summary.confidenceValues.reduce((sum, value) => sum + value, 0) / summary.confidenceValues.length).toFixed(4))
349
+ : 0,
350
+ }));
351
+ const supportingAtomIds = Array.from(new Set(knowledgePoints.flatMap((point) => (Array.isArray(point.relationPathAtomIds)
352
+ ? point.relationPathAtomIds
353
+ : []))
354
+ .map((atomId) => String(atomId || '').trim())
355
+ .filter(Boolean)
356
+ .concat(relationSummaries.flatMap((summary) => summary.targetAtomIds))));
357
+ const supportingAtomIdSet = new Set(supportingAtomIds);
358
+ const supportingTitles = knowledgePoints
359
+ .filter((point, index) => index > 0 || supportingAtomIdSet.has(String(point.atomId || '').trim()))
360
+ .map((point) => normalizeWhitespace(String(point.title || '').trim()))
361
+ .filter(Boolean);
362
+ const temporalCheckedAt = knowledgePoints
363
+ .map((point) => String(point.temporalValidity && point.temporalValidity.checkedAt || '').trim())
364
+ .filter(Boolean)
365
+ .sort()
366
+ .pop() || '';
367
+ const invalidKnowledgePoints = knowledgePoints.filter((point) => point.temporalValidity && point.temporalValidity.isValid === false);
368
+ const temporalDetails = knowledgePoints.flatMap((point) => (Array.isArray(point.temporalValidity && point.temporalValidity.details)
369
+ ? point.temporalValidity.details
370
+ : []));
371
+ const warningReasons = Array.from(new Set(invalidKnowledgePoints.flatMap((point) => (Array.isArray(point.temporalValidity && point.temporalValidity.reasons)
372
+ ? point.temporalValidity.reasons
373
+ : []))
374
+ .map((reason) => String(reason || '').trim())
375
+ .filter(Boolean)));
376
+ const normalizedTemporalDetails = temporalDetails
377
+ .filter((detail) => Boolean(detail && typeof detail === 'object'));
378
+ const temporalEdgeKinds = Array.from(new Set(normalizedTemporalDetails
379
+ .map((detail) => detail.edgeKind)
380
+ .filter(Boolean)));
381
+ const dedupedTemporalDetails = Array.from(new Map(normalizedTemporalDetails.map((detail) => [
382
+ [
383
+ String(detail.edgeId || '').trim(),
384
+ String(detail.edgeKind || '').trim(),
385
+ String(detail.sourceAtomId || '').trim(),
386
+ String(detail.targetAtomId || '').trim(),
387
+ String(detail.validFrom || '').trim(),
388
+ String(detail.validTo || '').trim(),
389
+ ].join('|'),
390
+ {
391
+ edgeId: String(detail.edgeId || '').trim(),
392
+ edgeKind: detail.edgeKind,
393
+ sourceAtomId: String(detail.sourceAtomId || '').trim(),
394
+ targetAtomId: String(detail.targetAtomId || '').trim(),
395
+ validFrom: String(detail.validFrom || '').trim(),
396
+ validTo: detail.validTo ? String(detail.validTo).trim() : undefined,
397
+ isActive: detail.isActive !== false,
398
+ },
399
+ ])).values());
400
+ const temporalValidity = {
401
+ checkedAt: temporalCheckedAt,
402
+ allPointsValid: invalidKnowledgePoints.length <= 0,
403
+ warningReasons,
404
+ invalidKnowledgePointTitles: invalidKnowledgePoints
405
+ .map((point) => normalizeWhitespace(String(point.title || '').trim()))
406
+ .filter(Boolean),
407
+ edgeKinds: temporalEdgeKinds,
408
+ details: dedupedTemporalDetails,
409
+ };
410
+ return {
411
+ anchorAtomId,
412
+ anchorTitle: normalizeWhitespace(String(anchorPoint.title || '').trim()) || anchorAtomId,
413
+ anchorDocumentId: anchorPoint.documentId,
414
+ supportingAtomIds: Array.from(new Set(supportingAtomIds)),
415
+ supportingTitles: Array.from(new Set(supportingTitles)),
416
+ relationKinds,
417
+ relationSummaries,
418
+ knowledgePointRelations: Array.from(knowledgePointRelationMap.values()),
419
+ temporalValidity,
420
+ };
421
+ }
422
+ async function resolveNodeTitle(opsStore, atomId, cache) {
423
+ const normalizedAtomId = String(atomId || '').trim();
424
+ if (!normalizedAtomId) {
425
+ return '';
426
+ }
427
+ if (cache.has(normalizedAtomId)) {
428
+ return String(cache.get(normalizedAtomId) || '').trim();
429
+ }
430
+ const atom = await opsStore.getNode(normalizedAtomId);
431
+ const resolvedTitle = normalizeWhitespace(String(atom?.title || '').trim()) || normalizedAtomId;
432
+ cache.set(normalizedAtomId, resolvedTitle);
433
+ return resolvedTitle;
434
+ }
435
+ async function resolveNodeMetrics(opsStore, atomId, cache) {
436
+ const normalizedAtomId = String(atomId || '').trim();
437
+ if (!normalizedAtomId) {
438
+ return null;
439
+ }
440
+ if (cache.has(normalizedAtomId)) {
441
+ return cache.get(normalizedAtomId) || null;
442
+ }
443
+ const atom = await opsStore.getNode(normalizedAtomId);
444
+ if (!atom) {
445
+ cache.set(normalizedAtomId, null);
446
+ return null;
447
+ }
448
+ const graphNode = atom;
449
+ const metrics = {
450
+ inDegree: Number.isFinite(Number(graphNode.inDegree)) ? Number(graphNode.inDegree) : undefined,
451
+ outDegree: Number.isFinite(Number(graphNode.outDegree)) ? Number(graphNode.outDegree) : undefined,
452
+ centrality: Number.isFinite(Number(graphNode.centrality)) ? Number(Number(graphNode.centrality).toFixed(4)) : undefined,
453
+ };
454
+ cache.set(normalizedAtomId, metrics);
455
+ return metrics;
456
+ }
457
+ async function buildAnchorGraphProfile(opsStore, anchorPoint, titleCache, metricsCache, incomingEdges, outgoingEdges, exclusion, useCompleteNeighborhoodDegree) {
458
+ const anchorAtomId = String(anchorPoint.atomId || '').trim();
459
+ if (!anchorAtomId) {
460
+ return undefined;
461
+ }
462
+ const title = normalizeWhitespace(String(anchorPoint.title || '').trim())
463
+ || await resolveNodeTitle(opsStore, anchorAtomId, titleCache)
464
+ || anchorAtomId;
465
+ const metrics = await resolveNodeMetrics(opsStore, anchorAtomId, metricsCache);
466
+ const derivedInDegree = useCompleteNeighborhoodDegree
467
+ ? await countUsableAdjacentNodes(opsStore, incomingEdges, 'predecessor', exclusion, titleCache)
468
+ : undefined;
469
+ const derivedOutDegree = useCompleteNeighborhoodDegree
470
+ ? await countUsableAdjacentNodes(opsStore, outgoingEdges, 'successor', exclusion, titleCache)
471
+ : undefined;
472
+ return {
473
+ atomId: anchorAtomId,
474
+ title,
475
+ inDegree: Number.isFinite(Number(derivedInDegree)) ? derivedInDegree : metrics?.inDegree,
476
+ outDegree: Number.isFinite(Number(derivedOutDegree)) ? derivedOutDegree : metrics?.outDegree,
477
+ centrality: metrics?.centrality,
478
+ };
479
+ }
480
+ function rankRelationEdges(edges) {
481
+ return edges
482
+ .slice()
483
+ .sort((left, right) => (Number(right.confidence || 0) - Number(left.confidence || 0)
484
+ || (left.provenance === 'fact' ? -1 : 1)));
485
+ }
486
+ function resolveGraphWindowScoringPolicy(intent) {
487
+ const structuralPriorities = {
488
+ prerequisite: 90,
489
+ sequence: 82,
490
+ application: 74,
491
+ causal: 68,
492
+ contrast: 58,
493
+ analogy: 52,
494
+ reference: 18,
495
+ };
496
+ if (intent === 'compare') {
497
+ return {
498
+ relationPriorities: {
499
+ contrast: 104,
500
+ analogy: 92,
501
+ causal: 70,
502
+ application: 64,
503
+ sequence: 54,
504
+ prerequisite: 48,
505
+ reference: 18,
506
+ },
507
+ defaultRelationPriority: 0,
508
+ intentAlignedRelationKinds: new Set(['contrast', 'analogy']),
509
+ confidenceWeight: 10,
510
+ factProvenanceBonus: 2,
511
+ bibliographyPenalty: 1000,
512
+ };
513
+ }
514
+ if (intent === 'how_to') {
515
+ return {
516
+ relationPriorities: {
517
+ prerequisite: 104,
518
+ sequence: 96,
519
+ application: 82,
520
+ causal: 70,
521
+ contrast: 44,
522
+ analogy: 38,
523
+ reference: 18,
524
+ },
525
+ defaultRelationPriority: 0,
526
+ intentAlignedRelationKinds: new Set(['prerequisite', 'sequence', 'application']),
527
+ confidenceWeight: 10,
528
+ factProvenanceBonus: 2,
529
+ bibliographyPenalty: 1000,
530
+ };
531
+ }
532
+ if (intent === 'explain') {
533
+ return {
534
+ relationPriorities: {
535
+ prerequisite: 94,
536
+ causal: 88,
537
+ sequence: 76,
538
+ application: 70,
539
+ analogy: 58,
540
+ contrast: 54,
541
+ reference: 18,
542
+ },
543
+ defaultRelationPriority: 0,
544
+ intentAlignedRelationKinds: new Set(['prerequisite', 'causal', 'application', 'sequence']),
545
+ confidenceWeight: 10,
546
+ factProvenanceBonus: 2,
547
+ bibliographyPenalty: 1000,
548
+ };
549
+ }
550
+ return {
551
+ relationPriorities: structuralPriorities,
552
+ defaultRelationPriority: 0,
553
+ intentAlignedRelationKinds: new Set(),
554
+ confidenceWeight: 10,
555
+ factProvenanceBonus: 2,
556
+ bibliographyPenalty: 1000,
557
+ };
558
+ }
559
+ function isBibliographyLikeGraphWindowTitle(title) {
560
+ const normalized = normalizeGraphComparableTitle(title);
561
+ if (!normalized) {
562
+ return false;
563
+ }
564
+ return normalized === 'reference'
565
+ || normalized === 'references'
566
+ || normalized === 'bibliography'
567
+ || normalized === 'works cited'
568
+ || normalized === 'citations'
569
+ || normalized.includes('参考文献')
570
+ || normalized.includes('参考资料')
571
+ || normalized.includes('引用文献')
572
+ || normalized.includes('文献引用');
573
+ }
574
+ function scoreGraphWindowCandidate(edge, title, scoringPolicy) {
575
+ const confidenceScore = Math.max(0, Math.min(1, Number(edge.confidence || 0))) * scoringPolicy.confidenceWeight;
576
+ const provenanceScore = edge.provenance === 'fact' ? scoringPolicy.factProvenanceBonus : 0;
577
+ const bibliographyPenalty = isBibliographyLikeGraphWindowTitle(title) ? scoringPolicy.bibliographyPenalty : 0;
578
+ const relationPriority = edge.relationKind
579
+ ? scoringPolicy.relationPriorities[edge.relationKind] ?? scoringPolicy.defaultRelationPriority
580
+ : scoringPolicy.defaultRelationPriority;
581
+ return relationPriority + confidenceScore + provenanceScore - bibliographyPenalty;
582
+ }
583
+ async function buildWindowNodes(opsStore, edges, direction, titleCache, metricsCache, exclusion, limit, missingIds, scoringPolicy) {
584
+ const rankedEdges = rankRelationEdges(edges);
585
+ const candidates = [];
586
+ const seenNodeKeys = new Set();
587
+ for (const edge of rankedEdges) {
588
+ const relatedAtomId = direction === 'predecessor'
589
+ ? String(edge.sourceAtomId || '').trim()
590
+ : String(edge.targetAtomId || '').trim();
591
+ if (!relatedAtomId || exclusion.atomIds.has(relatedAtomId)) {
592
+ continue;
593
+ }
594
+ const title = await resolveNodeTitle(opsStore, relatedAtomId, titleCache);
595
+ if (!title) {
596
+ missingIds.add(relatedAtomId);
597
+ continue;
598
+ }
599
+ if (isAnchorEquivalentNode(relatedAtomId, title, exclusion)) {
600
+ continue;
601
+ }
602
+ if (isBibliographyLikeGraphWindowTitle(title)) {
603
+ continue;
604
+ }
605
+ const nodeKey = relatedAtomId + '|' + normalizeGraphComparableTitle(title);
606
+ if (seenNodeKeys.has(nodeKey)) {
607
+ continue;
608
+ }
609
+ seenNodeKeys.add(nodeKey);
610
+ const metrics = await resolveNodeMetrics(opsStore, relatedAtomId, metricsCache);
611
+ const confidence = Number(Number(edge.confidence || 0).toFixed(4));
612
+ const intentAligned = scoringPolicy.intentAlignedRelationKinds.size <= 0
613
+ || Boolean(edge.relationKind && scoringPolicy.intentAlignedRelationKinds.has(edge.relationKind));
614
+ candidates.push({
615
+ score: scoreGraphWindowCandidate(edge, title, scoringPolicy),
616
+ confidence,
617
+ intentAligned,
618
+ node: {
619
+ atomId: relatedAtomId,
620
+ title,
621
+ relationKind: edge.relationKind,
622
+ confidence,
623
+ inDegree: metrics?.inDegree,
624
+ outDegree: metrics?.outDegree,
625
+ centrality: metrics?.centrality,
626
+ },
627
+ });
628
+ }
629
+ const alignedCandidateCount = candidates.filter((candidate) => candidate.intentAligned).length;
630
+ const misalignedCandidateCount = candidates.length - alignedCandidateCount;
631
+ const candidatePool = alignedCandidateCount > 0
632
+ ? candidates.filter((candidate) => candidate.intentAligned)
633
+ : candidates;
634
+ return {
635
+ nodes: candidatePool
636
+ .sort((left, right) => (right.score - left.score
637
+ || right.confidence - left.confidence
638
+ || left.node.title.localeCompare(right.node.title)))
639
+ .slice(0, limit)
640
+ .map((candidate) => candidate.node),
641
+ alignedCandidateCount,
642
+ misalignedCandidateCount,
643
+ usedMisalignedFallback: alignedCandidateCount <= 0 && misalignedCandidateCount > 0,
644
+ };
645
+ }
646
+ async function buildConnectionPaths(opsStore, anchorPoint, supportPoints, intent, budget, titleCache, missingSourceAtomIds) {
647
+ const anchorAtomId = String(anchorPoint.atomId || '').trim();
648
+ if (!anchorAtomId) {
649
+ return [];
650
+ }
651
+ const connectionPaths = [];
652
+ for (const point of supportPoints.slice(0, budget.maxConnectionPaths)) {
653
+ let pathResult = null;
654
+ let resolvedSourceAtomId = '';
655
+ for (const sourceAtomId of pointAtomIds(point)) {
656
+ if (!sourceAtomId || sourceAtomId === anchorAtomId) {
657
+ continue;
658
+ }
659
+ const nextPathResult = await opsStore.findPath(sourceAtomId, anchorAtomId, budget.maxPathDepth);
660
+ if (nextPathResult.found && Array.isArray(nextPathResult.path) && nextPathResult.path.length > 1) {
661
+ pathResult = nextPathResult;
662
+ resolvedSourceAtomId = sourceAtomId;
663
+ break;
664
+ }
665
+ }
666
+ if (!pathResult || !resolvedSourceAtomId) {
667
+ missingSourceAtomIds.add(String(point.atomId || '').trim());
668
+ continue;
669
+ }
670
+ const pathAtomIds = pathResult.path
671
+ .map((atomId) => String(atomId || '').trim())
672
+ .filter(Boolean);
673
+ const pathTitles = [];
674
+ for (const atomId of pathAtomIds) {
675
+ pathTitles.push(await resolveNodeTitle(opsStore, atomId, titleCache));
676
+ }
677
+ connectionPaths.push({
678
+ sourceAtomId: resolvedSourceAtomId,
679
+ sourceTitle: normalizeWhitespace(String(point.title || '').trim()) || resolvedSourceAtomId,
680
+ targetAtomId: anchorAtomId,
681
+ targetTitle: normalizeWhitespace(String(anchorPoint.title || '').trim()) || anchorAtomId,
682
+ pathAtomIds,
683
+ pathTitles,
684
+ pathEdges: Array.isArray(pathResult.edges)
685
+ ? pathResult.edges.map((edge) => ({
686
+ fromAtomId: String(edge.from || '').trim(),
687
+ toAtomId: String(edge.to || '').trim(),
688
+ relationKind: edge.relation,
689
+ }))
690
+ : [],
691
+ length: Math.max(0, Math.floor(Number(pathResult.length || 0))),
692
+ });
693
+ }
694
+ return rankConnectionPathsForIntent(connectionPaths, intent);
695
+ }
696
+ function scoreConnectionPathForIntent(path, intent) {
697
+ const scoringPolicy = resolveGraphWindowScoringPolicy(intent);
698
+ const relationScores = (Array.isArray(path.pathEdges) ? path.pathEdges : [])
699
+ .map((edge) => (edge.relationKind
700
+ ? scoringPolicy.relationPriorities[edge.relationKind] ?? scoringPolicy.defaultRelationPriority
701
+ : scoringPolicy.defaultRelationPriority));
702
+ const bestRelationScore = relationScores.length > 0 ? Math.max(...relationScores) : 0;
703
+ const averageRelationScore = relationScores.length > 0
704
+ ? relationScores.reduce((sum, score) => sum + score, 0) / relationScores.length
705
+ : 0;
706
+ const shorterPathScore = Math.max(0, 8 - Math.max(0, Number(path.length || 0))) * 0.01;
707
+ return Number((bestRelationScore + averageRelationScore * 0.001 + shorterPathScore).toFixed(6));
708
+ }
709
+ function rankConnectionPathsForIntent(connectionPaths, intent) {
710
+ return connectionPaths
711
+ .map((path, index) => ({
712
+ path,
713
+ index,
714
+ score: scoreConnectionPathForIntent(path, intent),
715
+ }))
716
+ .sort((left, right) => (right.score - left.score
717
+ || left.path.length - right.path.length
718
+ || left.index - right.index))
719
+ .map((entry) => entry.path);
720
+ }
721
+ function buildEvidenceSourceRefs(points) {
722
+ const refs = new Set();
723
+ const appendCitation = (citation) => {
724
+ const sourcePath = normalizeWhitespace(String(citation?.sourcePath || '').trim());
725
+ if (!sourcePath) {
726
+ return;
727
+ }
728
+ const startLine = Number(citation?.startLine);
729
+ refs.add(Number.isFinite(startLine) && startLine > 0 ? `${sourcePath}:${startLine}` : sourcePath);
730
+ };
731
+ points.forEach((point) => {
732
+ appendCitation(point.citation || null);
733
+ if (Array.isArray(point.citations)) {
734
+ point.citations.forEach((citation) => appendCitation(citation));
735
+ }
736
+ if (Array.isArray(point.matchedSpans)) {
737
+ point.matchedSpans.forEach((span) => {
738
+ const sourcePath = normalizeWhitespace(String(span.sourcePath || '').trim());
739
+ if (!sourcePath) {
740
+ return;
741
+ }
742
+ const startLine = Number(span.startLine);
743
+ refs.add(Number.isFinite(startLine) && startLine > 0 ? `${sourcePath}:${startLine}` : sourcePath);
744
+ });
745
+ }
746
+ });
747
+ return Array.from(refs.values()).slice(0, 6);
748
+ }
749
+ function buildDiagnostics(graphOpsAvailable, anchorReason, candidateCount, supportNodeCount, budget, missingConnectionPathSourceAtomIds, missingPredecessorAtomIds, missingSuccessorAtomIds, predecessorWindowResult, successorWindowResult) {
750
+ return {
751
+ graphOpsAvailable,
752
+ usedFallback: !graphOpsAvailable,
753
+ selectedAnchorReason: anchorReason,
754
+ candidateCount,
755
+ supportNodeCount,
756
+ supportNodeLimit: budget.maxSupportNodes,
757
+ pathDepthLimit: budget.maxPathDepth,
758
+ intentAlignedPredecessorCandidateCount: predecessorWindowResult?.alignedCandidateCount ?? 0,
759
+ intentAlignedSuccessorCandidateCount: successorWindowResult?.alignedCandidateCount ?? 0,
760
+ intentMisalignedPredecessorCandidateCount: predecessorWindowResult?.misalignedCandidateCount ?? 0,
761
+ intentMisalignedSuccessorCandidateCount: successorWindowResult?.misalignedCandidateCount ?? 0,
762
+ usedIntentMisalignedPredecessorFallback: predecessorWindowResult?.usedMisalignedFallback === true,
763
+ usedIntentMisalignedSuccessorFallback: successorWindowResult?.usedMisalignedFallback === true,
764
+ missingConnectionPathSourceAtomIds: Array.from(missingConnectionPathSourceAtomIds.values()),
765
+ missingPredecessorAtomIds: Array.from(missingPredecessorAtomIds.values()),
766
+ missingSuccessorAtomIds: Array.from(missingSuccessorAtomIds.values()),
767
+ };
768
+ }
769
+ async function assembleAgentConversationGraphContext(params) {
770
+ const knowledgePoints = Array.isArray(params.knowledgePoints)
771
+ ? params.knowledgePoints.filter((point) => Boolean(point && typeof point === 'object'))
772
+ : [];
773
+ if (knowledgePoints.length <= 0) {
774
+ return {
775
+ knowledgePoints: [],
776
+ graphContext: null,
777
+ };
778
+ }
779
+ const intent = classifyConversationIntent(params.message);
780
+ const budget = resolveBudget(intent, params.budget);
781
+ const graphWindowScoringPolicy = resolveGraphWindowScoringPolicy(intent);
782
+ const anchorSelection = selectAnchorPoint(params.message, knowledgePoints);
783
+ const anchorPoint = anchorSelection.point;
784
+ const supportPoints = rankSupportPoints(params.message, intent, anchorPoint, knowledgePoints, budget);
785
+ const supportAtomIdSet = new Set(supportPoints.map((point) => String(point.atomId || '').trim()).filter(Boolean));
786
+ const orderedKnowledgePoints = [
787
+ anchorPoint,
788
+ ...knowledgePoints.filter((point) => supportAtomIdSet.has(String(point.atomId || '').trim()) && point !== anchorPoint),
789
+ ...knowledgePoints.filter((point) => point !== anchorPoint && !supportAtomIdSet.has(String(point.atomId || '').trim())),
790
+ ];
791
+ const contextPoints = orderedKnowledgePoints.slice(0, 1 + budget.maxSupportNodes);
792
+ const baseGraphContext = buildBaseGraphContext(contextPoints);
793
+ if (!baseGraphContext) {
794
+ return {
795
+ knowledgePoints: orderedKnowledgePoints,
796
+ graphContext: null,
797
+ };
798
+ }
799
+ const evidenceSourceRefs = buildEvidenceSourceRefs(contextPoints);
800
+ if (!params.store || !(0, store_1.isOpsAdapter)(params.store)) {
801
+ return {
802
+ knowledgePoints: orderedKnowledgePoints,
803
+ graphContext: {
804
+ ...baseGraphContext,
805
+ evidenceSourceRefs,
806
+ diagnostics: buildDiagnostics(false, anchorSelection.reason, knowledgePoints.length, supportPoints.length, budget, new Set(), new Set(), new Set()),
807
+ },
808
+ };
809
+ }
810
+ const opsStore = params.store;
811
+ const titleCache = new Map();
812
+ const metricsCache = new Map();
813
+ orderedKnowledgePoints.forEach((point) => {
814
+ pointAtomIds(point).forEach((atomId) => {
815
+ titleCache.set(atomId, normalizeWhitespace(String(point.title || '').trim()) || atomId);
816
+ });
817
+ });
818
+ const missingConnectionPathSourceAtomIds = new Set();
819
+ const missingPredecessorAtomIds = new Set();
820
+ const missingSuccessorAtomIds = new Set();
821
+ const connectionPaths = await buildConnectionPaths(opsStore, anchorPoint, supportPoints, intent, budget, titleCache, missingConnectionPathSourceAtomIds);
822
+ const anchorExclusion = buildAnchorNodeExclusion(anchorPoint, baseGraphContext);
823
+ const capabilities = opsStore.getCapabilities();
824
+ const useCompleteNeighborhoodDegree = capabilities.serverSideQuery !== true;
825
+ const predecessorEdgeLimit = Math.max(budget.maxPredecessors * 8, 24);
826
+ const successorEdgeLimit = Math.max(budget.maxSuccessors * 8, 24);
827
+ const anchorAtomIds = pointAtomIds(anchorPoint);
828
+ const predecessorEdges = useCompleteNeighborhoodDegree || budget.maxPredecessors > 0
829
+ ? await queryAdjacentEdgesForAnchorAtoms(opsStore, anchorAtomIds, 'predecessor', useCompleteNeighborhoodDegree ? undefined : predecessorEdgeLimit)
830
+ : [];
831
+ const successorEdges = useCompleteNeighborhoodDegree || budget.maxSuccessors > 0
832
+ ? await queryAdjacentEdgesForAnchorAtoms(opsStore, anchorAtomIds, 'successor', useCompleteNeighborhoodDegree ? undefined : successorEdgeLimit)
833
+ : [];
834
+ const emptyPredecessorWindowResult = {
835
+ nodes: [],
836
+ alignedCandidateCount: 0,
837
+ misalignedCandidateCount: 0,
838
+ usedMisalignedFallback: false,
839
+ };
840
+ const emptySuccessorWindowResult = {
841
+ nodes: [],
842
+ alignedCandidateCount: 0,
843
+ misalignedCandidateCount: 0,
844
+ usedMisalignedFallback: false,
845
+ };
846
+ const predecessorWindowResult = budget.maxPredecessors > 0
847
+ ? await buildWindowNodes(opsStore, predecessorEdges, 'predecessor', titleCache, metricsCache, anchorExclusion, budget.maxPredecessors, missingPredecessorAtomIds, graphWindowScoringPolicy)
848
+ : emptyPredecessorWindowResult;
849
+ const successorWindowResult = budget.maxSuccessors > 0
850
+ ? await buildWindowNodes(opsStore, successorEdges, 'successor', titleCache, metricsCache, anchorExclusion, budget.maxSuccessors, missingSuccessorAtomIds, graphWindowScoringPolicy)
851
+ : emptySuccessorWindowResult;
852
+ const predecessorWindow = predecessorWindowResult.nodes;
853
+ const successorWindow = successorWindowResult.nodes;
854
+ const anchorGraphProfile = await buildAnchorGraphProfile(opsStore, anchorPoint, titleCache, metricsCache, predecessorEdges, successorEdges, anchorExclusion, useCompleteNeighborhoodDegree);
855
+ return {
856
+ knowledgePoints: orderedKnowledgePoints,
857
+ graphContext: {
858
+ ...baseGraphContext,
859
+ anchorGraphProfile,
860
+ connectionPaths,
861
+ predecessorWindow,
862
+ successorWindow,
863
+ evidenceSourceRefs,
864
+ diagnostics: buildDiagnostics(true, anchorSelection.reason, knowledgePoints.length, supportPoints.length, budget, missingConnectionPathSourceAtomIds, missingPredecessorAtomIds, missingSuccessorAtomIds, predecessorWindowResult, successorWindowResult),
865
+ },
866
+ };
867
+ }
868
+ function buildAgentConversationGraphContextFromKnowledgePoints(knowledgePoints) {
869
+ return buildBaseGraphContext(knowledgePoints);
870
+ }