noteconnection 1.7.0 → 1.9.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 (328) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +260 -1626
  3. package/dist/src/agent_workspace.contract.parity.test.js +159 -0
  4. package/dist/src/agent_workspace.frontend.test.js +10388 -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/android.knowledgebase.picker.contract.test.js +102 -0
  8. package/dist/src/appimage.patchelf.test.js +113 -0
  9. package/dist/src/appimage.portability.test.js +156 -0
  10. package/dist/src/backend/FileLoader.js +18 -4
  11. package/dist/src/backend/FileLoader.test.js +125 -0
  12. package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
  13. package/dist/src/backend/GraphBuilder.js +66 -18
  14. package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
  15. package/dist/src/backend/GraphMetrics.js +0 -3
  16. package/dist/src/backend/ResourceIdentity.js +155 -0
  17. package/dist/src/backend/ResourceIdentity.test.js +113 -0
  18. package/dist/src/backend/algorithms/CycleDetection.js +0 -1
  19. package/dist/src/backend/algorithms/HybridEngine.js +0 -3
  20. package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
  21. package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
  22. package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
  23. package/dist/src/backend/build_dag.js +0 -1
  24. package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
  25. package/dist/src/capacitor.runtime.contract.test.js +11 -2
  26. package/dist/src/copy.assets.contract.test.js +130 -0
  27. package/dist/src/core/Graph.js +138 -42
  28. package/dist/src/core/Graph.test.js +74 -0
  29. package/dist/src/core/NoteConnection.js +2 -2
  30. package/dist/src/core/PathBridge.js +293 -5
  31. package/dist/src/core/ResourceReference.js +17 -0
  32. package/dist/src/desktop.godot.pack.test.js +142 -0
  33. package/dist/src/desktop.runtime.graph.test.js +66 -0
  34. package/dist/src/export/WorkspaceExportBundle.js +1174 -0
  35. package/dist/src/export/WorkspaceExportBundle.test.js +1541 -0
  36. package/dist/src/export/index.js +18 -0
  37. package/dist/src/export/types.js +2 -0
  38. package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
  39. package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
  40. package/dist/src/foundation.release.evidence.contract.test.js +524 -0
  41. package/dist/src/foundation.sqlite.runtime.contract.test.js +88 -0
  42. package/dist/src/frontend/README.md +260 -1626
  43. package/dist/src/frontend/agent_workspace.js +6011 -0
  44. package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
  45. package/dist/src/frontend/analysis.js +31 -4
  46. package/dist/src/frontend/app.js +2178 -62
  47. package/dist/src/frontend/focus_mode_interactions.js +33 -0
  48. package/dist/src/frontend/godot_future_path_renderer.js +540 -0
  49. package/dist/src/frontend/godot_tree_interactions.js +178 -0
  50. package/dist/src/frontend/graph_state.mjs +105 -0
  51. package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
  52. package/dist/src/frontend/i18n.mjs +186 -0
  53. package/dist/src/frontend/index.html +672 -268
  54. package/dist/src/frontend/knowledge_projection_contract.js +230 -0
  55. package/dist/src/frontend/knowledge_projection_store.js +214 -0
  56. package/dist/src/frontend/layout_gpu.js +12 -4
  57. package/dist/src/frontend/libs/path_core.js +158 -42
  58. package/dist/src/frontend/locales/en.json +753 -5
  59. package/dist/src/frontend/locales/zh.json +753 -5
  60. package/dist/src/frontend/main.mjs +60 -0
  61. package/dist/src/frontend/markdown_runtime.js +827 -0
  62. package/dist/src/frontend/mobile_budget_runtime.js +33 -0
  63. package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
  64. package/dist/src/frontend/mobile_identity_contract.js +221 -0
  65. package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
  66. package/dist/src/frontend/notemd.css +49 -0
  67. package/dist/src/frontend/notemd.html +7 -1
  68. package/dist/src/frontend/notemd.js +64 -0
  69. package/dist/src/frontend/path.html +113 -2
  70. package/dist/src/frontend/path_app.js +2108 -130
  71. package/dist/src/frontend/path_layout.mjs +143 -0
  72. package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
  73. package/dist/src/frontend/path_modules_bridge.js +486 -0
  74. package/dist/src/frontend/path_state.mjs +118 -0
  75. package/dist/src/frontend/path_styles.css +146 -0
  76. package/dist/src/frontend/path_worker_bridge.mjs +85 -0
  77. package/dist/src/frontend/reader.js +393 -33
  78. package/dist/src/frontend/runtime_bridge.js +67 -54
  79. package/dist/src/frontend/runtime_bridge.mjs +279 -0
  80. package/dist/src/frontend/settings.js +130 -12
  81. package/dist/src/frontend/simulationWorker.js +1 -1
  82. package/dist/src/frontend/source_manager.js +297 -22
  83. package/dist/src/frontend/storage_provider.js +661 -71
  84. package/dist/src/frontend/styles.css +2814 -72
  85. package/dist/src/frontend/workbench_state.mjs +101 -0
  86. package/dist/src/frontend/workspace_panes.js +10272 -0
  87. package/dist/src/frontend.locale.contract.test.js +62 -0
  88. package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
  89. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  90. package/dist/src/graph.answer.operator.contract.test.js +72 -0
  91. package/dist/src/index.js +6 -2
  92. package/dist/src/index.knowledge-root.test.js +118 -0
  93. package/dist/src/indexing/IndexLifecycle.js +217 -0
  94. package/dist/src/indexing/IndexLifecycle.test.js +73 -0
  95. package/dist/src/indexing/SegmentBuilder.js +64 -0
  96. package/dist/src/indexing/UnitBuilder.js +48 -0
  97. package/dist/src/indexing/types.js +2 -0
  98. package/dist/src/knowledge.api.contract.test.js +172 -0
  99. package/dist/src/knowledge_projection.contract.test.js +134 -0
  100. package/dist/src/knowledge_projection_store.test.js +200 -0
  101. package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
  102. package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
  103. package/dist/src/learning/KnowledgeLearningPlatform.js +11181 -0
  104. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +745 -0
  105. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  106. package/dist/src/learning/KnowledgeLearningPlatform.test.js +3208 -0
  107. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2998 -0
  108. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3936 -0
  109. package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
  110. package/dist/src/learning/PublicAnswerContract.test.js +85 -0
  111. package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
  112. package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
  113. package/dist/src/learning/agentConversationExecution.js +211 -0
  114. package/dist/src/learning/agentConversationExecution.test.js +209 -0
  115. package/dist/src/learning/agentConversationSerialization.js +266 -0
  116. package/dist/src/learning/agentConversationSerialization.test.js +165 -0
  117. package/dist/src/learning/agentResponseBudget.js +179 -0
  118. package/dist/src/learning/agentResponseBudget.test.js +97 -0
  119. package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
  120. package/dist/src/learning/answerReleaseReview.js +5260 -0
  121. package/dist/src/learning/answerReleaseReview.test.js +4174 -0
  122. package/dist/src/learning/answerTaskPlan.js +473 -0
  123. package/dist/src/learning/api.js +2 -0
  124. package/dist/src/learning/conversationComposer.js +1721 -0
  125. package/dist/src/learning/conversationComposer.test.js +3220 -0
  126. package/dist/src/learning/domains/ConversationManager.js +53 -0
  127. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  128. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  129. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  130. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  131. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  132. package/dist/src/learning/domains/TutorRouter.js +313 -0
  133. package/dist/src/learning/domains/index.js +33 -0
  134. package/dist/src/learning/domains/types.js +7 -0
  135. package/dist/src/learning/errors.js +29 -0
  136. package/dist/src/learning/evidenceContextAssembler.js +1368 -0
  137. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  138. package/dist/src/learning/graphAnswerCoverage.js +37 -0
  139. package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
  140. package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
  141. package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
  142. package/dist/src/learning/graphAnswerFacts.js +94 -0
  143. package/dist/src/learning/graphAnswerFacts.test.js +39 -0
  144. package/dist/src/learning/graphAnswerPlan.js +908 -0
  145. package/dist/src/learning/graphAnswerPlan.test.js +907 -0
  146. package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
  147. package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
  148. package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
  149. package/dist/src/learning/graphClaimMatcher.js +104 -0
  150. package/dist/src/learning/graphConditionedContext.js +109 -0
  151. package/dist/src/learning/graphConditionedContext.test.js +99 -0
  152. package/dist/src/learning/graphContextAssembler.js +860 -0
  153. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  154. package/dist/src/learning/graphExpansionPolicy.js +22 -0
  155. package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
  156. package/dist/src/learning/identityMetadata.test.js +26 -0
  157. package/dist/src/learning/index.js +29 -0
  158. package/dist/src/learning/mobileAnswerProjection.js +104 -0
  159. package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
  160. package/dist/src/learning/queryBackend.js +1922 -0
  161. package/dist/src/learning/queryBackend.test.js +976 -0
  162. package/dist/src/learning/ragContextPack.js +356 -0
  163. package/dist/src/learning/ragContextPack.test.js +311 -0
  164. package/dist/src/learning/ragEvidenceQuality.js +96 -0
  165. package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
  166. package/dist/src/learning/ragPublicText.js +101 -0
  167. package/dist/src/learning/ragPublicText.test.js +57 -0
  168. package/dist/src/learning/ragSufficiencyJudge.js +171 -0
  169. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  170. package/dist/src/learning/ragSufficiencyProviderJudge.js +217 -0
  171. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +207 -0
  172. package/dist/src/learning/requestNormalization.js +304 -0
  173. package/dist/src/learning/requestNormalization.test.js +101 -0
  174. package/dist/src/learning/runtimeCapability.js +4677 -0
  175. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  176. package/dist/src/learning/store.js +1344 -0
  177. package/dist/src/learning/store.test.js +1425 -0
  178. package/dist/src/learning/tutorAdapter.js +2 -0
  179. package/dist/src/learning/types.js +2 -0
  180. package/dist/src/learning/vectorAccelerationAdapter.js +958 -0
  181. package/dist/src/learning/vectorAccelerationAdapter.test.js +412 -0
  182. package/dist/src/learning/workspaceHydration.js +168 -0
  183. package/dist/src/learning/workspaceHydration.test.js +30 -0
  184. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  185. package/dist/src/license.policy.contract.test.js +71 -0
  186. package/dist/src/markdown/MarkdownGateway.js +4 -0
  187. package/dist/src/markdown/MarkdownGateway.test.js +59 -0
  188. package/dist/src/memory/MemoryGovernance.js +74 -0
  189. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  190. package/dist/src/memory/types.js +2 -0
  191. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  192. package/dist/src/middleware/SseResponseWriter.js +122 -0
  193. package/dist/src/middleware/SseResponseWriter.test.js +54 -0
  194. package/dist/src/middleware/auth.js +38 -0
  195. package/dist/src/middleware/auth.test.js +33 -0
  196. package/dist/src/middleware/body-parser.js +45 -0
  197. package/dist/src/middleware/cors.js +44 -0
  198. package/dist/src/middleware/index.js +22 -0
  199. package/dist/src/middleware/request-trace.js +96 -0
  200. package/dist/src/mobile.android.evidence.contract.test.js +146 -0
  201. package/dist/src/mobile.android.signing.contract.test.js +66 -0
  202. package/dist/src/mobile.artifact.contract.test.js +144 -0
  203. package/dist/src/mobile.budget.contract.test.js +90 -0
  204. package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
  205. package/dist/src/mobile.graph.parity.contract.test.js +207 -0
  206. package/dist/src/mobile.identity.contract.test.js +66 -0
  207. package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
  208. package/dist/src/mobile.identity.corpus.test.js +40 -0
  209. package/dist/src/mobile.native.recovery.contract.test.js +64 -0
  210. package/dist/src/mobile.pipeline.test.js +11 -1
  211. package/dist/src/mobile.profile.contract.test.js +225 -0
  212. package/dist/src/mobile.semantic.parity.test.js +92 -0
  213. package/dist/src/mobile_exact_analyzer.test.js +113 -0
  214. package/dist/src/notemd/AppConfigToml.js +6 -4
  215. package/dist/src/notemd/ContentGenerator.js +4 -9
  216. package/dist/src/notemd/FileProcessor.js +4 -9
  217. package/dist/src/notemd/MermaidProcessor.js +400 -50
  218. package/dist/src/notemd/NotemdService.js +506 -26
  219. package/dist/src/notemd/PromptManager.js +15 -0
  220. package/dist/src/notemd/Translator.js +4 -9
  221. package/dist/src/notemd/cli/commands.js +357 -0
  222. package/dist/src/notemd/cli/dispatcher.js +225 -0
  223. package/dist/src/notemd/cli/index.js +169 -0
  224. package/dist/src/notemd/cli/parser.js +68 -0
  225. package/dist/src/notemd/cli/types.js +2 -0
  226. package/dist/src/notemd/constants.js +43 -0
  227. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  228. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  229. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  230. package/dist/src/notemd/diagram/intent.js +95 -0
  231. package/dist/src/notemd/diagram/planner.js +71 -0
  232. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  233. package/dist/src/notemd/diagram/types.js +18 -0
  234. package/dist/src/notemd/index.js +26 -0
  235. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  236. package/dist/src/notemd/operations/cliContracts.js +17 -0
  237. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  238. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  239. package/dist/src/notemd/operations/registry.js +991 -0
  240. package/dist/src/notemd/operations/types.js +2 -0
  241. package/dist/src/notemd/progressReporter.js +9 -0
  242. package/dist/src/notemd/providerDiagnostics.js +220 -0
  243. package/dist/src/notemd/providerProfiles.js +42 -0
  244. package/dist/src/notemd/providerTemplates.js +231 -0
  245. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  246. package/dist/src/notemd/search/SearchManager.js +13 -0
  247. package/dist/src/notemd/search/SearchProvider.js +2 -0
  248. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  249. package/dist/src/notemd.agent.manifest.test.js +85 -0
  250. package/dist/src/notemd.api.contract.test.js +14 -1
  251. package/dist/src/notemd.app_config_toml.test.js +2 -1
  252. package/dist/src/notemd.batch.workflow.test.js +117 -0
  253. package/dist/src/notemd.cli.e2e.test.js +136 -0
  254. package/dist/src/notemd.core.test.js +51 -0
  255. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  256. package/dist/src/notemd.providerTemplates.test.js +34 -0
  257. package/dist/src/notemd.server.integration.test.js +262 -39
  258. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  259. package/dist/src/pathbridge.handshake.contract.test.js +45 -2
  260. package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
  261. package/dist/src/pathcore.bundle.contract.test.js +8 -0
  262. package/dist/src/pathmode.background.contract.test.js +69 -0
  263. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  264. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  265. package/dist/src/platform/ExportProfile.js +86 -0
  266. package/dist/src/platform/MobileBudget.js +11 -0
  267. package/dist/src/platform/PlatformCapabilities.js +59 -0
  268. package/dist/src/platform/PlatformCapabilities.test.js +36 -0
  269. package/dist/src/platform/RenderMaterializer.js +33 -0
  270. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  271. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  272. package/dist/src/reader_renderer.js +404 -3
  273. package/dist/src/reader_renderer.test.js +87 -0
  274. package/dist/src/release.godot.mirror.contract.test.js +97 -0
  275. package/dist/src/resources/ResourceRegistry.js +263 -0
  276. package/dist/src/resources/ResourceRegistry.test.js +97 -0
  277. package/dist/src/resources/types.js +2 -0
  278. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  279. package/dist/src/routes/index.js +19 -0
  280. package/dist/src/routes/knowledge.js +981 -0
  281. package/dist/src/routes/knowledgePayload.js +185 -0
  282. package/dist/src/routes/knowledgePayload.test.js +53 -0
  283. package/dist/src/routes/markdown.js +295 -0
  284. package/dist/src/routes/notemd.js +1321 -0
  285. package/dist/src/routes/registry.contract.test.js +133 -0
  286. package/dist/src/routes/registry.shadow.contract.test.js +22 -0
  287. package/dist/src/routes/render.js +248 -0
  288. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  289. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  290. package/dist/src/routes/settings.js +6 -0
  291. package/dist/src/routes/staticFiles.js +94 -0
  292. package/dist/src/routes/types.js +2 -0
  293. package/dist/src/runtime.capabilities.test.js +18 -2
  294. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  295. package/dist/src/server.js +10342 -1325
  296. package/dist/src/server.migration.test.js +584 -10
  297. package/dist/src/server.port.fallback.contract.test.js +63 -0
  298. package/dist/src/session/SessionStateStore.js +81 -0
  299. package/dist/src/session/SessionStateStore.test.js +58 -0
  300. package/dist/src/session/types.js +2 -0
  301. package/dist/src/settings.runtime.contract.test.js +50 -0
  302. package/dist/src/shared/types.contract.test.js +107 -0
  303. package/dist/src/shared/types.js +22 -0
  304. package/dist/src/sidecar.build.lock.contract.test.js +201 -0
  305. package/dist/src/sidecar.freshness.contract.test.js +184 -0
  306. package/dist/src/sidecar.replacement.boundary.contract.test.js +131 -0
  307. package/dist/src/sidecar.supply.readiness.contract.test.js +155 -0
  308. package/dist/src/simulation.worker.offline.test.js +104 -0
  309. package/dist/src/source_manager.loadflow.test.js +46 -0
  310. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  311. package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
  312. package/dist/src/storage.provider.contract.test.js +152 -1
  313. package/dist/src/tauri.frontend.build.contract.test.js +68 -0
  314. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  315. package/dist/src/tauri.window.evidence.execution.test.js +68 -0
  316. package/dist/src/utils/RuntimePaths.js +10 -13
  317. package/dist/src/utils/RuntimePaths.test.js +27 -0
  318. package/dist/src/utils/platform.js +153 -0
  319. package/dist/src/windows.installer.build.test.js +109 -0
  320. package/dist/src/windows.installer.payload.test.js +76 -0
  321. package/dist/src/windows.installer.runtime.test.js +69 -0
  322. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  323. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  324. package/dist/src/workflows/types.js +2 -0
  325. package/dist/src/workspace/WorkspaceRegistry.js +135 -0
  326. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  327. package/dist/src/workspace/types.js +2 -0
  328. package/package.json +80 -15
@@ -0,0 +1,908 @@
1
+ "use strict";
2
+ /// <reference lib="es2022.intl" />
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.selectQueryConnectedKnowledgePoints = selectQueryConnectedKnowledgePoints;
5
+ exports.buildGraphAnswerPlan = buildGraphAnswerPlan;
6
+ const ragPublicText_1 = require("./ragPublicText");
7
+ const graphClaimMatcher_1 = require("./graphClaimMatcher");
8
+ const graphAnswerQualityPolicy_1 = require("./graphAnswerQualityPolicy");
9
+ const ragEvidenceQuality_1 = require("./ragEvidenceQuality");
10
+ const answerTaskPlan_1 = require("./answerTaskPlan");
11
+ function normalize(value) {
12
+ return String(value || '').replace(/\s+/g, ' ').trim();
13
+ }
14
+ function isCompoundLearningDefinitionQuery(message) {
15
+ const normalized = normalize(message).toLowerCase();
16
+ return classifyIntent(normalized) === 'definition'
17
+ && (/\b(?:learn|learning|study|knowledge\s+points?)\b/u.test(normalized)
18
+ || /学习|知识点|学哪些|通过哪些/u.test(normalized));
19
+ }
20
+ function hasBalancedMathDelimiters(value) {
21
+ const source = String(value || '');
22
+ const displayCount = (source.match(/(?<!\\)\$\$/gu) || []).length;
23
+ if (displayCount % 2 !== 0) {
24
+ return false;
25
+ }
26
+ const inlineSource = source.replace(/(?<!\\)\$\$/gu, '');
27
+ return ((inlineSource.match(/(?<!\\)\$/gu) || []).length % 2) === 0;
28
+ }
29
+ function normalizedTopicWords(value) {
30
+ return normalize(value).toLowerCase().replace(/\b([a-z]{4,})s\b/gu, (word, base) => (/(?:ss|us|is)$/u.test(word) ? word : base));
31
+ }
32
+ function containsNamedChineseSubject(source, subject) {
33
+ let offset = source.indexOf(subject);
34
+ while (offset >= 0) {
35
+ if (offset === 0 || !/[无非不未]/u.test(source[offset - 1]))
36
+ return true;
37
+ offset = source.indexOf(subject, offset + 1);
38
+ }
39
+ return false;
40
+ }
41
+ function knowledgePointEstablishesSubject(point, subject) {
42
+ const topic = normalize(subject).replace(/^(?:a|an|the)\s+/iu, '').replace(/[.?!。?!]+$/gu, '').trim();
43
+ if (!topic)
44
+ return false;
45
+ const compact = (value) => normalizedTopicWords(value).replace(/[^\p{L}\p{N}]+/gu, '');
46
+ const aliases = [point.title, ...String(point.title || '').split(/[()()]/u)];
47
+ if (aliases.some(alias => compact(alias) === compact(topic)))
48
+ return true;
49
+ const source = normalizedTopicWords([
50
+ point.title, point.summary, point.evidenceSnippet,
51
+ ...(point.matchedSpans || []).flatMap(span => [span.title, span.snippet]),
52
+ ].filter(Boolean).join('\n'));
53
+ const normalizedTopic = normalizedTopicWords(topic);
54
+ if (/\p{Script=Han}/u.test(normalizedTopic)) {
55
+ // Negated entity names (for example 无缓冲通道) do not establish the positive entity.
56
+ return containsNamedChineseSubject(source, normalizedTopic);
57
+ }
58
+ const subjectFeatures = (0, graphClaimMatcher_1.semanticFeatures)(normalizedTopic);
59
+ const sourceFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(source));
60
+ return subjectFeatures.length > 0 && subjectFeatures.every(feature => sourceFeatures.has(feature));
61
+ }
62
+ /** Source admission precedes RAG reads so citations, graph anchoring and composition share one decision. */
63
+ function selectQueryConnectedKnowledgePoints(message, knowledgePoints) {
64
+ const subjects = classifyIntent(message) === 'compare'
65
+ ? comparisonQueryBranches(message)
66
+ : comparisonQueryBranches((0, answerTaskPlan_1.extractDefinitionQuerySubject)(message));
67
+ const requestedSubjects = subjects.filter(Boolean);
68
+ if (requestedSubjects.length === 0)
69
+ return knowledgePoints;
70
+ const subjectMatches = requestedSubjects.map(subject => knowledgePoints.filter(point => knowledgePointEstablishesSubject(point, subject)));
71
+ // An unmatched phrase may describe a comparison aspect or graph chain. Partial
72
+ // recognition must not discard that branch's graph support before intent-aware assembly.
73
+ if (subjectMatches.some(matches => matches.length === 0))
74
+ return knowledgePoints;
75
+ const selected = new Set(subjectMatches.flat());
76
+ return knowledgePoints.filter(point => selected.has(point));
77
+ }
78
+ function definitionTitleMatchesSubject(title, subject) {
79
+ const normalizeTitle = (value) => normalize(value)
80
+ .replace(/\s*\((?:mermaid|code|diagram|formula)\s+block\)\s*$/iu, '')
81
+ .replace(/[^\p{L}\p{N}\s_-]+/gu, ' ')
82
+ .replace(/\s+/gu, '')
83
+ .toLowerCase();
84
+ const normalizedSubject = normalizeTitle(subject);
85
+ const normalizedTitle = normalizeTitle(title);
86
+ return Boolean(normalizedSubject && normalizedTitle && (normalizedTitle === normalizedSubject
87
+ || normalizedTitle.includes(normalizedSubject)));
88
+ }
89
+ function isDefinitionLearningSupportSpan(span, subject) {
90
+ const title = normalize(String(span.title || ''));
91
+ if (!title || /preamble|reference|参考文献|比较模型|comparison|mermaid|code|diagram/iu.test(title)) {
92
+ return false;
93
+ }
94
+ if (definitionTitleMatchesSubject(title, subject)) {
95
+ return true;
96
+ }
97
+ // A compound definition request may include a bounded learning bridge, but
98
+ // chapter titles such as applications, specifications, and comparisons are
99
+ // not definition evidence merely because their text contains a formula.
100
+ return /核心概念|数学|基础|key\s+concept|mathemat|prerequisite|前置|先修|learning\s+path|学习路径/iu.test(title);
101
+ }
102
+ function extractMathExpressions(value) {
103
+ const source = String(value || '');
104
+ const expressions = [];
105
+ for (const match of source.matchAll(/(?<!\\)\$\$([\s\S]*?)(?<!\\)\$\$/gu)) {
106
+ const expression = normalize(String(match[1] || '')).replace(/\s+/gu, '');
107
+ if (expression) {
108
+ expressions.push(expression);
109
+ }
110
+ }
111
+ const inlineSource = source.replace(/(?<!\\)\$\$([\s\S]*?)(?<!\\)\$\$/gu, '');
112
+ for (const match of inlineSource.matchAll(/(?<!\\)\$([^$\n]+?)(?<!\\)\$/gu)) {
113
+ const expression = normalize(String(match[1] || '')).replace(/\s+/gu, '');
114
+ if (expression) {
115
+ expressions.push(expression);
116
+ }
117
+ }
118
+ return Array.from(new Set(expressions));
119
+ }
120
+ function statementCarriesMathDelimiter(value) {
121
+ const normalized = normalize(value);
122
+ return /(?<!\\)\$\$[\s\S]+?(?<!\\)\$\$|(?<!\\)\$[^$\n]+?(?<!\\)\$/u.test(normalized);
123
+ }
124
+ function isCompoundDefinitionNoiseClause(value) {
125
+ const normalized = normalize(value);
126
+ if (!normalized) {
127
+ return true;
128
+ }
129
+ return /(?:常见用例|应用场景|关键技术规格|技术规格|性能指标|性能特征|比较数学模型|技术比较|流体容器技术比较|本节|本章|下表|参数\s*\(Parameter\)|preamble|reference|comparison|application|use\s+case|technical\s+specification|performance\s+characteristic|table|mermaid|diagram|code\s+block)/iu.test(normalized)
130
+ || /```|\|\s*[^|]+\s*\|/u.test(normalized);
131
+ }
132
+ function isDefinitionNoiseTitle(value, message) {
133
+ const normalized = normalize(value);
134
+ if (!normalized) {
135
+ return true;
136
+ }
137
+ const compactTitle = (title) => normalizedTopicWords(title).replace(/[^\p{L}\p{N}]+/gu, '');
138
+ const subject = compactTitle((0, answerTaskPlan_1.extractDefinitionQuerySubject)(message));
139
+ if (subject && [normalized, ...normalized.split(/[()()]/u)].some(title => compactTitle(title) === subject)) {
140
+ return false;
141
+ }
142
+ // Metadata words must be words: "table" must not discard an Immutable/Stable source.
143
+ return /\b(?:preambles?|references?|comparisons?|compare|use\s+case|application\s+scenario|technical\s+specification|performance\s+(?:metric|characteristic)|related\s+technology|mermaid|code\s+block|tables?)\b|参考文献|参考资料|比较|对比|常见用例|应用场景|关键技术规格|技术规格|性能指标|性能特征|统计度量|相关技术|表格/iu.test(normalized.replace(/[_-]+/gu, ' '));
144
+ }
145
+ function isDefinitionNoiseClaim(value) {
146
+ const normalized = normalize(value);
147
+ return !normalized || /^\d+(?:\.\d+)*[.、)]?$/u.test(normalized);
148
+ }
149
+ function isCompoundDefinitionMathClause(value) {
150
+ const normalized = normalize(value);
151
+ return statementCarriesMathDelimiter(normalized)
152
+ || /(?:径向分布|分布函数|数学|公式|方程|函数|描述|定义|radial\s+distribution|equation|formula|function|mathemat(?:ics|ical))/iu.test(normalized);
153
+ }
154
+ function selectCompoundDefinitionPublicClaimStatements(candidates) {
155
+ const eligibleCandidates = candidates
156
+ .filter((candidate) => !candidate.incompleteEnding)
157
+ .filter((candidate) => !isCompoundDefinitionNoiseClause(candidate.clause));
158
+ if (eligibleCandidates.length <= 0) {
159
+ return [];
160
+ }
161
+ // A compound definition has two bounded deliverables: the subject definition
162
+ // and a small mathematical/learning bridge. It must not inherit every clause
163
+ // from a full-document fragment merely because the subject is repeated in
164
+ // later application or comparison sections.
165
+ const selected = [];
166
+ const append = (candidate) => {
167
+ if (selected.length >= 4 || selected.includes(candidate)) {
168
+ return;
169
+ }
170
+ if (selected.some((entry) => normalize(entry.clause).toLowerCase() === normalize(candidate.clause).toLowerCase())) {
171
+ return;
172
+ }
173
+ selected.push(candidate);
174
+ };
175
+ const subjectCandidates = eligibleCandidates.filter((candidate) => candidate.anchorMatches > 0);
176
+ append(subjectCandidates[0] || eligibleCandidates[0]);
177
+ eligibleCandidates
178
+ .filter((candidate) => isCompoundDefinitionMathClause(candidate.clause))
179
+ .sort((left, right) => left.order - right.order)
180
+ .forEach(append);
181
+ return uniquePublicClaimStatements(selected.sort((left, right) => left.order - right.order));
182
+ }
183
+ function publicClaimAnchorFeatures(message, title) {
184
+ return new Set((0, graphClaimMatcher_1.semanticFeatures)(`${message} ${title || ''}`));
185
+ }
186
+ function comparisonQueryBranches(message) {
187
+ const normalizedMessage = normalize(message)
188
+ .toLowerCase()
189
+ .replace(/^(?:compare|contrast|difference\s+between)\s+/u, '')
190
+ .replace(/^(?:比较|对比)\s*/u, '');
191
+ return normalizedMessage
192
+ .split(/\s+(?:and|with|versus|vs\.?)\s+|\s*(?:与|和|跟)\s*/u)
193
+ .map((branch) => normalize(branch).replace(/^(?:a|an|the)\s+/iu, '').replace(/[.?!。?!]+$/gu, ''))
194
+ .filter(Boolean);
195
+ }
196
+ function comparisonIdentityMatchCount(text, requiredTerms) {
197
+ const normalized = normalize(text).toLowerCase();
198
+ const words = new Set(normalized.match(/[\p{L}\p{N}]+/gu) || []);
199
+ return Array.from(requiredTerms).filter(term => /\p{Script=Han}/u.test(term)
200
+ ? containsNamedChineseSubject(normalized, term)
201
+ : words.has(term)).length;
202
+ }
203
+ function comparisonBranchFeatures(message) {
204
+ const branches = comparisonQueryBranches(message)
205
+ .map((branch) => new Set((0, graphClaimMatcher_1.semanticFeatures)(branch)))
206
+ .filter((features) => features.size > 0);
207
+ if (branches.length !== 2) {
208
+ return [];
209
+ }
210
+ const sharedFeatures = new Set(Array.from(branches[0]).filter((feature) => branches[1].has(feature)));
211
+ return branches.map((features) => {
212
+ const branchSpecificFeatures = new Set(Array.from(features).filter((feature) => !sharedFeatures.has(feature)));
213
+ return branchSpecificFeatures.size > 0 ? branchSpecificFeatures : features;
214
+ });
215
+ }
216
+ function comparisonBranchIdentityTerms(message) {
217
+ const branches = comparisonQueryBranches(message)
218
+ .map((branch) => new Set((branch.match(/[\p{L}\p{N}]+/gu) || [])
219
+ .map((term) => term.toLowerCase())
220
+ .filter(Boolean)))
221
+ .filter((terms) => terms.size > 0);
222
+ if (branches.length !== 2) {
223
+ return [];
224
+ }
225
+ const sharedTerms = new Set(Array.from(branches[0]).filter((term) => branches[1].has(term)));
226
+ return branches.map((terms) => {
227
+ const branchSpecificTerms = new Set(Array.from(terms).filter((term) => !sharedTerms.has(term)));
228
+ return branchSpecificTerms.size > 0 ? branchSpecificTerms : terms;
229
+ });
230
+ }
231
+ function isTitleDependentComparisonClause(clause) {
232
+ return /^(?:(?:it|its|they|their|this|these|such|the former|the latter)\b|(?:其|它|该|这些|这种|前者|后者))/iu.test(normalize(clause));
233
+ }
234
+ function rankQualityPublicClaimStatements(value, title, message) {
235
+ const rawEvidence = String(value || '');
236
+ if (!normalize(rawEvidence)) {
237
+ return [];
238
+ }
239
+ const normalizedEvidence = (0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(rawEvidence);
240
+ const normalizedTitle = normalize(String(title || '').replace(/\s*\((?:mermaid|code|diagram)\s+block\)\s*$/iu, ''));
241
+ const titleMatchVariants = Array.from(new Set([
242
+ normalizedTitle,
243
+ normalizedTitle.replace(/^\d+(?:\.\d+)*[.、)]?\s*/u, ''),
244
+ ].filter(Boolean)));
245
+ const anchorFeatures = publicClaimAnchorFeatures(message, normalizedTitle);
246
+ const compareBranches = comparisonBranchFeatures(message);
247
+ const compareBranchIdentities = comparisonBranchIdentityTerms(message);
248
+ const segmentSource = classifyIntent(message) === 'definition'
249
+ && /(?:^|\r?\n)\s*#{1,6}\s+/u.test(rawEvidence)
250
+ ? rawEvidence
251
+ : normalizedEvidence;
252
+ const candidates = [
253
+ // Segment before whitespace normalization so Markdown headings and
254
+ // display-math blocks remain independent evidence units. Normalizing
255
+ // first would merge an entire document section into one claim.
256
+ ...(0, ragEvidenceQuality_1.segmentRagEvidenceClauses)(segmentSource),
257
+ ...(0, ragPublicText_1.extractPublicMermaidEvidenceLabels)(value),
258
+ ]
259
+ .map((clause) => (0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(clause))
260
+ .map((clause) => {
261
+ const matchingTitle = titleMatchVariants.find((variant) => (clause.toLowerCase().startsWith(`${variant.toLowerCase()} `)));
262
+ if (!matchingTitle) {
263
+ return clause;
264
+ }
265
+ const remainder = clause.slice(matchingTitle.length).trim();
266
+ const repeatsTitle = remainder.toLowerCase().startsWith(`${matchingTitle.toLowerCase()} `)
267
+ || remainder.toLowerCase().startsWith(matchingTitle.toLowerCase());
268
+ const titleWordCount = matchingTitle.split(/\s+/u).filter(Boolean).length;
269
+ const structuralHeading = /^\d+(?:\.\d+)*[.、)]?\s*/u.test(normalizedTitle)
270
+ || /[::]/u.test(normalizedTitle)
271
+ || (/[\u3400-\u9fff]/u.test(normalizedTitle) && normalizedTitle.length >= 8);
272
+ return structuralHeading
273
+ || repeatsTitle
274
+ || (titleWordCount > 1 && /^(?:a|an|the)\s+/iu.test(remainder))
275
+ ? remainder
276
+ : clause;
277
+ })
278
+ .filter((clause) => (clause
279
+ && !(0, ragPublicText_1.shouldRejectPublicEvidenceClause)(clause)
280
+ && !(0, ragPublicText_1.shouldRejectCompareProcedureEvidenceClause)(clause, message)))
281
+ .map((clause, order) => {
282
+ const clauseFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(clause));
283
+ const anchorMatches = Array.from(clauseFeatures)
284
+ .filter((feature) => anchorFeatures.has(feature))
285
+ .length;
286
+ const comparisonEvidenceText = isTitleDependentComparisonClause(clause) || anchorMatches > 0
287
+ ? `${normalizedTitle} ${clause}`
288
+ : clause;
289
+ const comparisonEvidenceFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(comparisonEvidenceText));
290
+ const comparisonBranchMatchCounts = (compareBranches.length === 2 || compareBranchIdentities.length === 2)
291
+ ? [0, 1].map((branchIndex) => Math.max(compareBranches[branchIndex]
292
+ ? Array.from(compareBranches[branchIndex])
293
+ .filter((feature) => comparisonEvidenceFeatures.has(feature))
294
+ .length
295
+ : 0, compareBranchIdentities[branchIndex]
296
+ ? comparisonIdentityMatchCount(comparisonEvidenceText, compareBranchIdentities[branchIndex])
297
+ : 0))
298
+ : [];
299
+ const comparisonBranchCoverage = comparisonBranchMatchCounts
300
+ .filter((matchCount) => matchCount > 0)
301
+ .length;
302
+ const incompleteEnding = /\b(?:and|or|with|at|in|between|is|are|from|to)$/iu.test(clause)
303
+ || /\b(?:vs|versus)\.?$/iu.test(clause)
304
+ || /[::]$/u.test(clause)
305
+ || /(?:以及|并且|其中|通常在|范围为|分别为|是|为)$/u.test(clause)
306
+ || !hasBalancedMathDelimiters(clause);
307
+ return {
308
+ clause,
309
+ score: (0, ragEvidenceQuality_1.scoreRagEvidenceClause)(clause).score + Math.min(0.8, anchorMatches * 0.2) - (incompleteEnding ? 0.5 : 0),
310
+ anchorMatches,
311
+ comparisonBranchCoverage,
312
+ comparisonBranchMatchCounts,
313
+ incompleteEnding,
314
+ order,
315
+ };
316
+ });
317
+ // An adjacent sentence may describe an established operand without repeating
318
+ // its name. Require shared source content; a matching fragment title alone is insufficient.
319
+ for (let index = 1; index < candidates.length; index += 1) {
320
+ const candidate = candidates[index];
321
+ const previous = candidates[index - 1];
322
+ if (candidate.comparisonBranchCoverage === 0 && previous.comparisonBranchCoverage > 0
323
+ && publicEvidenceClausesShareContext(candidate.clause, previous.clause)) {
324
+ candidate.comparisonBranchMatchCounts = [...previous.comparisonBranchMatchCounts];
325
+ candidate.comparisonBranchCoverage = previous.comparisonBranchCoverage;
326
+ }
327
+ }
328
+ return candidates.sort((left, right) => (Number(left.incompleteEnding) - Number(right.incompleteEnding)
329
+ || right.comparisonBranchCoverage - left.comparisonBranchCoverage
330
+ || right.anchorMatches - left.anchorMatches
331
+ || right.score - left.score
332
+ || left.order - right.order));
333
+ }
334
+ function normalizePublicClaimStatement(value) {
335
+ return value.replace(/(\d)\.\s+(?=\d)/gu, '$1.').trim();
336
+ }
337
+ function uniquePublicClaimStatements(candidates) {
338
+ const seen = new Set();
339
+ return candidates
340
+ .map((candidate) => normalizePublicClaimStatement(candidate.clause))
341
+ .filter((clause) => {
342
+ const key = normalize(clause).toLowerCase();
343
+ if (!key || seen.has(key)) {
344
+ return false;
345
+ }
346
+ seen.add(key);
347
+ return true;
348
+ });
349
+ }
350
+ function isComparisonDocumentaryClause(value) {
351
+ const normalized = normalize(value);
352
+ return /\b(?:preamble|technical\s+document|generated\s+from\s+(?:the\s+)?title|broad\s+background|documentary\s+context|source\s+anchoring)\b/iu.test(normalized)
353
+ || /本文档|本技术文档|仅基于标题|遵从(?:您的)?指示|综合技术文档|上下文段落|后续分支|前置节点/u.test(normalized)
354
+ // Full-document retrieval deliberately reads scaffolding around a fact.
355
+ // It is useful for provenance and conflict discovery, but never a
356
+ // comparison claim in its own right.
357
+ || /\b(?:scoped\s+comparison\s+document|full-document\s+[\w-]+\s+augmentation|opening\s+section\s+is\s+intentionally\s+separate|filler\s+paragraph|keeps?\s+the\s+remote\s+appendix\s+away|remote\s+appendix\s+away\s+from\s+the\s+matched\s+opening)\b/iu.test(normalized);
358
+ }
359
+ function comparisonClaimIdentityText(claim) {
360
+ const statement = normalize(claim.statement);
361
+ const leadingStatement = statement.split(/\b(?:records?|states?|notes?|reports?|indicates?|describes?|explains?|is|are|has|have|contains?|includes?|uses?|provides?)\b/iu)[0];
362
+ const sourcePaths = claim.evidenceRefs
363
+ .map((evidence) => normalize(evidence.sourcePath))
364
+ .filter(Boolean);
365
+ return [leadingStatement, ...sourcePaths].filter(Boolean).join(' ');
366
+ }
367
+ function comparisonClaimBranchMatchCounts(claim, semanticBranches, identityBranches) {
368
+ const identityText = comparisonClaimIdentityText(claim);
369
+ const features = new Set((0, graphClaimMatcher_1.semanticFeatures)(identityText));
370
+ return [0, 1].map((branchIndex) => Math.max(semanticBranches[branchIndex]
371
+ ? Array.from(semanticBranches[branchIndex]).filter((feature) => features.has(feature)).length
372
+ : 0, identityBranches[branchIndex]
373
+ ? comparisonIdentityMatchCount(identityText, identityBranches[branchIndex])
374
+ : 0));
375
+ }
376
+ function selectComparisonAnswerClaims(claims, message) {
377
+ const semanticBranches = comparisonBranchFeatures(message);
378
+ const identityBranches = comparisonBranchIdentityTerms(message);
379
+ if (semanticBranches.length !== 2 && identityBranches.length !== 2) {
380
+ return claims;
381
+ }
382
+ const branchMatchThresholds = [0, 1].map((branchIndex) => {
383
+ const identitySize = identityBranches[branchIndex]?.size || 0;
384
+ const semanticSize = semanticBranches[branchIndex]?.size || 0;
385
+ return Math.max(1, Math.min(2, identitySize || semanticSize || 1));
386
+ });
387
+ const candidates = claims.map((claim, index) => ({
388
+ claim,
389
+ index,
390
+ matchCounts: comparisonClaimBranchMatchCounts(claim, semanticBranches, identityBranches),
391
+ }));
392
+ const selected = new Set();
393
+ [0, 1].forEach((branchIndex) => {
394
+ candidates
395
+ .filter((candidate) => candidate.matchCounts[branchIndex] >= branchMatchThresholds[branchIndex])
396
+ .sort((left, right) => (right.matchCounts[branchIndex] - left.matchCounts[branchIndex]
397
+ || Number(right.claim.role === 'contrast') - Number(left.claim.role === 'contrast')
398
+ || right.claim.priority - left.claim.priority
399
+ || left.index - right.index))
400
+ .slice(0, 4)
401
+ .forEach((candidate) => selected.add(candidate.claim));
402
+ });
403
+ // A relation-backed contrast claim can be semantically implicit (for example,
404
+ // "the two nodes differ") but is still valid evidence for a compare request.
405
+ candidates
406
+ .filter((candidate) => candidate.claim.role === 'contrast' && candidate.claim.supportingEdgeIds.length > 0)
407
+ .sort((left, right) => right.claim.priority - left.claim.priority || left.index - right.index)
408
+ .slice(0, 2)
409
+ .forEach((candidate) => selected.add(candidate.claim));
410
+ if (selected.size <= 0) {
411
+ return claims;
412
+ }
413
+ return candidates
414
+ .filter((candidate) => selected.has(candidate.claim))
415
+ .sort((left, right) => left.index - right.index)
416
+ .map((candidate) => candidate.claim);
417
+ }
418
+ function selectComparisonAlignedPublicClaimStatements(candidates) {
419
+ const eligibleCandidates = candidates.filter((candidate) => !isComparisonDocumentaryClause(candidate.clause));
420
+ const maximumComparisonBranchCoverage = eligibleCandidates.reduce((maximum, candidate) => Math.max(maximum, candidate.comparisonBranchCoverage), 0);
421
+ if (maximumComparisonBranchCoverage >= 2) {
422
+ return uniquePublicClaimStatements(eligibleCandidates
423
+ .filter((candidate) => candidate.comparisonBranchCoverage >= 2)
424
+ .slice(0, 8));
425
+ }
426
+ if (maximumComparisonBranchCoverage <= 0) {
427
+ return uniquePublicClaimStatements(eligibleCandidates.filter((candidate) => candidate.anchorMatches > 0).slice(0, 4));
428
+ }
429
+ // When no single clause names both operands (common for Mermaid labels or
430
+ // two separate source documents), retain the strongest bounded evidence for
431
+ // each branch. This prevents a high-scoring preamble/anchor clause from
432
+ // consuming the comparison answer budget while preserving one-sided facts.
433
+ const branchCandidates = [];
434
+ const appendBranchCandidate = (candidate) => {
435
+ if (branchCandidates.includes(candidate) || branchCandidates.length >= 8) {
436
+ return;
437
+ }
438
+ branchCandidates.push(candidate);
439
+ };
440
+ [0, 1].forEach((branchIndex) => {
441
+ eligibleCandidates
442
+ .filter((candidate) => Number(candidate.comparisonBranchMatchCounts[branchIndex] || 0) > 0)
443
+ .sort((left, right) => (Number(right.comparisonBranchMatchCounts[branchIndex] || 0) - Number(left.comparisonBranchMatchCounts[branchIndex] || 0)
444
+ || right.anchorMatches - left.anchorMatches
445
+ || right.score - left.score
446
+ || left.order - right.order))
447
+ .slice(0, 4)
448
+ .forEach(appendBranchCandidate);
449
+ });
450
+ return uniquePublicClaimStatements(branchCandidates.sort((left, right) => left.order - right.order));
451
+ }
452
+ function isDiscourseContinuation(clause) {
453
+ return /^(?:(?:it|its|they|their|this|these|such|therefore|thus|hence|also|additionally|moreover|however|because|thereby|consequently|as a result)\b|(?:其|它|该|这些|这种|同时|此外|因此|所以|由此|并且|而且|其中|随后|进而))/iu.test(normalize(clause));
454
+ }
455
+ const CONTEXT_WORD_SEGMENTER = new Intl.Segmenter('zh', { granularity: 'word' });
456
+ const CHINESE_CONTEXT_FUNCTION_WORDS = new Set(['可以', '可能', '已经', '同一', '以及', '因为', '所以', '需要', '能够', '进行', '这种', '这个', '这些', '它们']);
457
+ function sourceContextFeatures(value) {
458
+ const features = new Set();
459
+ for (const part of CONTEXT_WORD_SEGMENTER.segment(value)) {
460
+ if (!part.isWordLike)
461
+ continue;
462
+ const word = normalizedTopicWords(part.segment);
463
+ if (/^(?:again|repeat|repeated|repeating|再次|重复|再度)$/u.test(word)) {
464
+ features.add('context:repeat');
465
+ }
466
+ else if (/\p{Script=Han}/u.test(word)) {
467
+ if (word.length < 2 || CHINESE_CONTEXT_FUNCTION_WORDS.has(word))
468
+ continue;
469
+ const concepts = (0, graphClaimMatcher_1.semanticFeatures)(word).filter(feature => feature.startsWith('concept:'));
470
+ if (concepts.length > 0)
471
+ concepts.forEach(feature => features.add(feature));
472
+ else
473
+ features.add(`han:${word}`);
474
+ }
475
+ else {
476
+ (0, graphClaimMatcher_1.semanticFeatures)(word).forEach(feature => features.add(feature));
477
+ }
478
+ }
479
+ return features;
480
+ }
481
+ function publicEvidenceClausesShareContext(left, right) {
482
+ if ((0, graphClaimMatcher_1.graphClaimSemanticSimilarity)(normalizedTopicWords(left), normalizedTopicWords(right)) >= 0.16)
483
+ return true;
484
+ const leftFeatures = sourceContextFeatures(left);
485
+ const rightFeatures = sourceContextFeatures(right);
486
+ const shorterSize = Math.min(leftFeatures.size, rightFeatures.size);
487
+ // A short continuation need not repeat the complete entity description.
488
+ // Word segmentation avoids treating an entire Chinese sentence as one identity token.
489
+ return shorterSize > 0 && Array.from(leftFeatures).filter(feature => rightFeatures.has(feature)).length / shorterSize >= 0.16;
490
+ }
491
+ function selectQueryConnectedPublicClaimStatements(value, title, message) {
492
+ const completeCandidates = rankQualityPublicClaimStatements(value, title, message)
493
+ .filter((candidate) => !candidate.incompleteEnding);
494
+ if (completeCandidates.length <= 0) {
495
+ return [];
496
+ }
497
+ const intent = classifyIntent(message);
498
+ if (intent === 'compare') {
499
+ return selectComparisonAlignedPublicClaimStatements(completeCandidates);
500
+ }
501
+ if (intent === 'procedure') {
502
+ return uniquePublicClaimStatements([...completeCandidates].sort((left, right) => left.order - right.order));
503
+ }
504
+ if (isCompoundLearningDefinitionQuery(message)) {
505
+ return selectCompoundDefinitionPublicClaimStatements(completeCandidates);
506
+ }
507
+ const sourceOrderedCandidates = [...completeCandidates].sort((left, right) => left.order - right.order);
508
+ const acceptedCandidates = sourceOrderedCandidates.filter((candidate) => candidate.anchorMatches > 0);
509
+ if (acceptedCandidates.length <= 0) {
510
+ acceptedCandidates.push(completeCandidates[0]);
511
+ }
512
+ let acceptedNewCandidate = true;
513
+ while (acceptedNewCandidate) {
514
+ acceptedNewCandidate = false;
515
+ sourceOrderedCandidates.forEach((candidate, index) => {
516
+ if (acceptedCandidates.includes(candidate)) {
517
+ return;
518
+ }
519
+ const precedingCandidate = sourceOrderedCandidates[index - 1];
520
+ const followsAcceptedEvidence = Boolean(precedingCandidate
521
+ && acceptedCandidates.includes(precedingCandidate)
522
+ && isDiscourseContinuation(candidate.clause));
523
+ if (acceptedCandidates.some(accepted => publicEvidenceClausesShareContext(candidate.clause, accepted.clause)) || followsAcceptedEvidence) {
524
+ acceptedCandidates.push(candidate);
525
+ acceptedNewCandidate = true;
526
+ }
527
+ });
528
+ }
529
+ const connectedCandidates = sourceOrderedCandidates.filter((candidate) => acceptedCandidates.includes(candidate));
530
+ if (classifyIntent(message) === 'definition') {
531
+ sourceOrderedCandidates
532
+ .filter((candidate) => statementCarriesMathDelimiter(candidate.clause))
533
+ .forEach((candidate) => {
534
+ if (!connectedCandidates.includes(candidate)) {
535
+ connectedCandidates.push(candidate);
536
+ }
537
+ });
538
+ }
539
+ return uniquePublicClaimStatements(connectedCandidates);
540
+ }
541
+ function selectContextualPublicClaimStatements(value, title, message) {
542
+ const completeCandidates = rankQualityPublicClaimStatements(value, title, message)
543
+ .filter((candidate) => !candidate.incompleteEnding);
544
+ if (classifyIntent(message) === 'compare') {
545
+ return selectComparisonAlignedPublicClaimStatements(completeCandidates);
546
+ }
547
+ return uniquePublicClaimStatements([...completeCandidates].sort((left, right) => left.order - right.order));
548
+ }
549
+ function isComparisonRelationClause(clause) {
550
+ return /\b(?:compare|comparison|contrast|difference|differences|compared|versus)\b|对比|比较|差异|区别/iu.test(normalize(clause));
551
+ }
552
+ function selectContrastRelationPublicClaimStatements(value, title, message) {
553
+ const completeCandidates = rankQualityPublicClaimStatements(value, title, message)
554
+ .filter((candidate) => !candidate.incompleteEnding);
555
+ if (completeCandidates.length <= 0) {
556
+ return [];
557
+ }
558
+ const branchAlignedStatements = new Set(selectComparisonAlignedPublicClaimStatements(completeCandidates)
559
+ .map((statement) => normalize(statement).toLowerCase()));
560
+ return uniquePublicClaimStatements(completeCandidates.filter((candidate) => (branchAlignedStatements.has(normalize(candidate.clause).toLowerCase())
561
+ || isComparisonRelationClause(candidate.clause))));
562
+ }
563
+ function contextualizeComparisonClaimStatements(statements, title, message) {
564
+ const normalizedTitle = normalize(String(title || '').replace(/\s*\((?:mermaid|code|diagram)\s+block\)\s*$/iu, ''));
565
+ const comparisonBranches = comparisonBranchFeatures(message);
566
+ const comparisonBranchIdentities = comparisonBranchIdentityTerms(message);
567
+ if (!normalizedTitle || (comparisonBranches.length !== 2 && comparisonBranchIdentities.length !== 2)) {
568
+ return statements;
569
+ }
570
+ const titleFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(normalizedTitle));
571
+ const completesComparisonBranch = (text, semanticTerms) => [0, 1].some((index) => {
572
+ const semanticBranch = comparisonBranches[index] || new Set();
573
+ const identityBranch = comparisonBranchIdentities[index] || new Set();
574
+ return (semanticBranch.size > 0
575
+ && Array.from(semanticBranch).some((feature) => semanticTerms.has(feature))) || (identityBranch.size > 0
576
+ && comparisonIdentityMatchCount(text, identityBranch) > 0);
577
+ });
578
+ const titleCompletesBranch = completesComparisonBranch(normalizedTitle, titleFeatures);
579
+ if (!titleCompletesBranch) {
580
+ return statements;
581
+ }
582
+ return statements.map((statement) => {
583
+ if (isComparisonRelationClause(statement)) {
584
+ return statement;
585
+ }
586
+ const statementFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(statement));
587
+ const statementNamesBranch = completesComparisonBranch(statement, statementFeatures);
588
+ return statementNamesBranch ? statement : `${normalizedTitle}: ${statement}`;
589
+ });
590
+ }
591
+ function classifyIntent(message) {
592
+ const value = normalize(message).toLowerCase();
593
+ if ((0, answerTaskPlan_1.isComparisonRequest)(value))
594
+ return 'compare';
595
+ if (/\b(?:why|cause|mechanism|reason|consequence)\b/u.test(value) || /为什么|原因|机制|导致/u.test(value))
596
+ return 'causal';
597
+ if ((0, answerTaskPlan_1.isProcedureRequest)(value))
598
+ return 'procedure';
599
+ if (/\b(?:what is|explain|define)\b/u.test(value) || /什么是|解释/u.test(value))
600
+ return 'definition';
601
+ return 'generic';
602
+ }
603
+ function inferRole(title, text, relationKind) {
604
+ const value = `${title} ${text}`.toLowerCase();
605
+ if (/\b(?:compare|comparison|compared|contrast|versus)\b|对比|比较|相比/u.test(value))
606
+ return 'contrast';
607
+ if (relationKind === 'prerequisite')
608
+ return 'prerequisite';
609
+ if (relationKind === 'sequence')
610
+ return 'sequence';
611
+ if (relationKind === 'causal')
612
+ return 'causal_consequence';
613
+ if (relationKind === 'contrast')
614
+ return 'contrast';
615
+ if (relationKind === 'analogy')
616
+ return 'analogy';
617
+ if (relationKind === 'application')
618
+ return 'application';
619
+ if (/boundary|边界|隔开|separat/u.test(value))
620
+ return 'boundary';
621
+ if (/thermal|heat|mechanism|exchange|传热|热交换|机制/u.test(value))
622
+ return 'mechanism';
623
+ if (/compos|consist|made of|组成|构成/u.test(value))
624
+ return 'composition';
625
+ if (/property|attribute|feature|特征|属性/u.test(value))
626
+ return 'attribute';
627
+ return 'definition';
628
+ }
629
+ function fragmentRelationKind(fragment, graphContext) {
630
+ const edgeIds = new Set(fragment.relationEdgeIds || []);
631
+ return (graphContext?.knowledgePointRelations || [])
632
+ .find((relation) => edgeIds.has(relation.edgeId))?.relationKind;
633
+ }
634
+ function makeClaimsFromPublicStatements(params) {
635
+ return params.publicStatements.map((publicStatement, statementIndex) => makeClaimFromPublicStatement({
636
+ index: params.index + statementIndex,
637
+ role: params.resolveRole(publicStatement),
638
+ evidenceText: params.evidenceText,
639
+ publicStatement,
640
+ anchorAtomId: params.anchorAtomId,
641
+ atomId: params.atomId,
642
+ sourcePath: params.sourcePath,
643
+ evidenceId: params.evidenceId,
644
+ citationIds: params.citationIds,
645
+ edgeIds: params.edgeIds,
646
+ confidence: params.confidence,
647
+ }));
648
+ }
649
+ function makeClaimFromPublicStatement(params) {
650
+ return {
651
+ claimId: `graph_claim_${params.index + 1}`,
652
+ role: params.role,
653
+ required: false,
654
+ priority: params.role === 'definition' ? 100 : Math.round(params.confidence * 90),
655
+ statement: params.publicStatement,
656
+ subjectAtomId: params.anchorAtomId,
657
+ supportingAtomIds: params.atomId && params.atomId !== params.anchorAtomId ? [params.atomId] : [],
658
+ supportingEdgeIds: params.edgeIds || [],
659
+ evidenceRefs: [{
660
+ evidenceId: params.evidenceId,
661
+ atomId: params.atomId,
662
+ sourcePath: params.sourcePath,
663
+ citationIds: params.citationIds || [],
664
+ text: params.evidenceText,
665
+ }],
666
+ confidence: Number(Math.max(0, Math.min(1, params.confidence)).toFixed(4)),
667
+ };
668
+ }
669
+ function makeClaimsFromRagFragment(params) {
670
+ const evidenceText = String(params.fragment.text || '').trim();
671
+ const selectedStatements = params.fragment.role === 'direct_support'
672
+ ? selectQueryConnectedPublicClaimStatements(evidenceText, params.fragment.title, params.message)
673
+ : params.relationKind === 'contrast' && classifyIntent(params.message) === 'compare'
674
+ ? selectContrastRelationPublicClaimStatements(evidenceText, params.fragment.title, params.message)
675
+ : selectContextualPublicClaimStatements(evidenceText, params.fragment.title, params.message);
676
+ return makeClaimsFromPublicStatements({
677
+ publicStatements: contextualizeComparisonClaimStatements(selectedStatements, params.fragment.title, params.message),
678
+ index: params.startIndex,
679
+ resolveRole: (statement) => inferRole(params.fragment.title || '', statement, params.relationKind),
680
+ evidenceText,
681
+ anchorAtomId: params.anchorAtomId,
682
+ atomId: params.fragment.atomId,
683
+ sourcePath: params.fragment.sourcePath,
684
+ evidenceId: params.fragment.fragmentId,
685
+ citationIds: params.fragment.citationIds,
686
+ edgeIds: params.fragment.relationEdgeIds,
687
+ confidence: Number(params.fragment.score || 0.75),
688
+ });
689
+ }
690
+ function makeClaimsFromMatchedSpan(params) {
691
+ const evidenceText = String(params.span.snippet || '').trim();
692
+ return makeClaimsFromPublicStatements({
693
+ publicStatements: contextualizeComparisonClaimStatements(selectQueryConnectedPublicClaimStatements(evidenceText, params.span.title, params.message), params.span.title, params.message),
694
+ index: params.startIndex,
695
+ resolveRole: (statement) => (params.leadingSpan ? 'definition' : inferRole(params.span.title, statement)),
696
+ evidenceText,
697
+ anchorAtomId: params.anchorAtomId,
698
+ atomId: params.span.atomId,
699
+ sourcePath: params.span.sourcePath,
700
+ evidenceId: params.span.citation?.citationId || `span_${params.span.atomId}_${params.startIndex + 1}`,
701
+ citationIds: params.span.citation ? [params.span.citation.citationId] : [],
702
+ confidence: params.span.score,
703
+ });
704
+ }
705
+ function extractPredicateFact(statement) {
706
+ const normalizedStatement = normalize(statement).replace(/[.!?。!?]+$/u, '');
707
+ const englishMatch = normalizedStatement.match(/^(.+?)\s+(?:is|are|was|were|equals?|remains?|becomes?|uses?|contains?|runs?|stores?|points?\s+to|depends?\s+on|located\s+(?:in|at)|available\s+(?:at|in))\s+(.+)$/iu);
708
+ if (englishMatch) {
709
+ return {
710
+ subject: normalize(englishMatch[1]).toLowerCase(),
711
+ value: normalize(englishMatch[2]).toLowerCase(),
712
+ };
713
+ }
714
+ const chineseMatch = normalizedStatement.match(/^(.+?)(?:是|为|位于|使用|包含|依赖于|指向)(.+)$/u);
715
+ if (chineseMatch) {
716
+ return {
717
+ subject: normalize(chineseMatch[1]).toLowerCase(),
718
+ value: normalize(chineseMatch[2]).toLowerCase(),
719
+ };
720
+ }
721
+ return null;
722
+ }
723
+ function claimsStateDifferentPredicateValues(left, right) {
724
+ const leftFact = extractPredicateFact(left.statement);
725
+ const rightFact = extractPredicateFact(right.statement);
726
+ return Boolean(leftFact
727
+ && rightFact
728
+ && (leftFact.subject === rightFact.subject
729
+ || (0, graphClaimMatcher_1.graphClaimSemanticSimilarity)(leftFact.subject, rightFact.subject) >= 0.72)
730
+ && leftFact.value !== rightFact.value);
731
+ }
732
+ function claimsCoverDifferentComparisonBranches(left, right, comparisonBranches, comparisonBranchIdentities) {
733
+ if (comparisonBranches.length !== 2 && comparisonBranchIdentities.length !== 2) {
734
+ return false;
735
+ }
736
+ const collectClaimIdentity = (claim) => [
737
+ claim.statement,
738
+ ...claim.evidenceRefs.map((evidence) => evidence.sourcePath),
739
+ ].join(' ');
740
+ const leftIdentity = collectClaimIdentity(left);
741
+ const rightIdentity = collectClaimIdentity(right);
742
+ const leftFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(leftIdentity));
743
+ const rightFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(rightIdentity));
744
+ const branchMatches = (features, branch) => (Array.from(branch).some((feature) => features.has(feature)));
745
+ return [0, 1].some((index) => {
746
+ const semanticBranch = comparisonBranches[index] || new Set();
747
+ const identityBranch = comparisonBranchIdentities[index] || new Set();
748
+ return (semanticBranch.size > 0
749
+ && branchMatches(leftFeatures, semanticBranch) !== branchMatches(rightFeatures, semanticBranch)) || (identityBranch.size > 0
750
+ && (comparisonIdentityMatchCount(leftIdentity, identityBranch) > 0)
751
+ !== (comparisonIdentityMatchCount(rightIdentity, identityBranch) > 0));
752
+ });
753
+ }
754
+ function claimsRequireSeparateCoverage(left, right, comparisonBranches, comparisonBranchIdentities) {
755
+ return claimsStateDifferentPredicateValues(left, right)
756
+ || claimsCoverDifferentComparisonBranches(left, right, comparisonBranches, comparisonBranchIdentities);
757
+ }
758
+ function buildGraphAnswerPlan(params) {
759
+ const anchor = params.knowledgePoints[0];
760
+ const anchorAtomId = params.graphContext?.anchorAtomId || anchor?.atomId || '';
761
+ const claims = [];
762
+ const redundantAtomIds = new Set();
763
+ const seen = new Set();
764
+ const seenCompoundMathExpressions = new Set();
765
+ const comparisonBranches = comparisonBranchFeatures(params.message);
766
+ const comparisonBranchIdentities = comparisonBranchIdentityTerms(params.message);
767
+ const definitionIntent = classifyIntent(params.message) === 'definition';
768
+ let nextClaimIndex = 0;
769
+ const append = (claim) => {
770
+ const key = normalize(claim.statement).toLowerCase();
771
+ if (!key || seen.has(key) || (definitionIntent && isDefinitionNoiseClaim(claim.statement)))
772
+ return;
773
+ if (isCompoundLearningDefinitionQuery(params.message)) {
774
+ const mathExpressions = extractMathExpressions(claim.statement);
775
+ if (mathExpressions.length > 0) {
776
+ const allExpressionsSeen = mathExpressions.every((expression) => seenCompoundMathExpressions.has(expression));
777
+ if (allExpressionsSeen) {
778
+ return;
779
+ }
780
+ mathExpressions.forEach((expression) => seenCompoundMathExpressions.add(expression));
781
+ }
782
+ }
783
+ const redundantClaim = claims.find((existing) => (existing.role === claim.role
784
+ && (0, graphClaimMatcher_1.graphClaimSemanticSimilarity)(existing.statement, claim.statement) >= 0.72
785
+ && (0, graphAnswerQualityPolicy_1.graphClaimsCanShareCoverage)(existing.statement, claim.statement)
786
+ && !claimsRequireSeparateCoverage(existing, claim, comparisonBranches, comparisonBranchIdentities)));
787
+ if (redundantClaim) {
788
+ const redundantAtomId = claim.supportingAtomIds[0] || claim.evidenceRefs[0]?.atomId;
789
+ if (redundantAtomId)
790
+ redundantAtomIds.add(redundantAtomId);
791
+ return;
792
+ }
793
+ seen.add(key);
794
+ claims.push(claim);
795
+ };
796
+ const matchedSpans = (anchor?.matchedSpans || [])
797
+ .filter((span) => !definitionIntent || !isDefinitionNoiseTitle(String(span.title || ''), params.message))
798
+ .filter((span) => !isCompoundLearningDefinitionQuery(params.message)
799
+ || isDefinitionLearningSupportSpan(span, (0, answerTaskPlan_1.extractDefinitionQuerySubject)(params.message)))
800
+ .slice(0, definitionIntent ? 8 : Number.MAX_SAFE_INTEGER);
801
+ matchedSpans.forEach((span, spanIndex) => {
802
+ const spanClaims = makeClaimsFromMatchedSpan({
803
+ startIndex: nextClaimIndex,
804
+ span,
805
+ anchorAtomId,
806
+ message: params.message,
807
+ leadingSpan: spanIndex === 0,
808
+ });
809
+ nextClaimIndex += spanClaims.length;
810
+ spanClaims.forEach(append);
811
+ });
812
+ const selectedSupportingAtomIds = new Set(params.graphContext?.supportingAtomIds || []);
813
+ const selectedGraphEdgeIds = new Set((params.graphContext?.knowledgePointRelations || []).map((relation) => relation.edgeId));
814
+ (params.ragContextPack?.fragments || [])
815
+ .filter((fragment) => fragment.role !== 'background')
816
+ .filter((fragment) => !definitionIntent || !isDefinitionNoiseTitle(String(fragment.title || ''), params.message))
817
+ .filter((fragment) => !isCompoundLearningDefinitionQuery(params.message)
818
+ || isDefinitionLearningSupportSpan({
819
+ title: String(fragment.title || ''),
820
+ snippet: String(fragment.text || ''),
821
+ }, (0, answerTaskPlan_1.extractDefinitionQuerySubject)(params.message)))
822
+ .filter((fragment) => (fragment.role !== 'graph_neighbor_support'
823
+ || selectedSupportingAtomIds.size <= 0
824
+ || Boolean(fragment.atomId && selectedSupportingAtomIds.has(fragment.atomId))
825
+ || (fragment.relationEdgeIds || []).some((edgeId) => selectedGraphEdgeIds.has(edgeId))))
826
+ .forEach((fragment) => {
827
+ const relationKind = fragmentRelationKind(fragment, params.graphContext);
828
+ const fragmentClaims = makeClaimsFromRagFragment({
829
+ startIndex: nextClaimIndex,
830
+ fragment,
831
+ relationKind,
832
+ anchorAtomId,
833
+ message: params.message,
834
+ });
835
+ nextClaimIndex += fragmentClaims.length;
836
+ fragmentClaims.forEach(append);
837
+ });
838
+ if (claims.length === 0 && anchor) {
839
+ const evidenceText = String(anchor.evidenceSnippet || anchor.summary || '').trim();
840
+ const fallbackClaims = makeClaimsFromPublicStatements({
841
+ publicStatements: contextualizeComparisonClaimStatements(selectQueryConnectedPublicClaimStatements(evidenceText, anchor.title, params.message), anchor.title, params.message),
842
+ index: nextClaimIndex,
843
+ resolveRole: () => 'definition',
844
+ evidenceText,
845
+ anchorAtomId,
846
+ atomId: anchor.atomId,
847
+ sourcePath: anchor.sourcePath || anchor.citation?.sourcePath || '',
848
+ evidenceId: anchor.citation?.citationId || `point_${anchor.atomId}`,
849
+ citationIds: anchor.citation ? [anchor.citation.citationId] : [],
850
+ confidence: anchor.score,
851
+ });
852
+ nextClaimIndex += fallbackClaims.length;
853
+ fallbackClaims.forEach(append);
854
+ }
855
+ const evidencedAtomIds = new Set(claims.flatMap((claim) => claim.supportingAtomIds));
856
+ const omittedCandidates = [
857
+ ...Array.from(redundantAtomIds).map((atomId) => ({ atomId, reason: 'redundant' })),
858
+ ...[
859
+ ...(params.graphContext?.predecessorWindow || []),
860
+ ...(params.graphContext?.successorWindow || []),
861
+ ].map((node) => ({ atomId: node.atomId, reason: 'weak_evidence' })),
862
+ ]
863
+ .filter((node) => node.atomId !== anchorAtomId && !evidencedAtomIds.has(node.atomId))
864
+ .filter((node, index, nodes) => nodes.findIndex((candidate) => candidate.atomId === node.atomId) === index);
865
+ const boundedClaims = classifyIntent(params.message) === 'compare'
866
+ ? selectComparisonAnswerClaims(claims, params.message)
867
+ : claims;
868
+ const roleOrder = {
869
+ definition: 10,
870
+ composition: 20,
871
+ boundary: 30,
872
+ attribute: 40,
873
+ prerequisite: 50,
874
+ mechanism: 60,
875
+ causal_consequence: 70,
876
+ sequence: 80,
877
+ application: 90,
878
+ contrast: 100,
879
+ analogy: 110,
880
+ temporal_warning: 120,
881
+ };
882
+ const sortedClaims = boundedClaims.sort((left, right) => (roleOrder[left.role] - roleOrder[right.role]
883
+ || right.priority - left.priority));
884
+ sortedClaims.forEach((claim) => {
885
+ // The upstream evidence pack is already bounded. At this boundary, novelty and
886
+ // confidence determine coverage; role cardinality does not imply redundancy.
887
+ claim.required = claim.confidence > 0.75;
888
+ });
889
+ if (!sortedClaims.some((claim) => claim.required) && sortedClaims.length > 0) {
890
+ sortedClaims[0].required = true;
891
+ }
892
+ const answerTaskPlan = (0, answerTaskPlan_1.buildAnswerTaskPlan)({
893
+ message: params.message,
894
+ knowledgePoints: params.knowledgePoints,
895
+ graphContext: params.graphContext,
896
+ ragContextPack: params.ragContextPack,
897
+ });
898
+ return {
899
+ intent: classifyIntent(params.message),
900
+ depth: sortedClaims.length <= 2 ? 'compact' : sortedClaims.length <= 7 ? 'standard' : 'deep',
901
+ anchorAtomId,
902
+ leadClaimId: sortedClaims[0]?.claimId || '',
903
+ claims: sortedClaims,
904
+ requiredRoles: Array.from(new Set(sortedClaims.filter((claim) => claim.required).map((claim) => claim.role))),
905
+ answerTaskPlan,
906
+ omittedCandidates,
907
+ };
908
+ }