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,1721 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeAgentConversationKnowledgePoints = mergeAgentConversationKnowledgePoints;
4
+ exports.collectAgentConversationAtomIds = collectAgentConversationAtomIds;
5
+ exports.buildScopedConversationReply = buildScopedConversationReply;
6
+ const answerReleaseReview_1 = require("./answerReleaseReview");
7
+ const graphContextAssembler_1 = require("./graphContextAssembler");
8
+ const graphAnswerPlan_1 = require("./graphAnswerPlan");
9
+ const graphAnswerCoverage_1 = require("./graphAnswerCoverage");
10
+ const graphAnswerFacts_1 = require("./graphAnswerFacts");
11
+ const ragPublicText_1 = require("./ragPublicText");
12
+ const graphAnswerQualityPolicy_1 = require("./graphAnswerQualityPolicy");
13
+ const answerTaskPlan_1 = require("./answerTaskPlan");
14
+ const LEGACY_FULL_RESPONSE_MAX_CHARS = 24000;
15
+ const LEGACY_FULL_RESPONSE_MAX_FRAGMENTS = 24;
16
+ const FULL_REPORT_FRAGMENT_ROLES = new Set([
17
+ 'direct_support',
18
+ 'parent_context',
19
+ 'adjacent_context',
20
+ 'graph_neighbor_support',
21
+ 'conflict',
22
+ ]);
23
+ function isFullResponseMode(params) {
24
+ return params.responseMode === 'full';
25
+ }
26
+ function normalizeSourcePathForComparison(value) {
27
+ return String(value || '')
28
+ .trim()
29
+ .replace(/\\/gu, '/')
30
+ .replace(/\/{2,}/gu, '/')
31
+ .replace(/\/+$/gu, '')
32
+ .toLowerCase();
33
+ }
34
+ function resolveFullReportAnchorSource(params) {
35
+ const anchorAtomId = normalizeWhitespace(String(params.graphContext?.anchorAtomId || ''));
36
+ const anchorDocumentId = normalizeWhitespace(String(params.graphContext?.anchorDocumentId || ''));
37
+ const knowledgePoints = Array.isArray(params.knowledgePoints) ? params.knowledgePoints : [];
38
+ const anchorPoint = knowledgePoints.find((point) => {
39
+ const pointAtomId = normalizeWhitespace(String(point.atomId || ''));
40
+ const pointAtomIds = Array.isArray(point.atomIds) ? point.atomIds.map((atomId) => normalizeWhitespace(String(atomId || ''))) : [];
41
+ return Boolean(anchorAtomId && (pointAtomId === anchorAtomId || pointAtomIds.includes(anchorAtomId)));
42
+ })
43
+ || knowledgePoints.find((point) => anchorDocumentId && normalizeWhitespace(String(point.documentId || '')) === anchorDocumentId)
44
+ || knowledgePoints[0];
45
+ const documentId = anchorDocumentId || normalizeWhitespace(String(anchorPoint?.documentId || ''));
46
+ let sourcePath = normalizeSourcePathForComparison(String(anchorPoint?.sourcePath || ''));
47
+ if (!sourcePath && documentId) {
48
+ const sourceFragment = (params.ragContextPack?.fragments || []).find((fragment) => (fragment.role !== 'graph_neighbor_support'
49
+ && normalizeWhitespace(String(fragment.documentId || '')) === documentId
50
+ && normalizeSourcePathForComparison(String(fragment.sourcePath || ''))));
51
+ sourcePath = normalizeSourcePathForComparison(String(sourceFragment?.sourcePath || ''));
52
+ }
53
+ return { documentId, sourcePath };
54
+ }
55
+ function isFullReportGraphNeighborFromAnchorSource(fragment, anchorSource) {
56
+ if (fragment.role !== 'graph_neighbor_support') {
57
+ return true;
58
+ }
59
+ const fragmentDocumentId = normalizeWhitespace(String(fragment.documentId || ''));
60
+ const fragmentSourcePath = normalizeSourcePathForComparison(String(fragment.sourcePath || ''));
61
+ if (!anchorSource.documentId && !anchorSource.sourcePath) {
62
+ return false;
63
+ }
64
+ if (anchorSource.documentId && fragmentDocumentId !== anchorSource.documentId) {
65
+ return false;
66
+ }
67
+ if (anchorSource.sourcePath && fragmentSourcePath !== anchorSource.sourcePath) {
68
+ return false;
69
+ }
70
+ return Boolean(fragmentDocumentId || fragmentSourcePath);
71
+ }
72
+ function hasFullReportBodyOutsideMermaid(fragment) {
73
+ const source = stripFullReportMermaid(String(fragment.text || ''))
74
+ .replace(/^#{1,6}\s+[^\n]*$/gmu, '')
75
+ .trim();
76
+ return Boolean(source) && !isFullReportInternalBlock(source);
77
+ }
78
+ function fullReportProductCaps(params) {
79
+ const budget = params.responseBudget;
80
+ const productCapDisabled = budget?.productCapDisabled === true;
81
+ if (!budget) {
82
+ return {
83
+ maxFragments: LEGACY_FULL_RESPONSE_MAX_FRAGMENTS,
84
+ maxChars: LEGACY_FULL_RESPONSE_MAX_CHARS,
85
+ productCapDisabled: false,
86
+ };
87
+ }
88
+ return {
89
+ maxFragments: productCapDisabled
90
+ ? budget.runtimeGovernor.maxFragmentsProcessed
91
+ : (budget.rag?.maxFragments || LEGACY_FULL_RESPONSE_MAX_FRAGMENTS),
92
+ maxChars: productCapDisabled
93
+ ? budget.runtimeGovernor.maxReportChars
94
+ : (budget.reportMaxChars || LEGACY_FULL_RESPONSE_MAX_CHARS),
95
+ productCapDisabled,
96
+ };
97
+ }
98
+ function selectFullReportFragments(params) {
99
+ const fragments = params.ragContextPack?.fragments || [];
100
+ const anchorSource = resolveFullReportAnchorSource(params);
101
+ const caps = fullReportProductCaps(params);
102
+ let truncated = false;
103
+ let truncationReason;
104
+ const candidates = fragments
105
+ .filter((fragment) => {
106
+ const title = normalizeWhitespace(String(fragment.title || '')).toLowerCase();
107
+ const headingPath = (Array.isArray(fragment.headingPath) ? fragment.headingPath : [])
108
+ .map((heading) => normalizeWhitespace(String(heading || '')).toLowerCase())
109
+ .join(' ');
110
+ const mermaidMarked = title.includes('mermaid') || headingPath.includes('mermaid');
111
+ return !title.includes('preamble')
112
+ && (!mermaidMarked || hasFullReportBodyOutsideMermaid(fragment))
113
+ && !headingPath.includes('preamble')
114
+ && (!headingPath.includes('mermaid') || hasFullReportBodyOutsideMermaid(fragment));
115
+ })
116
+ .filter((fragment) => (FULL_REPORT_FRAGMENT_ROLES.has(fragment.role)
117
+ && isFullReportGraphNeighborFromAnchorSource(fragment, anchorSource)));
118
+ const selected = [];
119
+ const selectedSections = new Set();
120
+ let usedChars = 0;
121
+ const sectionKey = (fragment) => {
122
+ const path = Array.isArray(fragment.headingPath) ? fragment.headingPath : [];
123
+ const leaf = normalizeWhitespace(String(path[path.length - 1] || fragment.title || ''))
124
+ .replace(/^#+\s*/u, '')
125
+ .replace(/\s*\((?:mermaid|code|diagram)\s+block\)\s*$/iu, '')
126
+ .toLowerCase();
127
+ return `${fragment.documentId}:${leaf}`;
128
+ };
129
+ const appendCandidate = (fragment) => {
130
+ if (selected.length >= caps.maxFragments || usedChars >= caps.maxChars) {
131
+ truncated = true;
132
+ truncationReason = caps.productCapDisabled
133
+ ? (selected.length >= caps.maxFragments ? 'runtime_fragment_limit' : 'runtime_report_chars_limit')
134
+ : (selected.length >= caps.maxFragments ? 'product_fragment_limit' : 'product_report_chars_limit');
135
+ return;
136
+ }
137
+ const text = String(fragment.text || '').trim();
138
+ if (!text
139
+ || !FULL_REPORT_FRAGMENT_ROLES.has(fragment.role)
140
+ || !isFullReportGraphNeighborFromAnchorSource(fragment, anchorSource)
141
+ || (fragment.truncated && !hasBalancedMathDelimiters(text))) {
142
+ return;
143
+ }
144
+ const remaining = caps.maxChars - usedChars;
145
+ if (text.length > remaining) {
146
+ return;
147
+ }
148
+ const key = sectionKey(fragment);
149
+ if (selectedSections.has(key)) {
150
+ return;
151
+ }
152
+ selected.push({
153
+ ...fragment,
154
+ text,
155
+ charCount: text.length,
156
+ });
157
+ usedChars += text.length;
158
+ selectedSections.add(key);
159
+ };
160
+ const rolePriority = {
161
+ parent_context: 0,
162
+ adjacent_context: 1,
163
+ graph_neighbor_support: 2,
164
+ direct_support: 3,
165
+ conflict: 4,
166
+ };
167
+ const grouped = new Map();
168
+ candidates.forEach((fragment) => {
169
+ const key = sectionKey(fragment);
170
+ const group = grouped.get(key) || [];
171
+ group.push(fragment);
172
+ grouped.set(key, group);
173
+ });
174
+ const sectionCandidates = Array.from(grouped.values())
175
+ .map((group) => group.sort((left, right) => {
176
+ const roleDelta = (rolePriority[left.role] ?? 9) - (rolePriority[right.role] ?? 9);
177
+ if (roleDelta !== 0) {
178
+ return roleDelta;
179
+ }
180
+ const boundaryDelta = Number(right.sourceBoundary === 'full_document') - Number(left.sourceBoundary === 'full_document');
181
+ if (boundaryDelta !== 0) {
182
+ return boundaryDelta;
183
+ }
184
+ const lengthDelta = String(right.text || '').length - String(left.text || '').length;
185
+ if (lengthDelta !== 0) {
186
+ return lengthDelta;
187
+ }
188
+ const leftLine = Number.isFinite(Number(left.startLine)) ? Number(left.startLine) : Number.MAX_SAFE_INTEGER;
189
+ const rightLine = Number.isFinite(Number(right.startLine)) ? Number(right.startLine) : Number.MAX_SAFE_INTEGER;
190
+ return leftLine - rightLine;
191
+ }))
192
+ .sort((left, right) => {
193
+ const leftFragment = left[0];
194
+ const rightFragment = right[0];
195
+ const leftLine = Number.isFinite(Number(leftFragment?.startLine)) ? Number(leftFragment?.startLine) : Number.MAX_SAFE_INTEGER;
196
+ const rightLine = Number.isFinite(Number(rightFragment?.startLine)) ? Number(rightFragment?.startLine) : Number.MAX_SAFE_INTEGER;
197
+ if (leftFragment?.documentId !== rightFragment?.documentId) {
198
+ return String(leftFragment?.documentId || '').localeCompare(String(rightFragment?.documentId || ''));
199
+ }
200
+ return leftLine - rightLine;
201
+ });
202
+ sectionCandidates.forEach((group) => appendCandidate(group[0]));
203
+ if (selected.length < sectionCandidates.length && !truncated) {
204
+ truncated = true;
205
+ truncationReason = caps.productCapDisabled ? 'runtime_report_chars_limit' : 'product_report_chars_limit';
206
+ }
207
+ return { fragments: selected, truncated, truncationReason };
208
+ }
209
+ function hasBalancedMathDelimiters(value) {
210
+ const source = String(value || '');
211
+ const displayCount = (source.match(/(?<!\\)\$\$/gu) || []).length;
212
+ if (displayCount % 2 !== 0) {
213
+ return false;
214
+ }
215
+ const inlineSource = source.replace(/(?<!\\)\$\$/gu, '');
216
+ return ((inlineSource.match(/(?<!\\)\$/gu) || []).length % 2) === 0;
217
+ }
218
+ function isFullReportInternalBlock(value) {
219
+ const normalized = normalizeWhitespace(value);
220
+ return !normalized
221
+ || /(?:遵从.{0,20}(?:指示|要求)|仅基于标题|所有推理过程|推理过程以英文|最终输出|输出为简体中文|本技术文档旨在|本文档旨在|we will|based only on the title|final output|all reasoning)/iu.test(normalized)
222
+ || /(?:grounded by|key evidence|missDiagnostics|workspaceReadiness|matchedAtomCount|titleLikeQueries|retrieval_candidates_below_threshold|rag context pack|planner)/iu.test(normalized);
223
+ }
224
+ function stripFullReportMermaid(value) {
225
+ return String(value || '')
226
+ .replace(/```mermaid[\s\S]*?(?:```|$)/giu, '')
227
+ .replace(/```(?:graphviz|dot)[\s\S]*?(?:```|$)/giu, '')
228
+ .trim();
229
+ }
230
+ function sanitizeFullReportBlock(value) {
231
+ const source = String(value || '').replace(/\r\n?/gu, '\n').trim();
232
+ if (!source) {
233
+ return '';
234
+ }
235
+ const lines = source.split('\n');
236
+ const sanitizedLines = [];
237
+ lines.forEach((line) => {
238
+ const normalizedLine = line.trim();
239
+ if (!normalizedLine) {
240
+ return;
241
+ }
242
+ if (/^#{1,6}\s+/u.test(normalizedLine) || /^\s*\|/u.test(normalizedLine)) {
243
+ if (!isFullReportInternalBlock(normalizedLine)) {
244
+ sanitizedLines.push(normalizedLine);
245
+ }
246
+ return;
247
+ }
248
+ const clauses = normalizedLine
249
+ .split(/(?<=[.!?。!?;;])(?:\s+|(?=[\p{L}]))/u)
250
+ .map((clause) => clause.trim())
251
+ .filter((clause) => clause && !isFullReportInternalBlock(clause));
252
+ if (clauses.length > 0) {
253
+ sanitizedLines.push(clauses.join(' '));
254
+ }
255
+ });
256
+ return sanitizedLines.join('\n').trim();
257
+ }
258
+ function isStandaloneFullReportHeading(value) {
259
+ return /^#{1,6}\s+\S[^\n]*$/u.test(String(value || '').trim());
260
+ }
261
+ function splitFullReportHeadingBody(value, headingPath) {
262
+ const normalized = String(value || '').trim();
263
+ const headingName = normalizeWhitespace(String(headingPath[headingPath.length - 1] || ''))
264
+ .replace(/^#+\s*/u, '')
265
+ .trim();
266
+ if (!normalized || !headingName || !/^#{1,6}\s+/u.test(normalized)) {
267
+ return { heading: '', body: normalized };
268
+ }
269
+ const escapedHeadingName = headingName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
270
+ const headingMatch = normalized.match(new RegExp(`^(#{1,6})\\s+${escapedHeadingName}(?:\\s+|$)([\\s\\S]*)$`, 'iu'));
271
+ if (!headingMatch) {
272
+ return { heading: '', body: normalized };
273
+ }
274
+ return {
275
+ heading: `${headingMatch[1]} ${headingName}`,
276
+ body: String(headingMatch[2] || '').trim(),
277
+ };
278
+ }
279
+ function appendFullReportBlock(lines, block, currentLength, dedupeValue = block, maxChars = LEGACY_FULL_RESPONSE_MAX_CHARS, onLimit) {
280
+ const normalized = sanitizeFullReportBlock(block);
281
+ if (!normalized || isFullReportInternalBlock(normalized) || !hasBalancedMathDelimiters(normalized)) {
282
+ return false;
283
+ }
284
+ const separatorLength = lines.length > 0 ? 2 : 0;
285
+ if (currentLength.value + separatorLength + normalized.length > maxChars) {
286
+ onLimit?.('report_chars_limit');
287
+ return false;
288
+ }
289
+ const key = normalizeWhitespace(dedupeValue).toLowerCase();
290
+ if (!key || currentLength.keys.has(key)) {
291
+ return false;
292
+ }
293
+ lines.push(normalized);
294
+ currentLength.value += separatorLength + normalized.length;
295
+ currentLength.keys.add(key);
296
+ return true;
297
+ }
298
+ function buildFullTechnicalReportResult(params, graphContext, graphAnswerPlan) {
299
+ const useChinese = useChineseAnswerLanguage(params);
300
+ const selection = selectFullReportFragments(params);
301
+ const caps = fullReportProductCaps(params);
302
+ if (selection.fragments.length <= 0) {
303
+ return {
304
+ answer: buildScopedConversationAnswer({ ...params, responseMode: 'slim' }, graphContext, graphAnswerPlan),
305
+ state: {
306
+ truncated: selection.truncated,
307
+ truncationReason: selection.truncationReason,
308
+ selectedFragmentCount: 0,
309
+ acceptedBlockCount: 0,
310
+ },
311
+ };
312
+ }
313
+ const reportBlocks = [];
314
+ const reportLength = { value: 0, keys: new Set() };
315
+ let truncated = selection.truncated;
316
+ let truncationReason = selection.truncationReason;
317
+ const markLimit = (reason) => {
318
+ truncated = true;
319
+ if (!truncationReason) {
320
+ truncationReason = caps.productCapDisabled ? `runtime_${reason}` : `product_${reason}`;
321
+ }
322
+ };
323
+ selection.fragments.forEach((fragment) => {
324
+ const source = stripFullReportMermaid(fragment.text);
325
+ let pendingHeading = '';
326
+ source
327
+ .split(/\n{2,}/u)
328
+ .map((block) => block.trim())
329
+ .filter(Boolean)
330
+ .forEach((block) => {
331
+ const normalizedBlock = block
332
+ .replace(/^#{1,6}\s+Water Glass\s*$/imu, '')
333
+ .trim();
334
+ if (!normalizedBlock) {
335
+ return;
336
+ }
337
+ const headingBody = splitFullReportHeadingBody(normalizedBlock, Array.isArray(fragment.headingPath) ? fragment.headingPath : []);
338
+ if (headingBody.heading && headingBody.body) {
339
+ appendFullReportBlock(reportBlocks, `${headingBody.heading}\n\n${headingBody.body}`, reportLength, headingBody.body, caps.maxChars, markLimit);
340
+ pendingHeading = '';
341
+ return;
342
+ }
343
+ if (isStandaloneFullReportHeading(normalizedBlock)) {
344
+ pendingHeading = normalizedBlock;
345
+ return;
346
+ }
347
+ const sectionBlock = pendingHeading
348
+ ? `${pendingHeading}\n\n${normalizedBlock}`
349
+ : normalizedBlock;
350
+ appendFullReportBlock(reportBlocks, sectionBlock, reportLength, pendingHeading ? normalizedBlock : sectionBlock, caps.maxChars, markLimit);
351
+ pendingHeading = '';
352
+ });
353
+ });
354
+ const directClaims = graphAnswerPlan.claims
355
+ .filter((claim) => claim.required || claim.role === 'definition')
356
+ .map((claim) => normalizeWhitespace((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(claim.statement)))
357
+ .filter(Boolean);
358
+ if (directClaims.length > 0 && !directClaims.some((claim) => reportBlocks.some((block) => normalizeWhitespace(block).toLowerCase().includes(claim.toLowerCase())))) {
359
+ const heading = useChinese ? '## 核心定义' : '## Definition';
360
+ appendFullReportBlock(reportBlocks, heading, reportLength, heading, caps.maxChars, markLimit);
361
+ directClaims.forEach((claim) => appendFullReportBlock(reportBlocks, claim, reportLength, claim, caps.maxChars, markLimit));
362
+ }
363
+ const answer = reportBlocks.join('\n\n').trim()
364
+ || buildScopedConversationAnswer({ ...params, responseMode: 'slim' }, graphContext, graphAnswerPlan);
365
+ return {
366
+ answer,
367
+ state: {
368
+ truncated,
369
+ truncationReason,
370
+ selectedFragmentCount: selection.fragments.length,
371
+ acceptedBlockCount: reportBlocks.length,
372
+ },
373
+ };
374
+ }
375
+ function buildFullTechnicalReport(params, graphContext, graphAnswerPlan) {
376
+ return buildFullTechnicalReportResult(params, graphContext, graphAnswerPlan).answer;
377
+ }
378
+ function normalizeWhitespace(value) {
379
+ return String(value || '').replace(/\s+/g, ' ').trim();
380
+ }
381
+ function humanizeRelationKind(value) {
382
+ return normalizeWhitespace(String(value || '').replace(/_/g, ' '));
383
+ }
384
+ function formatGraphConnectionPath(connectionPath) {
385
+ const titles = Array.isArray(connectionPath.pathTitles)
386
+ ? connectionPath.pathTitles.map((title) => normalizeWhitespace(String(title || '').trim())).filter(Boolean)
387
+ : [];
388
+ const edges = Array.isArray(connectionPath.pathEdges)
389
+ ? connectionPath.pathEdges
390
+ : [];
391
+ if (titles.length <= 1 || edges.length <= 0) {
392
+ return titles.join(' -> ');
393
+ }
394
+ const segments = [titles[0]];
395
+ edges.forEach((edge, index) => {
396
+ const nextTitle = titles[index + 1];
397
+ if (!nextTitle) {
398
+ return;
399
+ }
400
+ segments.push(humanizeRelationKind(edge && edge.relationKind ? edge.relationKind : 'link'), nextTitle);
401
+ });
402
+ return segments.join(' -> ');
403
+ }
404
+ function clampUnit(value) {
405
+ if (!Number.isFinite(value)) {
406
+ return 0;
407
+ }
408
+ return Number(Math.max(0, Math.min(1, value)).toFixed(4));
409
+ }
410
+ function addDaysIso(value, days) {
411
+ const parsed = Date.parse(value);
412
+ const baseTimestamp = Number.isFinite(parsed) ? parsed : 0;
413
+ return new Date(baseTimestamp + days * 24 * 60 * 60 * 1000).toISOString();
414
+ }
415
+ function buildFallbackKnowledgeRunId(params, generatedAt) {
416
+ const seed = [
417
+ generatedAt,
418
+ params.message,
419
+ params.usedScope.workspaceId || '',
420
+ params.usedScope.corpusId || '',
421
+ params.citations.map((citation) => citation.citationId).join('|'),
422
+ ].join('|');
423
+ let hash = 0;
424
+ for (let index = 0; index < seed.length; index += 1) {
425
+ hash = ((hash << 5) - hash + seed.charCodeAt(index)) | 0;
426
+ }
427
+ return `knowledge_run_${Math.abs(hash) || 1}`;
428
+ }
429
+ function escapeRegExpLiteral(value) {
430
+ return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
431
+ }
432
+ function buildKnowledgeCitation(item, index) {
433
+ const atom = item.atom;
434
+ const evidence = item.evidenceSpans[0];
435
+ return {
436
+ citationId: String(evidence?.id || `citation_${atom.id}_${index + 1}`).trim(),
437
+ atomId: atom.id,
438
+ documentId: atom.documentId,
439
+ sourcePath: atom.sourcePath,
440
+ title: atom.title,
441
+ snippet: normalizeWhitespace(String(evidence?.snippet
442
+ || atom.content
443
+ || atom.title
444
+ || '').slice(0, 280)),
445
+ startOffset: evidence?.startOffset,
446
+ endOffset: evidence?.endOffset,
447
+ startLine: evidence?.startLine,
448
+ endLine: evidence?.endLine,
449
+ score: Number(Number(item.score || 0).toFixed(4)),
450
+ };
451
+ }
452
+ function buildAgentConversationKnowledgePoint(item, index, buildCapabilities) {
453
+ const atom = item.atom;
454
+ const citation = buildKnowledgeCitation(item, index);
455
+ const summary = normalizeWhitespace(String(atom.content || atom.title || '').slice(0, 240)) || atom.title;
456
+ return {
457
+ atomId: atom.id,
458
+ atomIds: [atom.id],
459
+ documentId: atom.documentId,
460
+ sourcePath: atom.sourcePath,
461
+ title: atom.title,
462
+ summary,
463
+ evidenceSnippet: citation.snippet || summary || atom.title,
464
+ score: Number(Number(item.score || 0).toFixed(4)),
465
+ citation,
466
+ citations: citation ? [citation] : [],
467
+ matchedSpans: [
468
+ {
469
+ atomId: atom.id,
470
+ title: atom.title,
471
+ snippet: citation.snippet || summary || atom.title,
472
+ sourcePath: atom.sourcePath,
473
+ startOffset: citation.startOffset,
474
+ endOffset: citation.endOffset,
475
+ startLine: citation.startLine,
476
+ endLine: citation.endLine,
477
+ score: Number(Number(item.score || 0).toFixed(4)),
478
+ citation,
479
+ },
480
+ ],
481
+ matchCount: 1,
482
+ relationPath: item.relationPath.map((edge) => ({
483
+ edgeId: edge.id,
484
+ sourceAtomId: edge.sourceAtomId,
485
+ targetAtomId: edge.targetAtomId,
486
+ relationKind: edge.relationKind,
487
+ confidence: Number(Number(edge.confidence || 0).toFixed(4)),
488
+ })),
489
+ relationPathAtomIds: Array.from(new Set(item.relationPath.flatMap((edge) => [edge.sourceAtomId, edge.targetAtomId]))).filter((atomId) => atomId !== atom.id),
490
+ relationKinds: Array.from(new Set(item.relationPath.map((edge) => edge.relationKind))),
491
+ temporalValidity: {
492
+ isValid: item.temporalValidity.isValid,
493
+ checkedAt: item.temporalValidity.checkedAt,
494
+ reasons: [...item.temporalValidity.reasons],
495
+ details: Array.isArray(item.temporalValidity.details)
496
+ ? item.temporalValidity.details.map((detail) => ({ ...detail }))
497
+ : [],
498
+ },
499
+ capabilities: buildCapabilities(atom.id),
500
+ };
501
+ }
502
+ function mergeAgentConversationKnowledgePoints(items, buildCapabilities) {
503
+ const groups = new Map();
504
+ items.forEach((item, index) => {
505
+ const atom = item.atom;
506
+ const groupKey = String(atom.documentId || atom.id || `atom_${index}`).trim();
507
+ const citation = buildKnowledgeCitation(item, index);
508
+ const snippet = citation.snippet
509
+ || normalizeWhitespace(String(atom.content || atom.title || '').slice(0, 280))
510
+ || atom.title;
511
+ let group = groups.get(groupKey);
512
+ if (!group) {
513
+ const point = buildAgentConversationKnowledgePoint(item, index, buildCapabilities);
514
+ point.citation = citation;
515
+ point.citations = [];
516
+ point.matchedSpans = [];
517
+ point.atomIds = [];
518
+ point.matchCount = 0;
519
+ point.relationPath = [];
520
+ point.relationPathAtomIds = [];
521
+ point.relationKinds = [];
522
+ point.temporalValidity = undefined;
523
+ group = {
524
+ point,
525
+ atomIds: new Set(),
526
+ citationKeys: new Set(),
527
+ spanKeys: new Set(),
528
+ relationEdgeIds: new Set(),
529
+ relationPathAtomIds: new Set(),
530
+ relationKinds: new Set(),
531
+ temporalValidityCheckedAt: String(item.temporalValidity.checkedAt || ''),
532
+ temporalValidityReasons: new Set(),
533
+ temporalValidityDetails: new Map(),
534
+ };
535
+ groups.set(groupKey, group);
536
+ }
537
+ group.atomIds.add(atom.id);
538
+ group.point.atomIds = Array.from(group.atomIds.values());
539
+ group.point.score = Math.max(group.point.score, Number(Number(item.score || 0).toFixed(4)));
540
+ const citationKey = [
541
+ citation.documentId,
542
+ citation.sourcePath,
543
+ citation.startLine || '',
544
+ citation.endLine || '',
545
+ citation.snippet,
546
+ ].join('|');
547
+ if (!group.citationKeys.has(citationKey)) {
548
+ group.citationKeys.add(citationKey);
549
+ group.point.citations = [...(group.point.citations || []), citation];
550
+ if (!group.point.citation) {
551
+ group.point.citation = citation;
552
+ }
553
+ }
554
+ const spanKey = [
555
+ atom.id,
556
+ citation.startLine || '',
557
+ citation.endLine || '',
558
+ snippet,
559
+ ].join('|');
560
+ if (!group.spanKeys.has(spanKey)) {
561
+ group.spanKeys.add(spanKey);
562
+ group.point.matchedSpans = [
563
+ ...(group.point.matchedSpans || []),
564
+ {
565
+ atomId: atom.id,
566
+ title: atom.title,
567
+ snippet,
568
+ sourcePath: atom.sourcePath,
569
+ startOffset: citation.startOffset,
570
+ endOffset: citation.endOffset,
571
+ startLine: citation.startLine,
572
+ endLine: citation.endLine,
573
+ score: Number(Number(item.score || 0).toFixed(4)),
574
+ citation,
575
+ },
576
+ ];
577
+ group.point.matchCount = group.point.matchedSpans.length;
578
+ }
579
+ const relationPath = Array.isArray(item.relationPath) ? item.relationPath : [];
580
+ relationPath.forEach((edge) => {
581
+ const edgeId = String(edge && edge.id || '').trim();
582
+ if (!edgeId) {
583
+ return;
584
+ }
585
+ if (!Array.isArray(group.point.relationPath)) {
586
+ group.point.relationPath = [];
587
+ }
588
+ if (!group.relationEdgeIds.has(edgeId)) {
589
+ group.relationEdgeIds.add(edgeId);
590
+ group.point.relationPath.push({
591
+ edgeId,
592
+ sourceAtomId: String(edge.sourceAtomId || '').trim(),
593
+ targetAtomId: String(edge.targetAtomId || '').trim(),
594
+ relationKind: edge.relationKind,
595
+ confidence: Number(Number(edge.confidence || 0).toFixed(4)),
596
+ });
597
+ }
598
+ [edge.sourceAtomId, edge.targetAtomId]
599
+ .map((candidateAtomId) => String(candidateAtomId || '').trim())
600
+ .filter(Boolean)
601
+ .filter((candidateAtomId) => !group.atomIds.has(candidateAtomId))
602
+ .forEach((candidateAtomId) => group.relationPathAtomIds.add(candidateAtomId));
603
+ if (edge.relationKind) {
604
+ group.relationKinds.add(edge.relationKind);
605
+ }
606
+ });
607
+ const temporalValidity = item.temporalValidity && typeof item.temporalValidity === 'object'
608
+ ? item.temporalValidity
609
+ : null;
610
+ if (temporalValidity) {
611
+ const checkedAt = String(temporalValidity.checkedAt || '').trim();
612
+ if (checkedAt && checkedAt > group.temporalValidityCheckedAt) {
613
+ group.temporalValidityCheckedAt = checkedAt;
614
+ }
615
+ if (temporalValidity.isValid === false) {
616
+ group.point.temporalValidity = group.point.temporalValidity || {
617
+ isValid: true,
618
+ checkedAt: checkedAt,
619
+ reasons: [],
620
+ details: [],
621
+ };
622
+ group.point.temporalValidity.isValid = false;
623
+ }
624
+ if (!group.point.temporalValidity) {
625
+ group.point.temporalValidity = {
626
+ isValid: temporalValidity.isValid !== false,
627
+ checkedAt,
628
+ reasons: [],
629
+ details: [],
630
+ };
631
+ }
632
+ else if (temporalValidity.isValid === false) {
633
+ group.point.temporalValidity.isValid = false;
634
+ }
635
+ (Array.isArray(temporalValidity.reasons) ? temporalValidity.reasons : [])
636
+ .map((reason) => String(reason || '').trim())
637
+ .filter(Boolean)
638
+ .forEach((reason) => group.temporalValidityReasons.add(reason));
639
+ (Array.isArray(temporalValidity.details) ? temporalValidity.details : [])
640
+ .filter((detail) => Boolean(detail && typeof detail === 'object'))
641
+ .forEach((detail) => {
642
+ const detailKey = [
643
+ String(detail.edgeId || '').trim(),
644
+ String(detail.edgeKind || '').trim(),
645
+ String(detail.sourceAtomId || '').trim(),
646
+ String(detail.targetAtomId || '').trim(),
647
+ String(detail.validFrom || '').trim(),
648
+ String(detail.validTo || '').trim(),
649
+ ].join('|');
650
+ if (!group.temporalValidityDetails.has(detailKey)) {
651
+ group.temporalValidityDetails.set(detailKey, {
652
+ edgeId: String(detail.edgeId || '').trim(),
653
+ edgeKind: detail.edgeKind,
654
+ sourceAtomId: String(detail.sourceAtomId || '').trim(),
655
+ targetAtomId: String(detail.targetAtomId || '').trim(),
656
+ validFrom: String(detail.validFrom || '').trim(),
657
+ validTo: detail.validTo ? String(detail.validTo).trim() : undefined,
658
+ isActive: detail.isActive !== false,
659
+ });
660
+ }
661
+ });
662
+ }
663
+ });
664
+ return Array.from(groups.values()).map((group) => {
665
+ const citations = group.point.citations || [];
666
+ const spans = group.point.matchedSpans || [];
667
+ return {
668
+ ...group.point,
669
+ citation: group.point.citation || citations[0] || null,
670
+ citations,
671
+ matchedSpans: spans,
672
+ matchCount: spans.length,
673
+ evidenceSnippet: spans[0]?.snippet || group.point.evidenceSnippet,
674
+ relationPath: Array.isArray(group.point.relationPath) ? group.point.relationPath : [],
675
+ relationPathAtomIds: Array.from(group.relationPathAtomIds.values()),
676
+ relationKinds: Array.from(group.relationKinds.values()),
677
+ temporalValidity: group.point.temporalValidity
678
+ ? {
679
+ isValid: group.point.temporalValidity.isValid !== false,
680
+ checkedAt: group.temporalValidityCheckedAt || group.point.temporalValidity.checkedAt,
681
+ reasons: Array.from(group.temporalValidityReasons.values()),
682
+ details: Array.from(group.temporalValidityDetails.values()),
683
+ }
684
+ : undefined,
685
+ };
686
+ });
687
+ }
688
+ function collectAgentConversationAtomIds(knowledgePoints) {
689
+ const atomIds = new Set();
690
+ knowledgePoints.forEach((point) => {
691
+ const groupedAtomIds = Array.isArray(point.atomIds) && point.atomIds.length > 0
692
+ ? point.atomIds
693
+ : [point.atomId];
694
+ groupedAtomIds
695
+ .map((atomId) => String(atomId || '').trim())
696
+ .filter(Boolean)
697
+ .forEach((atomId) => atomIds.add(atomId));
698
+ });
699
+ return Array.from(atomIds.values());
700
+ }
701
+ function cleanConversationAnswerCandidate(value, point) {
702
+ let cleaned = String(value || '')
703
+ .replace(/```[\s\S]*?```/g, ' ')
704
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
705
+ .replace(/[*_~`>#|]/g, ' ')
706
+ .trim();
707
+ const title = normalizeWhitespace(String(point.title || '').replace(/[#*_~`]/g, ' '));
708
+ if (title) {
709
+ cleaned = cleaned
710
+ .replace(new RegExp(`^${escapeRegExpLiteral(title)}\\s*(?:[:\\uFF1A.\\-\\u2014]|\\r?\\n)+\\s*`, 'i'), '')
711
+ .trim();
712
+ const flattenedHeadingMatch = cleaned.match(new RegExp(`^${escapeRegExpLiteral(title)}\\s+(.+)$`, 'i'));
713
+ if (flattenedHeadingMatch) {
714
+ const remainder = normalizeWhitespace(flattenedHeadingMatch[1] || '');
715
+ const titleWordCount = title.split(/\s+/).filter(Boolean).length;
716
+ const repeatsTitle = new RegExp(`^${escapeRegExpLiteral(title)}(?:\\b|\\s)`, 'i').test(remainder);
717
+ const startsWithArticle = /^(?:a|an|the)\s+/i.test(remainder);
718
+ if (repeatsTitle || (titleWordCount > 1 && startsWithArticle)) {
719
+ cleaned = remainder;
720
+ }
721
+ }
722
+ }
723
+ return normalizeWhitespace(cleaned);
724
+ }
725
+ function selectScopedConversationDirectSentence(message, point) {
726
+ const candidates = [
727
+ ...(Array.isArray(point.matchedSpans) ? point.matchedSpans.map((span) => span.snippet) : []),
728
+ point.summary,
729
+ point.evidenceSnippet,
730
+ ]
731
+ .map((candidate) => cleanConversationAnswerCandidate(String(candidate || ''), point))
732
+ .filter(Boolean);
733
+ for (const candidate of candidates) {
734
+ const sentences = candidate.match(/[^.!?\u3002\uFF01\uFF1F]+[.!?\u3002\uFF01\uFF1F]?/g) || [candidate];
735
+ const directSentence = sentences
736
+ .map((sentence) => normalizeWhitespace(sentence))
737
+ .find((sentence) => {
738
+ const lower = sentence.toLowerCase();
739
+ return (lower.includes(' is ')
740
+ || lower.includes(' are ')
741
+ || lower.includes(' refers to ')
742
+ || lower.includes(' means ')
743
+ || sentence.includes('是')
744
+ || sentence.includes('指')) && sentence.length >= 12;
745
+ });
746
+ if (directSentence) {
747
+ return directSentence;
748
+ }
749
+ const firstSentence = normalizeWhitespace(sentences[0] || '');
750
+ if (firstSentence.length >= 12) {
751
+ return firstSentence;
752
+ }
753
+ }
754
+ return normalizeWhitespace(String(point.summary || point.evidenceSnippet || point.title || message || ''));
755
+ }
756
+ function classifyScopedConversationIntent(message) {
757
+ const normalized = String(message || '').trim().toLowerCase();
758
+ if (!normalized) {
759
+ return 'generic';
760
+ }
761
+ if (normalized.includes('compare')
762
+ || normalized.includes('difference')
763
+ || normalized.includes('vs')
764
+ || normalized.includes('区别')
765
+ || normalized.includes('对比')) {
766
+ return 'compare';
767
+ }
768
+ if (normalized.includes('how to')
769
+ || normalized.includes('how do')
770
+ || normalized.includes('steps')
771
+ || normalized.includes('plan')
772
+ || normalized.includes('如何')
773
+ || normalized.includes('怎么')
774
+ || normalized.includes('步骤')
775
+ || normalized.includes('方案')) {
776
+ return 'how_to';
777
+ }
778
+ if (/\b(?:why|cause|causes|caused|causal|because|reason|mechanism|mechanisms)\b/u.test(normalized)
779
+ || normalized.includes('为什么')
780
+ || normalized.includes('為什麼')
781
+ || normalized.includes('为何')
782
+ || normalized.includes('為何')
783
+ || normalized.includes('原因')
784
+ || normalized.includes('因果')
785
+ || normalized.includes('机制')
786
+ || normalized.includes('機制')
787
+ || normalized.includes('导致')
788
+ || normalized.includes('導致')) {
789
+ return 'causal_explain';
790
+ }
791
+ if (normalized.includes('what is')
792
+ || normalized.includes('explain')
793
+ || normalized.includes('解释')
794
+ || normalized.includes('什么是')) {
795
+ return 'explain';
796
+ }
797
+ return 'generic';
798
+ }
799
+ function containsCjk(value) {
800
+ return /[\u3040-\u30ff\u3400-\u9fff\uf900-\ufaff]/u.test(String(value || ''));
801
+ }
802
+ function useChineseAnswerLanguage(params) {
803
+ if (params.answerLanguage === 'zh') {
804
+ return true;
805
+ }
806
+ if (params.answerLanguage === 'en') {
807
+ return false;
808
+ }
809
+ return containsCjk([
810
+ params.message,
811
+ params.knowledgePoints[0]?.title,
812
+ params.knowledgePoints[0]?.summary,
813
+ params.graphContext?.anchorTitle,
814
+ ].filter(Boolean).join(' '));
815
+ }
816
+ function stripConversationAnswerTerminalPunctuation(value) {
817
+ return normalizeWhitespace(String(value || '').replace(/[.!?\u3002\uFF01\uFF1F]+$/u, ''));
818
+ }
819
+ function normalizeConversationAnswerSentence(value, useChinese) {
820
+ const normalized = normalizeWhitespace(String(value || ''));
821
+ if (!normalized) {
822
+ return '';
823
+ }
824
+ if (useChinese && /[.!?]$/u.test(normalized)) {
825
+ return normalized.replace(/[.!?]+$/u, '。');
826
+ }
827
+ return /[.!?\u3002\uFF01\uFF1F]$/u.test(normalized)
828
+ ? normalized
829
+ : `${normalized}${useChinese ? '。' : '.'}`;
830
+ }
831
+ function appendConversationAnswerSentence(sentences, sentence, useChinese) {
832
+ const normalizedSentence = normalizeConversationAnswerSentence(sentence, useChinese);
833
+ if (!normalizedSentence) {
834
+ return;
835
+ }
836
+ const normalizedKey = stripConversationAnswerTerminalPunctuation(normalizedSentence).toLowerCase();
837
+ const alreadyPresent = sentences.some((existingSentence) => (stripConversationAnswerTerminalPunctuation(existingSentence).toLowerCase() === normalizedKey));
838
+ if (!alreadyPresent) {
839
+ sentences.push(normalizedSentence);
840
+ }
841
+ }
842
+ function buildGraphConnectionPathAnswerSentence(graphContext, useChinese) {
843
+ const connectionPath = graphContext && Array.isArray(graphContext.connectionPaths)
844
+ ? graphContext.connectionPaths[0]
845
+ : null;
846
+ const pathTitles = connectionPath && Array.isArray(connectionPath.pathTitles)
847
+ ? connectionPath.pathTitles.map((title) => normalizeWhitespace(String(title || '').trim())).filter(Boolean)
848
+ : [];
849
+ if (pathTitles.length <= 1) {
850
+ return '';
851
+ }
852
+ if (useChinese) {
853
+ return `当前图中的关键路径是 ${pathTitles.join(' -> ')}`;
854
+ }
855
+ return `The strongest graph path runs through ${pathTitles.join(' -> ')}`;
856
+ }
857
+ function normalizeGraphAnswerDisplayTitle(value) {
858
+ return normalizeWhitespace(String(value || '')
859
+ .replace(/\s*\((?:mermaid|code|diagram)\s+block\)\s*$/iu, '')
860
+ .trim());
861
+ }
862
+ function buildGraphProfileAnswerSentence(graphContext, useChinese) {
863
+ if (!graphContext) {
864
+ return '';
865
+ }
866
+ const facts = (0, graphAnswerFacts_1.collectGraphAnswerFacts)(graphContext, {
867
+ anchorAtomId: graphContext.anchorAtomId,
868
+ anchorTitle: graphContext.anchorTitle,
869
+ normalizeTitle: (value) => normalizeGraphAnswerDisplayTitle(String(value || '')),
870
+ });
871
+ if (!facts) {
872
+ return '';
873
+ }
874
+ return (0, graphAnswerFacts_1.formatGraphAnswerProfileSentence)(facts, useChinese);
875
+ }
876
+ function sentenceComparableKey(value) {
877
+ return stripConversationAnswerTerminalPunctuation(value).toLowerCase();
878
+ }
879
+ function appendPlannedEvidenceSentence(sentences, candidate, useChinese) {
880
+ const normalized = normalizeConversationAnswerSentence((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(candidate), useChinese);
881
+ if (!normalized) {
882
+ return;
883
+ }
884
+ const candidateKey = sentenceComparableKey(normalized);
885
+ const alreadyCovered = sentences.some((sentence) => {
886
+ const existingKey = sentenceComparableKey(sentence);
887
+ return existingKey === candidateKey
888
+ || (candidateKey.length >= 32 && existingKey.includes(candidateKey))
889
+ || (existingKey.length >= 32 && candidateKey.includes(existingKey))
890
+ || (0, graphAnswerQualityPolicy_1.isPolaritySafeSemanticDuplicate)(existingKey, candidateKey);
891
+ });
892
+ if (!alreadyCovered) {
893
+ sentences.push(normalized);
894
+ }
895
+ }
896
+ function buildCoverageDrivenRagAnswer(params, graphContext, graphAnswerPlan, useChinese) {
897
+ const pack = params.ragContextPack;
898
+ if (!pack || !Array.isArray(pack.fragments) || pack.fragments.length <= 0) {
899
+ return '';
900
+ }
901
+ if (params.ragSufficiencyReview?.status === 'insufficient') {
902
+ return '';
903
+ }
904
+ const answerSentences = [];
905
+ const hasLearningRouteTask = Boolean(graphAnswerPlan.answerTaskPlan?.subtasks.some((subtask) => subtask.kind === 'learning_route'));
906
+ graphAnswerPlan.claims
907
+ .forEach((claim) => appendPlannedEvidenceSentence(answerSentences, claim.statement, useChinese));
908
+ if (answerSentences.length <= 0) {
909
+ return '';
910
+ }
911
+ if (params.ragSufficiencyReview?.status === 'borderline') {
912
+ appendPlannedEvidenceSentence(answerSentences, useChinese
913
+ ? '当前证据覆盖仍然有限,因此这只能作为基于已命中材料的部分回答'
914
+ : 'The available evidence is still partial, so this answer stays within the matched material', useChinese);
915
+ }
916
+ if (!hasLearningRouteTask) {
917
+ appendConversationAnswerSentence(answerSentences, buildGraphConnectionPathAnswerSentence(graphContext, useChinese), useChinese);
918
+ appendConversationAnswerSentence(answerSentences, buildGraphProfileAnswerSentence(graphContext, useChinese), useChinese);
919
+ }
920
+ return (0, answerTaskPlan_1.formatTaskAwareAnswer)(answerSentences, graphAnswerPlan.answerTaskPlan, useChinese);
921
+ }
922
+ function buildScopedConversationAnswer(params, graphContext, graphAnswerPlan) {
923
+ if (isFullResponseMode(params)) {
924
+ return buildFullTechnicalReport(params, graphContext, graphAnswerPlan);
925
+ }
926
+ const useChinese = useChineseAnswerLanguage(params);
927
+ if (params.knowledgePoints.length <= 0) {
928
+ const query = normalizeWhitespace(String(params.message || '')) || (useChinese ? '当前问题' : 'your query');
929
+ if (params.recalledMemories.length > 0) {
930
+ return useChinese
931
+ ? `当前范围没有匹配“${query}”的知识点,但已找回 ${params.recalledMemories.length} 条相关会话记忆。请调整知识范围,或将这条记忆作为后续检索锚点。`
932
+ : `No scoped knowledge points matched "${query}", but I recovered ${params.recalledMemories.length} relevant conversation memory note(s). Refine the corpus scope or use the recalled memory as a follow-up anchor.`;
933
+ }
934
+ return useChinese
935
+ ? `当前范围没有匹配“${query}”的知识点。请调整范围、补充笔记,或使用更具体的检索词。`
936
+ : `No scoped knowledge points matched "${query}". Refine the scope, add more notes to the corpus, or broaden the query terms.`;
937
+ }
938
+ const leadingPoint = params.knowledgePoints[0];
939
+ const answerSentences = [];
940
+ const ragAnswer = buildCoverageDrivenRagAnswer(params, graphContext, graphAnswerPlan, useChinese);
941
+ if (ragAnswer) {
942
+ return ragAnswer;
943
+ }
944
+ if (graphAnswerPlan.claims.length > 0) {
945
+ const hasLearningRouteTask = Boolean(graphAnswerPlan.answerTaskPlan?.subtasks.some((subtask) => subtask.kind === 'learning_route'));
946
+ graphAnswerPlan.claims.forEach((claim) => {
947
+ appendConversationAnswerSentence(answerSentences, claim.statement, useChinese);
948
+ });
949
+ if (params.ragSufficiencyReview?.status === 'borderline') {
950
+ appendConversationAnswerSentence(answerSentences, useChinese
951
+ ? '当前证据覆盖仍然有限,因此回答只陈述已有材料能够支持的内容'
952
+ : 'The evidence coverage is still partial, so the answer stays within the retrieved material', useChinese);
953
+ }
954
+ if (!hasLearningRouteTask) {
955
+ appendConversationAnswerSentence(answerSentences, buildGraphConnectionPathAnswerSentence(graphContext, useChinese), useChinese);
956
+ appendConversationAnswerSentence(answerSentences, buildGraphProfileAnswerSentence(graphContext, useChinese), useChinese);
957
+ }
958
+ return (0, answerTaskPlan_1.formatTaskAwareAnswer)(answerSentences, graphAnswerPlan.answerTaskPlan, useChinese);
959
+ }
960
+ const directSentence = selectScopedConversationDirectSentence(params.message, leadingPoint);
961
+ if (directSentence) {
962
+ appendConversationAnswerSentence(answerSentences, directSentence, useChinese);
963
+ }
964
+ if (answerSentences.length <= 0) {
965
+ const title = normalizeWhitespace(String(leadingPoint.title || '').trim());
966
+ const fallback = normalizeWhitespace(String(leadingPoint.evidenceSnippet || leadingPoint.summary || '').trim());
967
+ if (title && fallback && title !== fallback) {
968
+ appendConversationAnswerSentence(answerSentences, `${title}: ${fallback}`, useChinese);
969
+ }
970
+ else {
971
+ appendConversationAnswerSentence(answerSentences, fallback || title || normalizeWhitespace(String(params.message || '')), useChinese);
972
+ }
973
+ }
974
+ appendConversationAnswerSentence(answerSentences, buildGraphConnectionPathAnswerSentence(graphContext, useChinese), useChinese);
975
+ appendConversationAnswerSentence(answerSentences, buildGraphProfileAnswerSentence(graphContext, useChinese), useChinese);
976
+ if (answerSentences.length < 2 && params.knowledgePoints.length > 1) {
977
+ appendConversationAnswerSentence(answerSentences, selectScopedConversationDirectSentence(params.message, params.knowledgePoints[1]), useChinese);
978
+ }
979
+ return answerSentences.join(useChinese ? '' : ' ');
980
+ }
981
+ function buildScopedConversationOverviewMarkdown(params, graphContext) {
982
+ const useChinese = useChineseAnswerLanguage(params);
983
+ const strongestPoint = params.knowledgePoints[0];
984
+ const lines = [
985
+ useChinese ? '## 回答上下文' : '## Answer Context',
986
+ '',
987
+ ];
988
+ if (strongestPoint) {
989
+ lines.push(useChinese
990
+ ? `当前最佳范围锚点:**${strongestPoint.title}**。`
991
+ : `Best scoped anchor: **${strongestPoint.title}**.`, '');
992
+ }
993
+ else {
994
+ lines.push(useChinese
995
+ ? '当前请求没有得到足够强的范围内知识点匹配。'
996
+ : 'No scoped knowledge point produced a strong match for the current request.', '');
997
+ }
998
+ lines.push(...(useChinese
999
+ ? [
1000
+ `- 相关知识点:**${params.knowledgePoints.length}**`,
1001
+ `- 返回引用:**${params.citations.length}**`,
1002
+ `- 已召回范围内记忆:**${params.recalledMemories.length}**`,
1003
+ ]
1004
+ : [
1005
+ `- Relevant knowledge points: **${params.knowledgePoints.length}**`,
1006
+ `- Citations returned: **${params.citations.length}**`,
1007
+ `- Scoped memories recalled: **${params.recalledMemories.length}**`,
1008
+ ]));
1009
+ if (graphContext && graphContext.relationKinds.length > 0) {
1010
+ lines.push(useChinese
1011
+ ? `- 图谱支持关系:**${graphContext.relationKinds.join(', ')}**`
1012
+ : `- Graph-supported relations: **${graphContext.relationKinds.join(', ')}**`);
1013
+ }
1014
+ if (graphContext) {
1015
+ lines.push(useChinese
1016
+ ? `- 时序有效性:**${graphContext.temporalValidity.allPointsValid ? '有效' : '警告'}**`
1017
+ : `- Temporal validity: **${graphContext.temporalValidity.allPointsValid ? 'valid' : 'warning'}**`);
1018
+ }
1019
+ if (graphContext && Array.isArray(graphContext.connectionPaths) && graphContext.connectionPaths.length > 0) {
1020
+ lines.push(useChinese
1021
+ ? `- 显式连接路径:**${graphContext.connectionPaths.length}**`
1022
+ : `- Explicit connection paths: **${graphContext.connectionPaths.length}**`);
1023
+ }
1024
+ if (graphContext && Array.isArray(graphContext.predecessorWindow) && graphContext.predecessorWindow.length > 0) {
1025
+ lines.push(useChinese
1026
+ ? `- 直接前置节点:**${graphContext.predecessorWindow.length}**`
1027
+ : `- Immediate predecessors: **${graphContext.predecessorWindow.length}**`);
1028
+ }
1029
+ if (graphContext && Array.isArray(graphContext.successorWindow) && graphContext.successorWindow.length > 0) {
1030
+ lines.push(useChinese
1031
+ ? `- 直接后继节点:**${graphContext.successorWindow.length}**`
1032
+ : `- Immediate successors: **${graphContext.successorWindow.length}**`);
1033
+ }
1034
+ return lines.join('\n');
1035
+ }
1036
+ function buildScopedConversationExplanationMarkdown(params, graphContext) {
1037
+ const useChinese = useChineseAnswerLanguage(params);
1038
+ if (params.knowledgePoints.length <= 0) {
1039
+ return useChinese
1040
+ ? '## 说明\n\n当前范围没有返回足够强的知识点来直接解释该请求。'
1041
+ : '## Explanation\n\nThe current scope did not return a strong enough knowledge point to explain the request directly.';
1042
+ }
1043
+ const intent = classifyScopedConversationIntent(params.message);
1044
+ const strongestPoint = params.knowledgePoints[0];
1045
+ const explanationLines = [
1046
+ useChinese ? '## 说明' : '## Explanation',
1047
+ '',
1048
+ ];
1049
+ if (intent === 'compare') {
1050
+ explanationLines.push(useChinese
1051
+ ? `将 **${strongestPoint.title}** 作为当前范围内的比较基准。`
1052
+ : `Use **${strongestPoint.title}** as the comparison baseline inside the current scope.`);
1053
+ }
1054
+ else if (intent === 'how_to') {
1055
+ explanationLines.push(useChinese
1056
+ ? `将 **${strongestPoint.title}** 作为后续具体步骤的起始锚点。`
1057
+ : `Use **${strongestPoint.title}** as the starting anchor for the next concrete steps.`);
1058
+ }
1059
+ else if (intent === 'explain') {
1060
+ explanationLines.push(useChinese
1061
+ ? `**${strongestPoint.title}** 是当前用于说明的最佳范围锚点。`
1062
+ : `**${strongestPoint.title}** is the current best scoped anchor for the explanation.`);
1063
+ }
1064
+ else {
1065
+ explanationLines.push(useChinese
1066
+ ? `**${strongestPoint.title}** 是当前最佳范围锚点。`
1067
+ : `**${strongestPoint.title}** is the current best scoped anchor.`);
1068
+ }
1069
+ const summary = normalizeWhitespace(String(strongestPoint.summary || strongestPoint.evidenceSnippet || '').trim());
1070
+ if (summary) {
1071
+ explanationLines.push('', summary);
1072
+ }
1073
+ if (graphContext && graphContext.relationKinds.length > 0) {
1074
+ explanationLines.push('', useChinese
1075
+ ? `**${graphContext.anchorTitle}** 周边的图谱支持关系包括:${graphContext.relationKinds.join(', ')}。`
1076
+ : `Graph support around **${graphContext.anchorTitle}** includes: ${graphContext.relationKinds.join(', ')}.`);
1077
+ }
1078
+ if (graphContext && Array.isArray(graphContext.knowledgePointRelations) && graphContext.knowledgePointRelations.length > 0) {
1079
+ const relationPreview = graphContext.knowledgePointRelations
1080
+ .slice(0, 2)
1081
+ .map((relation) => `${relation.sourceTitle} -> ${relation.relationKind} -> ${relation.targetTitle}`)
1082
+ .join('; ');
1083
+ explanationLines.push('', useChinese
1084
+ ? `当前结果集内的直接图链接:${relationPreview}。`
1085
+ : `Direct graph links inside the current result set: ${relationPreview}.`);
1086
+ }
1087
+ if (graphContext && Array.isArray(graphContext.connectionPaths) && graphContext.connectionPaths.length > 0) {
1088
+ explanationLines.push('', useChinese
1089
+ ? `显式图路径:${formatGraphConnectionPath(graphContext.connectionPaths[0])}。`
1090
+ : `Explicit graph path: ${formatGraphConnectionPath(graphContext.connectionPaths[0])}.`);
1091
+ }
1092
+ if (graphContext) {
1093
+ const predecessorTitles = (0, graphAnswerFacts_1.collectGraphAnswerWindowTitles)(graphContext, 'predecessorWindow', {
1094
+ anchorAtomId: graphContext.anchorAtomId,
1095
+ anchorTitle: graphContext.anchorTitle,
1096
+ normalizeTitle: (value) => normalizeGraphAnswerDisplayTitle(String(value || '')),
1097
+ }, 3);
1098
+ if (predecessorTitles.length > 0) {
1099
+ explanationLines.push('', useChinese
1100
+ ? '直接前置窗口:' + predecessorTitles.join('、') + '。'
1101
+ : 'Immediate predecessor window: ' + predecessorTitles.join(', ') + '.');
1102
+ }
1103
+ }
1104
+ if (graphContext) {
1105
+ const successorTitles = (0, graphAnswerFacts_1.collectGraphAnswerWindowTitles)(graphContext, 'successorWindow', {
1106
+ anchorAtomId: graphContext.anchorAtomId,
1107
+ anchorTitle: graphContext.anchorTitle,
1108
+ normalizeTitle: (value) => normalizeGraphAnswerDisplayTitle(String(value || '')),
1109
+ }, 3);
1110
+ if (successorTitles.length > 0) {
1111
+ explanationLines.push('', useChinese
1112
+ ? '直接后继窗口:' + successorTitles.join('、') + '。'
1113
+ : 'Immediate successor window: ' + successorTitles.join(', ') + '.');
1114
+ }
1115
+ }
1116
+ if (graphContext && graphContext.temporalValidity.allPointsValid === false) {
1117
+ const reasonSummary = graphContext.temporalValidity.warningReasons.length > 0
1118
+ ? graphContext.temporalValidity.warningReasons.join(', ')
1119
+ : 'temporal validity checks reported a warning';
1120
+ explanationLines.push('', useChinese
1121
+ ? `时序有效性警告:${reasonSummary}。`
1122
+ : `Temporal validity warning: ${reasonSummary}.`);
1123
+ }
1124
+ const supersedesCount = graphContext
1125
+ ? (Array.isArray(graphContext.temporalValidity.details)
1126
+ ? graphContext.temporalValidity.details.filter((detail) => detail.edgeKind === 'supersedes').length
1127
+ : 0)
1128
+ : 0;
1129
+ if (supersedesCount > 0) {
1130
+ explanationLines.push('', useChinese
1131
+ ? `时序谱系表明该锚点替代了 ${supersedesCount} 个更早版本。`
1132
+ : `Temporal lineage indicates this anchor supersedes ${supersedesCount} earlier revision${supersedesCount === 1 ? '' : 's'}.`);
1133
+ }
1134
+ const supportingTitles = params.knowledgePoints
1135
+ .slice(1, 3)
1136
+ .map((point) => normalizeWhitespace(String(point.title || '').trim()))
1137
+ .filter(Boolean);
1138
+ if (supportingTitles.length > 0) {
1139
+ explanationLines.push('', useChinese
1140
+ ? (intent === 'compare'
1141
+ ? `支持比较节点:${supportingTitles.join('、')}。`
1142
+ : `支持范围节点:${supportingTitles.join('、')}。`)
1143
+ : (intent === 'compare'
1144
+ ? `Supporting comparison nodes: ${supportingTitles.join(', ')}.`
1145
+ : `Supporting scoped nodes: ${supportingTitles.join(', ')}.`));
1146
+ }
1147
+ if (params.recalledMemories.length > 0) {
1148
+ explanationLines.push('', useChinese
1149
+ ? `范围内记忆召回为该说明提供了 ${params.recalledMemories.length} 条先前笔记。`
1150
+ : `Scoped memory recall contributed ${params.recalledMemories.length} prior note(s) to this explanation.`);
1151
+ }
1152
+ if (params.citations.length > 0) {
1153
+ explanationLines.push('', useChinese
1154
+ ? `该说明由当前范围内的 ${params.citations.length} 条引用支撑。`
1155
+ : `The explanation is grounded by ${params.citations.length} citation(s) from the current scope.`);
1156
+ }
1157
+ return explanationLines.join('\n');
1158
+ }
1159
+ function buildScopedConversationEvidenceMarkdown(params) {
1160
+ const evidenceLines = params.citations.slice(0, 3).map((citation, index) => (`${index + 1}. **${citation.title}** (${citation.sourcePath}${citation.startLine ? `:${citation.startLine}` : ''})\n - ${citation.snippet}`));
1161
+ if (evidenceLines.length <= 0) {
1162
+ return useChineseAnswerLanguage(params)
1163
+ ? '## 证据摘要\n\n当前范围没有返回引用。'
1164
+ : '## Evidence Summary\n\nNo scoped citations were returned.';
1165
+ }
1166
+ return [
1167
+ useChineseAnswerLanguage(params) ? '## 证据摘要' : '## Evidence Summary',
1168
+ '',
1169
+ ...evidenceLines,
1170
+ ].join('\n');
1171
+ }
1172
+ function buildScopedConversationMemoryNotice(params) {
1173
+ const useChinese = useChineseAnswerLanguage(params);
1174
+ if (params.recalledMemories.length <= 0) {
1175
+ return useChinese
1176
+ ? '本回合未召回范围内记忆。'
1177
+ : 'No scoped memory note was recalled for this turn.';
1178
+ }
1179
+ if (params.recalledMemories.length === 1) {
1180
+ return useChinese
1181
+ ? '已召回 1 条范围内记忆并合并到回答上下文。'
1182
+ : '1 scoped memory note was recalled and merged into the answer context.';
1183
+ }
1184
+ return useChinese
1185
+ ? `已召回 ${params.recalledMemories.length} 条范围内记忆并合并到回答上下文。`
1186
+ : `${params.recalledMemories.length} scoped memory notes were recalled and merged into the answer context.`;
1187
+ }
1188
+ function buildScopedConversationActionGuideMarkdown(params, graphContext, answerTaskPlan) {
1189
+ const useChinese = useChineseAnswerLanguage(params);
1190
+ if (params.knowledgePoints.length <= 0) {
1191
+ return useChinese
1192
+ ? '## 下一步行动\n\n当前回合没有可执行的限定知识卡片。'
1193
+ : '## Next Actions\n\nNo actionable scoped knowledge card is available for this turn.';
1194
+ }
1195
+ const intent = classifyScopedConversationIntent(params.message);
1196
+ const topTitles = params.knowledgePoints
1197
+ .slice(0, 3)
1198
+ .map((point) => `- ${point.title}`);
1199
+ const actionHints = params.memoryActions
1200
+ .slice(0, 2)
1201
+ .map((action) => useChinese
1202
+ ? (action.kind === 'persist_session_memory' ? '- 记录本回合的用户关注点。' : '- 根据当前证据评估是否需要保存学习记忆。')
1203
+ : normalizeWhitespace(String(action.reason || '').trim()))
1204
+ .filter(Boolean)
1205
+ .map((reason) => `- ${reason}`);
1206
+ const graphActionHints = [];
1207
+ const learningRoute = answerTaskPlan?.learningRoute || [];
1208
+ if (answerTaskPlan?.subtasks.some((subtask) => subtask.kind === 'learning_route') && learningRoute.length > 0) {
1209
+ graphActionHints.push(useChinese
1210
+ ? `- 建议按以下顺序学习:${learningRoute.map((node) => `${node.order}. ${node.title}`).join(' -> ')}。`
1211
+ : `- Follow the evidence-backed learning order: ${learningRoute.map((node) => `${node.order}. ${node.title}`).join(' -> ')}.`);
1212
+ }
1213
+ if (graphContext && graphContext.relationKinds.includes('prerequisite')) {
1214
+ graphActionHints.push(useChinese ? '- 在进入引导学习前,先在聚焦模式查看前置概念。' : '- Inspect prerequisite-linked concepts in focus mode before guided learning.');
1215
+ }
1216
+ if (graphContext && graphContext.temporalValidity.allPointsValid === false) {
1217
+ graphActionHints.push(useChinese ? '- 提升答案前,确认是否存在更新或替代当前锚点的笔记。' : '- Validate whether a fresher or superseding note should replace this anchor before promotion.');
1218
+ }
1219
+ const supersedesCount = graphContext
1220
+ ? (Array.isArray(graphContext.temporalValidity.details)
1221
+ ? graphContext.temporalValidity.details.filter((detail) => detail.edgeKind === 'supersedes').length
1222
+ : 0)
1223
+ : 0;
1224
+ if (supersedesCount > 0) {
1225
+ graphActionHints.push(useChinese ? '- 提升答案前,追踪已被替代的知识谱系。' : '- Trace the superseded lineage before promoting this answer.');
1226
+ }
1227
+ if (graphContext && Array.isArray(graphContext.knowledgePointRelations) && graphContext.knowledgePointRelations.length > 0) {
1228
+ const firstRelation = graphContext.knowledgePointRelations[0];
1229
+ graphActionHints.push(useChinese
1230
+ ? `- 在扩展到外部支持节点前,沿 ${firstRelation.sourceTitle} 与 ${firstRelation.targetTitle} 之间的直接图路径查看。`
1231
+ : `- Follow the direct graph path between ${firstRelation.sourceTitle} and ${firstRelation.targetTitle} before branching to external support nodes.`);
1232
+ }
1233
+ if (graphContext && Array.isArray(graphContext.connectionPaths) && graphContext.connectionPaths.length > 0) {
1234
+ const firstConnectionPath = graphContext.connectionPaths[0];
1235
+ const titles = Array.isArray(firstConnectionPath.pathTitles)
1236
+ ? firstConnectionPath.pathTitles.map((title) => normalizeWhitespace(String(title || '').trim())).filter(Boolean)
1237
+ : [];
1238
+ if (titles.length > 1) {
1239
+ graphActionHints.push(useChinese ? `- 检查路径顺序:${titles.join(' -> ')}。` : `- Review the path order: ${titles.join(' -> ')}.`);
1240
+ }
1241
+ }
1242
+ if (graphContext) {
1243
+ const predecessorTitles = (0, graphAnswerFacts_1.collectGraphAnswerWindowTitles)(graphContext, 'predecessorWindow', {
1244
+ anchorAtomId: graphContext.anchorAtomId,
1245
+ anchorTitle: graphContext.anchorTitle,
1246
+ normalizeTitle: (value) => normalizeGraphAnswerDisplayTitle(String(value || '')),
1247
+ }, 2);
1248
+ if (predecessorTitles.length > 0) {
1249
+ graphActionHints.push(useChinese
1250
+ ? '- 扩展答案前,先查看前置上下文:' + predecessorTitles.join('、') + '。'
1251
+ : '- Inspect prerequisite context from ' + predecessorTitles.join(', ') + ' before expanding the answer.');
1252
+ }
1253
+ }
1254
+ if (graphContext) {
1255
+ const successorTitles = (0, graphAnswerFacts_1.collectGraphAnswerWindowTitles)(graphContext, 'successorWindow', {
1256
+ anchorAtomId: graphContext.anchorAtomId,
1257
+ anchorTitle: graphContext.anchorTitle,
1258
+ normalizeTitle: (value) => normalizeGraphAnswerDisplayTitle(String(value || '')),
1259
+ }, 2);
1260
+ if (successorTitles.length > 0) {
1261
+ graphActionHints.push(useChinese
1262
+ ? '- 可沿后续节点继续:' + successorTitles.join('、') + '。'
1263
+ : '- Use likely next-step nodes such as ' + successorTitles.join(', ') + ' to continue follow-through after this answer.');
1264
+ }
1265
+ }
1266
+ return [
1267
+ useChinese ? '## 下一步行动' : '## Next Actions',
1268
+ '',
1269
+ intent === 'compare'
1270
+ ? (useChinese ? '先查看下方限定知识卡片中的高相关节点,再判断关键差异:' : 'Use the scoped knowledge cards below to inspect the strongest nodes side by side before deciding which distinctions matter most:')
1271
+ : intent === 'how_to'
1272
+ ? (useChinese ? '使用下方限定知识卡片,将解释推进为具体的引导学习或聚焦步骤:' : 'Use the scoped knowledge cards below to move from explanation into concrete guided-learning or focus-mode steps:')
1273
+ : (useChinese ? '使用下方限定知识卡片,继续聚焦或引导学习高相关节点:' : 'Use the scoped knowledge cards below to continue with focus mode or guided learning for the highest-signal nodes:'),
1274
+ ...topTitles,
1275
+ ...(graphActionHints.length > 0 ? ['', useChinese ? '图谱辅助行动:' : 'Graph-aware follow-through:', ...graphActionHints] : []),
1276
+ ...(actionHints.length > 0
1277
+ ? ['', useChinese ? '本回合建议行动:' : 'Suggested follow-through from the current turn:', ...actionHints]
1278
+ : []),
1279
+ ].join('\n');
1280
+ }
1281
+ function buildKnowledgeRunClaimFromCitation(runId, citation, index) {
1282
+ const snippet = normalizeWhitespace(String(citation.snippet || '').trim());
1283
+ const hasSourcePath = normalizeWhitespace(String(citation.sourcePath || '')).length > 0;
1284
+ const hasLine = Number.isFinite(Number(citation.startLine)) && Number(citation.startLine) > 0;
1285
+ const status = hasSourcePath && snippet && hasLine
1286
+ ? 'verified'
1287
+ : hasSourcePath && snippet
1288
+ ? 'weak'
1289
+ : 'not_proven';
1290
+ return {
1291
+ claimId: `${runId}_claim_${index + 1}`,
1292
+ status,
1293
+ title: normalizeWhitespace(String(citation.title || '').trim()) || `Evidence claim ${index + 1}`,
1294
+ statement: snippet || normalizeWhitespace(String(citation.title || '').trim()) || 'Citation evidence was returned.',
1295
+ citationId: citation.citationId,
1296
+ atomId: citation.atomId,
1297
+ documentId: citation.documentId,
1298
+ sourcePath: citation.sourcePath,
1299
+ startLine: citation.startLine,
1300
+ endLine: citation.endLine,
1301
+ snippet,
1302
+ confidence: clampUnit(Number(citation.score || 0)),
1303
+ reason: status === 'verified'
1304
+ ? 'The claim is backed by a cited source span with a concrete line reference.'
1305
+ : status === 'weak'
1306
+ ? 'The claim has a cited source path and snippet, but no concrete line reference.'
1307
+ : 'The citation is missing enough source-span detail to prove the claim.',
1308
+ };
1309
+ }
1310
+ function buildKnowledgeRunClaimFromPoint(runId, point, index) {
1311
+ const snippet = normalizeWhitespace(String(point.evidenceSnippet || point.summary || '').trim());
1312
+ return {
1313
+ claimId: `${runId}_claim_${index + 1}`,
1314
+ status: 'not_proven',
1315
+ title: normalizeWhitespace(String(point.title || '').trim()) || `Knowledge point ${index + 1}`,
1316
+ statement: snippet || normalizeWhitespace(String(point.title || '').trim()) || 'A knowledge point was returned without citation evidence.',
1317
+ atomId: point.atomId,
1318
+ documentId: point.documentId,
1319
+ sourcePath: point.sourcePath,
1320
+ snippet,
1321
+ confidence: clampUnit(Number(point.score || 0)),
1322
+ reason: 'The answer can point to a retrieved knowledge node, but no explicit citation span was returned.',
1323
+ };
1324
+ }
1325
+ function buildRejectedKnowledgeRunClaim(runId, message) {
1326
+ return {
1327
+ claimId: `${runId}_claim_1`,
1328
+ status: 'rejected',
1329
+ title: 'No scoped evidence',
1330
+ statement: `No scoped evidence proved the request: ${normalizeWhitespace(message || 'local knowledge request')}`,
1331
+ snippet: '',
1332
+ confidence: 0,
1333
+ reason: 'No citation or retrieved knowledge point was available for this turn.',
1334
+ };
1335
+ }
1336
+ function buildKnowledgeRunEvidenceClaims(runId, params) {
1337
+ const seenCitationKeys = new Set();
1338
+ const citationClaims = params.citations
1339
+ .filter((citation) => {
1340
+ const citationKey = [
1341
+ citation.citationId,
1342
+ citation.documentId,
1343
+ citation.sourcePath,
1344
+ citation.startLine || '',
1345
+ citation.endLine || '',
1346
+ citation.snippet,
1347
+ ].join('|');
1348
+ if (seenCitationKeys.has(citationKey)) {
1349
+ return false;
1350
+ }
1351
+ seenCitationKeys.add(citationKey);
1352
+ return true;
1353
+ })
1354
+ .map((citation, index) => buildKnowledgeRunClaimFromCitation(runId, citation, index));
1355
+ if (citationClaims.length > 0) {
1356
+ return citationClaims;
1357
+ }
1358
+ const pointClaims = params.knowledgePoints
1359
+ .slice(0, 3)
1360
+ .map((point, index) => buildKnowledgeRunClaimFromPoint(runId, point, index));
1361
+ if (pointClaims.length > 0) {
1362
+ return pointClaims;
1363
+ }
1364
+ return [buildRejectedKnowledgeRunClaim(runId, params.message)];
1365
+ }
1366
+ function buildKnowledgeRunEvidenceRef(claim) {
1367
+ const sourcePath = normalizeWhitespace(String(claim.sourcePath || '').trim());
1368
+ if (!sourcePath) {
1369
+ return '';
1370
+ }
1371
+ const startLine = Number(claim.startLine);
1372
+ return Number.isFinite(startLine) && startLine > 0
1373
+ ? `${sourcePath}:${startLine}`
1374
+ : sourcePath;
1375
+ }
1376
+ function buildKnowledgeRunReviewCards(runId, generatedAt, claims) {
1377
+ return claims
1378
+ .filter((claim) => claim.status === 'verified' || claim.status === 'weak')
1379
+ .slice(0, 3)
1380
+ .map((claim, index) => {
1381
+ const evidenceRef = buildKnowledgeRunEvidenceRef(claim);
1382
+ return {
1383
+ cardId: `${runId}_card_${index + 1}`,
1384
+ sourceClaimId: claim.claimId,
1385
+ atomId: claim.atomId,
1386
+ suggestedActionKind: 'review',
1387
+ prompt: `What does the cited source establish about ${claim.title}?`,
1388
+ expectedAnswer: claim.snippet || claim.statement,
1389
+ evidenceRefs: evidenceRef ? [evidenceRef] : [],
1390
+ nextReviewAt: addDaysIso(generatedAt, 1),
1391
+ };
1392
+ });
1393
+ }
1394
+ function buildKnowledgeRunQuality(claims, reviewCards, params, graphContext) {
1395
+ const coveredClaimCount = claims.filter((claim) => claim.status === 'verified' || claim.status === 'weak').length;
1396
+ const evidenceCoverage = claims.length > 0 ? coveredClaimCount / claims.length : 0;
1397
+ const scopeDiscipline = params.usedScope.source === 'scoped'
1398
+ || (params.usedScope.documentIds.length <= 0
1399
+ && params.usedScope.atomIds.length <= 0
1400
+ && params.usedScope.sourcePathPrefixes.length <= 0);
1401
+ const memoryActionsGoverned = params.memoryActions.every((action) => (Boolean(action.kind)
1402
+ && Boolean(action.status)
1403
+ && Boolean(action.layer)
1404
+ && Boolean(action.namespace)));
1405
+ const intent = classifyScopedConversationIntent(params.message);
1406
+ const predecessorWindow = graphContext && Array.isArray(graphContext.predecessorWindow)
1407
+ ? graphContext.predecessorWindow
1408
+ : [];
1409
+ const successorWindow = graphContext && Array.isArray(graphContext.successorWindow)
1410
+ ? graphContext.successorWindow
1411
+ : [];
1412
+ const connectionPaths = graphContext && Array.isArray(graphContext.connectionPaths)
1413
+ ? graphContext.connectionPaths
1414
+ : [];
1415
+ const diagnostics = graphContext && graphContext.diagnostics && typeof graphContext.diagnostics === 'object'
1416
+ ? graphContext.diagnostics
1417
+ : null;
1418
+ const prerequisiteSignalPresent = predecessorWindow.length > 0
1419
+ || connectionPaths.some((connectionPath) => (Array.isArray(connectionPath.pathEdges)
1420
+ && connectionPath.pathEdges.some((edge) => edge && edge.relationKind === 'prerequisite')))
1421
+ || Boolean(graphContext && graphContext.relationKinds.includes('prerequisite'));
1422
+ const prerequisiteOrderRequired = intent === 'how_to' || (intent === 'explain' && prerequisiteSignalPresent);
1423
+ const prerequisiteOrderPassed = !prerequisiteOrderRequired
1424
+ || predecessorWindow.length > 0
1425
+ || connectionPaths.some((connectionPath) => (Array.isArray(connectionPath.pathEdges)
1426
+ && connectionPath.pathEdges.some((edge) => edge && edge.relationKind === 'prerequisite')));
1427
+ const comparisonBranchRequired = intent === 'compare';
1428
+ const comparisonBranchSignalPresent = Boolean(graphContext
1429
+ && (graphContext.relationKinds.includes('contrast')
1430
+ || graphContext.relationKinds.includes('analogy')
1431
+ || (Array.isArray(graphContext.knowledgePointRelations)
1432
+ && graphContext.knowledgePointRelations.some((relation) => (relation
1433
+ && (relation.relationKind === 'contrast' || relation.relationKind === 'analogy'))))
1434
+ || (Array.isArray(graphContext.supportingTitles) && graphContext.supportingTitles.length >= 2)
1435
+ || connectionPaths.length >= 2));
1436
+ const comparisonBranchPassed = !comparisonBranchRequired
1437
+ || comparisonBranchSignalPresent;
1438
+ const temporalWarningRequired = Boolean(graphContext
1439
+ && (graphContext.temporalValidity.allPointsValid === false
1440
+ || (Array.isArray(graphContext.temporalValidity.details)
1441
+ && graphContext.temporalValidity.details.some((detail) => detail.edgeKind === 'supersedes'))));
1442
+ const temporalWarningPassed = !temporalWarningRequired
1443
+ || Boolean(graphContext
1444
+ && Array.isArray(graphContext.temporalValidity.warningReasons)
1445
+ && graphContext.temporalValidity.warningReasons.length > 0);
1446
+ const graphOpFallbackPassed = !diagnostics
1447
+ || diagnostics.graphOpsAvailable === true
1448
+ || diagnostics.usedFallback === true;
1449
+ const supportNodeCount = diagnostics
1450
+ ? Math.max(0, Math.floor(Number(diagnostics.supportNodeCount || 0)))
1451
+ : (graphContext && Array.isArray(graphContext.supportingAtomIds) ? graphContext.supportingAtomIds.length : 0);
1452
+ const supportNodeLimit = diagnostics
1453
+ ? Math.max(1, Math.floor(Number(diagnostics.supportNodeLimit || supportNodeCount || 1)))
1454
+ : Math.max(supportNodeCount || 1, 1);
1455
+ const pathDepthLimit = diagnostics
1456
+ ? Math.max(1, Math.floor(Number(diagnostics.pathDepthLimit || 1)))
1457
+ : 6;
1458
+ const graphBudgetPassed = connectionPaths.every((connectionPath) => Math.max(0, Math.floor(Number(connectionPath.length || 0))) <= pathDepthLimit)
1459
+ && predecessorWindow.length <= Math.max(0, supportNodeLimit)
1460
+ && successorWindow.length <= Math.max(0, supportNodeLimit)
1461
+ && supportNodeCount <= supportNodeLimit;
1462
+ const gates = [
1463
+ {
1464
+ gateId: 'evidence_coverage',
1465
+ passed: evidenceCoverage >= 0.8,
1466
+ observedValue: Number(evidenceCoverage.toFixed(4)),
1467
+ threshold: 0.8,
1468
+ message: coveredClaimCount > 0
1469
+ ? `${coveredClaimCount} of ${claims.length} claim(s) have citation evidence.`
1470
+ : 'No claim has enough citation evidence.',
1471
+ },
1472
+ {
1473
+ gateId: 'scope_discipline',
1474
+ passed: scopeDiscipline,
1475
+ observedValue: scopeDiscipline ? 1 : 0,
1476
+ threshold: 1,
1477
+ message: scopeDiscipline
1478
+ ? 'The answer stayed inside the resolved scope contract.'
1479
+ : 'The answer used a global result while scoped filters were active.',
1480
+ },
1481
+ {
1482
+ gateId: 'recall_transfer',
1483
+ passed: reviewCards.length > 0,
1484
+ observedValue: reviewCards.length,
1485
+ threshold: 1,
1486
+ message: reviewCards.length > 0
1487
+ ? `${reviewCards.length} review card(s) were generated from cited claims.`
1488
+ : 'No active-recall card could be generated from the available evidence.',
1489
+ },
1490
+ {
1491
+ gateId: 'memory_governance',
1492
+ passed: memoryActionsGoverned,
1493
+ observedValue: memoryActionsGoverned ? 1 : 0,
1494
+ threshold: 1,
1495
+ message: memoryActionsGoverned
1496
+ ? 'Memory actions include the governance fields needed for audit.'
1497
+ : 'At least one memory action is missing governance metadata.',
1498
+ },
1499
+ {
1500
+ gateId: 'graph_prerequisite_order',
1501
+ passed: prerequisiteOrderPassed,
1502
+ observedValue: prerequisiteOrderPassed ? 1 : 0,
1503
+ threshold: prerequisiteOrderRequired ? 1 : 0,
1504
+ message: prerequisiteOrderRequired
1505
+ ? (prerequisiteOrderPassed
1506
+ ? 'Prerequisite-oriented queries retained upstream path order or predecessor context.'
1507
+ : 'The current graph context did not preserve enough prerequisite ordering for this query.')
1508
+ : 'No prerequisite-ordering requirement was active for this query.',
1509
+ },
1510
+ {
1511
+ gateId: 'graph_comparison_branch',
1512
+ passed: comparisonBranchPassed,
1513
+ observedValue: comparisonBranchPassed ? 1 : 0,
1514
+ threshold: comparisonBranchRequired ? 1 : 0,
1515
+ message: comparisonBranchRequired
1516
+ ? (comparisonBranchPassed
1517
+ ? 'Comparison intent retained support nodes or branch structure in the graph context.'
1518
+ : 'Comparison intent did not retain enough branch structure in the graph context.')
1519
+ : 'No comparison-branch requirement was active for this query.',
1520
+ },
1521
+ {
1522
+ gateId: 'graph_temporal_warning',
1523
+ passed: temporalWarningPassed,
1524
+ observedValue: temporalWarningPassed ? 1 : 0,
1525
+ threshold: temporalWarningRequired ? 1 : 0,
1526
+ message: temporalWarningRequired
1527
+ ? (temporalWarningPassed
1528
+ ? 'Temporal invalidity or supersession was surfaced as an explicit warning.'
1529
+ : 'Temporal invalidity was present but not surfaced as an explicit warning.')
1530
+ : 'No temporal warning was required for this query.',
1531
+ },
1532
+ {
1533
+ gateId: 'graph_op_fallback',
1534
+ passed: graphOpFallbackPassed,
1535
+ observedValue: graphOpFallbackPassed ? 1 : 0,
1536
+ threshold: 1,
1537
+ message: diagnostics
1538
+ ? (diagnostics.graphOpsAvailable === true
1539
+ ? 'Graph operations were available for this turn.'
1540
+ : diagnostics.usedFallback === true
1541
+ ? 'Graph operations fell back cleanly without breaking the answer contract.'
1542
+ : 'Graph operation availability and fallback state were inconsistent.')
1543
+ : 'No graph diagnostics payload was available; compatibility fallback accepted.',
1544
+ },
1545
+ {
1546
+ gateId: 'graph_budget',
1547
+ passed: graphBudgetPassed,
1548
+ observedValue: graphBudgetPassed ? 1 : 0,
1549
+ threshold: 1,
1550
+ message: graphBudgetPassed
1551
+ ? 'Graph context stayed within bounded support/path/window budgets.'
1552
+ : 'Graph context exceeded the bounded support/path/window budgets for this turn.',
1553
+ },
1554
+ ];
1555
+ const passedGateCount = gates.filter((gate) => gate.passed).length;
1556
+ const score = Number(((passedGateCount / gates.length) * 100).toFixed(2));
1557
+ const status = gates.every((gate) => gate.passed)
1558
+ ? 'pass'
1559
+ : gates[0].passed && gates[1].passed
1560
+ ? 'caution'
1561
+ : 'fail';
1562
+ return {
1563
+ score,
1564
+ status,
1565
+ gates,
1566
+ };
1567
+ }
1568
+ function buildKnowledgeRunReviewState(reviewCards) {
1569
+ const totalReviewCards = reviewCards.length;
1570
+ return {
1571
+ consumedCardIds: [],
1572
+ completedReviewCardCount: 0,
1573
+ remainingReviewCardCount: totalReviewCards,
1574
+ completedAt: null,
1575
+ };
1576
+ }
1577
+ function buildKnowledgeRun(params, graphContext) {
1578
+ const generatedAt = String(params.generatedAt || new Date().toISOString()).trim();
1579
+ const runId = params.nextRunId ? params.nextRunId() : buildFallbackKnowledgeRunId(params, generatedAt);
1580
+ const evidenceClaims = buildKnowledgeRunEvidenceClaims(runId, params);
1581
+ const reviewCards = buildKnowledgeRunReviewCards(runId, generatedAt, evidenceClaims);
1582
+ const reviewState = buildKnowledgeRunReviewState(reviewCards);
1583
+ const quality = buildKnowledgeRunQuality(evidenceClaims, reviewCards, params, graphContext);
1584
+ const countStatus = (status) => (evidenceClaims.filter((claim) => claim.status === status).length);
1585
+ return {
1586
+ runId,
1587
+ generatedAt,
1588
+ status: quality.status,
1589
+ scope: {
1590
+ source: params.usedScope.source,
1591
+ workspaceId: params.usedScope.workspaceId,
1592
+ corpusId: params.usedScope.corpusId,
1593
+ documentIds: [...params.usedScope.documentIds],
1594
+ atomIds: [...params.usedScope.atomIds],
1595
+ sourcePathPrefixes: [...params.usedScope.sourcePathPrefixes],
1596
+ languages: [...params.usedScope.languages],
1597
+ matchedAtomCount: params.usedScope.matchedAtomCount,
1598
+ scopeSource: params.usedScope.scopeSource,
1599
+ },
1600
+ evidenceClaims,
1601
+ quality,
1602
+ reviewCards,
1603
+ reviewState,
1604
+ summary: {
1605
+ claimCount: evidenceClaims.length,
1606
+ verifiedClaimCount: countStatus('verified'),
1607
+ weakClaimCount: countStatus('weak'),
1608
+ notProvenClaimCount: countStatus('not_proven'),
1609
+ rejectedClaimCount: countStatus('rejected'),
1610
+ reviewCardCount: reviewCards.length,
1611
+ completedReviewCardCount: reviewState.completedReviewCardCount,
1612
+ remainingReviewCardCount: reviewState.remainingReviewCardCount,
1613
+ },
1614
+ };
1615
+ }
1616
+ function buildScopedConversationReply(params) {
1617
+ const blocks = [];
1618
+ const graphContext = params.graphContext || (0, graphContextAssembler_1.buildAgentConversationGraphContextFromKnowledgePoints)(params.knowledgePoints);
1619
+ const auditGraphAnswerPlan = (0, graphAnswerPlan_1.buildGraphAnswerPlan)({
1620
+ message: params.message,
1621
+ knowledgePoints: params.knowledgePoints,
1622
+ graphContext,
1623
+ ragContextPack: params.ragContextPack,
1624
+ });
1625
+ const fullReportAssembly = params.responseMode === 'full'
1626
+ ? buildFullTechnicalReportResult(params, graphContext, auditGraphAnswerPlan)
1627
+ : undefined;
1628
+ const draftAnswer = fullReportAssembly?.answer
1629
+ || buildScopedConversationAnswer(params, graphContext, auditGraphAnswerPlan);
1630
+ const knowledgeRun = buildKnowledgeRun(params, graphContext);
1631
+ const answerReleaseReview = (0, answerReleaseReview_1.reviewAnswerRelease)({
1632
+ message: params.message,
1633
+ answerLanguage: params.answerLanguage,
1634
+ responseMode: params.responseMode === 'full' ? 'full' : 'slim',
1635
+ draftAnswer,
1636
+ knowledgePoints: params.knowledgePoints,
1637
+ citations: params.citations,
1638
+ usedScope: params.usedScope,
1639
+ graphContext,
1640
+ ragContextPack: params.ragContextPack,
1641
+ ragSufficiencyReview: params.ragSufficiencyReview,
1642
+ graphAnswerPlan: auditGraphAnswerPlan,
1643
+ reviewedAt: params.generatedAt,
1644
+ });
1645
+ knowledgeRun.answerReleaseReview = answerReleaseReview;
1646
+ const answer = answerReleaseReview.publicAnswer;
1647
+ const publicGraphAnswerPlan = answerReleaseReview.publicGraphAnswerPlan;
1648
+ const releasedGraphAnswerPlan = publicGraphAnswerPlan || {
1649
+ ...auditGraphAnswerPlan,
1650
+ leadClaimId: '',
1651
+ claims: [],
1652
+ requiredRoles: [],
1653
+ depth: 'compact',
1654
+ };
1655
+ const graphAnswerCoverage = answerReleaseReview.graphAnswerCoverage
1656
+ || (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)(answer, publicGraphAnswerPlan || null);
1657
+ knowledgeRun.graphAnswerPlan = releasedGraphAnswerPlan;
1658
+ knowledgeRun.graphAnswerCoverage = graphAnswerCoverage;
1659
+ knowledgeRun.answerTaskPlan = releasedGraphAnswerPlan.answerTaskPlan;
1660
+ knowledgeRun.answerTaskCoverage = answerReleaseReview.answerTaskCoverage;
1661
+ const useChinese = useChineseAnswerLanguage(params);
1662
+ const overviewMarkdown = buildScopedConversationOverviewMarkdown(params, graphContext);
1663
+ const explanationMarkdown = buildScopedConversationExplanationMarkdown(params, graphContext);
1664
+ const evidenceMarkdown = buildScopedConversationEvidenceMarkdown(params);
1665
+ const memoryNotice = buildScopedConversationMemoryNotice(params);
1666
+ const actionGuideMarkdown = buildScopedConversationActionGuideMarkdown(params, graphContext, releasedGraphAnswerPlan.answerTaskPlan);
1667
+ blocks.push({
1668
+ blockId: params.nextBlockId(),
1669
+ type: 'structured_answer',
1670
+ title: useChinese ? '可信回答' : 'Grounded Answer',
1671
+ directAnswer: answer,
1672
+ answerTaskPlan: releasedGraphAnswerPlan.answerTaskPlan,
1673
+ answerTaskCoverage: answerReleaseReview.answerTaskCoverage,
1674
+ overviewMarkdown,
1675
+ explanationMarkdown,
1676
+ evidenceMarkdown,
1677
+ nextActionsMarkdown: params.knowledgePoints.length > 0 ? actionGuideMarkdown : undefined,
1678
+ knowledgePointCount: params.knowledgePoints.length,
1679
+ citationCount: params.citations.length,
1680
+ recalledMemoryCount: params.recalledMemories.length,
1681
+ });
1682
+ blocks.push({
1683
+ blockId: params.nextBlockId(),
1684
+ type: 'knowledge_run_summary',
1685
+ title: useChinese ? '知识运行' : 'Knowledge Run',
1686
+ knowledgeRun,
1687
+ });
1688
+ if (memoryNotice) {
1689
+ blocks.push({
1690
+ blockId: params.nextBlockId(),
1691
+ type: 'system_notice',
1692
+ text: memoryNotice,
1693
+ });
1694
+ }
1695
+ if (params.citations.length > 0) {
1696
+ blocks.push({
1697
+ blockId: params.nextBlockId(),
1698
+ type: 'citations',
1699
+ title: useChinese ? '引用' : 'Citations',
1700
+ citations: params.citations.map((citation) => ({ ...citation })),
1701
+ });
1702
+ }
1703
+ if (params.knowledgePoints.length > 0) {
1704
+ blocks.push({
1705
+ blockId: params.nextBlockId(),
1706
+ type: 'knowledge_actions',
1707
+ title: useChinese ? '知识操作' : 'Knowledge Actions',
1708
+ atomIds: collectAgentConversationAtomIds(params.knowledgePoints),
1709
+ });
1710
+ }
1711
+ return {
1712
+ answer,
1713
+ assistantBlocks: blocks,
1714
+ knowledgeRun,
1715
+ graphContext,
1716
+ answerReleaseReview,
1717
+ graphAnswerPlan: releasedGraphAnswerPlan,
1718
+ graphAnswerCoverage,
1719
+ ...(fullReportAssembly ? { fullReportAssembly: fullReportAssembly.state } : {}),
1720
+ };
1721
+ }