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,907 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const graphAnswerPlan_1 = require("./graphAnswerPlan");
4
+ const knowledgePoint = {
5
+ atomId: 'water_glass',
6
+ atomIds: ['water_glass', 'material_boundary', 'thermal_exchange'],
7
+ documentId: 'doc_water_glass',
8
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
9
+ title: 'Water Glass',
10
+ summary: 'A water glass is a transparent drinking vessel.',
11
+ evidenceSnippet: 'A water glass is a transparent drinking vessel.',
12
+ score: 0.98,
13
+ citation: null,
14
+ citations: [],
15
+ matchedSpans: [
16
+ {
17
+ atomId: 'water_glass',
18
+ title: 'Water Glass',
19
+ snippet: 'A water glass is a transparent drinking vessel used to contain water.',
20
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
21
+ score: 0.98,
22
+ citation: null,
23
+ },
24
+ {
25
+ atomId: 'material_boundary',
26
+ title: 'Material Boundary',
27
+ snippet: 'Its solid wall forms a material boundary between the liquid and the surrounding environment.',
28
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
29
+ score: 0.91,
30
+ citation: null,
31
+ },
32
+ {
33
+ atomId: 'thermal_exchange',
34
+ title: 'Thermal Exchange',
35
+ snippet: 'Heat moves through the glass wall, so the material and thickness affect thermal exchange.',
36
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
37
+ score: 0.9,
38
+ citation: null,
39
+ },
40
+ ],
41
+ matchCount: 3,
42
+ relationPath: [],
43
+ relationPathAtomIds: [],
44
+ relationKinds: [],
45
+ capabilities: [],
46
+ };
47
+ const graphContext = {
48
+ anchorAtomId: 'water_glass',
49
+ anchorTitle: 'Water Glass',
50
+ anchorGraphProfile: { atomId: 'water_glass', title: 'Water Glass', inDegree: 2, outDegree: 3 },
51
+ supportingAtomIds: ['container', 'heat_transfer'],
52
+ supportingTitles: ['Container', 'Heat Transfer'],
53
+ relationKinds: ['prerequisite', 'application'],
54
+ relationSummaries: [],
55
+ knowledgePointRelations: [{
56
+ edgeId: 'edge_water_heat',
57
+ relationKind: 'application',
58
+ sourceAtomId: 'water_glass',
59
+ sourceTitle: 'Water Glass',
60
+ targetAtomId: 'heat_transfer',
61
+ targetTitle: 'Heat Transfer',
62
+ confidence: 0.88,
63
+ }],
64
+ predecessorWindow: [
65
+ { atomId: 'container', title: 'Container', relationKind: 'prerequisite', confidence: 0.92 },
66
+ ],
67
+ successorWindow: [
68
+ { atomId: 'heat_transfer', title: 'Heat Transfer', relationKind: 'application', confidence: 0.88 },
69
+ ],
70
+ temporalValidity: {
71
+ checkedAt: '2026-07-11T00:00:00.000Z',
72
+ allPointsValid: true,
73
+ warningReasons: [],
74
+ invalidKnowledgePointTitles: [],
75
+ },
76
+ };
77
+ const ragContextPack = {
78
+ query: 'what is water glass',
79
+ generatedAt: '2026-07-11T00:00:00.000Z',
80
+ sourceBoundary: 'full_document',
81
+ budget: { maxFragments: 20, maxCharsPerFragment: 1500, maxTotalChars: 7600 },
82
+ fragments: [
83
+ {
84
+ fragmentId: 'neighbor_heat_transfer',
85
+ role: 'graph_neighbor_support',
86
+ text: 'Heat Transfer explains why the glass wall conducts heat between the drink and the environment.',
87
+ atomId: 'heat_transfer',
88
+ documentId: 'doc_heat_transfer',
89
+ sourcePath: 'Knowledge_Base/physics/heat-transfer.md',
90
+ title: 'Heat Transfer',
91
+ headingPath: ['Heat Transfer'],
92
+ charCount: 96,
93
+ tokenEstimate: 24,
94
+ truncated: false,
95
+ citationIds: ['citation_heat_transfer'],
96
+ relationEdgeIds: ['edge_water_heat'],
97
+ sourceBoundary: 'full_document',
98
+ score: 0.88,
99
+ },
100
+ ],
101
+ sourceDecisions: [],
102
+ totalCharCount: 96,
103
+ tokenEstimate: 24,
104
+ };
105
+ describe('buildGraphAnswerPlan', () => {
106
+ test('drops truncated math clauses instead of publishing an incomplete formula', () => {
107
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
108
+ message: 'what is amorphous ice?',
109
+ knowledgePoints: [{
110
+ ...knowledgePoint,
111
+ atomId: 'amorphous_ice',
112
+ title: 'Amorphous Ice',
113
+ sourcePath: 'Knowledge_Base/waterglass/Amorphous ice.md',
114
+ summary: 'Amorphous ice is a solid form of water.',
115
+ evidenceSnippet: 'Amorphous ice is a solid form of water.',
116
+ matchedSpans: [{
117
+ atomId: 'amorphous_ice_math',
118
+ title: 'Core Mathematics',
119
+ snippet: 'The radial distribution function is defined by $$g(r) = \\frac{V}{N^2} \\left\\langle \\sum_{i=1}^{N} \\delta(\\mathbf{r} - (\\mathb',
120
+ sourcePath: 'Knowledge_Base/waterglass/Amorphous ice.md',
121
+ score: 0.93,
122
+ citation: null,
123
+ }],
124
+ }],
125
+ graphContext,
126
+ });
127
+ expect(plan.claims.some((claim) => claim.statement.includes('\\mathb'))).toBe(false);
128
+ });
129
+ test('models compound definition and learning-route deliverables separately', () => {
130
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
131
+ message: 'what is amorphous ice? Which knowledge points should I learn?',
132
+ knowledgePoints: [{
133
+ ...knowledgePoint,
134
+ atomId: 'amorphous_ice',
135
+ atomIds: ['amorphous_ice', 'water_structure', 'radial_distribution', 'glass_transition'],
136
+ documentId: 'doc_amorphous_ice',
137
+ sourcePath: 'Knowledge_Base/waterglass/Amorphous ice.md',
138
+ title: 'Amorphous Ice',
139
+ summary: 'Amorphous ice is a solid form of water without long-range crystalline order.',
140
+ evidenceSnippet: 'Amorphous ice is a solid form of water without long-range crystalline order.',
141
+ matchedSpans: [
142
+ {
143
+ atomId: 'amorphous_ice',
144
+ title: 'Amorphous Ice',
145
+ snippet: 'Amorphous ice is a solid form of water without long-range crystalline order.',
146
+ sourcePath: 'Knowledge_Base/waterglass/Amorphous ice.md',
147
+ score: 0.98,
148
+ citation: null,
149
+ },
150
+ {
151
+ atomId: 'water_structure',
152
+ title: 'Water Molecular Structure',
153
+ snippet: 'Water molecules and hydrogen bonding explain the local structure of ice.',
154
+ sourcePath: 'Knowledge_Base/waterglass/Amorphous ice.md',
155
+ score: 0.86,
156
+ citation: null,
157
+ },
158
+ {
159
+ atomId: 'radial_distribution',
160
+ title: 'Radial Distribution Function',
161
+ snippet: 'The radial distribution function g(r) distinguishes short-range from long-range order.',
162
+ sourcePath: 'Knowledge_Base/waterglass/Amorphous ice.md',
163
+ score: 0.84,
164
+ citation: null,
165
+ },
166
+ ],
167
+ }],
168
+ graphContext,
169
+ });
170
+ expect(plan.answerTaskPlan).toEqual(expect.objectContaining({
171
+ schemaVersion: '1',
172
+ primarySubject: 'Amorphous Ice',
173
+ subtasks: expect.arrayContaining([
174
+ expect.objectContaining({ kind: 'definition', required: true }),
175
+ expect.objectContaining({ kind: 'learning_route', required: true, expectedOutput: 'ordered_nodes' }),
176
+ ]),
177
+ }));
178
+ expect(plan.answerTaskPlan?.learningRoute?.length).toBeGreaterThanOrEqual(3);
179
+ expect(plan.answerTaskPlan?.learningRoute?.map((node) => node.title)).toEqual(expect.arrayContaining([
180
+ 'Amorphous Ice',
181
+ 'Water Molecular Structure',
182
+ 'Radial Distribution Function',
183
+ ]));
184
+ });
185
+ test('turns grouped anchor spans and evidenced graph neighbors into required semantic claims', () => {
186
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
187
+ message: 'what is water glass',
188
+ knowledgePoints: [knowledgePoint],
189
+ graphContext,
190
+ ragContextPack,
191
+ });
192
+ expect(plan.anchorAtomId).toBe('water_glass');
193
+ expect(plan.claims.map((claim) => claim.role)).toEqual(expect.arrayContaining([
194
+ 'definition',
195
+ 'boundary',
196
+ 'mechanism',
197
+ 'application',
198
+ ]));
199
+ expect(plan.claims.filter((claim) => claim.required).length).toBeGreaterThanOrEqual(3);
200
+ expect(plan.claims.find((claim) => claim.role === 'application')?.supportingEdgeIds)
201
+ .toContain('edge_water_heat');
202
+ });
203
+ test('does not promote a title-only graph neighbor into a factual claim', () => {
204
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
205
+ message: 'what is water glass',
206
+ knowledgePoints: [knowledgePoint],
207
+ graphContext,
208
+ });
209
+ expect(plan.claims.some((claim) => claim.supportingAtomIds.includes('container'))).toBe(false);
210
+ expect(plan.omittedCandidates).toContainEqual(expect.objectContaining({
211
+ atomId: 'container',
212
+ reason: 'weak_evidence',
213
+ }));
214
+ });
215
+ test('orders claims by discourse dependency instead of raw confidence', () => {
216
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
217
+ message: 'explain water glass in detail',
218
+ knowledgePoints: [knowledgePoint],
219
+ graphContext,
220
+ ragContextPack: {
221
+ ...ragContextPack,
222
+ fragments: ragContextPack.fragments.map((fragment) => ({ ...fragment, score: 0.99 })),
223
+ },
224
+ });
225
+ const roles = plan.claims.map((claim) => claim.role);
226
+ expect(roles.indexOf('boundary')).toBeLessThan(roles.indexOf('mechanism'));
227
+ expect(roles.indexOf('mechanism')).toBeLessThan(roles.indexOf('application'));
228
+ });
229
+ test('omits semantically redundant graph evidence from the public claim plan', () => {
230
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
231
+ message: 'what is water glass',
232
+ knowledgePoints: [knowledgePoint],
233
+ graphContext: {
234
+ ...graphContext,
235
+ supportingAtomIds: [...graphContext.supportingAtomIds, 'heat_transfer_duplicate'],
236
+ },
237
+ ragContextPack: {
238
+ ...ragContextPack,
239
+ fragments: [
240
+ ...ragContextPack.fragments,
241
+ {
242
+ ...ragContextPack.fragments[0],
243
+ fragmentId: 'neighbor_heat_transfer_duplicate',
244
+ atomId: 'heat_transfer_duplicate',
245
+ text: 'Thermal energy passes through the glass wall between the drink and its environment.',
246
+ score: 0.86,
247
+ },
248
+ ],
249
+ },
250
+ });
251
+ expect(plan.claims.filter((claim) => claim.role === 'application')).toHaveLength(1);
252
+ expect(plan.omittedCandidates).toContainEqual({
253
+ atomId: 'heat_transfer_duplicate',
254
+ reason: 'redundant',
255
+ });
256
+ });
257
+ test('preserves polarity-opposed claims instead of semantic-deduplicating them', () => {
258
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
259
+ message: 'explain glass wall heat transfer',
260
+ knowledgePoints: [{
261
+ ...knowledgePoint,
262
+ matchedSpans: knowledgePoint.matchedSpans ? [knowledgePoint.matchedSpans[0]] : [],
263
+ }],
264
+ graphContext: {
265
+ ...graphContext,
266
+ supportingAtomIds: [],
267
+ supportingTitles: [],
268
+ knowledgePointRelations: [],
269
+ },
270
+ ragContextPack: {
271
+ ...ragContextPack,
272
+ fragments: [
273
+ {
274
+ ...ragContextPack.fragments[0],
275
+ fragmentId: 'positive_heat_transfer',
276
+ role: 'direct_support',
277
+ atomId: 'positive_heat_transfer',
278
+ title: 'Heat Transfer',
279
+ text: 'The glass wall conducts heat through the liquid boundary.',
280
+ relationEdgeIds: [],
281
+ },
282
+ {
283
+ ...ragContextPack.fragments[0],
284
+ fragmentId: 'negative_heat_transfer',
285
+ role: 'direct_support',
286
+ atomId: 'negative_heat_transfer',
287
+ title: 'Heat Transfer',
288
+ text: 'The glass wall does not conduct heat through the liquid boundary.',
289
+ relationEdgeIds: [],
290
+ },
291
+ ],
292
+ },
293
+ });
294
+ expect(plan.claims.map((claim) => claim.statement)).toEqual(expect.arrayContaining([
295
+ 'The glass wall conducts heat through the liquid boundary.',
296
+ 'The glass wall does not conduct heat through the liquid boundary.',
297
+ ]));
298
+ });
299
+ test('requires distinct high-confidence claims even when they share one semantic role', () => {
300
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
301
+ message: 'explain water glass in detail',
302
+ knowledgePoints: [knowledgePoint],
303
+ graphContext: {
304
+ ...graphContext,
305
+ supportingAtomIds: [
306
+ ...graphContext.supportingAtomIds,
307
+ 'heat_conduction',
308
+ 'thermal_capacity',
309
+ ],
310
+ },
311
+ ragContextPack: {
312
+ ...ragContextPack,
313
+ fragments: [
314
+ {
315
+ ...ragContextPack.fragments[0],
316
+ fragmentId: 'neighbor_heat_transfer_conduction',
317
+ atomId: 'heat_conduction',
318
+ text: 'The glass wall conducts thermal energy from the drink to the surrounding air.',
319
+ score: 0.93,
320
+ },
321
+ {
322
+ ...ragContextPack.fragments[0],
323
+ fragmentId: 'neighbor_heat_transfer_capacity',
324
+ atomId: 'thermal_capacity',
325
+ text: 'The wall heat capacity delays how quickly the drink approaches ambient temperature.',
326
+ score: 0.91,
327
+ },
328
+ ],
329
+ },
330
+ });
331
+ const applicationClaims = plan.claims.filter((claim) => claim.role === 'application');
332
+ expect(applicationClaims).toHaveLength(2);
333
+ expect(applicationClaims.every((claim) => claim.required)).toBe(true);
334
+ });
335
+ test('plans public semantic statements instead of renderer and table payloads', () => {
336
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
337
+ message: 'explain water glass in detail',
338
+ knowledgePoints: [{
339
+ ...knowledgePoint,
340
+ matchedSpans: [
341
+ ...(knowledgePoint.matchedSpans || []),
342
+ {
343
+ atomId: 'thermal_equation_diagram',
344
+ title: 'Thermal Equation Diagram',
345
+ snippet: 'Heat crosses the glass wall by conduction. ```mermaid\ngraph TD\nA --> B\n```',
346
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
347
+ score: 0.89,
348
+ citation: null,
349
+ },
350
+ {
351
+ atomId: 'parameter_table',
352
+ title: 'Parameter Table',
353
+ snippet: 'The following table lists typical values. | Parameter | Unit | | :--- | :--- |',
354
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
355
+ score: 0.88,
356
+ citation: null,
357
+ },
358
+ ],
359
+ }],
360
+ graphContext,
361
+ });
362
+ expect(plan.claims).toContainEqual(expect.objectContaining({
363
+ statement: 'Heat crosses the glass wall by conduction.',
364
+ }));
365
+ expect(plan.claims.some((claim) => claim.statement.includes('```'))).toBe(false);
366
+ expect(plan.claims.some((claim) => claim.statement.includes('| Parameter |'))).toBe(false);
367
+ });
368
+ test('selects a complete clause from dense mathematical evidence while retaining provenance', () => {
369
+ const denseEvidence = [
370
+ 'Thermal transfer is governed by q = kA(T1 - T2) / L.',
371
+ 'This document will explain the following table as requested.',
372
+ 'For soda-lime glass the coefficient is usually between',
373
+ ].join(' ');
374
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
375
+ message: 'explain thermal transfer in a water glass',
376
+ knowledgePoints: [{
377
+ ...knowledgePoint,
378
+ matchedSpans: [{
379
+ atomId: 'thermal_transfer_dense',
380
+ title: 'Thermal Transfer',
381
+ snippet: denseEvidence,
382
+ sourcePath: 'Knowledge_Base/waterglass/thermal-transfer.md',
383
+ score: 0.94,
384
+ citation: null,
385
+ }],
386
+ }],
387
+ graphContext,
388
+ });
389
+ expect(plan.claims[0].statement).toBe('Thermal transfer is governed by q = kA(T1 - T2) / L.');
390
+ expect(plan.claims[0].evidenceRefs[0].text).toBe(denseEvidence);
391
+ });
392
+ test('retains complete internal claims and preserves their evidence provenance', () => {
393
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
394
+ message: 'what is water glass',
395
+ knowledgePoints: [knowledgePoint],
396
+ graphContext,
397
+ ragContextPack: {
398
+ ...ragContextPack,
399
+ fragments: [{
400
+ ...ragContextPack.fragments[0],
401
+ fragmentId: 'direct_water_glass_dense_evidence',
402
+ role: 'direct_support',
403
+ atomId: 'water_glass',
404
+ title: 'Water Glass',
405
+ text: [
406
+ 'A water glass is a transparent vessel used to hold water.',
407
+ 'It separates the liquid from the surrounding environment.',
408
+ 'Its wall conducts heat between the drink and the air.',
409
+ 'It is compared with a PET Plastic Cup and a Stainless Steel Metal Cup.',
410
+ 'The thermal field can be described by $$\\frac{\\partial T}{\\partial t}=\\alpha\\nabla^2 T$$.',
411
+ 'This clause is intentionally beyond the public definition budget and should remain internal.',
412
+ ].join(' '),
413
+ score: 0.9,
414
+ }],
415
+ },
416
+ });
417
+ const directClaims = plan.claims
418
+ .filter((claim) => claim.evidenceRefs.some((reference) => reference.evidenceId === 'direct_water_glass_dense_evidence'))
419
+ .map((claim) => claim.statement);
420
+ expect(directClaims).toEqual(expect.arrayContaining([
421
+ 'A water glass is a transparent vessel used to hold water.',
422
+ 'It separates the liquid from the surrounding environment.',
423
+ 'Its wall conducts heat between the drink and the air.',
424
+ ]));
425
+ expect(directClaims).toContain('It is compared with a PET Plastic Cup and a Stainless Steel Metal Cup.');
426
+ expect(directClaims).not.toContain('This clause is intentionally beyond the public definition budget and should remain internal.');
427
+ expect(plan.claims.find((claim) => claim.statement.includes('PET Plastic Cup'))?.evidenceRefs[0].evidenceId)
428
+ .toBe('direct_water_glass_dense_evidence');
429
+ });
430
+ test('retains distinct fact values from semantically similar conflict evidence', () => {
431
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
432
+ message: 'what is release date conflict probe?',
433
+ knowledgePoints: [knowledgePoint],
434
+ graphContext,
435
+ ragContextPack: {
436
+ ...ragContextPack,
437
+ fragments: [
438
+ {
439
+ ...ragContextPack.fragments[0],
440
+ fragmentId: 'announced_release_date',
441
+ role: 'direct_support',
442
+ atomId: 'release_date_probe',
443
+ title: 'Release Date Conflict Probe',
444
+ text: 'The migration release date is 2026-07-01.',
445
+ score: 0.92,
446
+ },
447
+ {
448
+ ...ragContextPack.fragments[0],
449
+ fragmentId: 'revised_release_date',
450
+ role: 'conflict',
451
+ atomId: 'release_date_probe',
452
+ title: 'Release Date Conflict Probe',
453
+ text: 'The migration release date is 2026-08-15.',
454
+ score: 0.92,
455
+ },
456
+ ],
457
+ },
458
+ });
459
+ expect(plan.claims.map((claim) => claim.statement)).toEqual(expect.arrayContaining([
460
+ 'The migration release date is 2026-07-01.',
461
+ 'The migration release date is 2026-08-15.',
462
+ ]));
463
+ });
464
+ test('retains distinct values when equivalent fact subjects use different word order', () => {
465
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
466
+ message: 'what is release date conflict probe?',
467
+ knowledgePoints: [knowledgePoint],
468
+ graphContext,
469
+ ragContextPack: {
470
+ ...ragContextPack,
471
+ fragments: [
472
+ {
473
+ ...ragContextPack.fragments[0],
474
+ fragmentId: 'announced_release_date_reordered',
475
+ role: 'direct_support',
476
+ atomId: 'release_date_probe',
477
+ title: 'Release Date Conflict Probe',
478
+ text: 'The migration release date is 2026-07-01.',
479
+ score: 0.92,
480
+ },
481
+ {
482
+ ...ragContextPack.fragments[0],
483
+ fragmentId: 'revised_release_date_reordered',
484
+ role: 'conflict',
485
+ atomId: 'release_date_probe',
486
+ title: 'Release Date Conflict Probe',
487
+ text: 'The release date for migration is 2026-08-15.',
488
+ score: 0.92,
489
+ },
490
+ ],
491
+ },
492
+ });
493
+ expect(plan.claims.map((claim) => claim.statement)).toEqual(expect.arrayContaining([
494
+ 'The migration release date is 2026-07-01.',
495
+ 'The release date for migration is 2026-08-15.',
496
+ ]));
497
+ });
498
+ test('retains comparison evidence that completes separate requested branches', () => {
499
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
500
+ message: 'compare cross version one state source with cross version two state source',
501
+ knowledgePoints: [knowledgePoint],
502
+ graphContext,
503
+ ragContextPack: {
504
+ ...ragContextPack,
505
+ fragments: [
506
+ {
507
+ ...ragContextPack.fragments[0],
508
+ fragmentId: 'version_one_state',
509
+ role: 'direct_support',
510
+ atomId: 'version_one_state',
511
+ sourcePath: 'Knowledge_Base/test/cross version one state source.md',
512
+ title: 'Cross Version One State Source',
513
+ text: 'Cross version one state source records that the migration gate status is enabled in version 1.0.',
514
+ score: 0.92,
515
+ },
516
+ {
517
+ ...ragContextPack.fragments[0],
518
+ fragmentId: 'version_two_state',
519
+ role: 'direct_support',
520
+ atomId: 'version_two_state',
521
+ sourcePath: 'Knowledge_Base/test/cross version two state source.md',
522
+ title: 'Cross Version Two State Source',
523
+ text: 'Cross version two state source records that the migration gate status is disabled in version 2.0.',
524
+ score: 0.92,
525
+ },
526
+ ],
527
+ },
528
+ });
529
+ expect(plan.claims.map((claim) => claim.statement)).toEqual(expect.arrayContaining([
530
+ 'Cross version one state source records that the migration gate status is enabled in version 1.0.',
531
+ 'Cross version two state source records that the migration gate status is disabled in version 2.0.',
532
+ ]));
533
+ });
534
+ test('labels comparison claims when otherwise identical evidence is distinguished only by node title', () => {
535
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
536
+ message: 'compare basalt with granite',
537
+ knowledgePoints: [knowledgePoint],
538
+ graphContext,
539
+ ragContextPack: {
540
+ ...ragContextPack,
541
+ fragments: [
542
+ {
543
+ ...ragContextPack.fragments[0],
544
+ fragmentId: 'basalt_density',
545
+ role: 'direct_support',
546
+ atomId: 'basalt',
547
+ sourcePath: 'Knowledge_Base/test/source-a.md',
548
+ title: 'Basalt',
549
+ text: 'Its density is high.',
550
+ score: 0.92,
551
+ },
552
+ {
553
+ ...ragContextPack.fragments[0],
554
+ fragmentId: 'granite_density',
555
+ role: 'direct_support',
556
+ atomId: 'granite',
557
+ sourcePath: 'Knowledge_Base/test/source-b.md',
558
+ title: 'Granite',
559
+ text: 'Its density is high.',
560
+ score: 0.92,
561
+ },
562
+ ],
563
+ },
564
+ });
565
+ expect(plan.claims.map((claim) => claim.statement)).toEqual(expect.arrayContaining([
566
+ 'Basalt: Its density is high.',
567
+ 'Granite: Its density is high.',
568
+ ]));
569
+ });
570
+ test('retains comparison branches whose only distinct token is a short version number', () => {
571
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
572
+ message: 'compare iOS 17 with iOS 18',
573
+ knowledgePoints: [knowledgePoint],
574
+ graphContext,
575
+ ragContextPack: {
576
+ ...ragContextPack,
577
+ fragments: [
578
+ {
579
+ ...ragContextPack.fragments[0],
580
+ fragmentId: 'ios_17_status',
581
+ role: 'direct_support',
582
+ atomId: 'ios_17',
583
+ sourcePath: 'Knowledge_Base/test/opaque-a.md',
584
+ title: 'iOS 17',
585
+ text: 'Its response status is stable.',
586
+ score: 0.92,
587
+ },
588
+ {
589
+ ...ragContextPack.fragments[0],
590
+ fragmentId: 'ios_18_status',
591
+ role: 'direct_support',
592
+ atomId: 'ios_18',
593
+ sourcePath: 'Knowledge_Base/test/opaque-b.md',
594
+ title: 'iOS 18',
595
+ text: 'Its response status is stable.',
596
+ score: 0.92,
597
+ },
598
+ ],
599
+ },
600
+ });
601
+ expect(plan.claims.map((claim) => claim.statement)).toEqual(expect.arrayContaining([
602
+ 'iOS 17: Its response status is stable.',
603
+ 'iOS 18: Its response status is stable.',
604
+ ]));
605
+ });
606
+ test('retains both operand claims when one side has only a two-token branch match', () => {
607
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
608
+ message: 'compare temporal current release source with temporal planned roadmap source',
609
+ knowledgePoints: [knowledgePoint],
610
+ graphContext,
611
+ ragContextPack: {
612
+ ...ragContextPack,
613
+ query: 'compare temporal current release source with temporal planned roadmap source',
614
+ fragments: [
615
+ {
616
+ ...ragContextPack.fragments[0],
617
+ fragmentId: 'planned_release_date',
618
+ role: 'direct_support',
619
+ atomId: 'planned_release_date',
620
+ sourcePath: 'Knowledge_Base/test/temporal-planned.md',
621
+ title: 'Temporal Planned Roadmap Source',
622
+ text: 'Temporal planned roadmap source records that the migration release date is 2026-09-20 in the planned rollout draft.',
623
+ score: 0.94,
624
+ },
625
+ {
626
+ ...ragContextPack.fragments[0],
627
+ fragmentId: 'current_release_date',
628
+ role: 'direct_support',
629
+ atomId: 'current_release_date',
630
+ sourcePath: 'Knowledge_Base/test/temporal-current.md',
631
+ title: 'Temporal Current Release Source',
632
+ text: 'Temporal current release source records that the migration release date is 2026-08-15 in the current release record.',
633
+ score: 0.93,
634
+ },
635
+ {
636
+ ...ragContextPack.fragments[0],
637
+ fragmentId: 'current_retry_limit',
638
+ role: 'direct_support',
639
+ atomId: 'current_retry_limit',
640
+ sourcePath: 'Knowledge_Base/test/temporal-current-retry.md',
641
+ title: 'Temporal Current Retry Limit Source',
642
+ text: 'Temporal current retry limit source records that the retry limit is 3 in the current release record.',
643
+ score: 0.92,
644
+ },
645
+ {
646
+ ...ragContextPack.fragments[0],
647
+ fragmentId: 'historical_retry_limit',
648
+ role: 'direct_support',
649
+ atomId: 'historical_retry_limit',
650
+ sourcePath: 'Knowledge_Base/test/temporal-history.md',
651
+ title: 'Temporal Historical Retry Limit Source',
652
+ text: 'Temporal historical retry limit source records that the retry limit is 5 in the historical rollback archive.',
653
+ score: 0.9,
654
+ },
655
+ ],
656
+ },
657
+ });
658
+ expect(plan.claims.map((claim) => claim.statement)).toEqual(expect.arrayContaining([
659
+ 'Temporal planned roadmap source records that the migration release date is 2026-09-20 in the planned rollout draft.',
660
+ 'Temporal current release source records that the migration release date is 2026-08-15 in the current release record.',
661
+ ]));
662
+ expect(plan.claims.map((claim) => claim.statement)).not.toEqual(expect.arrayContaining([
663
+ 'Temporal current retry limit source records that the retry limit is 3 in the current release record.',
664
+ 'Temporal historical retry limit source records that the retry limit is 5 in the historical rollback archive.',
665
+ ]));
666
+ });
667
+ test('retains comparison branches made only of one-character labels', () => {
668
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
669
+ message: 'compare A with B',
670
+ knowledgePoints: [knowledgePoint],
671
+ graphContext,
672
+ ragContextPack: {
673
+ ...ragContextPack,
674
+ fragments: [
675
+ {
676
+ ...ragContextPack.fragments[0],
677
+ fragmentId: 'label_a_status',
678
+ role: 'direct_support',
679
+ atomId: 'label_a',
680
+ sourcePath: 'Knowledge_Base/test/opaque-a.md',
681
+ title: 'A',
682
+ text: 'Its response status is stable.',
683
+ score: 0.92,
684
+ },
685
+ {
686
+ ...ragContextPack.fragments[0],
687
+ fragmentId: 'label_b_status',
688
+ role: 'direct_support',
689
+ atomId: 'label_b',
690
+ sourcePath: 'Knowledge_Base/test/opaque-b.md',
691
+ title: 'B',
692
+ text: 'Its response status is stable.',
693
+ score: 0.92,
694
+ },
695
+ ],
696
+ },
697
+ });
698
+ expect(plan.claims.map((claim) => claim.statement)).toEqual(expect.arrayContaining([
699
+ 'A: Its response status is stable.',
700
+ 'B: Its response status is stable.',
701
+ ]));
702
+ });
703
+ test('does not treat unrelated clauses as comparison evidence solely because their fragment title matches a branch', () => {
704
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
705
+ message: 'compare basalt with granite',
706
+ knowledgePoints: [knowledgePoint],
707
+ graphContext,
708
+ ragContextPack: {
709
+ ...ragContextPack,
710
+ fragments: [
711
+ {
712
+ ...ragContextPack.fragments[0],
713
+ fragmentId: 'basalt_density_with_noise',
714
+ role: 'direct_support',
715
+ atomId: 'basalt',
716
+ sourcePath: 'Knowledge_Base/test/basalt.md',
717
+ title: 'Basalt',
718
+ text: 'Its density is high. Mars has two moons.',
719
+ score: 0.92,
720
+ },
721
+ {
722
+ ...ragContextPack.fragments[0],
723
+ fragmentId: 'granite_density_with_noise',
724
+ role: 'direct_support',
725
+ atomId: 'granite',
726
+ sourcePath: 'Knowledge_Base/test/granite.md',
727
+ title: 'Granite',
728
+ text: 'Its density is high. Mars has two moons.',
729
+ score: 0.92,
730
+ },
731
+ ],
732
+ },
733
+ });
734
+ const statements = plan.claims.map((claim) => claim.statement);
735
+ expect(statements).toEqual(expect.arrayContaining([
736
+ 'Basalt: Its density is high.',
737
+ 'Granite: Its density is high.',
738
+ ]));
739
+ expect(statements).not.toContain('Basalt: Mars has two moons.');
740
+ expect(statements).not.toContain('Granite: Mars has two moons.');
741
+ });
742
+ test('removes structural CJK headings without removing an English subject phrase', () => {
743
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
744
+ message: 'explain optics in a water glass',
745
+ knowledgePoints: [{
746
+ ...knowledgePoint,
747
+ matchedSpans: [{
748
+ atomId: 'optics_heading',
749
+ title: '3. 光学:光与系统的相互作用',
750
+ snippet: '3. 光学:光与系统的相互作用\n当光线穿过空气、玻璃和水时会发生折射。',
751
+ sourcePath: 'Knowledge_Base/waterglass/optics.md',
752
+ score: 0.94,
753
+ citation: null,
754
+ }],
755
+ }],
756
+ graphContext,
757
+ });
758
+ expect(plan.claims[0].statement).toBe('当光线穿过空气、玻璃和水时会发生折射。');
759
+ });
760
+ test('does not promote a numeric section marker into a definition claim', () => {
761
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
762
+ message: 'what is water glass',
763
+ knowledgePoints: [{
764
+ ...knowledgePoint,
765
+ matchedSpans: [{
766
+ atomId: 'thermal_section',
767
+ title: '4. Thermodynamics: Heat Transfer',
768
+ snippet: '#### 4. Thermodynamics: Heat Transfer. The temperature field changes through heat conduction.',
769
+ sourcePath: 'Knowledge_Base/waterglass/thermal.md',
770
+ score: 0.9,
771
+ citation: null,
772
+ }],
773
+ }],
774
+ graphContext,
775
+ });
776
+ expect(plan.claims.map((claim) => claim.statement)).not.toContain('4.');
777
+ expect(plan.claims.map((claim) => claim.statement)).toContain('The temperature field changes through heat conduction.');
778
+ });
779
+ test('turns multilingual comparison prose into a required contrast claim', () => {
780
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
781
+ message: 'compare water glass and plastic cup',
782
+ knowledgePoints: [knowledgePoint],
783
+ graphContext,
784
+ ragContextPack: {
785
+ ...ragContextPack,
786
+ query: 'compare water glass and plastic cup',
787
+ fragments: [
788
+ {
789
+ ...ragContextPack.fragments[0],
790
+ fragmentId: 'rag_parent_material_comparison',
791
+ role: 'parent_context',
792
+ atomId: 'water_glass',
793
+ title: '相关技术与比较数学模型',
794
+ text: [
795
+ '光学系统比较模型: 水杯透镜 vs.',
796
+ '热损失模型可写为 Q = kAΔT/d。',
797
+ '这是一种远比水杯复杂的数学优化。',
798
+ '玻璃水杯通常比PET塑料杯更透明。',
799
+ '对于相同尺寸的杯子,PET塑料杯的热损失速率大约是玻璃杯的1/5。',
800
+ ].join(' '),
801
+ score: 0.88,
802
+ },
803
+ ],
804
+ },
805
+ });
806
+ expect(plan.claims.map((claim) => ({
807
+ role: claim.role,
808
+ required: claim.required,
809
+ statement: claim.statement,
810
+ }))).toContainEqual(expect.objectContaining({
811
+ role: 'contrast',
812
+ required: true,
813
+ statement: '对于相同尺寸的杯子,PET塑料杯的热损失速率大约是玻璃杯的1/5。',
814
+ }));
815
+ expect(plan.requiredRoles).toContain('contrast');
816
+ expect(plan.claims).toContainEqual(expect.objectContaining({
817
+ role: 'contrast',
818
+ required: true,
819
+ statement: '玻璃水杯通常比PET塑料杯更透明。',
820
+ }));
821
+ expect(plan.claims.some((claim) => claim.statement.includes('数学优化'))).toBe(false);
822
+ });
823
+ test('does not spend compare-claim budget on a clause that covers only one requested branch', () => {
824
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
825
+ message: 'compare water glass and plastic cup',
826
+ knowledgePoints: [knowledgePoint],
827
+ graphContext,
828
+ ragContextPack: {
829
+ ...ragContextPack,
830
+ query: 'compare water glass and plastic cup',
831
+ fragments: [{
832
+ ...ragContextPack.fragments[0],
833
+ fragmentId: 'rag_parent_mixed_comparisons',
834
+ role: 'parent_context',
835
+ atomId: 'water_glass',
836
+ title: '相关技术与比较数学模型',
837
+ text: [
838
+ 'PET塑料杯的热损失速率大约是玻璃杯的1/5。',
839
+ '消色差透镜使用两种不同色散特性的玻璃组合。',
840
+ ].join(' '),
841
+ score: 0.88,
842
+ }],
843
+ },
844
+ });
845
+ expect(plan.claims).toContainEqual(expect.objectContaining({
846
+ role: 'contrast',
847
+ statement: 'PET塑料杯的热损失速率大约是玻璃杯的1/5。',
848
+ }));
849
+ expect(plan.claims.some((claim) => claim.statement.includes('消色差透镜'))).toBe(false);
850
+ });
851
+ test('drops full-document comparison scaffolding while retaining remote facts for both operands', () => {
852
+ const plan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
853
+ message: 'compare nominal date full scan source with field date full scan source',
854
+ knowledgePoints: [knowledgePoint],
855
+ graphContext,
856
+ ragContextPack: {
857
+ ...ragContextPack,
858
+ query: 'compare nominal date full scan source with field date full scan source',
859
+ fragments: [
860
+ {
861
+ ...ragContextPack.fragments[0],
862
+ fragmentId: 'nominal_full_document',
863
+ role: 'direct_support',
864
+ atomId: 'nominal_date',
865
+ sourcePath: 'Knowledge_Base/test/nominal-date.md',
866
+ title: 'Nominal Date Full Scan Source',
867
+ text: 'Nominal date full scan source is the scoped comparison document for full-document release-date augmentation. This opening section is intentionally separate from the remote release-date statement. Local date filler paragraph one keeps the remote appendix away from the matched opening span.',
868
+ },
869
+ {
870
+ ...ragContextPack.fragments[0],
871
+ fragmentId: 'field_full_document',
872
+ role: 'direct_support',
873
+ atomId: 'field_date',
874
+ sourcePath: 'Knowledge_Base/test/field-date.md',
875
+ title: 'Field Date Full Scan Source',
876
+ text: 'Field date full scan source is the scoped comparison document for full-document release-date augmentation. This opening section is intentionally separate from the remote release-date statement. Local date filler paragraph one keeps the remote appendix away from the matched opening span.',
877
+ },
878
+ {
879
+ ...ragContextPack.fragments[0],
880
+ fragmentId: 'nominal_remote_date',
881
+ role: 'direct_support',
882
+ atomId: 'nominal_remote_date',
883
+ sourcePath: 'Knowledge_Base/test/nominal-date.md',
884
+ title: 'Remote Nominal Date Appendix',
885
+ text: 'The migration release date is 2026-07-01 in the remote nominal date appendix.',
886
+ },
887
+ {
888
+ ...ragContextPack.fragments[0],
889
+ fragmentId: 'field_remote_date',
890
+ role: 'direct_support',
891
+ atomId: 'field_remote_date',
892
+ sourcePath: 'Knowledge_Base/test/field-date.md',
893
+ title: 'Remote Field Date Appendix',
894
+ text: 'The migration release date is 2026-08-15 in the remote field date appendix.',
895
+ },
896
+ ],
897
+ },
898
+ });
899
+ const statements = plan.claims.map((claim) => claim.statement);
900
+ expect(statements).toEqual(expect.arrayContaining([
901
+ 'The migration release date is 2026-07-01 in the remote nominal date appendix.',
902
+ 'The migration release date is 2026-08-15 in the remote field date appendix.',
903
+ ]));
904
+ expect(statements.join(' ')).not.toContain('filler paragraph');
905
+ expect(statements.join(' ')).not.toContain('scoped comparison document');
906
+ });
907
+ });