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,209 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const KnowledgeLearningPlatform_1 = require("./KnowledgeLearningPlatform");
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const agentConversationExecution_1 = require("./agentConversationExecution");
11
+ const agentResponseBudget_1 = require("./agentResponseBudget");
12
+ describe('agent conversation execution limits', () => {
13
+ const document = {
14
+ documentId: 'bounded-source',
15
+ sourcePath: 'bounded/source.md',
16
+ content: '# Bounded Source\n\nA bounded source explains safe execution and complete evidence. '.repeat(24),
17
+ };
18
+ test('client capability hints cannot exceed the declared host tier', async () => {
19
+ const platform = new KnowledgeLearningPlatform_1.KnowledgeLearningPlatform({
20
+ autoPersist: false,
21
+ responseBudgetCapability: { memoryClass: 'low', workload: 'normal' },
22
+ });
23
+ await platform.ingestKnowledge({ documents: [document] });
24
+ const response = await platform.agentConversation({
25
+ message: 'Explain bounded source', responseMode: 'full', persistMemory: false,
26
+ responseBudgetCapability: { memoryClass: 'high', workload: 'max' },
27
+ });
28
+ expect(response.responseBudget?.tier).toBe('standard');
29
+ });
30
+ test('declines oversized sources without claiming a complete source scan', async () => {
31
+ const options = {
32
+ autoPersist: false,
33
+ responseExecutionPolicy: { maxSourceBytes: 96, maxTotalSourceBytes: 128 },
34
+ };
35
+ const platform = new KnowledgeLearningPlatform_1.KnowledgeLearningPlatform(options);
36
+ await platform.ingestKnowledge({ documents: [document] });
37
+ const response = await platform.agentConversation({
38
+ message: 'Explain bounded source', responseMode: 'full', persistMemory: false,
39
+ });
40
+ expect(response.trace.ragContextPack?.sourceDecisions).toEqual(expect.arrayContaining([
41
+ expect.objectContaining({ status: 'source_window_unavailable', reason: 'runtime_source_bytes_limit' }),
42
+ ]));
43
+ expect(response.summary.responseTruncated).toBe(true);
44
+ expect(response.trace.ragSufficiencyReview?.status).not.toBe('sufficient');
45
+ });
46
+ test('expired query work cannot progress to memory or artifact writes', async () => {
47
+ const backend = {
48
+ id: 'delayed-query',
49
+ query: async (context) => {
50
+ await new Promise(resolve => setTimeout(resolve, 25));
51
+ return { candidates: context.atoms.map(atom => ({ atomId: atom.id, score: 1 })) };
52
+ },
53
+ };
54
+ const options = { autoPersist: false, graphQueryBackend: backend, responseExecutionPolicy: { timeoutMs: 5 } };
55
+ const platform = new KnowledgeLearningPlatform_1.KnowledgeLearningPlatform(options);
56
+ await platform.ingestKnowledge({ documents: [document] });
57
+ await expect(platform.agentConversation({
58
+ userId: 'deadline-user', message: 'Explain bounded source', persistMemory: true,
59
+ })).rejects.toEqual(expect.objectContaining({ code: 'runtime_timeout' }));
60
+ const memory = await platform.listConversationMemory({ userId: 'deadline-user' });
61
+ expect(memory.entries).toHaveLength(0);
62
+ expect((await platform.addConversationMemory({ userId: 'deadline-user', content: 'next operation' })).added).toBe(true);
63
+ });
64
+ test('bounds admitted turns before they enter the serialized state queue', async () => {
65
+ let release;
66
+ const held = new Promise(resolve => { release = resolve; });
67
+ const backend = {
68
+ id: 'held-query',
69
+ query: async (context) => {
70
+ await held;
71
+ return { candidates: context.atoms.map(atom => ({ atomId: atom.id, score: 1 })) };
72
+ },
73
+ };
74
+ const options = { autoPersist: false, graphQueryBackend: backend, responseExecutionPolicy: { maxPendingTurns: 2 } };
75
+ const platform = new KnowledgeLearningPlatform_1.KnowledgeLearningPlatform(options);
76
+ await platform.ingestKnowledge({ documents: [document] });
77
+ const first = platform.agentConversation({ message: 'First', persistMemory: false });
78
+ const second = platform.agentConversation({ message: 'Second', persistMemory: false });
79
+ const rejected = platform.agentConversation({ message: 'Third', persistMemory: false }).catch(error => error);
80
+ await new Promise(resolve => setImmediate(resolve));
81
+ release();
82
+ await Promise.all([first, second]);
83
+ expect((await rejected).code).toBe('runtime_turn_capacity');
84
+ });
85
+ test('cancels cooperative query I/O without retrying or writing memory', async () => {
86
+ const controller = new AbortController();
87
+ let entered;
88
+ const started = new Promise(resolve => { entered = resolve; });
89
+ let queries = 0;
90
+ const backend = {
91
+ id: 'cancellable-query',
92
+ query: async (context) => {
93
+ queries++;
94
+ const signal = context.signal;
95
+ entered();
96
+ await new Promise((_resolve, reject) => {
97
+ if (signal.aborted)
98
+ reject(signal.reason);
99
+ else
100
+ signal.addEventListener('abort', () => reject(signal.reason), { once: true });
101
+ });
102
+ return { candidates: [] };
103
+ },
104
+ };
105
+ const platform = new KnowledgeLearningPlatform_1.KnowledgeLearningPlatform({ autoPersist: false, graphQueryBackend: backend });
106
+ await platform.ingestKnowledge({ documents: [document] });
107
+ const turn = platform.agentConversation({ userId: 'cancelled-user', message: 'Bounded source' }, controller.signal)
108
+ .catch(error => error);
109
+ await started;
110
+ controller.abort();
111
+ expect((await turn).code).toBe('runtime_cancelled');
112
+ expect(queries).toBe(1);
113
+ expect((await platform.listConversationMemory({ userId: 'cancelled-user' })).entries).toHaveLength(0);
114
+ });
115
+ test('rejects dense source structure while preserving direct evidence', async () => {
116
+ const options = { autoPersist: false, responseExecutionPolicy: { maxSourceLines: 4 } };
117
+ const platform = new KnowledgeLearningPlatform_1.KnowledgeLearningPlatform(options);
118
+ await platform.ingestKnowledge({ documents: [document] });
119
+ const response = await platform.agentConversation({ message: 'Explain bounded source', persistMemory: false });
120
+ expect(response.trace.ragContextPack?.sourceDecisions.some(item => item.reason === 'runtime_source_lines_limit')).toBe(true);
121
+ expect(response.trace.ragSufficiencyReview?.status).not.toBe('sufficient');
122
+ expect(response.citations.length).toBeGreaterThan(0);
123
+ });
124
+ test('keeps the default tier conservative when only the host ceiling is configured', async () => {
125
+ const platform = new KnowledgeLearningPlatform_1.KnowledgeLearningPlatform({
126
+ autoPersist: false,
127
+ responseBudgetHostCapability: { memoryClass: 'high', workload: 'max' },
128
+ });
129
+ await platform.ingestKnowledge({ documents: [document] });
130
+ const request = { message: 'Explain bounded source', responseMode: 'full', persistMemory: false };
131
+ expect((await platform.agentConversation(request)).responseBudget?.tier).toBe('standard');
132
+ expect((await platform.agentConversation({
133
+ ...request, responseBudgetCapability: { memoryClass: 'high', workload: 'max' },
134
+ })).responseBudget?.tier).toBe('max');
135
+ });
136
+ test('a cancelled queued turn never reaches query or persistence', async () => {
137
+ let release;
138
+ let entered;
139
+ const started = new Promise(resolve => { entered = resolve; });
140
+ const held = new Promise(resolve => { release = resolve; });
141
+ const query = jest.fn(async () => { entered(); await held; return { candidates: [] }; });
142
+ const platform = new KnowledgeLearningPlatform_1.KnowledgeLearningPlatform({ autoPersist: false, graphQueryBackend: { id: 'held', query } });
143
+ const first = platform.agentConversation({ message: 'First', persistMemory: false });
144
+ await started;
145
+ const controller = new AbortController();
146
+ const second = platform.agentConversation({ userId: 'queued', message: 'Second' }, controller.signal).catch(error => error);
147
+ controller.abort();
148
+ release();
149
+ await first;
150
+ expect((await second).code).toBe('runtime_cancelled');
151
+ expect(query).toHaveBeenCalledTimes(1);
152
+ expect((await platform.listConversationMemory({ userId: 'queued' })).entries).toHaveLength(0);
153
+ });
154
+ });
155
+ describe('bounded source file I/O', () => {
156
+ let directory;
157
+ beforeEach(async () => { directory = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'source-budget-')); });
158
+ afterEach(async () => {
159
+ jest.restoreAllMocks();
160
+ await promises_1.default.rm(directory, { recursive: true, force: true });
161
+ });
162
+ test('counts UTF-8 bytes at the exact file limit and charges cumulative reads', async () => {
163
+ const filePath = path_1.default.join(directory, 'unicode.md');
164
+ const source = '知识🧠';
165
+ const bytes = Buffer.byteLength(source);
166
+ await promises_1.default.writeFile(filePath, source);
167
+ await agentConversationExecution_1.AgentConversationExecution.run({
168
+ budget: (0, agentResponseBudget_1.resolveAgentResponseBudget)(),
169
+ policy: (0, agentConversationExecution_1.normalizeAgentConversationExecutionPolicy)({ maxSourceBytes: bytes, maxTotalSourceBytes: bytes }),
170
+ }, async (execution) => {
171
+ expect(await execution.readSourceFile(filePath)).toBe(source);
172
+ expect(execution.diagnostics().sourceBytes).toBe(bytes);
173
+ await expect(execution.readSourceFile(filePath)).rejects.toHaveProperty('code', 'runtime_total_source_bytes_limit');
174
+ });
175
+ });
176
+ test('bounds actual bytes when a source grows after stat and closes the descriptor', async () => {
177
+ const filePath = path_1.default.join(directory, 'growing.md');
178
+ await promises_1.default.writeFile(filePath, 'small');
179
+ const file = await promises_1.default.open(filePath, 'r');
180
+ const oldStat = await file.stat();
181
+ await promises_1.default.appendFile(filePath, 'x'.repeat(128 * 1024));
182
+ jest.spyOn(file, 'stat').mockResolvedValueOnce(oldStat);
183
+ const close = jest.spyOn(file, 'close');
184
+ jest.spyOn(promises_1.default, 'open').mockResolvedValueOnce(file);
185
+ await expect(agentConversationExecution_1.AgentConversationExecution.run({
186
+ budget: (0, agentResponseBudget_1.resolveAgentResponseBudget)(),
187
+ policy: (0, agentConversationExecution_1.normalizeAgentConversationExecutionPolicy)({ maxSourceBytes: 16 }),
188
+ }, execution => execution.readSourceFile(filePath))).rejects.toHaveProperty('code', 'runtime_source_bytes_limit');
189
+ expect(close).toHaveBeenCalled();
190
+ await expect(file.stat()).rejects.toHaveProperty('code', 'EBADF');
191
+ });
192
+ test('aborts a source stream and closes its descriptor', async () => {
193
+ const filePath = path_1.default.join(directory, 'cancelled.md');
194
+ await promises_1.default.writeFile(filePath, Buffer.alloc(256 * 1024, 65));
195
+ const file = await promises_1.default.open(filePath, 'r');
196
+ const controller = new AbortController();
197
+ const createReadStream = file.createReadStream.bind(file);
198
+ jest.spyOn(file, 'createReadStream').mockImplementation(options => {
199
+ const stream = createReadStream(options);
200
+ stream.once('data', () => controller.abort());
201
+ return stream;
202
+ });
203
+ jest.spyOn(promises_1.default, 'open').mockResolvedValueOnce(file);
204
+ await expect(agentConversationExecution_1.AgentConversationExecution.run({
205
+ budget: (0, agentResponseBudget_1.resolveAgentResponseBudget)(), policy: (0, agentConversationExecution_1.normalizeAgentConversationExecutionPolicy)(), signal: controller.signal,
206
+ }, execution => execution.readSourceFile(filePath))).rejects.toHaveProperty('code', 'runtime_cancelled');
207
+ await expect(file.stat()).rejects.toHaveProperty('code', 'EBADF');
208
+ });
209
+ });
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentConversationSerializationError = void 0;
4
+ exports.measureJsonBytes = measureJsonBytes;
5
+ exports.serializeAgentConversationResponse = serializeAgentConversationResponse;
6
+ exports.serializeAgentConversationHttpResponse = serializeAgentConversationHttpResponse;
7
+ exports.serializeAgentConversationTurnEvent = serializeAgentConversationTurnEvent;
8
+ const mobileAnswerProjection_1 = require("./mobileAnswerProjection");
9
+ const TRUNCATION_REASON = 'runtime_serialized_bytes_limit';
10
+ // Covers a completed event, its validated 160-byte turn ID, timestamp and SSE framing.
11
+ // Every transport reserves the same space to release the same answer.
12
+ const TURN_WIRE_RESERVE_BYTES = 288;
13
+ const DEFAULT_WIRE_LIMIT = 8 * 1024 * 1024;
14
+ const MAX_RELEASE_CITATIONS = 64;
15
+ const MAX_RELEASE_CLAIMS = 128;
16
+ class AgentConversationSerializationError extends Error {
17
+ constructor() {
18
+ super(TRUNCATION_REASON);
19
+ this.code = TRUNCATION_REASON;
20
+ this.statusCode = 413;
21
+ }
22
+ }
23
+ exports.AgentConversationSerializationError = AgentConversationSerializationError;
24
+ /** Exact for JSON data; stops before allocating a serialized copy of an oversized response. */
25
+ function measureJsonBytes(value, limit) {
26
+ let bytes = 0;
27
+ const ancestors = new Set();
28
+ const stringBytes = (text) => {
29
+ bytes += 2;
30
+ for (let i = 0; i < text.length && bytes <= limit; i++) {
31
+ const code = text.charCodeAt(i);
32
+ if (code === 34 || code === 92 || code === 8 || code === 9 || code === 10 || code === 12 || code === 13)
33
+ bytes += 2;
34
+ else if (code < 32)
35
+ bytes += 6;
36
+ else if (code < 128)
37
+ bytes++;
38
+ else if (code < 2048)
39
+ bytes += 2;
40
+ else if (code >= 0xd800 && code <= 0xdbff) {
41
+ const next = text.charCodeAt(i + 1);
42
+ if (next >= 0xdc00 && next <= 0xdfff) {
43
+ bytes += 4;
44
+ i++;
45
+ }
46
+ else
47
+ bytes += 6;
48
+ }
49
+ else if (code >= 0xdc00 && code <= 0xdfff)
50
+ bytes += 6;
51
+ else
52
+ bytes += 3;
53
+ }
54
+ };
55
+ const visit = (item, depth) => {
56
+ if (bytes > limit)
57
+ return;
58
+ if (item === null || item === undefined || typeof item === 'function' || typeof item === 'symbol') {
59
+ bytes += 4;
60
+ return;
61
+ }
62
+ if (typeof item === 'string') {
63
+ stringBytes(item);
64
+ return;
65
+ }
66
+ if (typeof item === 'boolean') {
67
+ bytes += item ? 4 : 5;
68
+ return;
69
+ }
70
+ if (typeof item === 'number') {
71
+ bytes += Number.isFinite(item) ? String(item).length : 4;
72
+ return;
73
+ }
74
+ if (typeof item !== 'object')
75
+ throw new TypeError('Conversation responses must contain JSON data');
76
+ if (ancestors.has(item) || depth > 64)
77
+ throw new TypeError('Cyclic or excessively nested conversation response');
78
+ if (typeof item.toJSON === 'function')
79
+ throw new TypeError('Conversation responses must contain plain JSON data');
80
+ ancestors.add(item);
81
+ bytes += 2;
82
+ if (Array.isArray(item)) {
83
+ for (let i = 0; i < item.length && bytes <= limit; i++) {
84
+ if (i)
85
+ bytes++;
86
+ visit(item[i], depth + 1);
87
+ }
88
+ }
89
+ else {
90
+ let first = true;
91
+ for (const key in item) {
92
+ if (bytes > limit)
93
+ break;
94
+ if (!Object.prototype.hasOwnProperty.call(item, key))
95
+ continue;
96
+ const child = item[key];
97
+ if (child === undefined || typeof child === 'function' || typeof child === 'symbol')
98
+ continue;
99
+ if (!first)
100
+ bytes++;
101
+ first = false;
102
+ stringBytes(key);
103
+ bytes++;
104
+ visit(child, depth + 1);
105
+ }
106
+ }
107
+ ancestors.delete(item);
108
+ };
109
+ visit(value, 0);
110
+ return bytes;
111
+ }
112
+ function wireLimit(result) {
113
+ const requested = Number(result.responseBudget?.runtimeGovernor.maxSerializedBytes);
114
+ return Number.isFinite(requested) && requested > 0 ? Math.floor(requested) : DEFAULT_WIRE_LIMIT;
115
+ }
116
+ function countUnescaped(value, token) {
117
+ const escapedToken = token.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
118
+ return (value.match(new RegExp(`(?<!\\\\)${escapedToken}`, 'gu')) || []).length;
119
+ }
120
+ function clipBalancedMarkdown(source, maxChars) {
121
+ if (source.length <= maxChars)
122
+ return source;
123
+ const marker = '\n\n[Response truncated by runtime safety governor.]';
124
+ if (maxChars < marker.length)
125
+ return '';
126
+ let clipped = source.slice(0, maxChars - marker.length).trimEnd();
127
+ // Keep whole claims when possible, and never leave an unterminated code/math block.
128
+ const boundary = Math.max(clipped.lastIndexOf('\n\n'), clipped.lastIndexOf('. '), clipped.lastIndexOf('。'));
129
+ if (boundary >= clipped.length / 2)
130
+ clipped = clipped.slice(0, boundary + (clipped[boundary] === '\n' ? 0 : 1)).trimEnd();
131
+ const fences = [...clipped.matchAll(/^\s*(`{3,}|~{3,}).*$/gm)];
132
+ let openFence;
133
+ for (const fence of fences) {
134
+ if (!openFence)
135
+ openFence = { marker: fence[1], index: fence.index };
136
+ else if (fence[1][0] === openFence.marker[0] && fence[1].length >= openFence.marker.length)
137
+ openFence = undefined;
138
+ }
139
+ if (openFence)
140
+ clipped = clipped.slice(0, openFence.index).trimEnd();
141
+ if (countUnescaped(clipped, '$$') % 2)
142
+ clipped = clipped.slice(0, clipped.lastIndexOf('$$')).trimEnd();
143
+ if (countUnescaped(clipped.replace(/(?<!\\)\$\$/gu, ''), '$') % 2)
144
+ clipped = clipped.slice(0, clipped.lastIndexOf('$')).trimEnd();
145
+ if (countUnescaped(clipped.replace(/^\s*`{3,}.*$/gm, ''), '`') % 2)
146
+ clipped = clipped.slice(0, clipped.lastIndexOf('`')).trimEnd();
147
+ if (clipped.lastIndexOf('[') > clipped.lastIndexOf(']'))
148
+ clipped = clipped.slice(0, clipped.lastIndexOf('[')).trimEnd();
149
+ if (/[\ud800-\udbff]$/.test(clipped))
150
+ clipped = clipped.slice(0, -1);
151
+ return `${clipped}${marker}`.trim();
152
+ }
153
+ function compactResponse(result, answerMaxChars) {
154
+ const sourceCitations = result.citations || [];
155
+ const sourceClaims = result.trace?.answerClaimCitations || [];
156
+ let answer = clipBalancedMarkdown(result.answer || '', answerMaxChars);
157
+ // Oversized metadata must not leave a retained claim with a dangling citation.
158
+ const boundedEvidence = sourceCitations.length <= MAX_RELEASE_CITATIONS && sourceClaims.length <= MAX_RELEASE_CLAIMS;
159
+ const citations = boundedEvidence ? sourceCitations.map(citation => ({
160
+ citationId: citation.citationId, atomId: citation.atomId, documentId: citation.documentId,
161
+ sourcePath: citation.sourcePath, title: citation.title,
162
+ snippet: citation.snippet.length > 1024 ? `${citation.snippet.slice(0, 1021)}...` : citation.snippet,
163
+ startOffset: citation.startOffset, endOffset: citation.endOffset, startLine: citation.startLine, endLine: citation.endLine,
164
+ score: citation.score,
165
+ })) : [];
166
+ const citationIds = new Set(citations.map(citation => citation.citationId));
167
+ const claims = boundedEvidence ? sourceClaims.filter(claim => answer.includes(claim.text)) : [];
168
+ const claimsSupported = claims.every(claim => claim.citationIds.length <= MAX_RELEASE_CITATIONS
169
+ && claim.citationIds.every(id => citationIds.has(id)));
170
+ if (!boundedEvidence || !claimsSupported) {
171
+ answer = clipBalancedMarkdown('[Response truncated by runtime safety governor. Narrow the request to retain its evidence.]', answerMaxChars);
172
+ citations.length = 0;
173
+ claims.length = 0;
174
+ }
175
+ const compact = {
176
+ userId: result.userId,
177
+ sessionId: result.sessionId,
178
+ assistantMessage: answer,
179
+ answer,
180
+ responseMode: result.responseMode,
181
+ ...(result.responseProfile ? { responseProfile: result.responseProfile } : {}),
182
+ ...(result.responseBudget ? { responseBudget: result.responseBudget } : {}),
183
+ assistantBlocks: [], knowledgePoints: [], citations, recalledMemories: [], memoryActions: [],
184
+ summary: {
185
+ generatedAt: result.summary.generatedAt,
186
+ topK: result.summary.topK,
187
+ returnedKnowledgePoints: 0,
188
+ returnedCitations: citations.length,
189
+ recalledMemoryCount: 0,
190
+ appliedMemoryCount: 0,
191
+ queryEvidenceCoverageRatioPct: citations.length ? result.summary.queryEvidenceCoverageRatioPct : 0,
192
+ responseTruncated: true,
193
+ responseTruncationReason: TRUNCATION_REASON,
194
+ },
195
+ trace: {
196
+ sessionId: result.sessionId,
197
+ invocationId: result.trace?.invocationId || '',
198
+ retrieval: {},
199
+ recalledMemoryCount: 0,
200
+ appliedMemoryCount: 0,
201
+ usedScope: {
202
+ source: result.trace?.usedScope?.source || 'global',
203
+ workspaceId: result.trace?.usedScope?.workspaceId || null,
204
+ corpusId: result.trace?.usedScope?.corpusId || null,
205
+ documentIds: [], atomIds: [], sourcePathPrefixes: [], languages: [],
206
+ matchedAtomCount: result.trace?.usedScope?.matchedAtomCount || 0,
207
+ },
208
+ ...(claims.length ? { answerClaimCitations: claims } : {}),
209
+ responseTruncated: true,
210
+ responseTruncationReason: TRUNCATION_REASON,
211
+ },
212
+ };
213
+ if (result.responseProfile === 'mobile_compact')
214
+ compact.mobileProjection = (0, mobileAnswerProjection_1.projectAnswerForMobile)(compact);
215
+ return compact;
216
+ }
217
+ function releaseWithinBudget(result, maxBytes) {
218
+ if (measureJsonBytes(result, maxBytes) <= maxBytes) {
219
+ const truncated = result.summary.responseTruncationReason === TRUNCATION_REASON;
220
+ return { json: JSON.stringify(result), result, truncated, ...(truncated ? { reason: TRUNCATION_REASON } : {}) };
221
+ }
222
+ let lower = 0;
223
+ let upper = Math.min(32000, result.answer.length, result.responseBudget?.runtimeGovernor.maxReportChars || 32000);
224
+ let best = compactResponse(result, 0);
225
+ if (measureJsonBytes(best, maxBytes) > maxBytes) {
226
+ // If citations cannot fit, release no scientific claims and no obsolete coverage.
227
+ best = compactResponse({ ...result, citations: [], answer: '', trace: { ...result.trace, answerClaimCitations: [] } }, 0);
228
+ delete best.responseBudget;
229
+ if (measureJsonBytes(best, maxBytes) > maxBytes)
230
+ throw new AgentConversationSerializationError();
231
+ return { json: JSON.stringify(best), result: best, truncated: true, reason: TRUNCATION_REASON };
232
+ }
233
+ while (lower <= upper) {
234
+ const chars = Math.floor((lower + upper) / 2);
235
+ const candidate = compactResponse(result, chars);
236
+ if (measureJsonBytes(candidate, maxBytes) <= maxBytes) {
237
+ best = candidate;
238
+ lower = chars + 1;
239
+ }
240
+ else
241
+ upper = chars - 1;
242
+ }
243
+ return { json: JSON.stringify(best), result: best, truncated: true, reason: TRUNCATION_REASON };
244
+ }
245
+ function serializeAgentConversationResponse(result) {
246
+ return releaseWithinBudget(result, wireLimit(result) - TURN_WIRE_RESERVE_BYTES);
247
+ }
248
+ function serializeAgentConversationHttpResponse(result) {
249
+ const serialized = serializeAgentConversationResponse(result);
250
+ return { ...serialized, json: `{"success":true,"result":${serialized.json}}` };
251
+ }
252
+ function serializeAgentConversationTurnEvent(eventType, payload) {
253
+ if (eventType === 'turn_completed' && payload && typeof payload === 'object' && 'result' in payload && payload.result) {
254
+ const event = payload;
255
+ const limit = wireLimit(event.result);
256
+ const envelope = { ...event, result: null };
257
+ const framingBytes = Buffer.byteLength(`event: ${eventType}\ndata: \n\n`);
258
+ const envelopeBytes = measureJsonBytes(envelope, limit) - 4 + framingBytes;
259
+ const serialized = releaseWithinBudget(event.result, limit - Math.max(TURN_WIRE_RESERVE_BYTES, envelopeBytes));
260
+ return { json: JSON.stringify({ ...event, result: serialized.result }), truncated: serialized.truncated };
261
+ }
262
+ const payloadLimit = DEFAULT_WIRE_LIMIT - Buffer.byteLength(`event: ${eventType}\ndata: \n\n`);
263
+ if (measureJsonBytes(payload, payloadLimit) > payloadLimit)
264
+ throw new AgentConversationSerializationError();
265
+ return { json: JSON.stringify(payload), truncated: false };
266
+ }
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const agentConversationSerialization_1 = require("./agentConversationSerialization");
4
+ function makeResponse(overrides = {}) {
5
+ const answer = overrides.answer || 'A bounded answer.';
6
+ return {
7
+ userId: 'serialization-user',
8
+ sessionId: 'serialization-session',
9
+ assistantMessage: answer,
10
+ answer,
11
+ responseMode: 'full',
12
+ responseBudget: {
13
+ mode: 'unbounded',
14
+ tier: 'unbounded',
15
+ productCapDisabled: true,
16
+ rag: {
17
+ maxFragments: 4096,
18
+ maxCharsPerFragment: 16000,
19
+ maxTotalChars: 320000,
20
+ productCapDisabled: true,
21
+ runtimeMaxFragments: 4096,
22
+ runtimeMaxCharsPerFragment: 16000,
23
+ runtimeMaxTotalChars: 64 * 1024 * 1024,
24
+ },
25
+ runtimeGovernor: {
26
+ timeoutMs: 180000,
27
+ maxSerializedBytes: 1200,
28
+ maxFragmentsProcessed: 4096,
29
+ maxReportChars: 320000,
30
+ },
31
+ },
32
+ knowledgePoints: [],
33
+ citations: [],
34
+ recalledMemories: [],
35
+ memoryActions: [],
36
+ summary: {
37
+ generatedAt: '2026-09-03T00:00:00.000Z',
38
+ topK: 6,
39
+ returnedKnowledgePoints: 0,
40
+ returnedCitations: 0,
41
+ recalledMemoryCount: 0,
42
+ appliedMemoryCount: 0,
43
+ queryEvidenceCoverageRatioPct: 100,
44
+ },
45
+ trace: {
46
+ sessionId: 'serialization-session',
47
+ invocationId: 'serialization-invocation',
48
+ retrieval: {},
49
+ recalledMemoryCount: 0,
50
+ appliedMemoryCount: 0,
51
+ usedScope: {
52
+ source: 'global',
53
+ workspaceId: null,
54
+ corpusId: null,
55
+ documentIds: [],
56
+ atomIds: [],
57
+ sourcePathPrefixes: [],
58
+ languages: [],
59
+ matchedAtomCount: 0,
60
+ },
61
+ responseBudget: undefined,
62
+ },
63
+ ...overrides,
64
+ };
65
+ }
66
+ describe('agent conversation serialization governor', () => {
67
+ test.each(['知识🧠', 'quotes " \\ \n\t', '\ud800', '\u0000', { nested: [null, undefined, 1, false] }])('measures JSON escaping and UTF-8 before allocation: %p', value => {
68
+ const bytes = Buffer.byteLength(JSON.stringify(value));
69
+ expect((0, agentConversationSerialization_1.measureJsonBytes)(value, bytes)).toBe(bytes);
70
+ expect((0, agentConversationSerialization_1.measureJsonBytes)(value, bytes - 1)).toBeGreaterThan(bytes - 1);
71
+ });
72
+ test('the response fits but the full SSE envelope does not', () => {
73
+ const response = makeResponse();
74
+ response.responseBudget.runtimeGovernor.maxSerializedBytes = 2000;
75
+ response.answer += 'x'.repeat(Math.max(0, 1998 - Buffer.byteLength(JSON.stringify(response))));
76
+ expect(Buffer.byteLength(JSON.stringify(response))).toBe(1998);
77
+ const event = (0, agentConversationSerialization_1.serializeAgentConversationTurnEvent)('turn_completed', {
78
+ type: 'turn_completed', turnId: 'turn_envelope', emittedAt: '2026-09-12T00:00:00.000Z', result: response,
79
+ });
80
+ expect(Buffer.byteLength(`event: turn_completed\ndata: ${event.json}\n\n`)).toBeLessThanOrEqual(2000);
81
+ expect(event.truncated).toBe(true);
82
+ expect(JSON.parse(event.json).result.summary.responseTruncated).toBe(true);
83
+ });
84
+ test('one released projection is reused for JSON, SSE and replay with surviving citations', () => {
85
+ const response = makeResponse({ answer: 'A supported claim. '.repeat(800), assistantMessage: 'A supported claim. '.repeat(800) });
86
+ response.responseBudget.runtimeGovernor.maxSerializedBytes = 4000;
87
+ response.citations = [{ citationId: 'cite-1', atomId: 'atom-1', documentId: 'doc-1', sourcePath: 'source.md', title: 'Source', snippet: 'A supported claim.', score: 1 }];
88
+ response.summary.returnedCitations = 99;
89
+ response.trace.answerClaimCitations = [{ claimId: 'claim-1', text: 'A supported claim.', citationIds: ['cite-1'], fragmentIds: ['fragment-1'], sourcePaths: ['source.md'], supportStatus: 'supported' }];
90
+ const http = (0, agentConversationSerialization_1.serializeAgentConversationHttpResponse)(response);
91
+ const event = { type: 'turn_completed', turnId: 'turn_citations', emittedAt: '2026-09-12T00:00:00.000Z', result: response };
92
+ const live = (0, agentConversationSerialization_1.serializeAgentConversationTurnEvent)('turn_completed', event);
93
+ const replay = (0, agentConversationSerialization_1.serializeAgentConversationTurnEvent)('turn_completed', { ...event, result: http.result });
94
+ expect(JSON.parse(live.json).result).toEqual(http.result);
95
+ expect(JSON.parse(replay.json).result).toEqual(http.result);
96
+ expect(http.result.answer).toContain('A supported claim.');
97
+ expect(http.result.citations.map(citation => citation.citationId)).toEqual(['cite-1']);
98
+ expect(http.result.summary.returnedCitations).toBe(http.result.citations.length);
99
+ expect(http.result.summary.returnedKnowledgePoints).toBe(http.result.knowledgePoints.length);
100
+ });
101
+ test('does not initially stringify oversized diagnostic collections', () => {
102
+ const response = makeResponse();
103
+ response.trace.retrieval = { hugeDiagnostics: Array.from({ length: 10000 }, () => 'diagnostic'.repeat(50)) };
104
+ const stringify = jest.spyOn(JSON, 'stringify');
105
+ try {
106
+ const serialized = (0, agentConversationSerialization_1.serializeAgentConversationResponse)(response);
107
+ expect(stringify).not.toHaveBeenCalledWith(response);
108
+ expect(Buffer.byteLength(serialized.json)).toBeLessThanOrEqual(1200);
109
+ expect(serialized.result.summary.queryEvidenceCoverageRatioPct).toBe(0);
110
+ }
111
+ finally {
112
+ stringify.mockRestore();
113
+ }
114
+ });
115
+ test('rejects a ceiling too small for the required response contract', () => {
116
+ const response = makeResponse();
117
+ response.responseBudget.runtimeGovernor.maxSerializedBytes = 64;
118
+ expect(() => (0, agentConversationSerialization_1.serializeAgentConversationHttpResponse)(response)).toThrow('runtime_serialized_bytes_limit');
119
+ });
120
+ test('keeps responses unchanged while under the runtime byte limit', () => {
121
+ const response = makeResponse({
122
+ responseBudget: {
123
+ ...makeResponse().responseBudget,
124
+ runtimeGovernor: {
125
+ ...makeResponse().responseBudget.runtimeGovernor,
126
+ maxSerializedBytes: 100000,
127
+ },
128
+ },
129
+ });
130
+ const serialized = (0, agentConversationSerialization_1.serializeAgentConversationResponse)(response);
131
+ expect(serialized.truncated).toBe(false);
132
+ expect(JSON.parse(serialized.json).answer).toBe(response.answer);
133
+ });
134
+ test('compacts oversized unbounded responses and preserves balanced display math', () => {
135
+ const response = makeResponse({
136
+ answer: `## Report\n\n${'Long evidence. '.repeat(2000)}\n\n$$\\frac{a}{b}$$`,
137
+ assistantMessage: `## Report\n\n${'Long evidence. '.repeat(2000)}\n\n$$\\frac{a}{b}$$`,
138
+ });
139
+ const serialized = (0, agentConversationSerialization_1.serializeAgentConversationResponse)(response);
140
+ expect(serialized.truncated).toBe(true);
141
+ expect(Buffer.byteLength(serialized.json, 'utf8')).toBeLessThanOrEqual(1200);
142
+ expect(serialized.result.summary.responseTruncated).toBe(true);
143
+ expect(serialized.result.summary.responseTruncationReason).toBe('runtime_serialized_bytes_limit');
144
+ expect((serialized.result.answer.match(/(?<!\\)\$\$/gu) || []).length % 2).toBe(0);
145
+ });
146
+ test('applies the same governor to a completed SSE event', () => {
147
+ const response = makeResponse({ answer: 'Event payload '.repeat(2000) });
148
+ const serialized = (0, agentConversationSerialization_1.serializeAgentConversationTurnEvent)('turn_completed', {
149
+ type: 'turn_completed',
150
+ turnId: 'turn_serialization',
151
+ emittedAt: '2026-09-03T00:00:00.000Z',
152
+ result: response,
153
+ });
154
+ const event = JSON.parse(serialized.json);
155
+ expect(serialized.truncated).toBe(true);
156
+ expect(event.result.summary.responseTruncated).toBe(true);
157
+ });
158
+ test('serializes the HTTP success envelope within the runtime limit', () => {
159
+ const response = makeResponse({ answer: 'HTTP payload '.repeat(2000) });
160
+ const serialized = (0, agentConversationSerialization_1.serializeAgentConversationHttpResponse)(response);
161
+ expect(serialized.truncated).toBe(true);
162
+ expect(Buffer.byteLength(serialized.json, 'utf8')).toBeLessThanOrEqual(1200);
163
+ expect(JSON.parse(serialized.json).success).toBe(true);
164
+ });
165
+ });