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,233 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Diagram Pipeline unit tests — validates intent inference, planner,
5
+ * spec validation, and response parser (all pure functions).
6
+ */
7
+ const intent_1 = require("./notemd/diagram/intent");
8
+ const planner_1 = require("./notemd/diagram/planner");
9
+ const diagramSpec_1 = require("./notemd/diagram/diagramSpec");
10
+ const diagramSpecResponseParser_1 = require("./notemd/diagram/diagramSpecResponseParser");
11
+ const diagramSpecPrompt_1 = require("./notemd/diagram/prompts/diagramSpecPrompt");
12
+ // ── Intent Inference ──
13
+ describe('Diagram Intent Inference', () => {
14
+ test('empty content defaults to mindmap', () => {
15
+ const result = (0, intent_1.inferDiagramIntent)('');
16
+ expect(result.intent).toBe('mindmap');
17
+ expect(result.confidence).toBeLessThan(0.5);
18
+ });
19
+ test('whitespace-only defaults to mindmap', () => {
20
+ const result = (0, intent_1.inferDiagramIntent)(' \n ');
21
+ expect(result.intent).toBe('mindmap');
22
+ });
23
+ test('data table with numeric values infers dataChart', () => {
24
+ const md = [
25
+ '| Metric | Value |',
26
+ '| ------ | ----- |',
27
+ '| CPU | 85% |',
28
+ '| Memory | 72% |',
29
+ 'Monthly growth trend: 12% increase.',
30
+ ].join('\n');
31
+ const result = (0, intent_1.inferDiagramIntent)(md);
32
+ expect(result.intent).toBe('dataChart');
33
+ expect(result.confidence).toBeGreaterThan(0.8);
34
+ });
35
+ test('request-response vocabulary infers sequence', () => {
36
+ const md = 'Client sends request to server. Server processes and returns response. Gateway routes the request. Service handles the response.';
37
+ const result = (0, intent_1.inferDiagramIntent)(md);
38
+ expect(result.intent).toBe('sequence');
39
+ });
40
+ test('arrow markers infers sequence', () => {
41
+ const result = (0, intent_1.inferDiagramIntent)('User -> API -> Database -> Response');
42
+ expect(result.intent).toBe('sequence');
43
+ });
44
+ test('database schema vocabulary infers erDiagram', () => {
45
+ const md = 'The entity has a primary key. The table references a foreign key. The schema defines the column.';
46
+ const result = (0, intent_1.inferDiagramIntent)(md);
47
+ expect(result.intent).toBe('erDiagram');
48
+ });
49
+ test('state transition vocabulary infers stateDiagram', () => {
50
+ const md = 'The state machine transitions from pending to running. Completed state follows. Failed state triggers retry.';
51
+ const result = (0, intent_1.inferDiagramIntent)(md);
52
+ expect(result.intent).toBe('stateDiagram');
53
+ });
54
+ test('workflow/process vocabulary infers flowchart', () => {
55
+ const md = 'Step 1: Validate input. If valid then process, else stop. Continue to next step.';
56
+ const result = (0, intent_1.inferDiagramIntent)(md);
57
+ expect(result.intent).toBe('flowchart');
58
+ });
59
+ test('numbered steps infers flowchart', () => {
60
+ const md = '1. First step\n2. Second step\n3. Third step';
61
+ const result = (0, intent_1.inferDiagramIntent)(md);
62
+ expect(result.intent).toBe('flowchart');
63
+ });
64
+ test('concept map keywords infers canvasMap', () => {
65
+ const md = 'This is a concept map showing the knowledge graph of spatial clusters.';
66
+ const result = (0, intent_1.inferDiagramIntent)(md);
67
+ expect(result.intent).toBe('canvasMap');
68
+ });
69
+ test('general content defaults to mindmap', () => {
70
+ const result = (0, intent_1.inferDiagramIntent)('The quick brown fox jumps over the lazy dog.');
71
+ expect(result.intent).toBe('mindmap');
72
+ expect(result.confidence).toBeGreaterThan(0.4);
73
+ });
74
+ });
75
+ // ── Planner ──
76
+ describe('Diagram Planner', () => {
77
+ test('best-fit mode routes dataChart to vega-lite', () => {
78
+ const md = '| A | B |\n| - | - |\n| 1 | 2 |\n| 3 | 4 |\nchart';
79
+ const plan = (0, planner_1.buildDiagramPlan)(md, { compatibilityMode: 'best-fit' });
80
+ expect(plan.renderTarget).toBe('vega-lite');
81
+ });
82
+ test('best-fit mode routes sequence to mermaid', () => {
83
+ const md = 'Client sends request. Server returns response. Gateway routes. Service processes.';
84
+ const plan = (0, planner_1.buildDiagramPlan)(md, { compatibilityMode: 'best-fit' });
85
+ expect(plan.intent).toBe('sequence');
86
+ expect(plan.renderTarget).toBe('mermaid');
87
+ expect(plan.mermaidDiagramType).toBe('sequenceDiagram');
88
+ });
89
+ test('legacy-mermaid mode forces mermaid render target', () => {
90
+ const md = '| A | B |\n| - | - |\n| 1 | 2 |\n| 3 | 4 |\nchart share distribution';
91
+ const plan = (0, planner_1.buildDiagramPlan)(md, { compatibilityMode: 'legacy-mermaid' });
92
+ expect(plan.renderTarget).toBe('mermaid');
93
+ expect(plan.legacyCompatibilityMode).toBe(true);
94
+ });
95
+ test('explicit intent overrides inference', () => {
96
+ const plan = (0, planner_1.buildDiagramPlan)('any content', {
97
+ requestedIntent: 'flowchart',
98
+ compatibilityMode: 'best-fit'
99
+ });
100
+ expect(plan.intent).toBe('flowchart');
101
+ expect(plan.confidence).toBeGreaterThan(0.9);
102
+ });
103
+ test('mindmap intent resolves to mindmap mermaid type', () => {
104
+ const plan = (0, planner_1.buildDiagramPlan)('# Just a regular note about topics', { compatibilityMode: 'best-fit' });
105
+ expect(plan.intent).toBe('mindmap');
106
+ expect(plan.mermaidDiagramType).toBe('mindmap');
107
+ });
108
+ test('fallback targets include html for dataChart (non-mermaid)', () => {
109
+ const md = '| A | B |\n| - | - |\n| 1 | 2 |\n| 3 | 4 |\nchart share breakdown';
110
+ const plan = (0, planner_1.buildDiagramPlan)(md, { compatibilityMode: 'best-fit' });
111
+ expect(plan.renderTarget).toBe('vega-lite');
112
+ // dataChart has no mermaidDiagramType, so only html is a valid fallback
113
+ expect(plan.fallbackTargets).toContain('html');
114
+ });
115
+ });
116
+ // ── Spec Validation ──
117
+ describe('Diagram Spec Validation', () => {
118
+ const validMindmapSpec = {
119
+ intent: 'mindmap',
120
+ title: 'Test Diagram',
121
+ nodes: [{ id: 'n1', label: 'Node 1' }, { id: 'n2', label: 'Node 2' }],
122
+ edges: [{ from: 'n1', to: 'n2' }],
123
+ };
124
+ test('valid mindmap spec passes validation', () => {
125
+ const result = (0, diagramSpec_1.validateDiagramSpec)(validMindmapSpec);
126
+ expect(result.valid).toBe(true);
127
+ expect(result.errors).toHaveLength(0);
128
+ });
129
+ test('missing title fails validation', () => {
130
+ const result = (0, diagramSpec_1.validateDiagramSpec)({ ...validMindmapSpec, title: '' });
131
+ expect(result.valid).toBe(false);
132
+ expect(result.errors.some(e => e.includes('title'))).toBe(true);
133
+ });
134
+ test('duplicate node ids fail validation', () => {
135
+ const result = (0, diagramSpec_1.validateDiagramSpec)({
136
+ ...validMindmapSpec,
137
+ nodes: [{ id: 'n1', label: 'A' }, { id: 'n1', label: 'B' }],
138
+ });
139
+ expect(result.valid).toBe(false);
140
+ expect(result.errors.some(e => e.includes('duplicate'))).toBe(true);
141
+ });
142
+ test('edge to missing node fails validation', () => {
143
+ const result = (0, diagramSpec_1.validateDiagramSpec)({
144
+ ...validMindmapSpec,
145
+ edges: [{ from: 'n1', to: 'missing' }],
146
+ });
147
+ expect(result.valid).toBe(false);
148
+ expect(result.errors.some(e => e.includes('missing'))).toBe(true);
149
+ });
150
+ test('unsupported intent fails validation', () => {
151
+ const result = (0, diagramSpec_1.validateDiagramSpec)({ ...validMindmapSpec, intent: 'bogus' });
152
+ expect(result.valid).toBe(false);
153
+ expect(result.errors.some(e => e.includes('unsupported'))).toBe(true);
154
+ });
155
+ test('assertValidDiagramSpec throws on invalid spec', () => {
156
+ expect(() => (0, diagramSpec_1.assertValidDiagramSpec)({ ...validMindmapSpec, title: '' })).toThrow();
157
+ });
158
+ test('dataChart intent requires data series', () => {
159
+ const result = (0, diagramSpec_1.validateDiagramSpec)({ ...validMindmapSpec, intent: 'dataChart', nodes: [] });
160
+ expect(result.valid).toBe(false);
161
+ expect(result.errors.some(e => e.includes('data series'))).toBe(true);
162
+ });
163
+ });
164
+ // ── Response Parser ──
165
+ describe('Diagram Spec Response Parser', () => {
166
+ test('parses valid JSON DiagramSpec', () => {
167
+ const json = JSON.stringify({
168
+ intent: 'mindmap',
169
+ title: 'Test',
170
+ nodes: [{ id: 'a', label: 'A' }],
171
+ });
172
+ const spec = (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)(json);
173
+ expect(spec.intent).toBe('mindmap');
174
+ expect(spec.title).toBe('Test');
175
+ expect(spec.nodes).toHaveLength(1);
176
+ expect(spec.nodes[0].id).toBe('a');
177
+ });
178
+ test('strips code fences from response', () => {
179
+ const json = '```json\n' + JSON.stringify({
180
+ intent: 'flowchart',
181
+ title: 'Test',
182
+ nodes: [{ id: 'a', label: 'A' }],
183
+ }) + '\n```';
184
+ const spec = (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)(json);
185
+ expect(spec.intent).toBe('flowchart');
186
+ expect(spec.title).toBe('Test');
187
+ });
188
+ test('handles diagramSpec wrapper key', () => {
189
+ const json = JSON.stringify({
190
+ diagramSpec: {
191
+ intent: 'sequence',
192
+ title: 'Test',
193
+ nodes: [{ id: 'a', label: 'A' }],
194
+ }
195
+ });
196
+ const spec = (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)(json);
197
+ expect(spec.intent).toBe('sequence');
198
+ });
199
+ test('throws on non-JSON input', () => {
200
+ expect(() => (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)('not json at all')).toThrow();
201
+ });
202
+ test('normalizes edge fields (source→from, target→to)', () => {
203
+ const json = JSON.stringify({
204
+ intent: 'mindmap', title: 'E',
205
+ nodes: [{ id: 'a', label: 'A' }, { id: 'b', label: 'B' }],
206
+ edges: [{ source: 'a', target: 'b', label: 'connects' }],
207
+ });
208
+ const spec = (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)(json);
209
+ expect(spec.edges[0].from).toBe('a');
210
+ expect(spec.edges[0].to).toBe('b');
211
+ });
212
+ });
213
+ // ── Prompt Builder ──
214
+ describe('Diagram Spec Prompt', () => {
215
+ test('includes required intent in output', () => {
216
+ const prompt = (0, diagramSpecPrompt_1.buildDiagramSpecPrompt)({ requiredIntent: 'flowchart' });
217
+ expect(prompt).toContain('REQUIRED');
218
+ expect(prompt).toContain('flowchart');
219
+ });
220
+ test('includes preferred intent', () => {
221
+ const prompt = (0, diagramSpecPrompt_1.buildDiagramSpecPrompt)({ preferredIntent: 'sequence' });
222
+ expect(prompt).toContain('Preferred');
223
+ expect(prompt).toContain('sequence');
224
+ });
225
+ test('includes target language', () => {
226
+ const prompt = (0, diagramSpecPrompt_1.buildDiagramSpecPrompt)({ targetLanguage: 'zh' });
227
+ expect(prompt).toContain('zh');
228
+ });
229
+ test('output rules forbid code fences', () => {
230
+ const prompt = (0, diagramSpecPrompt_1.buildDiagramSpecPrompt)();
231
+ expect(prompt).toContain('Do not wrap');
232
+ });
233
+ });
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const providerTemplates_1 = require("./notemd/providerTemplates");
4
+ const constants_1 = require("./notemd/constants");
5
+ describe('notemd provider templates', () => {
6
+ test('exposes curated templates inspired by Cherry Studio and DeepTutor', () => {
7
+ expect(providerTemplates_1.NOTEMD_PROVIDER_TEMPLATES.length).toBeGreaterThanOrEqual(6);
8
+ expect((0, providerTemplates_1.getNotemdProviderTemplate)('openai-official')?.providerName).toBe('OpenAI');
9
+ expect((0, providerTemplates_1.getNotemdProviderTemplate)('lmstudio-local')?.inspiredBy).toContain('DeepTutor');
10
+ });
11
+ test('applies a template to the matching provider and switches activeProvider', () => {
12
+ const next = (0, providerTemplates_1.applyProviderTemplateToSettings)(constants_1.DEFAULT_SETTINGS, 'openrouter-gateway');
13
+ const provider = next.providers.find((item) => item.name === 'OpenRouter');
14
+ expect(next.activeProvider).toBe('OpenRouter');
15
+ expect(provider).toBeTruthy();
16
+ expect(provider?.baseUrl).toContain('openrouter.ai');
17
+ expect(provider?.model).toContain('openai/');
18
+ });
19
+ test('merges provider_templates into the notemd TOML section without dropping existing overrides', () => {
20
+ const next = (0, providerTemplates_1.mergeProviderTemplatesIntoNotemdSection)({
21
+ active_provider: 'DeepSeek',
22
+ provider_templates: {
23
+ custom_keep: {
24
+ label: 'Keep Me',
25
+ },
26
+ },
27
+ });
28
+ const templates = next.provider_templates;
29
+ expect(templates).toBeTruthy();
30
+ expect(templates.custom_keep.label).toBe('Keep Me');
31
+ expect(templates['openai-official']).toBeTruthy();
32
+ expect(templates['openai-official'].provider_name).toBe('OpenAI');
33
+ });
34
+ });
@@ -39,7 +39,7 @@ const os = __importStar(require("os"));
39
39
  const path = __importStar(require("path"));
40
40
  class TempDir {
41
41
  constructor(prefix) {
42
- this.path = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), `${prefix}-`));
42
+ this.path = fs.realpathSync.native(fs.mkdtempSync(path.join(os.tmpdir(), `${prefix}-`)));
43
43
  }
44
44
  mkdir(relativePath) {
45
45
  const target = path.join(this.path, relativePath);
@@ -94,7 +94,7 @@ function getFreePort() {
94
94
  });
95
95
  });
96
96
  }
97
- function requestJson(port, method, requestPath, body) {
97
+ function requestJson(port, method, requestPath, body, headers = {}) {
98
98
  return new Promise((resolve, reject) => {
99
99
  const payload = typeof body === 'undefined' ? null : Buffer.from(JSON.stringify(body), 'utf8');
100
100
  const req = http.request({
@@ -106,8 +106,9 @@ function requestJson(port, method, requestPath, body) {
106
106
  ? {
107
107
  'Content-Type': 'application/json',
108
108
  'Content-Length': String(payload.length),
109
+ ...headers,
109
110
  }
110
- : undefined,
111
+ : headers,
111
112
  }, (res) => {
112
113
  let text = '';
113
114
  res.setEncoding('utf8');
@@ -141,11 +142,35 @@ describe('NoteMD server integration', () => {
141
142
  let temp;
142
143
  let envRestorers;
143
144
  let server;
145
+ let serverModule;
144
146
  let port;
145
147
  let kbFilePath;
146
148
  let runtimeDataDir;
147
149
  let appConfigPath;
148
150
  let originalArgv;
151
+ function loadFreshServerModule() {
152
+ jest.resetModules();
153
+ process.argv = process.argv.slice(0, 2);
154
+ jest.doMock('./index', () => ({
155
+ buildGraph: jest.fn().mockResolvedValue(undefined),
156
+ }));
157
+ jest.doMock('./core/PathBridge', () => ({
158
+ PathBridge: jest.fn().mockImplementation(() => ({})),
159
+ }));
160
+ jest.doMock('./reader_renderer', () => ({
161
+ renderMathPng: jest.fn().mockResolvedValue({
162
+ pngBase64: 'math',
163
+ width: 100,
164
+ height: 50,
165
+ }),
166
+ renderMermaidPng: jest.fn().mockResolvedValue({
167
+ pngBase64: 'mermaid',
168
+ width: 100,
169
+ height: 50,
170
+ }),
171
+ }));
172
+ return require('./server');
173
+ }
149
174
  beforeAll(async () => {
150
175
  temp = new TempDir('noteconnection-notemd');
151
176
  const projectRoot = temp.mkdir('project');
@@ -186,45 +211,29 @@ describe('NoteMD server integration', () => {
186
211
  envRestorers.push(setEnv('npm_config_workers', undefined));
187
212
  envRestorers.push(setEnv('npm_config_static', undefined));
188
213
  port = await getFreePort();
189
- jest.resetModules();
190
214
  originalArgv = [...process.argv];
191
- process.argv = process.argv.slice(0, 2);
192
- jest.doMock('./index', () => ({
193
- buildGraph: jest.fn().mockResolvedValue(undefined),
194
- }));
195
- jest.doMock('./core/PathBridge', () => ({
196
- PathBridge: jest.fn().mockImplementation(() => ({})),
197
- }));
198
- jest.doMock('./reader_renderer', () => ({
199
- renderMathPng: jest.fn().mockResolvedValue({
200
- pngBase64: 'math',
201
- width: 100,
202
- height: 50,
203
- }),
204
- renderMermaidPng: jest.fn().mockResolvedValue({
205
- pngBase64: 'mermaid',
206
- width: 100,
207
- height: 50,
208
- }),
209
- }));
210
- const serverModule = require('./server');
215
+ serverModule = loadFreshServerModule();
211
216
  server = await serverModule.startServer({ port });
212
- });
213
- afterAll(async () => {
214
- if (server) {
215
- if (typeof server.closeAllConnections === 'function') {
216
- server.closeAllConnections();
217
- }
218
- await new Promise((resolve, reject) => {
219
- server.close((err) => {
220
- if (err) {
221
- reject(err);
222
- return;
223
- }
224
- resolve();
225
- });
226
- });
217
+ }, 20000);
218
+ async function shutdownServerInstance(instance) {
219
+ if (!instance) {
220
+ return;
221
+ }
222
+ if (typeof instance.closeAllConnections === 'function') {
223
+ instance.closeAllConnections();
227
224
  }
225
+ await new Promise((resolve, reject) => {
226
+ instance.close((err) => {
227
+ if (err) {
228
+ reject(err);
229
+ return;
230
+ }
231
+ resolve();
232
+ });
233
+ });
234
+ }
235
+ afterAll(async () => {
236
+ await shutdownServerInstance(server);
228
237
  envRestorers.reverse().forEach((restore) => restore());
229
238
  process.argv = originalArgv;
230
239
  jest.dontMock('./index');
@@ -444,6 +453,93 @@ describe('NoteMD server integration', () => {
444
453
  expect(workspaceResponse.body.workspace.filePath).toBe(kbFilePath);
445
454
  expect(workspaceResponse.body.workspace.folderPath).toBe(path.dirname(kbFilePath));
446
455
  });
456
+ test('NoteMD protocol rejects wrong content type, oversized input and missing cancellation targets', async () => {
457
+ const wrongType = await requestJson(port, 'POST', '/api/notemd/settings', {}, { 'Content-Type': 'text/plain' });
458
+ expect(wrongType.status).toBe(415);
459
+ const oversized = await requestJson(port, 'POST', '/api/notemd/settings', { pad: 'x'.repeat(600 * 1024) });
460
+ expect(oversized.status).toBe(413);
461
+ const cancel = await requestJson(port, 'POST', '/api/notemd/cancel', { operationId: 'missing-operation' });
462
+ expect(cancel.status).toBe(404);
463
+ });
464
+ test('NoteMD protocol rejects files outside the configured workspace without mutation', async () => {
465
+ const outside = temp.file('outside/rejected.md', '# Outside\n$\nunsafe\n$');
466
+ const response = await requestJson(port, 'POST', '/api/notemd/fix-formulas', { filePath: outside, inPlace: true });
467
+ expect(response.status).toBe(403);
468
+ expect(fs.readFileSync(outside, 'utf8')).toBe('# Outside\n$\nunsafe\n$');
469
+ });
470
+ test('NoteMD protocol emits one operation and one completion while processing a real file', async () => {
471
+ const response = await requestJson(port, 'POST', '/api/notemd/process-file', {
472
+ filePath: kbFilePath, operationId: 'notemd-stream-contract',
473
+ }, { accept: 'text/event-stream' });
474
+ expect(response.status).toBe(200);
475
+ const frames = String(response.body).split('\n\n').filter(Boolean);
476
+ const operations = frames.filter(frame => frame.startsWith('event: operation\n'));
477
+ const completed = frames.filter(frame => frame.startsWith('event: done\n'));
478
+ expect(operations).toHaveLength(1);
479
+ expect(completed).toHaveLength(1);
480
+ expect(JSON.parse(completed[0].slice(completed[0].indexOf('data: ') + 6))).toEqual(expect.objectContaining({ success: true, operationId: 'notemd-stream-contract' }));
481
+ });
482
+ test('NoteMD registry owns aliases and unknown routes without an inline fallback', async () => {
483
+ const alias = await requestJson(port, 'PUT', '/api/notemd/fix-formulas', { filePath: kbFilePath, inPlace: true });
484
+ expect(alias.status).toBe(200);
485
+ expect((await requestJson(port, 'POST', '/api/notemd/unknown', {})).status).toBe(404);
486
+ const diagnostics = await requestJson(port, 'GET', '/api/runtime-diagnostics');
487
+ expect(diagnostics.body.routeMigration.registryHits).toBeGreaterThan(0);
488
+ expect(diagnostics.body.routeMigration.inlineFallbacksByFamily.notemd || 0).toBe(0);
489
+ });
490
+ test('NoteMD operation IDs cannot replace an active operation and cancellation reaches its owner', async () => {
491
+ const { NotemdService } = require('./notemd/NotemdService');
492
+ let entered;
493
+ let release;
494
+ const started = new Promise(resolve => { entered = resolve; });
495
+ const held = new Promise(resolve => { release = resolve; });
496
+ let signal;
497
+ const processFile = jest.spyOn(NotemdService.prototype, 'processFile')
498
+ .mockImplementationOnce(async (...args) => {
499
+ signal = args[3];
500
+ entered();
501
+ await held;
502
+ signal.throwIfAborted();
503
+ return { outputPath: kbFilePath };
504
+ }).mockResolvedValueOnce({ outputPath: kbFilePath });
505
+ const payload = { filePath: kbFilePath, operationId: 'notemd-admission-contract' };
506
+ const first = requestJson(port, 'POST', '/api/notemd/process-file', payload);
507
+ try {
508
+ await started;
509
+ const duplicate = await requestJson(port, 'POST', '/api/notemd/process-file', payload);
510
+ expect(duplicate.status).toBe(409);
511
+ expect(processFile).toHaveBeenCalledTimes(1);
512
+ expect((await requestJson(port, 'POST', '/api/notemd/cancel', { operationId: payload.operationId })).status).toBe(200);
513
+ expect(signal?.aborted).toBe(true);
514
+ release();
515
+ expect((await first).status).toBe(499);
516
+ }
517
+ finally {
518
+ release();
519
+ await first;
520
+ processFile.mockRestore();
521
+ }
522
+ });
523
+ test('NoteMD authentication is enforced before route dispatch', async () => {
524
+ const restoreToken = setEnv('NOTE_CONNECTION_AUTH_TOKEN', 'notemd-fixture-token');
525
+ let authenticated;
526
+ try {
527
+ const authPort = await getFreePort();
528
+ authenticated = await loadFreshServerModule().startServer({ port: authPort });
529
+ expect((await requestJson(authPort, 'GET', '/api/notemd/settings')).status).toBe(401);
530
+ expect((await requestJson(authPort, 'GET', '/api/notemd/settings', undefined, { 'X-NoteConnection-Token': 'notemd-fixture-token' })).status).toBe(200);
531
+ }
532
+ finally {
533
+ await shutdownServerInstance(authenticated);
534
+ restoreToken();
535
+ }
536
+ });
537
+ test.each(['/batch-fix-formulas', '/batch-generate-content', '/workflow', '/batch-workflow', '/extract-original-text'])('NoteMD registry validates the %s filesystem boundary', async (endpoint) => {
538
+ const outside = temp.file('outside/extended.md', '# Outside');
539
+ const response = await requestJson(port, 'POST', '/api/notemd' + endpoint, { filePath: outside, folderPath: path.dirname(outside), skipGenerate: true });
540
+ expect(response.status).toBe(403);
541
+ expect(fs.readFileSync(outside, 'utf8')).toBe('# Outside');
542
+ });
447
543
  test('extract-concepts and duplicate endpoints return structured results', async () => {
448
544
  const conceptsResponse = await requestJson(port, 'POST', '/api/notemd/extract-concepts', {
449
545
  filePath: kbFilePath,
@@ -462,4 +558,131 @@ describe('NoteMD server integration', () => {
462
558
  expect(fs.existsSync(appConfigPath)).toBe(true);
463
559
  expect(fs.existsSync(path.join(runtimeDataDir, 'notemd_config.json'))).toBe(false);
464
560
  });
561
+ test('foundation readiness and backend sufficiency reflect embedded sqlite graph runtime after ingest', async () => {
562
+ const ingestResponse = await requestJson(port, 'POST', '/api/knowledge/ingest', {
563
+ incremental: true,
564
+ documents: [
565
+ {
566
+ documentId: 'doc_foundation_runtime',
567
+ sourcePath: path.join(path.dirname(kbFilePath), 'foundation_runtime.md'),
568
+ language: 'en',
569
+ content: '# Foundation Runtime\n\nEmbedded graph backend readiness should be measurable after ingest.',
570
+ },
571
+ ],
572
+ });
573
+ expect(ingestResponse.status).toBe(200);
574
+ expect(ingestResponse.body.success).toBe(true);
575
+ const readinessResponse = await requestJson(port, 'GET', '/api/knowledge/foundation/readiness');
576
+ expect(readinessResponse.status).toBe(200);
577
+ expect(readinessResponse.body.success).toBe(true);
578
+ expect(readinessResponse.body.readiness).toEqual(expect.objectContaining({
579
+ status: 'integrated',
580
+ decision: 'go',
581
+ ready: true,
582
+ }));
583
+ expect(readinessResponse.body.readiness.baseline).toEqual(expect.objectContaining({
584
+ storeType: 'sqlite',
585
+ storageRequestedProvider: 'sqlite',
586
+ storageResolvedProvider: 'sqlite',
587
+ storageSupportsSqlite: true,
588
+ graphBackendStatus: 'independent',
589
+ graphBackendSignalKind: 'embedded_graphdb',
590
+ graphBackendIndependent: true,
591
+ queryBackendDefaultMode: 'local_hybrid',
592
+ vectorAdapterStatus: 'independent',
593
+ vectorAdapterSignalKind: 'embedding_ann',
594
+ }));
595
+ expect(readinessResponse.body.readiness.promotionCriteriaPassed).toBe(readinessResponse.body.readiness.promotionCriteriaTotal);
596
+ const sufficiencyResponse = await requestJson(port, 'GET', '/api/knowledge/backend/sufficiency');
597
+ expect(sufficiencyResponse.status).toBe(200);
598
+ expect(sufficiencyResponse.body.success).toBe(true);
599
+ expect(sufficiencyResponse.body.sufficiency).toEqual(expect.objectContaining({
600
+ sufficient: true,
601
+ }));
602
+ expect(sufficiencyResponse.body.sufficiency.checks.knowledgeGraph).toEqual(expect.objectContaining({
603
+ passed: true,
604
+ reason: 'embedded_graphdb',
605
+ }));
606
+ expect(sufficiencyResponse.body.sufficiency.checks.queryBackend).toEqual(expect.objectContaining({
607
+ passed: true,
608
+ }));
609
+ expect(sufficiencyResponse.body.sufficiency.checks.vectorIndex).toEqual(expect.objectContaining({
610
+ passed: true,
611
+ reason: 'embedding_ann',
612
+ }));
613
+ });
614
+ test('turn-cache alert trend restart durability', async () => {
615
+ await requestJson(port, 'POST', '/api/knowledge/conversation', { userId: 'durability-user', message: 'Graph theory', persistMemory: false });
616
+ const diagnostics = await requestJson(port, 'GET', '/api/knowledge/conversation/turn-cache/diagnostics');
617
+ expect(diagnostics.body.result.counters.executionSuccessCount).toBeGreaterThan(0);
618
+ const trend = await requestJson(port, 'GET', '/api/knowledge/conversation/turn-cache/diagnostics/trend');
619
+ expect(trend.body.result.records.length).toBeGreaterThan(0);
620
+ const storagePath = trend.body.result.storage.filePath;
621
+ const deadline = Date.now() + 3000;
622
+ while (!fs.existsSync(storagePath) && Date.now() < deadline)
623
+ await new Promise(resolve => setTimeout(resolve, 10));
624
+ expect(fs.existsSync(storagePath)).toBe(true);
625
+ const persisted = JSON.parse(fs.readFileSync(storagePath, 'utf8'));
626
+ expect(persisted.records.length).toBeGreaterThan(0);
627
+ await shutdownServerInstance(server);
628
+ port = await getFreePort();
629
+ serverModule = loadFreshServerModule();
630
+ server = await serverModule.startServer({ port });
631
+ const restored = await requestJson(port, 'GET', '/api/knowledge/conversation/turn-cache/diagnostics/trend/export');
632
+ expect(restored.status).toBe(200);
633
+ expect(restored.body.result.storage.lastLoadedRecordCount).toBeGreaterThan(0);
634
+ expect(restored.body.result.records).toEqual(expect.arrayContaining([expect.objectContaining(persisted.records[0])]));
635
+ });
636
+ test('embedded sqlite graph runtime survives server restart and preserves query/store diagnostics', async () => {
637
+ const sqliteStorePath = path.join(runtimeDataDir, 'knowledge_graph_store.graphdb.v1.sqlite');
638
+ const ingestResponse = await requestJson(port, 'POST', '/api/knowledge/ingest', {
639
+ incremental: true,
640
+ documents: [
641
+ {
642
+ documentId: 'doc_restart_runtime',
643
+ sourcePath: path.join(path.dirname(kbFilePath), 'restart_runtime.md'),
644
+ language: 'en',
645
+ content: '# Restart Runtime\n\nPersist this graph content across server restart for sqlite proof.',
646
+ },
647
+ ],
648
+ });
649
+ expect(ingestResponse.status).toBe(200);
650
+ expect(ingestResponse.body.success).toBe(true);
651
+ expect(fs.existsSync(sqliteStorePath)).toBe(true);
652
+ await shutdownServerInstance(server);
653
+ port = await getFreePort();
654
+ serverModule = loadFreshServerModule();
655
+ server = await serverModule.startServer({ port });
656
+ const diagnosticsResponse = await requestJson(port, 'GET', '/api/knowledge/store-diagnostics');
657
+ expect(diagnosticsResponse.status).toBe(200);
658
+ expect(diagnosticsResponse.body.success).toBe(true);
659
+ expect(diagnosticsResponse.body.store).toEqual(expect.objectContaining({
660
+ storeType: 'graphdb',
661
+ storageEngine: 'sqlite',
662
+ requestedProvider: 'sqlite',
663
+ resolvedProvider: 'sqlite',
664
+ backendReady: true,
665
+ usingFallback: false,
666
+ }));
667
+ const queryResponse = await requestJson(port, 'POST', '/api/knowledge/query', {
668
+ query: 'persist graph content restart sqlite proof',
669
+ topK: 3,
670
+ });
671
+ expect(queryResponse.status).toBe(200);
672
+ expect(queryResponse.body.success).toBe(true);
673
+ expect(Array.isArray(queryResponse.body.result.items)).toBe(true);
674
+ expect(queryResponse.body.result.items.length).toBeGreaterThan(0);
675
+ expect(String(queryResponse.body.result.items[0]?.atom?.documentId || '')).toBe('doc_restart_runtime');
676
+ const readinessResponse = await requestJson(port, 'GET', '/api/knowledge/foundation/readiness');
677
+ expect(readinessResponse.status).toBe(200);
678
+ expect(readinessResponse.body.readiness).toEqual(expect.objectContaining({
679
+ status: 'integrated',
680
+ decision: 'go',
681
+ }));
682
+ expect(readinessResponse.body.readiness.baseline).toEqual(expect.objectContaining({
683
+ storeType: 'sqlite',
684
+ graphBackendSignalKind: 'embedded_graphdb',
685
+ graphBackendIndependent: true,
686
+ }));
687
+ });
465
688
  });