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,1425 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("fs"));
37
+ const http = __importStar(require("http"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const store_1 = require("./store");
41
+ function createSnapshot(seed) {
42
+ return {
43
+ schemaVersion: 1,
44
+ savedAt: new Date('2026-01-01T00:00:00.000Z').toISOString(),
45
+ idCounter: 1,
46
+ atoms: [],
47
+ evidenceSpans: [],
48
+ relationEdges: [],
49
+ temporalEdges: [],
50
+ documents: [],
51
+ activeStableKeyToAtomId: [],
52
+ activeAtomIds: [],
53
+ learnerStates: [],
54
+ tutorTraces: [],
55
+ ingestLatencyHistoryMs: [],
56
+ recomputeLatencyHistoryMs: [],
57
+ queryLatencyHistoryMs: [],
58
+ latestIngestSummary: null,
59
+ sessionActionTelemetry: {
60
+ executionCount: 0,
61
+ analyzedAnswerCount: 0,
62
+ inferredMasteryUpdateCount: 0,
63
+ explicitMasteryUpdateCount: 0,
64
+ memoryPersistedCount: 0,
65
+ memoryPromotionAppliedCount: 0,
66
+ memoryPromotionCount: 0,
67
+ verifiedTutorCount: 0,
68
+ pendingVerificationCount: 0,
69
+ outcomeCounts: {
70
+ correct: 0,
71
+ partial: 0,
72
+ incorrect: 0,
73
+ skipped: 0,
74
+ },
75
+ },
76
+ sessionExecutionHistory: [],
77
+ userMemory: {
78
+ [seed]: {
79
+ session: [],
80
+ unit: [],
81
+ long_term: [],
82
+ },
83
+ },
84
+ relationEdgeSignatures: [],
85
+ };
86
+ }
87
+ function createAtom(id, title, content) {
88
+ return {
89
+ id,
90
+ stableKey: `${id}_stable`,
91
+ documentId: 'doc_ops',
92
+ sourcePath: '/ops/doc.md',
93
+ title,
94
+ content: content || `${title} content`,
95
+ representationType: 'text',
96
+ keywords: [title.toLowerCase()],
97
+ evidenceSpanIds: [],
98
+ createdAt: new Date('2026-01-01T00:00:00.000Z').toISOString(),
99
+ updatedAt: new Date('2026-01-01T00:00:00.000Z').toISOString(),
100
+ metadata: {
101
+ sectionPath: ['root'],
102
+ version: 1,
103
+ sourceHash: `${id}_hash`,
104
+ language: 'en',
105
+ },
106
+ };
107
+ }
108
+ function createRelation(id, sourceAtomId, targetAtomId, relationKind = 'sequence') {
109
+ return {
110
+ id,
111
+ sourceAtomId,
112
+ targetAtomId,
113
+ relationKind,
114
+ provenance: 'fact',
115
+ confidence: 0.95,
116
+ evidenceSpanIds: [],
117
+ temporal: {
118
+ validFrom: new Date('2026-01-01T00:00:00.000Z').toISOString(),
119
+ },
120
+ };
121
+ }
122
+ function buildProbeMetadataFromSnapshot(snapshot) {
123
+ if (!snapshot) {
124
+ return null;
125
+ }
126
+ return {
127
+ schemaVersion: snapshot.schemaVersion,
128
+ savedAt: snapshot.savedAt,
129
+ atomCount: snapshot.atoms.length,
130
+ relationEdgeCount: snapshot.relationEdges.length,
131
+ temporalEdgeCount: snapshot.temporalEdges.length,
132
+ documentCount: snapshot.documents.length,
133
+ };
134
+ }
135
+ async function startMockGraphDbSnapshotServer() {
136
+ let persistedSnapshot = null;
137
+ const requests = [];
138
+ const server = http.createServer((req, res) => {
139
+ const method = String(req.method || 'GET').toUpperCase();
140
+ const requestPath = String(req.url || '');
141
+ let rawBody = '';
142
+ req.setEncoding('utf8');
143
+ req.on('data', (chunk) => {
144
+ rawBody += chunk;
145
+ });
146
+ req.on('end', () => {
147
+ let parsedBody = null;
148
+ if (rawBody.trim().length > 0) {
149
+ try {
150
+ parsedBody = JSON.parse(rawBody);
151
+ }
152
+ catch {
153
+ parsedBody = rawBody;
154
+ }
155
+ }
156
+ requests.push({
157
+ method,
158
+ path: requestPath,
159
+ body: parsedBody,
160
+ });
161
+ const nodeMatch = requestPath.match(/^\/graphdb\/ops\/node\/(.+)$/);
162
+ if (nodeMatch && method === 'GET') {
163
+ if (!persistedSnapshot) {
164
+ res.statusCode = 404;
165
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
166
+ res.setHeader('X-Error-Code', 'snapshot_not_found');
167
+ res.end(JSON.stringify({ error: 'snapshot_not_found' }));
168
+ return;
169
+ }
170
+ const rawId = decodeURIComponent(nodeMatch[1] || '').trim();
171
+ const node = persistedSnapshot.atoms.find((atom) => atom.id === rawId || atom.stableKey === rawId) ?? null;
172
+ if (!node) {
173
+ res.statusCode = 404;
174
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
175
+ res.setHeader('X-Error-Code', 'node_not_found');
176
+ res.end(JSON.stringify({ error: 'node_not_found' }));
177
+ return;
178
+ }
179
+ res.statusCode = 200;
180
+ res.setHeader('Content-Type', 'application/json');
181
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
182
+ res.end(JSON.stringify({ success: true, node }));
183
+ return;
184
+ }
185
+ if (requestPath === '/graphdb/ops/nodes' && method === 'POST') {
186
+ if (!persistedSnapshot) {
187
+ res.statusCode = 404;
188
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
189
+ res.setHeader('X-Error-Code', 'snapshot_not_found');
190
+ res.end(JSON.stringify({ error: 'snapshot_not_found' }));
191
+ return;
192
+ }
193
+ const filter = (parsedBody
194
+ && typeof parsedBody === 'object'
195
+ && !Array.isArray(parsedBody)
196
+ && parsedBody.filter
197
+ && typeof parsedBody.filter === 'object') ? parsedBody.filter : {};
198
+ let nodes = persistedSnapshot.atoms;
199
+ if (Array.isArray(filter.nodeIds) && filter.nodeIds.length > 0) {
200
+ const idSet = new Set(filter.nodeIds.map((item) => String(item || '')));
201
+ nodes = nodes.filter((atom) => idSet.has(atom.id) || idSet.has(atom.stableKey ?? ''));
202
+ }
203
+ if (String(filter.stableKey || '').trim()) {
204
+ nodes = nodes.filter((atom) => atom.stableKey === String(filter.stableKey));
205
+ }
206
+ if (Number.isFinite(Number(filter.limit)) && Number(filter.limit) > 0) {
207
+ nodes = nodes.slice(0, Math.max(1, Math.floor(Number(filter.limit))));
208
+ }
209
+ res.statusCode = 200;
210
+ res.setHeader('Content-Type', 'application/json');
211
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
212
+ res.end(JSON.stringify({ success: true, nodes }));
213
+ return;
214
+ }
215
+ if (requestPath === '/graphdb/ops/edges' && method === 'POST') {
216
+ if (!persistedSnapshot) {
217
+ res.statusCode = 404;
218
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
219
+ res.setHeader('X-Error-Code', 'snapshot_not_found');
220
+ res.end(JSON.stringify({ error: 'snapshot_not_found' }));
221
+ return;
222
+ }
223
+ const filter = (parsedBody
224
+ && typeof parsedBody === 'object'
225
+ && !Array.isArray(parsedBody)
226
+ && parsedBody.filter
227
+ && typeof parsedBody.filter === 'object') ? parsedBody.filter : {};
228
+ let edges = persistedSnapshot.relationEdges;
229
+ if (String(filter.fromNodeId || '').trim()) {
230
+ edges = edges.filter((edge) => edge.sourceAtomId === String(filter.fromNodeId));
231
+ }
232
+ if (String(filter.toNodeId || '').trim()) {
233
+ edges = edges.filter((edge) => edge.targetAtomId === String(filter.toNodeId));
234
+ }
235
+ if (String(filter.relationKind || '').trim()) {
236
+ edges = edges.filter((edge) => edge.relationKind === String(filter.relationKind));
237
+ }
238
+ if (Number.isFinite(Number(filter.limit)) && Number(filter.limit) > 0) {
239
+ edges = edges.slice(0, Math.max(1, Math.floor(Number(filter.limit))));
240
+ }
241
+ res.statusCode = 200;
242
+ res.setHeader('Content-Type', 'application/json');
243
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
244
+ res.end(JSON.stringify({ success: true, edges }));
245
+ return;
246
+ }
247
+ if (requestPath === '/graphdb/ops/path' && method === 'POST') {
248
+ if (!persistedSnapshot) {
249
+ res.statusCode = 404;
250
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
251
+ res.setHeader('X-Error-Code', 'snapshot_not_found');
252
+ res.end(JSON.stringify({ error: 'snapshot_not_found' }));
253
+ return;
254
+ }
255
+ const sourceId = String(parsedBody?.sourceId || '').trim();
256
+ const targetId = String(parsedBody?.targetId || '').trim();
257
+ const maxDepth = Number.isFinite(Number(parsedBody?.maxDepth))
258
+ ? Math.max(1, Math.floor(Number(parsedBody?.maxDepth)))
259
+ : 10;
260
+ const adjacency = new Map();
261
+ for (const edge of persistedSnapshot.relationEdges) {
262
+ if (!adjacency.has(edge.sourceAtomId))
263
+ adjacency.set(edge.sourceAtomId, []);
264
+ adjacency.get(edge.sourceAtomId).push({
265
+ to: edge.targetAtomId,
266
+ relation: edge.relationKind,
267
+ });
268
+ }
269
+ const visited = new Set();
270
+ const queue = [
271
+ { nodeId: sourceId, path: [sourceId], edges: [] },
272
+ ];
273
+ visited.add(sourceId);
274
+ let pathResult = { path: [], length: 0, edges: [], found: false };
275
+ while (queue.length > 0) {
276
+ const current = queue.shift();
277
+ if (current.path.length > maxDepth)
278
+ continue;
279
+ if (current.nodeId === targetId) {
280
+ pathResult = {
281
+ path: current.path,
282
+ length: current.path.length - 1,
283
+ edges: current.edges,
284
+ found: true,
285
+ };
286
+ break;
287
+ }
288
+ const neighbors = adjacency.get(current.nodeId) ?? [];
289
+ for (const neighbor of neighbors) {
290
+ if (!visited.has(neighbor.to)) {
291
+ visited.add(neighbor.to);
292
+ queue.push({
293
+ nodeId: neighbor.to,
294
+ path: [...current.path, neighbor.to],
295
+ edges: [...current.edges, { from: current.nodeId, to: neighbor.to, relation: neighbor.relation }],
296
+ });
297
+ }
298
+ }
299
+ }
300
+ res.statusCode = 200;
301
+ res.setHeader('Content-Type', 'application/json');
302
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
303
+ res.end(JSON.stringify({ success: true, pathResult }));
304
+ return;
305
+ }
306
+ if (requestPath !== '/graphdb/snapshot') {
307
+ res.statusCode = 404;
308
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
309
+ res.setHeader('X-Error-Code', 'not_found');
310
+ res.end(JSON.stringify({ error: 'not_found' }));
311
+ return;
312
+ }
313
+ if (method === 'GET') {
314
+ if (!persistedSnapshot) {
315
+ res.statusCode = 404;
316
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
317
+ res.setHeader('X-Error-Code', 'snapshot_not_found');
318
+ res.end(JSON.stringify({ error: 'snapshot_not_found' }));
319
+ return;
320
+ }
321
+ res.statusCode = 200;
322
+ res.setHeader('Content-Type', 'application/json');
323
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
324
+ res.end(JSON.stringify({ success: true, snapshot: persistedSnapshot }));
325
+ return;
326
+ }
327
+ if (method === 'POST') {
328
+ const nextSnapshot = (parsedBody
329
+ && typeof parsedBody === 'object'
330
+ && !Array.isArray(parsedBody)
331
+ && Object.prototype.hasOwnProperty.call(parsedBody, 'snapshot'))
332
+ ? parsedBody.snapshot
333
+ : null;
334
+ if (!nextSnapshot) {
335
+ res.statusCode = 400;
336
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
337
+ res.setHeader('X-Error-Code', 'snapshot_required');
338
+ res.end(JSON.stringify({ error: 'snapshot_required' }));
339
+ return;
340
+ }
341
+ persistedSnapshot = nextSnapshot;
342
+ res.statusCode = 200;
343
+ res.setHeader('Content-Type', 'application/json');
344
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
345
+ res.end(JSON.stringify({ success: true }));
346
+ return;
347
+ }
348
+ res.statusCode = 405;
349
+ res.setHeader('X-Request-Id', `graphdb-mock-${requests.length}`);
350
+ res.setHeader('X-Error-Code', 'method_not_allowed');
351
+ res.end(JSON.stringify({ error: 'method_not_allowed' }));
352
+ });
353
+ });
354
+ const port = await new Promise((resolve, reject) => {
355
+ server.once('error', reject);
356
+ server.listen(0, '127.0.0.1', () => {
357
+ const address = server.address();
358
+ if (!address || typeof address !== 'object') {
359
+ reject(new Error('Failed to allocate mock graphdb port.'));
360
+ return;
361
+ }
362
+ resolve(address.port);
363
+ });
364
+ });
365
+ return {
366
+ endpoint: `http://127.0.0.1:${port}/graphdb`,
367
+ requests,
368
+ close: () => new Promise((resolve, reject) => {
369
+ server.close((error) => {
370
+ if (error) {
371
+ reject(error);
372
+ return;
373
+ }
374
+ resolve();
375
+ });
376
+ }),
377
+ };
378
+ }
379
+ describe('Knowledge graph store backend factory', () => {
380
+ test('memory backend stores and restores snapshots in-process', async () => {
381
+ const store = (0, store_1.createKnowledgeGraphStore)({
382
+ backend: 'memory',
383
+ filePath: '/tmp/unused.v1.json',
384
+ });
385
+ expect(store.getDiagnostics().storeType).toBe('memory');
386
+ expect(await store.loadSnapshot()).toBeNull();
387
+ const snapshot = createSnapshot('memory_user');
388
+ await store.saveSnapshot(snapshot);
389
+ const restored = await store.loadSnapshot();
390
+ expect(restored).toEqual(snapshot);
391
+ expect(store.getDiagnostics().exists).toBe(true);
392
+ expect(store.getDiagnostics().loaded).toBe(true);
393
+ });
394
+ test('unknown backend value is normalized to file backend', async () => {
395
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-test-'));
396
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
397
+ try {
398
+ expect((0, store_1.normalizeKnowledgeGraphStoreBackend)('unknown-backend')).toBe('file');
399
+ const store = (0, store_1.createKnowledgeGraphStore)({
400
+ backend: 'unknown-backend',
401
+ filePath,
402
+ });
403
+ const snapshot = createSnapshot('file_user');
404
+ await store.saveSnapshot(snapshot);
405
+ const restored = await store.loadSnapshot();
406
+ expect(restored).toEqual(snapshot);
407
+ const diagnostics = store.getDiagnostics();
408
+ expect(diagnostics.storeType).toBe('file');
409
+ expect(diagnostics.exists).toBe(true);
410
+ expect(fs.existsSync(filePath)).toBe(true);
411
+ }
412
+ finally {
413
+ fs.rmSync(tempRoot, { recursive: true, force: true });
414
+ }
415
+ });
416
+ test('refreshes the in-process snapshot cache after a save', async () => {
417
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-cache-coherence-'));
418
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
419
+ try {
420
+ const store = (0, store_1.createFileBackedKnowledgeGraphStore)({ filePath });
421
+ const first = createSnapshot('cache_first');
422
+ first.atoms = [createAtom('atom_first', 'First')];
423
+ const second = createSnapshot('cache_second');
424
+ second.atoms = [createAtom('atom_second', 'Second')];
425
+ await store.saveSnapshot(first);
426
+ expect(await store.getNode('atom_first')).toEqual(first.atoms[0]);
427
+ await store.saveSnapshot(second);
428
+ expect(await store.getNode('atom_first')).toBeNull();
429
+ expect(await store.getNode('atom_second')).toEqual(second.atoms[0]);
430
+ }
431
+ finally {
432
+ fs.rmSync(tempRoot, { recursive: true, force: true });
433
+ }
434
+ });
435
+ test('uses a unique sibling temporary path for each save', async () => {
436
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-atomic-save-'));
437
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
438
+ const renameSpy = jest.spyOn(fs.promises, 'rename');
439
+ try {
440
+ const store = (0, store_1.createKnowledgeGraphStore)({ backend: 'file', filePath });
441
+ await store.saveSnapshot(createSnapshot('atomic_first'));
442
+ await store.saveSnapshot(createSnapshot('atomic_second'));
443
+ const temporaryPaths = renameSpy.mock.calls
444
+ .map(([sourcePath]) => String(sourcePath));
445
+ expect(new Set(temporaryPaths).size).toBe(2);
446
+ expect(fs.existsSync(filePath)).toBe(true);
447
+ expect(fs.readdirSync(path.dirname(filePath)).filter((name) => name.includes('.tmp'))).toHaveLength(0);
448
+ }
449
+ finally {
450
+ renameSpy.mockRestore();
451
+ fs.rmSync(tempRoot, { recursive: true, force: true });
452
+ }
453
+ });
454
+ test('keeps the target parseable when saves overlap', async () => {
455
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-concurrent-save-'));
456
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
457
+ try {
458
+ const store = (0, store_1.createFileBackedKnowledgeGraphStore)({ filePath });
459
+ const first = createSnapshot('concurrent_first');
460
+ first.atoms = [createAtom('atom_concurrent_first', 'Concurrent First')];
461
+ const second = createSnapshot('concurrent_second');
462
+ second.atoms = [createAtom('atom_concurrent_second', 'Concurrent Second')];
463
+ await Promise.all([
464
+ store.saveSnapshot(first),
465
+ store.saveSnapshot(second),
466
+ ]);
467
+ const persisted = JSON.parse(fs.readFileSync(filePath, 'utf8'));
468
+ expect(['atom_concurrent_first', 'atom_concurrent_second']).toContain(persisted.atoms[0]?.id);
469
+ expect(fs.readdirSync(path.dirname(filePath)).filter((name) => name.endsWith('.tmp'))).toHaveLength(0);
470
+ }
471
+ finally {
472
+ fs.rmSync(tempRoot, { recursive: true, force: true });
473
+ }
474
+ });
475
+ test('normalizes graphdb adapter provider aliases', () => {
476
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('file')).toBe('file');
477
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('local-file')).toBe('file');
478
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('external_http')).toBe('http');
479
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('remote-http')).toBe('http');
480
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('service')).toBe('http');
481
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('sqlite')).toBe('sqlite');
482
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('embedded_sqlite')).toBe('sqlite');
483
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('none')).toBe('none');
484
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('disabled')).toBe('none');
485
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('fallback_only')).toBe('none');
486
+ expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('unknown')).toBe('file');
487
+ });
488
+ test('normalizes cross-platform storage provider kinds without conflating http and projection', () => {
489
+ expect((0, store_1.normalizeStorageProviderKind)('sqlite')).toBe('sqlite');
490
+ expect((0, store_1.normalizeStorageProviderKind)('embedded-sqlite')).toBe('sqlite');
491
+ expect((0, store_1.normalizeStorageProviderKind)('file')).toBe('file');
492
+ expect((0, store_1.normalizeStorageProviderKind)('external_http')).toBe('remote');
493
+ expect((0, store_1.normalizeStorageProviderKind)('remote')).toBe('remote');
494
+ expect((0, store_1.normalizeStorageProviderKind)('mobile_projection')).toBe('projection');
495
+ expect((0, store_1.normalizeStorageProviderKind)('unknown')).toBeUndefined();
496
+ });
497
+ test('reports file resolution when a requested sqlite graph adapter is unavailable', async () => {
498
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-provider-resolution-'));
499
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
500
+ try {
501
+ const store = (0, store_1.createKnowledgeGraphStore)({
502
+ backend: 'graphdb',
503
+ filePath,
504
+ graphdb: { adapter: null },
505
+ graphDbRequestedProvider: 'sqlite',
506
+ graphDbFallbackEnabled: true,
507
+ });
508
+ expect(store.getDiagnostics()).toEqual(expect.objectContaining({
509
+ requestedProvider: 'sqlite',
510
+ resolvedProvider: 'file',
511
+ fallbackReason: 'sqlite_runtime_unavailable',
512
+ storageEngine: 'file',
513
+ }));
514
+ }
515
+ finally {
516
+ fs.rmSync(tempRoot, { recursive: true, force: true });
517
+ }
518
+ });
519
+ test('normalizes graphdb operation mode aliases', () => {
520
+ expect((0, store_1.normalizeGraphDbStoreOperationMode)('snapshot')).toBe('snapshot_only');
521
+ expect((0, store_1.normalizeGraphDbStoreOperationMode)('snapshot_only')).toBe('snapshot_only');
522
+ expect((0, store_1.normalizeGraphDbStoreOperationMode)('ops')).toBe('ops_preferred');
523
+ expect((0, store_1.normalizeGraphDbStoreOperationMode)('ops_preferred')).toBe('ops_preferred');
524
+ expect((0, store_1.normalizeGraphDbStoreOperationMode)('operations')).toBe('ops_preferred');
525
+ expect((0, store_1.normalizeGraphDbStoreOperationMode)('unknown')).toBe('snapshot_only');
526
+ });
527
+ test('graphdb adapter factory supports provider-based rollout selection', async () => {
528
+ const disabledAdapter = (0, store_1.createGraphDbSnapshotAdapter)({
529
+ provider: 'none',
530
+ filePath: '/tmp/unused.graphdb.v1.json',
531
+ });
532
+ expect(disabledAdapter).toBeNull();
533
+ const fileAdapter = (0, store_1.createGraphDbSnapshotAdapter)({
534
+ provider: 'file',
535
+ filePath: '/tmp/knowledge_graph_store.graphdb.v1.json',
536
+ fileAdapterId: 'file-adapter-from-factory',
537
+ });
538
+ expect(fileAdapter?.id).toBe('file-adapter-from-factory');
539
+ const mockServer = await startMockGraphDbSnapshotServer();
540
+ try {
541
+ const httpAdapter = (0, store_1.createGraphDbSnapshotAdapter)({
542
+ provider: 'external_http',
543
+ filePath: '/tmp/unused.graphdb.v1.json',
544
+ adapterId: 'http-adapter-from-factory',
545
+ httpEndpoint: mockServer.endpoint,
546
+ httpTimeoutMs: 800,
547
+ httpMaxRetries: 0,
548
+ });
549
+ expect(httpAdapter?.id).toBe('http-adapter-from-factory');
550
+ }
551
+ finally {
552
+ await mockServer.close();
553
+ }
554
+ });
555
+ test('graphdb backend falls back to file store when adapter is not configured', async () => {
556
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-graphdb-fallback-'));
557
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
558
+ try {
559
+ expect((0, store_1.normalizeKnowledgeGraphStoreBackend)('graphdb')).toBe('graphdb');
560
+ const store = (0, store_1.createKnowledgeGraphStore)({
561
+ backend: 'graphdb',
562
+ filePath,
563
+ });
564
+ const snapshot = createSnapshot('graphdb_fallback_user');
565
+ await store.saveSnapshot(snapshot);
566
+ const restored = await store.loadSnapshot();
567
+ expect(restored).toEqual(snapshot);
568
+ expect(fs.existsSync(filePath)).toBe(true);
569
+ const diagnostics = store.getDiagnostics();
570
+ expect(diagnostics.storeType).toBe('graphdb');
571
+ expect(diagnostics.backendReady).toBe(false);
572
+ expect(diagnostics.usingFallback).toBe(true);
573
+ expect(diagnostics.fallbackStoreType).toBe('file');
574
+ expect(diagnostics.graphDbOperationMode).toBe('snapshot_only');
575
+ expect(diagnostics.graphDbAdapterCapabilityMode).toBe('unknown');
576
+ expect(diagnostics.graphDbReadPath).toBe('fallback');
577
+ expect(diagnostics.graphDbWritePath).toBe('fallback');
578
+ }
579
+ finally {
580
+ fs.rmSync(tempRoot, { recursive: true, force: true });
581
+ }
582
+ });
583
+ test('graphdb backend uses configured adapter and bypasses file fallback', async () => {
584
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-graphdb-adapter-'));
585
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
586
+ let persisted = null;
587
+ const adapter = {
588
+ id: 'mock-graphdb',
589
+ async loadSnapshot() {
590
+ return persisted;
591
+ },
592
+ async saveSnapshot(snapshot) {
593
+ persisted = snapshot;
594
+ },
595
+ getDiagnostics() {
596
+ return {
597
+ location: 'graphdb://mock',
598
+ exists: persisted !== null,
599
+ loaded: true,
600
+ };
601
+ },
602
+ };
603
+ try {
604
+ const store = (0, store_1.createKnowledgeGraphStore)({
605
+ backend: 'graphdb',
606
+ filePath,
607
+ graphdb: { adapter },
608
+ });
609
+ const snapshot = createSnapshot('graphdb_adapter_user');
610
+ await store.saveSnapshot(snapshot);
611
+ const restored = await store.loadSnapshot();
612
+ expect(restored).toEqual(snapshot);
613
+ expect(fs.existsSync(filePath)).toBe(false);
614
+ const diagnostics = store.getDiagnostics();
615
+ expect(diagnostics.storeType).toBe('graphdb');
616
+ expect(diagnostics.location).toBe('graphdb://mock');
617
+ expect(diagnostics.backendReady).toBe(true);
618
+ expect(diagnostics.usingFallback).toBe(false);
619
+ expect(diagnostics.adapterId).toBe('mock-graphdb');
620
+ expect(diagnostics.graphDbOperationMode).toBe('snapshot_only');
621
+ expect(diagnostics.graphDbAdapterCapabilityMode).toBe('snapshot_only');
622
+ expect(diagnostics.graphDbReadPath).toBe('snapshot');
623
+ expect(diagnostics.graphDbWritePath).toBe('snapshot');
624
+ }
625
+ finally {
626
+ fs.rmSync(tempRoot, { recursive: true, force: true });
627
+ }
628
+ });
629
+ test('graphdb backend negotiates ops-capable adapter path when operation mode is ops_preferred', async () => {
630
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-graphdb-ops-capable-'));
631
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
632
+ let persisted = null;
633
+ let probeCount = 0;
634
+ let loadOpsCount = 0;
635
+ let saveOpsCount = 0;
636
+ const adapter = {
637
+ id: 'mock-graphdb-ops-capable',
638
+ getCapabilities() {
639
+ return {
640
+ mode: 'ops_capable',
641
+ supportedReadOperations: ['load_snapshot', 'load_snapshot_by_ops', 'probe_snapshot_metadata'],
642
+ supportedWriteOperations: ['save_snapshot', 'save_snapshot_by_ops'],
643
+ };
644
+ },
645
+ async loadSnapshot() {
646
+ throw new Error('snapshot_path_should_not_be_used_when_ops_is_available');
647
+ },
648
+ async saveSnapshot() {
649
+ throw new Error('snapshot_path_should_not_be_used_when_ops_is_available');
650
+ },
651
+ async probeSnapshotMetadata() {
652
+ probeCount += 1;
653
+ if (!persisted) {
654
+ return null;
655
+ }
656
+ return {
657
+ schemaVersion: persisted.schemaVersion,
658
+ savedAt: persisted.savedAt,
659
+ atomCount: persisted.atoms.length,
660
+ relationEdgeCount: persisted.relationEdges.length,
661
+ temporalEdgeCount: persisted.temporalEdges.length,
662
+ documentCount: persisted.documents.length,
663
+ };
664
+ },
665
+ async loadSnapshotByOps() {
666
+ loadOpsCount += 1;
667
+ return persisted;
668
+ },
669
+ async saveSnapshotByOps(snapshot) {
670
+ saveOpsCount += 1;
671
+ persisted = snapshot;
672
+ },
673
+ getDiagnostics() {
674
+ return {
675
+ location: 'graphdb://mock-ops-capable',
676
+ exists: persisted !== null,
677
+ loaded: true,
678
+ capabilityMode: 'ops_capable',
679
+ supportedReadOperations: ['load_snapshot', 'load_snapshot_by_ops', 'probe_snapshot_metadata'],
680
+ supportedWriteOperations: ['save_snapshot', 'save_snapshot_by_ops'],
681
+ lastReadPath: 'ops',
682
+ lastWritePath: 'ops',
683
+ };
684
+ },
685
+ };
686
+ try {
687
+ const store = (0, store_1.createKnowledgeGraphStore)({
688
+ backend: 'graphdb',
689
+ filePath,
690
+ graphdb: { adapter },
691
+ graphDbOperationMode: 'ops_preferred',
692
+ });
693
+ const snapshot = createSnapshot('graphdb_ops_capable_user');
694
+ await store.saveSnapshot(snapshot);
695
+ const restored = await store.loadSnapshot();
696
+ expect(restored).toEqual(snapshot);
697
+ expect(probeCount).toBeGreaterThanOrEqual(1);
698
+ expect(loadOpsCount).toBeGreaterThanOrEqual(1);
699
+ expect(saveOpsCount).toBeGreaterThanOrEqual(1);
700
+ const diagnostics = store.getDiagnostics();
701
+ expect(diagnostics.storeType).toBe('graphdb');
702
+ expect(diagnostics.graphDbOperationMode).toBe('ops_preferred');
703
+ expect(diagnostics.graphDbAdapterCapabilityMode).toBe('ops_capable');
704
+ expect(diagnostics.graphDbReadPath).toBe('ops');
705
+ expect(diagnostics.graphDbWritePath).toBe('ops');
706
+ expect(diagnostics.graphDbSupportedReadOperations).toEqual(expect.arrayContaining(['load_snapshot_by_ops', 'probe_snapshot_metadata']));
707
+ expect(diagnostics.graphDbSupportedWriteOperations).toEqual(expect.arrayContaining(['save_snapshot_by_ops']));
708
+ expect(diagnostics.graphDbLastSnapshotMetadata?.schemaVersion).toBe(1);
709
+ }
710
+ finally {
711
+ fs.rmSync(tempRoot, { recursive: true, force: true });
712
+ }
713
+ });
714
+ test('graphdb backend requires loadSnapshotByOps before classifying read path as ops', async () => {
715
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-graphdb-probe-only-'));
716
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
717
+ let persisted = null;
718
+ let probeCount = 0;
719
+ let loadCount = 0;
720
+ let saveCount = 0;
721
+ const adapter = {
722
+ id: 'mock-graphdb-probe-only',
723
+ getCapabilities() {
724
+ return {
725
+ mode: 'ops_capable',
726
+ supportedReadOperations: ['load_snapshot', 'load_snapshot_by_ops', 'probe_snapshot_metadata'],
727
+ supportedWriteOperations: ['save_snapshot', 'save_snapshot_by_ops'],
728
+ };
729
+ },
730
+ async loadSnapshot() {
731
+ loadCount += 1;
732
+ return persisted;
733
+ },
734
+ async saveSnapshot(snapshot) {
735
+ saveCount += 1;
736
+ persisted = snapshot;
737
+ },
738
+ async probeSnapshotMetadata() {
739
+ probeCount += 1;
740
+ return buildProbeMetadataFromSnapshot(persisted);
741
+ },
742
+ };
743
+ try {
744
+ const store = (0, store_1.createKnowledgeGraphStore)({
745
+ backend: 'graphdb',
746
+ filePath,
747
+ graphdb: { adapter },
748
+ graphDbFallbackEnabled: false,
749
+ graphDbOperationMode: 'ops_preferred',
750
+ });
751
+ const snapshot = createSnapshot('graphdb_probe_only_user');
752
+ await store.saveSnapshot(snapshot);
753
+ const restored = await store.loadSnapshot();
754
+ expect(restored).toEqual(snapshot);
755
+ expect(probeCount).toBeGreaterThanOrEqual(1);
756
+ expect(loadCount).toBeGreaterThanOrEqual(1);
757
+ expect(saveCount).toBeGreaterThanOrEqual(1);
758
+ const diagnostics = store.getDiagnostics();
759
+ expect(diagnostics.graphDbOperationMode).toBe('ops_preferred');
760
+ expect(diagnostics.graphDbAdapterCapabilityMode).toBe('ops_capable');
761
+ expect(diagnostics.graphDbReadPath).toBe('snapshot');
762
+ expect(diagnostics.graphDbWritePath).toBe('snapshot');
763
+ expect(diagnostics.usingFallback).toBe(false);
764
+ }
765
+ finally {
766
+ fs.rmSync(tempRoot, { recursive: true, force: true });
767
+ }
768
+ });
769
+ test('graphdb backend downgrades ops_preferred path to snapshot within adapter before fallback store', async () => {
770
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-graphdb-ops-downgrade-'));
771
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
772
+ let persisted = null;
773
+ let loadOpsCount = 0;
774
+ let saveOpsCount = 0;
775
+ let loadSnapshotCount = 0;
776
+ let saveSnapshotCount = 0;
777
+ const adapter = {
778
+ id: 'mock-graphdb-ops-downgrade',
779
+ getCapabilities() {
780
+ return {
781
+ mode: 'ops_capable',
782
+ supportedReadOperations: ['load_snapshot', 'load_snapshot_by_ops'],
783
+ supportedWriteOperations: ['save_snapshot', 'save_snapshot_by_ops'],
784
+ };
785
+ },
786
+ async loadSnapshotByOps() {
787
+ loadOpsCount += 1;
788
+ throw new Error('ops_read_path_unavailable');
789
+ },
790
+ async saveSnapshotByOps() {
791
+ saveOpsCount += 1;
792
+ throw new Error('ops_write_path_unavailable');
793
+ },
794
+ async loadSnapshot() {
795
+ loadSnapshotCount += 1;
796
+ return persisted;
797
+ },
798
+ async saveSnapshot(snapshot) {
799
+ saveSnapshotCount += 1;
800
+ persisted = snapshot;
801
+ },
802
+ };
803
+ try {
804
+ const store = (0, store_1.createKnowledgeGraphStore)({
805
+ backend: 'graphdb',
806
+ filePath,
807
+ graphdb: { adapter },
808
+ graphDbFallbackEnabled: false,
809
+ graphDbOperationMode: 'ops_preferred',
810
+ });
811
+ const snapshot = createSnapshot('graphdb_ops_downgrade_user');
812
+ await store.saveSnapshot(snapshot);
813
+ const restored = await store.loadSnapshot();
814
+ expect(restored).toEqual(snapshot);
815
+ expect(saveOpsCount).toBeGreaterThanOrEqual(1);
816
+ expect(loadOpsCount).toBeGreaterThanOrEqual(1);
817
+ expect(saveSnapshotCount).toBeGreaterThanOrEqual(1);
818
+ expect(loadSnapshotCount).toBeGreaterThanOrEqual(1);
819
+ const diagnostics = store.getDiagnostics();
820
+ expect(diagnostics.graphDbOperationMode).toBe('ops_preferred');
821
+ expect(diagnostics.graphDbAdapterCapabilityMode).toBe('ops_capable');
822
+ expect(diagnostics.graphDbReadPath).toBe('snapshot');
823
+ expect(diagnostics.graphDbWritePath).toBe('snapshot');
824
+ expect(diagnostics.usingFallback).toBe(false);
825
+ expect(diagnostics.lastError).toBeUndefined();
826
+ }
827
+ finally {
828
+ fs.rmSync(tempRoot, { recursive: true, force: true });
829
+ }
830
+ });
831
+ test('graphdb backend keeps snapshot path when adapter is snapshot-only even if operation mode requests ops', async () => {
832
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-graphdb-ops-fallback-'));
833
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
834
+ let persisted = null;
835
+ let loadCount = 0;
836
+ let saveCount = 0;
837
+ const adapter = {
838
+ id: 'mock-graphdb-snapshot-only',
839
+ getCapabilities() {
840
+ return {
841
+ mode: 'snapshot_only',
842
+ supportedReadOperations: ['load_snapshot'],
843
+ supportedWriteOperations: ['save_snapshot'],
844
+ };
845
+ },
846
+ async loadSnapshot() {
847
+ loadCount += 1;
848
+ return persisted;
849
+ },
850
+ async saveSnapshot(snapshot) {
851
+ saveCount += 1;
852
+ persisted = snapshot;
853
+ },
854
+ getDiagnostics() {
855
+ return {
856
+ location: 'graphdb://mock-snapshot-only',
857
+ exists: persisted !== null,
858
+ loaded: true,
859
+ capabilityMode: 'snapshot_only',
860
+ supportedReadOperations: ['load_snapshot'],
861
+ supportedWriteOperations: ['save_snapshot'],
862
+ lastReadPath: 'snapshot',
863
+ lastWritePath: 'snapshot',
864
+ };
865
+ },
866
+ };
867
+ try {
868
+ const store = (0, store_1.createKnowledgeGraphStore)({
869
+ backend: 'graphdb',
870
+ filePath,
871
+ graphdb: { adapter },
872
+ graphDbOperationMode: 'ops_preferred',
873
+ });
874
+ const snapshot = createSnapshot('graphdb_snapshot_only_user');
875
+ await store.saveSnapshot(snapshot);
876
+ const restored = await store.loadSnapshot();
877
+ expect(restored).toEqual(snapshot);
878
+ expect(loadCount).toBeGreaterThanOrEqual(1);
879
+ expect(saveCount).toBeGreaterThanOrEqual(1);
880
+ const diagnostics = store.getDiagnostics();
881
+ expect(diagnostics.graphDbOperationMode).toBe('ops_preferred');
882
+ expect(diagnostics.graphDbAdapterCapabilityMode).toBe('snapshot_only');
883
+ expect(diagnostics.graphDbReadPath).toBe('snapshot');
884
+ expect(diagnostics.graphDbWritePath).toBe('snapshot');
885
+ expect(diagnostics.graphDbSupportedReadOperations).toEqual(['load_snapshot']);
886
+ expect(diagnostics.graphDbSupportedWriteOperations).toEqual(['save_snapshot']);
887
+ }
888
+ finally {
889
+ fs.rmSync(tempRoot, { recursive: true, force: true });
890
+ }
891
+ });
892
+ test('graphdb backend fails closed when fallback is disabled and adapter is unavailable', async () => {
893
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-graphdb-strict-no-adapter-'));
894
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
895
+ try {
896
+ const store = (0, store_1.createKnowledgeGraphStore)({
897
+ backend: 'graphdb',
898
+ filePath,
899
+ graphdb: { adapter: null },
900
+ graphDbFallbackEnabled: false,
901
+ });
902
+ const snapshot = createSnapshot('graphdb_strict_user');
903
+ await expect(store.saveSnapshot(snapshot)).rejects.toThrow('graphdb_adapter_unavailable_no_fallback');
904
+ await expect(store.loadSnapshot()).rejects.toThrow('graphdb_adapter_unavailable_no_fallback');
905
+ const diagnostics = store.getDiagnostics();
906
+ expect(diagnostics.storeType).toBe('graphdb');
907
+ expect(diagnostics.fallbackEnabled).toBe(false);
908
+ expect(diagnostics.usingFallback).toBe(false);
909
+ expect(diagnostics.graphDbOperationMode).toBe('snapshot_only');
910
+ expect(diagnostics.graphDbAdapterCapabilityMode).toBe('unknown');
911
+ expect(String(diagnostics.lastError || '')).toContain('graphdb_adapter_unavailable_no_fallback');
912
+ }
913
+ finally {
914
+ fs.rmSync(tempRoot, { recursive: true, force: true });
915
+ }
916
+ });
917
+ test('built-in file graphdb adapter persists snapshots to dedicated path', async () => {
918
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-file-graphdb-'));
919
+ const graphDbPath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.graphdb.v1.json');
920
+ try {
921
+ const adapter = (0, store_1.createFileGraphDbSnapshotAdapter)({
922
+ provider: 'file',
923
+ filePath: graphDbPath,
924
+ id: 'file-graphdb-test',
925
+ });
926
+ expect(adapter).not.toBeNull();
927
+ const snapshot = createSnapshot('file_graphdb_user');
928
+ await adapter.saveSnapshot(snapshot);
929
+ const restored = await adapter.loadSnapshot();
930
+ expect(restored).toEqual(snapshot);
931
+ expect(fs.existsSync(graphDbPath)).toBe(true);
932
+ const diagnostics = adapter?.getDiagnostics ? adapter.getDiagnostics() : {};
933
+ expect(diagnostics.location).toContain('knowledge_graph_store.graphdb.v1.json');
934
+ expect(diagnostics.exists).toBe(true);
935
+ expect(diagnostics.capabilityMode).toBe('ops_capable');
936
+ expect(diagnostics.supportedReadOperations).toEqual(expect.arrayContaining(['load_snapshot', 'load_snapshot_by_ops', 'probe_snapshot_metadata']));
937
+ expect(diagnostics.supportedWriteOperations).toEqual(expect.arrayContaining(['save_snapshot', 'save_snapshot_by_ops']));
938
+ }
939
+ finally {
940
+ fs.rmSync(tempRoot, { recursive: true, force: true });
941
+ }
942
+ });
943
+ test('built-in http graphdb adapter persists snapshots through remote endpoint', async () => {
944
+ const mockServer = await startMockGraphDbSnapshotServer();
945
+ try {
946
+ const adapter = (0, store_1.createGraphDbSnapshotAdapter)({
947
+ provider: 'http',
948
+ filePath: '/tmp/unused.graphdb.v1.json',
949
+ adapterId: 'http-graphdb-test',
950
+ httpEndpoint: mockServer.endpoint,
951
+ httpTimeoutMs: 1200,
952
+ httpMaxRetries: 0,
953
+ });
954
+ expect(adapter).not.toBeNull();
955
+ expect(adapter?.id).toBe('http-graphdb-test');
956
+ const snapshot = createSnapshot('http_graphdb_user');
957
+ await adapter.saveSnapshot(snapshot);
958
+ const restored = await adapter.loadSnapshot();
959
+ expect(restored).toEqual(snapshot);
960
+ const diagnostics = adapter.getDiagnostics ? adapter.getDiagnostics() : {};
961
+ expect(String(diagnostics.location || '')).toContain('/graphdb/snapshot');
962
+ expect(diagnostics.exists).toBe(true);
963
+ expect(diagnostics.capabilityMode).toBe('ops_capable');
964
+ expect(diagnostics.supportedReadOperations).toEqual(expect.arrayContaining(['load_snapshot', 'get_node', 'query_nodes', 'query_edges', 'find_path']));
965
+ expect(diagnostics.supportedWriteOperations).toEqual(['save_snapshot']);
966
+ expect(diagnostics.requestedProvider).toBe('remote');
967
+ expect(diagnostics.resolvedProvider).toBe('remote');
968
+ expect(String(diagnostics.connector?.healthStatus || '')).toBe('ready');
969
+ expect(String(diagnostics.connector?.circuitState || '')).toBe('closed');
970
+ expect(Number(diagnostics.connector?.requestCount || 0)).toBeGreaterThanOrEqual(2);
971
+ expect(Number(diagnostics.connector?.successCount || 0)).toBeGreaterThanOrEqual(2);
972
+ expect(Number(diagnostics.connector?.failureCount || 0)).toBe(0);
973
+ expect(String(diagnostics.connector?.lastRequestId || '')).toContain('graphdb-mock-');
974
+ expect(mockServer.requests.some((item) => item.method === 'POST' && item.path === '/graphdb/snapshot')).toBe(true);
975
+ expect(mockServer.requests.some((item) => item.method === 'GET' && item.path === '/graphdb/snapshot')).toBe(true);
976
+ }
977
+ finally {
978
+ await mockServer.close();
979
+ }
980
+ });
981
+ test('reports a SQLite runtime fallback when a file adapter is supplied for a SQLite request', () => {
982
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-provider-mismatch-'));
983
+ const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.graphdb.v1.json');
984
+ try {
985
+ const adapter = (0, store_1.createFileGraphDbSnapshotAdapter)({ filePath, id: 'file-fallback-adapter' });
986
+ const store = (0, store_1.createKnowledgeGraphStore)({
987
+ backend: 'graphdb',
988
+ filePath,
989
+ graphdb: { adapter },
990
+ graphDbRequestedProvider: 'sqlite',
991
+ graphDbFallbackEnabled: true,
992
+ });
993
+ expect(store.getDiagnostics()).toEqual(expect.objectContaining({
994
+ requestedProvider: 'sqlite',
995
+ resolvedProvider: 'file',
996
+ fallbackReason: 'sqlite_runtime_unavailable',
997
+ storageEngine: 'file',
998
+ }));
999
+ }
1000
+ finally {
1001
+ fs.rmSync(tempRoot, { recursive: true, force: true });
1002
+ }
1003
+ });
1004
+ test('returns an unavailable SQLite adapter instead of throwing when its parent path cannot be created', () => {
1005
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-sqlite-init-failure-'));
1006
+ const parentFile = path.join(tempRoot, 'not-a-directory');
1007
+ fs.writeFileSync(parentFile, 'occupied', 'utf8');
1008
+ try {
1009
+ const adapter = (0, store_1.createGraphDbSnapshotAdapter)({
1010
+ provider: 'sqlite',
1011
+ sqlitePath: path.join(parentFile, 'knowledge.sqlite'),
1012
+ });
1013
+ expect(adapter).toBeNull();
1014
+ const fallbackPath = path.join(tempRoot, 'fallback', 'knowledge.json');
1015
+ const store = (0, store_1.createKnowledgeGraphStore)({
1016
+ backend: 'graphdb',
1017
+ filePath: fallbackPath,
1018
+ graphdb: { adapter: null },
1019
+ graphDbRequestedProvider: 'sqlite',
1020
+ });
1021
+ expect(store.getDiagnostics()).toEqual(expect.objectContaining({
1022
+ resolvedProvider: 'file',
1023
+ fallbackReason: 'sqlite_runtime_unavailable',
1024
+ }));
1025
+ }
1026
+ finally {
1027
+ fs.rmSync(tempRoot, { recursive: true, force: true });
1028
+ }
1029
+ });
1030
+ test('graphdb store proxies node/edge/path operations through http ops adapter when ops mode is preferred', async () => {
1031
+ const mockServer = await startMockGraphDbSnapshotServer();
1032
+ try {
1033
+ const adapter = (0, store_1.createGraphDbSnapshotAdapter)({
1034
+ provider: 'http',
1035
+ filePath: '/tmp/unused.graphdb.v1.json',
1036
+ adapterId: 'http-graphdb-ops-test',
1037
+ httpEndpoint: mockServer.endpoint,
1038
+ httpTimeoutMs: 1200,
1039
+ httpMaxRetries: 0,
1040
+ });
1041
+ expect(adapter).not.toBeNull();
1042
+ const store = (0, store_1.createKnowledgeGraphStore)({
1043
+ backend: 'graphdb',
1044
+ graphdb: { adapter },
1045
+ graphDbFallbackEnabled: false,
1046
+ graphDbOperationMode: 'ops_preferred',
1047
+ });
1048
+ const snapshot = createSnapshot('http_graphdb_ops_user');
1049
+ snapshot.atoms = [
1050
+ createAtom('atom_a', 'Alpha'),
1051
+ createAtom('atom_b', 'Beta'),
1052
+ createAtom('atom_c', 'Gamma'),
1053
+ ];
1054
+ snapshot.relationEdges = [
1055
+ createRelation('edge_a_b', 'atom_a', 'atom_b'),
1056
+ createRelation('edge_b_c', 'atom_b', 'atom_c'),
1057
+ ];
1058
+ await store.saveSnapshot(snapshot);
1059
+ expect((0, store_1.isOpsAdapter)(store)).toBe(true);
1060
+ if (!(0, store_1.isOpsAdapter)(store)) {
1061
+ throw new Error('Expected ops adapter interface on graphdb store.');
1062
+ }
1063
+ const opsStore = store;
1064
+ const node = await opsStore.getNode('atom_b');
1065
+ expect(node?.id).toBe('atom_b');
1066
+ const nodes = await opsStore.queryNodes({ nodeIds: ['atom_c', 'missing'] });
1067
+ expect(nodes.map((item) => item.id)).toEqual(['atom_c']);
1068
+ const edges = await opsStore.queryEdges({ fromNodeId: 'atom_a' });
1069
+ expect(edges.map((item) => item.id)).toEqual(['edge_a_b']);
1070
+ const pathResult = await opsStore.findPath('atom_a', 'atom_c', 4);
1071
+ expect(pathResult.found).toBe(true);
1072
+ expect(pathResult.path).toEqual(['atom_a', 'atom_b', 'atom_c']);
1073
+ const diagnostics = store.getDiagnostics();
1074
+ expect(diagnostics.graphDbQueryPath).toBe('ops');
1075
+ expect(Number(diagnostics.graphDbQueryOpsReadCount || 0)).toBeGreaterThanOrEqual(4);
1076
+ expect(String(diagnostics.graphDbAdapterCapabilityMode || '')).toBe('ops_capable');
1077
+ expect(diagnostics.graphDbSupportedReadOperations).toEqual(expect.arrayContaining(['get_node', 'query_nodes', 'query_edges', 'find_path']));
1078
+ expect(mockServer.requests.some((item) => item.path === '/graphdb/ops/node/atom_b')).toBe(true);
1079
+ expect(mockServer.requests.some((item) => item.path === '/graphdb/ops/nodes')).toBe(true);
1080
+ expect(mockServer.requests.some((item) => item.path === '/graphdb/ops/edges')).toBe(true);
1081
+ expect(mockServer.requests.some((item) => item.path === '/graphdb/ops/path')).toBe(true);
1082
+ }
1083
+ finally {
1084
+ await mockServer.close();
1085
+ }
1086
+ });
1087
+ test('built-in sqlite graphdb adapter persists snapshots and serves ops locally', async () => {
1088
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-sqlite-graphdb-'));
1089
+ const sqlitePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.graphdb.v1.sqlite');
1090
+ let adapter = null;
1091
+ try {
1092
+ adapter = (0, store_1.createGraphDbSnapshotAdapter)({
1093
+ provider: 'sqlite',
1094
+ sqlitePath,
1095
+ adapterId: 'embedded-sqlite-graphdb-test',
1096
+ });
1097
+ expect(adapter).not.toBeNull();
1098
+ const snapshot = createSnapshot('sqlite_graphdb_user');
1099
+ snapshot.atoms = [
1100
+ createAtom('atom_a', 'Alpha'),
1101
+ createAtom('atom_b', 'Beta'),
1102
+ createAtom('atom_c', 'Gamma'),
1103
+ ];
1104
+ snapshot.relationEdges = [
1105
+ createRelation('edge_a_b', 'atom_a', 'atom_b'),
1106
+ createRelation('edge_b_c', 'atom_b', 'atom_c'),
1107
+ ];
1108
+ await adapter.saveSnapshot(snapshot);
1109
+ const restored = await adapter.loadSnapshot();
1110
+ expect(restored).toEqual(snapshot);
1111
+ const node = await adapter.getNodeByOps?.('atom_b');
1112
+ expect(node?.id).toBe('atom_b');
1113
+ const nodes = await adapter.queryNodesByOps?.({ nodeIds: ['atom_c', 'missing'] });
1114
+ expect(nodes?.map((item) => item.id)).toEqual(['atom_c']);
1115
+ const edges = await adapter.queryEdgesByOps?.({ fromNodeId: 'atom_a' });
1116
+ expect(edges?.map((item) => item.id)).toEqual(['edge_a_b']);
1117
+ const pathResult = await adapter.findPathByOps?.('atom_a', 'atom_c', 4);
1118
+ expect(pathResult?.found).toBe(true);
1119
+ expect(pathResult?.path).toEqual(['atom_a', 'atom_b', 'atom_c']);
1120
+ const diagnostics = adapter.getDiagnostics ? adapter.getDiagnostics() : {};
1121
+ expect(String(diagnostics.location || '')).toContain('knowledge_graph_store.graphdb.v1.sqlite');
1122
+ expect(diagnostics.storeType).toBe('graphdb');
1123
+ expect(String(diagnostics.storageEngine || '')).toBe('sqlite');
1124
+ expect(diagnostics.requestedProvider).toBe('sqlite');
1125
+ expect(diagnostics.resolvedProvider).toBe('sqlite');
1126
+ expect(diagnostics.fallbackReason).toBeUndefined();
1127
+ expect(diagnostics.capabilityMode).toBe('ops_capable');
1128
+ expect(diagnostics.supportedReadOperations).toEqual(expect.arrayContaining(['load_snapshot', 'get_node', 'query_nodes', 'query_edges', 'find_path']));
1129
+ expect(diagnostics.supportedWriteOperations).toEqual(['save_snapshot']);
1130
+ }
1131
+ finally {
1132
+ try {
1133
+ adapter?.close?.();
1134
+ }
1135
+ catch {
1136
+ }
1137
+ fs.rmSync(tempRoot, { recursive: true, force: true });
1138
+ }
1139
+ });
1140
+ test('graphdb sqlite store diagnostics refresh snapshot metadata after snapshot writes', async () => {
1141
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-sqlite-store-diag-'));
1142
+ const sqlitePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.graphdb.v1.sqlite');
1143
+ const fallbackPath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
1144
+ let adapter = null;
1145
+ try {
1146
+ adapter = (0, store_1.createGraphDbSnapshotAdapter)({
1147
+ provider: 'sqlite',
1148
+ sqlitePath,
1149
+ adapterId: 'embedded-sqlite-graphdb-diagnostics-test',
1150
+ });
1151
+ expect(adapter).not.toBeNull();
1152
+ const store = (0, store_1.createKnowledgeGraphStore)({
1153
+ backend: 'graphdb',
1154
+ filePath: fallbackPath,
1155
+ graphdb: {
1156
+ adapter,
1157
+ },
1158
+ graphDbFallbackEnabled: false,
1159
+ graphDbOperationMode: 'ops_preferred',
1160
+ });
1161
+ const snapshot = createSnapshot('sqlite_store_diag_user');
1162
+ snapshot.atoms = [
1163
+ createAtom('atom_diag_a', 'Diag Alpha'),
1164
+ createAtom('atom_diag_b', 'Diag Beta'),
1165
+ ];
1166
+ snapshot.relationEdges = [
1167
+ createRelation('edge_diag_a_b', 'atom_diag_a', 'atom_diag_b'),
1168
+ ];
1169
+ snapshot.documents = [
1170
+ {
1171
+ documentId: 'doc_diag',
1172
+ sourcePath: '/diag/doc.md',
1173
+ sourceHash: 'doc_diag_hash',
1174
+ version: 1,
1175
+ updatedAt: new Date('2026-01-01T00:00:00.000Z').toISOString(),
1176
+ atomStableKeyToId: [
1177
+ ['atom_diag_a_stable', 'atom_diag_a'],
1178
+ ['atom_diag_b_stable', 'atom_diag_b'],
1179
+ ],
1180
+ atomIds: ['atom_diag_a', 'atom_diag_b'],
1181
+ evidenceSpanIds: [],
1182
+ relationEdgeIds: ['edge_diag_a_b'],
1183
+ temporalEdgeIds: [],
1184
+ },
1185
+ ];
1186
+ await store.saveSnapshot(snapshot);
1187
+ const diagnostics = store.getDiagnostics();
1188
+ expect(diagnostics.storeType).toBe('graphdb');
1189
+ expect(diagnostics.storageEngine).toBe('sqlite');
1190
+ expect(diagnostics.graphDbLastSnapshotMetadata).toEqual(expect.objectContaining({
1191
+ schemaVersion: 1,
1192
+ atomCount: 2,
1193
+ relationEdgeCount: 1,
1194
+ documentCount: 1,
1195
+ }));
1196
+ }
1197
+ finally {
1198
+ try {
1199
+ adapter?.close?.();
1200
+ }
1201
+ catch {
1202
+ }
1203
+ fs.rmSync(tempRoot, { recursive: true, force: true });
1204
+ }
1205
+ });
1206
+ test('sqlite graphdb adapter reopens after close and replays the last committed snapshot', async () => {
1207
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-sqlite-replay-'));
1208
+ const sqlitePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.graphdb.v1.sqlite');
1209
+ let firstAdapter = null;
1210
+ let secondAdapter = null;
1211
+ try {
1212
+ firstAdapter = (0, store_1.createGraphDbSnapshotAdapter)({
1213
+ provider: 'sqlite',
1214
+ sqlitePath,
1215
+ adapterId: 'embedded-sqlite-replay-before-close',
1216
+ });
1217
+ expect(firstAdapter).not.toBeNull();
1218
+ const snapshot = createSnapshot('sqlite_restart_user');
1219
+ snapshot.atoms = [createAtom('restart_atom', 'Restart Alpha')];
1220
+ snapshot.documents = [{
1221
+ documentId: 'restart_doc',
1222
+ sourcePath: 'restart.md',
1223
+ sourceHash: 'restart_hash',
1224
+ version: 1,
1225
+ updatedAt: '2026-01-01T00:00:00.000Z',
1226
+ atomStableKeyToId: [['restart_stable', 'restart_atom']],
1227
+ atomIds: ['restart_atom'],
1228
+ evidenceSpanIds: [],
1229
+ relationEdgeIds: [],
1230
+ temporalEdgeIds: [],
1231
+ }];
1232
+ await firstAdapter.saveSnapshot(snapshot);
1233
+ firstAdapter.close?.();
1234
+ secondAdapter = (0, store_1.createGraphDbSnapshotAdapter)({
1235
+ provider: 'sqlite',
1236
+ sqlitePath,
1237
+ adapterId: 'embedded-sqlite-replay-after-close',
1238
+ });
1239
+ expect(secondAdapter).not.toBeNull();
1240
+ await expect(secondAdapter.loadSnapshot()).resolves.toEqual(snapshot);
1241
+ await expect(secondAdapter.getNodeByOps?.('restart_atom')).resolves.toEqual(snapshot.atoms[0]);
1242
+ await expect(secondAdapter.probeSnapshotMetadata?.()).resolves.toEqual(expect.objectContaining({
1243
+ atomCount: 1,
1244
+ documentCount: 1,
1245
+ }));
1246
+ }
1247
+ finally {
1248
+ try {
1249
+ firstAdapter?.close?.();
1250
+ secondAdapter?.close?.();
1251
+ }
1252
+ catch {
1253
+ }
1254
+ fs.rmSync(tempRoot, { recursive: true, force: true });
1255
+ }
1256
+ });
1257
+ test('graphdb store diagnostics restores snapshot metadata after reopening the sqlite adapter', async () => {
1258
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-sqlite-diagnostics-reopen-'));
1259
+ const sqlitePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.graphdb.v1.sqlite');
1260
+ let firstAdapter = null;
1261
+ let secondAdapter = null;
1262
+ let firstStore = null;
1263
+ let secondStore = null;
1264
+ try {
1265
+ const snapshot = createSnapshot('sqlite_store_diagnostics_reopen_user');
1266
+ snapshot.atoms = [createAtom('reopen_atom', 'Reopen Alpha')];
1267
+ snapshot.documents = [{
1268
+ documentId: 'reopen_doc',
1269
+ sourcePath: '/reopen/doc.md',
1270
+ sourceHash: 'reopen_hash',
1271
+ version: 1,
1272
+ updatedAt: '2026-01-01T00:00:00.000Z',
1273
+ atomStableKeyToId: [['reopen_atom_stable', 'reopen_atom']],
1274
+ atomIds: ['reopen_atom'],
1275
+ evidenceSpanIds: [],
1276
+ relationEdgeIds: [],
1277
+ temporalEdgeIds: [],
1278
+ }];
1279
+ firstAdapter = (0, store_1.createGraphDbSnapshotAdapter)({ provider: 'sqlite', sqlitePath, adapterId: 'sqlite-diagnostics-reopen-first' });
1280
+ expect(firstAdapter).not.toBeNull();
1281
+ firstStore = (0, store_1.createKnowledgeGraphStore)({
1282
+ backend: 'graphdb',
1283
+ graphdb: { adapter: firstAdapter },
1284
+ graphDbFallbackEnabled: false,
1285
+ graphDbOperationMode: 'ops_preferred',
1286
+ });
1287
+ await firstStore.saveSnapshot(snapshot);
1288
+ firstStore.close?.();
1289
+ firstStore = null;
1290
+ firstAdapter = null;
1291
+ secondAdapter = (0, store_1.createGraphDbSnapshotAdapter)({ provider: 'sqlite', sqlitePath, adapterId: 'sqlite-diagnostics-reopen-second' });
1292
+ expect(secondAdapter).not.toBeNull();
1293
+ secondStore = (0, store_1.createKnowledgeGraphStore)({
1294
+ backend: 'graphdb',
1295
+ graphdb: { adapter: secondAdapter },
1296
+ graphDbFallbackEnabled: false,
1297
+ graphDbOperationMode: 'ops_preferred',
1298
+ });
1299
+ await expect(secondStore.loadSnapshot()).resolves.toEqual(snapshot);
1300
+ expect(secondStore.getDiagnostics().graphDbLastSnapshotMetadata).toEqual(expect.objectContaining({
1301
+ atomCount: 1,
1302
+ documentCount: 1,
1303
+ }));
1304
+ }
1305
+ finally {
1306
+ try {
1307
+ firstStore?.close?.();
1308
+ }
1309
+ catch { }
1310
+ try {
1311
+ secondStore?.close?.();
1312
+ }
1313
+ catch { }
1314
+ try {
1315
+ firstAdapter?.close?.();
1316
+ }
1317
+ catch { }
1318
+ try {
1319
+ secondAdapter?.close?.();
1320
+ }
1321
+ catch { }
1322
+ fs.rmSync(tempRoot, { recursive: true, force: true });
1323
+ }
1324
+ });
1325
+ test('graphdb store diagnostics derives metadata from ops reads when the adapter has no probe method', async () => {
1326
+ const snapshot = createSnapshot('ops_read_metadata_user');
1327
+ snapshot.atoms = [createAtom('ops_read_atom', 'Ops Read Alpha')];
1328
+ snapshot.documents = [{
1329
+ documentId: 'ops_read_doc',
1330
+ sourcePath: '/ops-read/doc.md',
1331
+ sourceHash: 'ops_read_hash',
1332
+ version: 1,
1333
+ updatedAt: '2026-01-01T00:00:00.000Z',
1334
+ atomStableKeyToId: [['ops_read_atom_stable', 'ops_read_atom']],
1335
+ atomIds: ['ops_read_atom'],
1336
+ evidenceSpanIds: [],
1337
+ relationEdgeIds: [],
1338
+ temporalEdgeIds: [],
1339
+ }];
1340
+ const adapter = {
1341
+ id: 'ops-only-without-probe',
1342
+ opsCapable: true,
1343
+ loadSnapshot: async () => snapshot,
1344
+ saveSnapshot: async () => undefined,
1345
+ loadSnapshotByOps: async () => snapshot,
1346
+ saveSnapshotByOps: async () => undefined,
1347
+ getCapabilities: () => ({ mode: 'ops_capable', snapshotSupported: true, writeSupported: true }),
1348
+ };
1349
+ const store = (0, store_1.createKnowledgeGraphStore)({
1350
+ backend: 'graphdb',
1351
+ graphdb: { adapter },
1352
+ graphDbFallbackEnabled: false,
1353
+ graphDbOperationMode: 'ops_preferred',
1354
+ });
1355
+ await expect(store.loadSnapshot()).resolves.toEqual(snapshot);
1356
+ expect(store.getDiagnostics().graphDbLastSnapshotMetadata).toEqual(expect.objectContaining({
1357
+ atomCount: 1,
1358
+ documentCount: 1,
1359
+ }));
1360
+ });
1361
+ test('built-in http graphdb adapter surfaces circuit-open telemetry after repeated transient failures', async () => {
1362
+ const server = http.createServer((req, res) => {
1363
+ const method = String(req.method || 'GET').toUpperCase();
1364
+ const requestPath = String(req.url || '');
1365
+ if (requestPath !== '/graphdb/snapshot' || method !== 'GET') {
1366
+ res.statusCode = 404;
1367
+ res.setHeader('Content-Type', 'application/json');
1368
+ res.setHeader('X-Request-Id', 'graphdb-flaky-nf');
1369
+ res.setHeader('X-Error-Code', 'not_found');
1370
+ res.end(JSON.stringify({ error: 'not_found' }));
1371
+ return;
1372
+ }
1373
+ res.statusCode = 503;
1374
+ res.setHeader('Content-Type', 'application/json');
1375
+ res.setHeader('X-Request-Id', 'graphdb-flaky-503');
1376
+ res.setHeader('X-Error-Code', 'overloaded');
1377
+ res.end(JSON.stringify({ error: 'overloaded' }));
1378
+ });
1379
+ const port = await new Promise((resolve, reject) => {
1380
+ server.once('error', reject);
1381
+ server.listen(0, '127.0.0.1', () => {
1382
+ const address = server.address();
1383
+ if (!address || typeof address !== 'object') {
1384
+ reject(new Error('Failed to allocate graphdb flaky mock port.'));
1385
+ return;
1386
+ }
1387
+ resolve(address.port);
1388
+ });
1389
+ });
1390
+ try {
1391
+ const adapter = (0, store_1.createGraphDbSnapshotAdapter)({
1392
+ provider: 'http',
1393
+ filePath: '/tmp/unused.graphdb.v1.json',
1394
+ adapterId: 'http-graphdb-circuit',
1395
+ httpEndpoint: `http://127.0.0.1:${port}/graphdb`,
1396
+ httpTimeoutMs: 1200,
1397
+ httpMaxRetries: 0,
1398
+ httpRetryDelayMs: 0,
1399
+ httpCircuitFailureThreshold: 2,
1400
+ httpCircuitCooldownMs: 60000,
1401
+ });
1402
+ expect(adapter).not.toBeNull();
1403
+ await expect(adapter.loadSnapshot()).rejects.toThrow('graphdb_http_request_failed:503');
1404
+ await expect(adapter.loadSnapshot()).rejects.toThrow('graphdb_http_request_failed:503');
1405
+ await expect(adapter.loadSnapshot()).rejects.toThrow('graphdb_http_circuit_open');
1406
+ const diagnostics = adapter.getDiagnostics ? adapter.getDiagnostics() : {};
1407
+ expect(String(diagnostics.connector?.healthStatus || '')).toBe('unavailable');
1408
+ expect(String(diagnostics.connector?.circuitState || '')).toBe('open');
1409
+ expect(Number(diagnostics.connector?.shortCircuitCount || 0)).toBeGreaterThanOrEqual(1);
1410
+ expect(Number(diagnostics.connector?.failureCount || 0)).toBeGreaterThanOrEqual(2);
1411
+ expect(String(diagnostics.connector?.lastErrorCode || '')).toBe('circuit_open');
1412
+ }
1413
+ finally {
1414
+ await new Promise((resolve, reject) => {
1415
+ server.close((error) => {
1416
+ if (error) {
1417
+ reject(error);
1418
+ return;
1419
+ }
1420
+ resolve();
1421
+ });
1422
+ });
1423
+ }
1424
+ });
1425
+ });