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
@@ -111,7 +111,6 @@ class StructureGenerator {
111
111
  // Calculate degrees
112
112
  const inDegreeMap = new Map();
113
113
  const outDegreeMap = new Map();
114
- // Initialize maps
115
114
  notes.forEach(n => {
116
115
  inDegreeMap.set(n.id, 0);
117
116
  outDegreeMap.set(n.id, 0);
@@ -42,11 +42,14 @@ describe('capacitor bridge serialization contract', () => {
42
42
  const source = fs.readFileSync(storageProviderPath, 'utf8');
43
43
  expect(source).toContain('CAPACITOR_BRIDGE_MAX_CHUNK_BYTES');
44
44
  expect(source).toContain('CAPACITOR_GRAPH_SERIALIZATION_MAX_BYTES');
45
+ expect(source).toContain('getMobileRuntimeBudget().maxProjectionBytes');
46
+ expect(source).toContain('Math.min(\n CAPACITOR_GRAPH_SERIALIZATION_MAX_BYTES,');
45
47
  expect(source).toContain('splitCapacitorPayloadIntoChunks');
46
48
  expect(source).toContain('writeCapacitorChunkSequenceToDirectory');
47
49
  expect(source).toContain('appendFile');
48
50
  expect(source).toContain('createCapacitorGraphDataJsonChunks');
49
51
  expect(source).toContain("serializationMode: 'chunked-bridge-json-stream'");
52
+ expect(source).toContain('maxPayloadBytes: graphSerializationMaxBytes');
50
53
  expect(source).not.toContain('JSON.stringify(graphData, null, 2)');
51
54
  });
52
55
  });
@@ -69,7 +69,16 @@ describe('capacitor runtime parity closure contract', () => {
69
69
  });
70
70
  test('storage provider keeps capacitor local build fallback path', () => {
71
71
  const source = fs.readFileSync(storageProviderPath, 'utf8');
72
- expect(source).toContain('CAPACITOR_GRAPH_BUILD_MAX_FILES');
72
+ expect(source).toContain('getMobileRuntimeBudget');
73
+ expect(source).toContain('measureUtf8Bytes');
74
+ expect(source).toContain('maxBytes: getMobileRuntimeBudget().maxDocumentBytes');
75
+ expect(source).toContain('maxBytes: getMobileRuntimeBudget().maxProjectionBytes');
76
+ expect(source).toContain('maxDocuments');
77
+ expect(source).toContain('maxBytes: budget.maxDocumentBytes');
78
+ expect(source).toContain('maxTotalInputBytes');
79
+ expect(source).toContain('maxDepth');
80
+ expect(source).toContain('const entryDepth = currentDepth + 1;');
81
+ expect(source).toContain("MOBILE_BUDGET_EXCEEDED");
73
82
  expect(source).toContain('collectCapacitorMarkdownFiles');
74
83
  expect(source).toContain('buildCapacitorGraphData');
75
84
  expect(source).toContain('buildCapacitorGraphDataWithWorkerFallback');
@@ -82,7 +91,7 @@ describe('capacitor runtime parity closure contract', () => {
82
91
  expect(source).toContain('supportsMobileWasmCompute');
83
92
  expect(source).toContain('mobileWasmReason');
84
93
  expect(source).toContain('capacitorBuildGraph');
85
- expect(source).toContain("return await capacitorReadText(normalized);");
94
+ expect(source).toContain("return await capacitorReadText(normalized, {");
86
95
  });
87
96
  test('source panel no longer hard-blocks capacitor load action before cache/build flow', () => {
88
97
  const source = fs.readFileSync(sourceManagerPath, 'utf8');
@@ -0,0 +1,130 @@
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 os = __importStar(require("os"));
38
+ const path = __importStar(require("path"));
39
+ class TempDir {
40
+ constructor(prefix) {
41
+ this.path = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), `${prefix}-`));
42
+ }
43
+ child(relative) {
44
+ return path.join(this.path, relative);
45
+ }
46
+ mkdir(relative) {
47
+ const target = this.child(relative);
48
+ fs.mkdirSync(target, { recursive: true });
49
+ return target;
50
+ }
51
+ file(relative, content) {
52
+ const target = this.child(relative);
53
+ fs.mkdirSync(path.dirname(target), { recursive: true });
54
+ fs.writeFileSync(target, content, 'utf8');
55
+ return target;
56
+ }
57
+ cleanup() {
58
+ fs.rmSync(this.path, { recursive: true, force: true });
59
+ }
60
+ }
61
+ describe('copy-assets runtime-first contract', () => {
62
+ const repoRoot = path.resolve(__dirname, '..');
63
+ const copyAssetsPath = path.join(repoRoot, 'scripts', 'copy-assets.js');
64
+ let temp;
65
+ let srcDir;
66
+ let destDir;
67
+ let copyProjectAssets;
68
+ beforeAll(() => {
69
+ ({ copyProjectAssets } = require(copyAssetsPath));
70
+ });
71
+ beforeEach(() => {
72
+ temp = new TempDir('noteconnection-copy-assets');
73
+ srcDir = temp.mkdir('src');
74
+ destDir = temp.mkdir('dest');
75
+ });
76
+ afterEach(() => {
77
+ temp.cleanup();
78
+ });
79
+ test('excludes generated graph payloads by default', () => {
80
+ temp.file(path.join('src', 'app.js'), 'console.log("app");');
81
+ temp.file(path.join('src', 'data.js'), 'const graphData = {"nodes":[{"id":"A"}],"edges":[]}');
82
+ temp.file(path.join('src', 'graph_data.json'), '{"nodes":[{"id":"A"}],"edges":[]}');
83
+ copyProjectAssets({
84
+ src: srcDir,
85
+ dest: destDir,
86
+ logger: console
87
+ });
88
+ expect(fs.existsSync(path.join(destDir, 'app.js'))).toBe(true);
89
+ expect(fs.existsSync(path.join(destDir, 'data.js'))).toBe(false);
90
+ expect(fs.existsSync(path.join(destDir, 'graph_data.json'))).toBe(false);
91
+ });
92
+ test('copies generated graph payloads only when explicitly requested', () => {
93
+ temp.file(path.join('src', 'data.js'), 'const graphData = {"nodes":[{"id":"A"}],"edges":[]}');
94
+ temp.file(path.join('src', 'graph_data.json'), '{"nodes":[{"id":"A"}],"edges":[]}');
95
+ copyProjectAssets({
96
+ src: srcDir,
97
+ dest: destDir,
98
+ includeGeneratedGraphAssets: true,
99
+ logger: console
100
+ });
101
+ expect(fs.existsSync(path.join(destDir, 'data.js'))).toBe(true);
102
+ expect(fs.existsSync(path.join(destDir, 'graph_data.json'))).toBe(true);
103
+ });
104
+ test('full mode remains valid when repository head no longer ships bundled graph payload files', () => {
105
+ temp.file(path.join('src', 'app.js'), 'console.log("app");');
106
+ expect(() => copyProjectAssets({
107
+ src: srcDir,
108
+ dest: destDir,
109
+ includeGeneratedGraphAssets: true,
110
+ logger: console
111
+ })).not.toThrow();
112
+ expect(fs.existsSync(path.join(destDir, 'app.js'))).toBe(true);
113
+ expect(fs.existsSync(path.join(destDir, 'data.js'))).toBe(false);
114
+ expect(fs.existsSync(path.join(destDir, 'graph_data.json'))).toBe(false);
115
+ });
116
+ test('does not copy LFS pointer placeholders even in explicit full mode', () => {
117
+ temp.file(path.join('src', 'data.js'), [
118
+ 'version https://git-lfs.github.com/spec/v1',
119
+ 'oid sha256:abc123',
120
+ 'size 177419264'
121
+ ].join('\n'));
122
+ copyProjectAssets({
123
+ src: srcDir,
124
+ dest: destDir,
125
+ includeGeneratedGraphAssets: true,
126
+ logger: console
127
+ });
128
+ expect(fs.existsSync(path.join(destDir, 'data.js'))).toBe(false);
129
+ });
130
+ });
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Graph = void 0;
4
+ const ResourceReference_1 = require("./ResourceReference");
4
5
  /**
5
6
  * Directed Graph implementation for managing notes and dependencies.
6
7
  * 用于管理笔记和依赖关系的有向图实现。
@@ -10,6 +11,7 @@ class Graph {
10
11
  this.nodes = new Map();
11
12
  this.adjacencyList = new Map();
12
13
  this.reverseAdjacencyList = new Map();
14
+ this.aliases = new Map();
13
15
  }
14
16
  /**
15
17
  * Adds a node to the graph.
@@ -17,11 +19,90 @@ class Graph {
17
19
  * @param node The node to add | 要添加的节点
18
20
  */
19
21
  addNode(node) {
20
- if (!this.nodes.has(node.id)) {
21
- this.nodes.set(node.id, { ...node, inDegree: 0, outDegree: 0 });
22
- this.adjacencyList.set(node.id, []);
23
- this.reverseAdjacencyList.set(node.id, []);
22
+ if (this.nodes.has(node.id)) {
23
+ return;
24
24
  }
25
+ const aliases = [node.id, node.sourceUri, ...(node.identityAliases ?? [])]
26
+ .filter((alias) => typeof alias === 'string' && alias.length > 0);
27
+ const normalizedAliases = new Set(aliases.map(alias => (0, ResourceReference_1.normalizeResourceReference)(alias)));
28
+ normalizedAliases.forEach(alias => {
29
+ const existingNodeId = this.aliases.get(alias);
30
+ if (existingNodeId && existingNodeId !== node.id) {
31
+ throw new Error(`Resource identity alias collision: "${alias}" is claimed by both "${existingNodeId}" and "${node.id}"`);
32
+ }
33
+ });
34
+ this.nodes.set(node.id, { ...node, inDegree: 0, outDegree: 0 });
35
+ this.adjacencyList.set(node.id, []);
36
+ this.reverseAdjacencyList.set(node.id, []);
37
+ normalizedAliases.forEach(alias => {
38
+ this.aliases.set(alias, node.id);
39
+ });
40
+ }
41
+ /**
42
+ * Rehydrates a graph from either a current or legacy JSON snapshot.
43
+ *
44
+ * The snapshot is validated before mutating the returned graph. Edges must
45
+ * reference declared nodes so a corrupt or partially-written snapshot cannot
46
+ * silently create synthetic nodes and change graph semantics during replay.
47
+ */
48
+ static fromJSON(data) {
49
+ if (!data || typeof data !== 'object' || Array.isArray(data)) {
50
+ throw new Error('Graph snapshot must be an object.');
51
+ }
52
+ const snapshot = data;
53
+ if (!Array.isArray(snapshot.nodes) || !Array.isArray(snapshot.edges)) {
54
+ throw new Error('Graph snapshot must contain nodes and edges arrays.');
55
+ }
56
+ const graph = new Graph();
57
+ const nodeIds = new Set();
58
+ snapshot.nodes.forEach((rawNode, index) => {
59
+ if (!rawNode || typeof rawNode !== 'object' || Array.isArray(rawNode)) {
60
+ throw new Error(`Graph snapshot node at index ${index} must be an object.`);
61
+ }
62
+ const node = rawNode;
63
+ if (typeof node.id !== 'string' || node.id.trim().length === 0) {
64
+ throw new Error(`Graph snapshot node at index ${index} requires a non-empty id.`);
65
+ }
66
+ if (nodeIds.has(node.id)) {
67
+ throw new Error(`Graph snapshot contains duplicate node id: ${node.id}.`);
68
+ }
69
+ nodeIds.add(node.id);
70
+ graph.addNode({
71
+ ...node,
72
+ id: node.id,
73
+ label: typeof node.label === 'string' && node.label.length > 0 ? node.label : node.id,
74
+ inDegree: 0,
75
+ outDegree: 0,
76
+ });
77
+ });
78
+ snapshot.edges.forEach((rawEdge, index) => {
79
+ if (!rawEdge || typeof rawEdge !== 'object' || Array.isArray(rawEdge)) {
80
+ throw new Error(`Graph snapshot edge at index ${index} must be an object.`);
81
+ }
82
+ const edge = rawEdge;
83
+ if (typeof edge.source !== 'string' || typeof edge.target !== 'string') {
84
+ throw new Error(`Graph snapshot edge at index ${index} requires source and target ids.`);
85
+ }
86
+ if (!graph.hasNode(edge.source) || !graph.hasNode(edge.target)) {
87
+ throw new Error(`Graph snapshot edge at index ${index} references an undeclared node: ${edge.source} -> ${edge.target}.`);
88
+ }
89
+ graph.addEdge(edge.source, edge.target, edge.type, edge.weight);
90
+ });
91
+ return graph;
92
+ }
93
+ /** Restore this instance atomically from a legacy or current JSON snapshot. */
94
+ restore(data) {
95
+ const restored = Graph.fromJSON(data);
96
+ this.nodes = restored.nodes;
97
+ this.adjacencyList = restored.adjacencyList;
98
+ this.reverseAdjacencyList = restored.reverseAdjacencyList;
99
+ this.aliases = restored.aliases;
100
+ }
101
+ resolveNodeId(reference) {
102
+ if (this.nodes.has(reference)) {
103
+ return reference;
104
+ }
105
+ return this.aliases.get((0, ResourceReference_1.normalizeResourceReference)(reference));
25
106
  }
26
107
  /**
27
108
  * Retrieves a node by its ID.
@@ -29,16 +110,17 @@ class Graph {
29
110
  * @param id The node ID | 节点 ID
30
111
  * @returns The node or undefined if not found | 节点,如果未找到则返回 undefined
31
112
  */
32
- getNode(id) {
33
- return this.nodes.get(id);
113
+ getNode(reference) {
114
+ const nodeId = this.resolveNodeId(reference);
115
+ return nodeId ? this.nodes.get(nodeId) : undefined;
34
116
  }
35
117
  /**
36
118
  * Checks if a node exists in the graph.
37
119
  * 检查图中是否存在该节点。
38
120
  * @param id The node ID | 节点 ID
39
121
  */
40
- hasNode(id) {
41
- return this.nodes.has(id);
122
+ hasNode(reference) {
123
+ return this.resolveNodeId(reference) !== undefined;
42
124
  }
43
125
  /**
44
126
  * Adds a directed edge between two nodes.
@@ -49,30 +131,32 @@ class Graph {
49
131
  * @param weight Edge weight (confidence) | 边权重 (置信度)
50
132
  */
51
133
  addEdge(source, target, type = 'dependency', weight = 1) {
52
- if (!this.nodes.has(source)) {
53
- this.addNode({ id: source, label: source, inDegree: 0, outDegree: 0 });
134
+ const sourceId = this.resolveNodeId(source) ?? source;
135
+ const targetId = this.resolveNodeId(target) ?? target;
136
+ if (!this.nodes.has(sourceId)) {
137
+ this.addNode({ id: sourceId, label: sourceId, inDegree: 0, outDegree: 0 });
54
138
  }
55
- if (!this.nodes.has(target)) {
56
- this.addNode({ id: target, label: target, inDegree: 0, outDegree: 0 });
139
+ if (!this.nodes.has(targetId)) {
140
+ this.addNode({ id: targetId, label: targetId, inDegree: 0, outDegree: 0 });
57
141
  }
58
- const edge = { source, target, type, weight };
142
+ const edge = { source: sourceId, target: targetId, type, weight };
59
143
  // Add to adjacency list (outgoing)
60
- const outgoing = this.adjacencyList.get(source) || [];
144
+ const outgoing = this.adjacencyList.get(sourceId) || [];
61
145
  // Prevent duplicate edges
62
- if (!outgoing.some(e => e.target === target && e.type === type)) {
146
+ if (!outgoing.some(e => e.target === targetId && e.type === type)) {
63
147
  outgoing.push(edge);
64
- this.adjacencyList.set(source, outgoing);
148
+ this.adjacencyList.set(sourceId, outgoing);
65
149
  // Update out-degree
66
- const sourceNode = this.nodes.get(source);
150
+ const sourceNode = this.nodes.get(sourceId);
67
151
  sourceNode.outDegree++;
68
152
  }
69
153
  // Add to reverse adjacency list (incoming)
70
- const incoming = this.reverseAdjacencyList.get(target) || [];
71
- if (!incoming.some(e => e.source === source && e.type === type)) {
154
+ const incoming = this.reverseAdjacencyList.get(targetId) || [];
155
+ if (!incoming.some(e => e.source === sourceId && e.type === type)) {
72
156
  incoming.push(edge);
73
- this.reverseAdjacencyList.set(target, incoming);
157
+ this.reverseAdjacencyList.set(targetId, incoming);
74
158
  // Update in-degree
75
- const targetNode = this.nodes.get(target);
159
+ const targetNode = this.nodes.get(targetId);
76
160
  targetNode.inDegree++;
77
161
  }
78
162
  }
@@ -81,16 +165,17 @@ class Graph {
81
165
  * 获取节点的所有出边。
82
166
  * @param id Node ID | 节点 ID
83
167
  */
84
- getOutgoingEdges(id) {
85
- return this.adjacencyList.get(id) || [];
168
+ getOutgoingEdges(reference) {
169
+ const nodeId = this.resolveNodeId(reference);
170
+ return nodeId ? (this.adjacencyList.get(nodeId) || []) : [];
86
171
  }
87
172
  /**
88
173
  * Gets all outgoing neighbor IDs for a node.
89
174
  * 获取节点的所有出度邻居 ID。
90
175
  * @param id Node ID | 节点 ID
91
176
  */
92
- getNeighbors(id) {
93
- return (this.adjacencyList.get(id) || []).map(edge => edge.target);
177
+ getNeighbors(reference) {
178
+ return this.getOutgoingEdges(reference).map(edge => edge.target);
94
179
  }
95
180
  /**
96
181
  * Gets all nodes in the graph.
@@ -111,8 +196,9 @@ class Graph {
111
196
  * 获取节点的所有入边。
112
197
  * @param id Node ID | 节点 ID
113
198
  */
114
- getIncomingEdges(id) {
115
- return this.reverseAdjacencyList.get(id) || [];
199
+ getIncomingEdges(reference) {
200
+ const nodeId = this.resolveNodeId(reference);
201
+ return nodeId ? (this.reverseAdjacencyList.get(nodeId) || []) : [];
116
202
  }
117
203
  /**
118
204
  * Returns the graph data in a serializable format.
@@ -129,11 +215,14 @@ class Graph {
129
215
  * 获取所有前驱节点(入边的传递闭包)。
130
216
  * @param id Target node ID
131
217
  */
132
- getPredecessors(id) {
218
+ getPredecessors(reference) {
133
219
  const predecessors = new Set();
134
- const queue = [id];
220
+ const nodeId = this.resolveNodeId(reference);
221
+ if (!nodeId)
222
+ return predecessors;
223
+ const queue = [nodeId];
135
224
  const visited = new Set();
136
- visited.add(id);
225
+ visited.add(nodeId);
137
226
  while (queue.length > 0) {
138
227
  const current = queue.shift();
139
228
  const incoming = this.getIncomingEdges(current);
@@ -152,11 +241,14 @@ class Graph {
152
241
  * 获取所有后继节点(出边的传递闭包)。
153
242
  * @param id Source node ID
154
243
  */
155
- getSuccessors(id) {
244
+ getSuccessors(reference) {
156
245
  const successors = new Set();
157
- const queue = [id];
246
+ const nodeId = this.resolveNodeId(reference);
247
+ if (!nodeId)
248
+ return successors;
249
+ const queue = [nodeId];
158
250
  const visited = new Set();
159
- visited.add(id);
251
+ visited.add(nodeId);
160
252
  while (queue.length > 0) {
161
253
  const current = queue.shift();
162
254
  const outgoing = this.getOutgoingEdges(current);
@@ -178,15 +270,19 @@ class Graph {
178
270
  * @returns Array of node IDs representing the path, or empty if no path found
179
271
  */
180
272
  getShortestPath(source, target) {
181
- if (source === target)
182
- return [source];
183
- const queue = [source];
273
+ const sourceId = this.resolveNodeId(source);
274
+ const targetId = this.resolveNodeId(target);
275
+ if (!sourceId || !targetId)
276
+ return [];
277
+ if (sourceId === targetId)
278
+ return [sourceId];
279
+ const queue = [sourceId];
184
280
  const visited = new Set();
185
281
  const parent = new Map();
186
- visited.add(source);
282
+ visited.add(sourceId);
187
283
  while (queue.length > 0) {
188
284
  const current = queue.shift();
189
- if (current === target)
285
+ if (current === targetId)
190
286
  break;
191
287
  const neighbors = this.getNeighbors(current);
192
288
  for (const neighbor of neighbors) {
@@ -197,12 +293,12 @@ class Graph {
197
293
  }
198
294
  }
199
295
  }
200
- if (!visited.has(target))
296
+ if (!visited.has(targetId))
201
297
  return [];
202
298
  // Reconstruct path
203
- const path = [target];
204
- let curr = target;
205
- while (curr !== source) {
299
+ const path = [targetId];
300
+ let curr = targetId;
301
+ while (curr !== sourceId) {
206
302
  curr = parent.get(curr);
207
303
  path.unshift(curr);
208
304
  }
@@ -34,4 +34,78 @@ describe('Graph Core', () => {
34
34
  expect(nodeA?.outDegree).toBe(1);
35
35
  expect(graph.getOutgoingEdges('A')).toHaveLength(1);
36
36
  });
37
+ test('resolves source URI and relative path aliases without changing the public node ID', () => {
38
+ graph.addNode({
39
+ id: 'Index',
40
+ label: 'Index',
41
+ inDegree: 0,
42
+ outDegree: 0,
43
+ sourceUri: 'note://workspace/v1/algebra/index.md',
44
+ revision: 'sha256:revision',
45
+ identityAliases: ['algebra/index.md', 'Index'],
46
+ });
47
+ graph.addNode({ id: 'Limits', label: 'Limits', inDegree: 0, outDegree: 0 });
48
+ expect(graph.getNode('note://workspace/v1/algebra/index.md')?.id).toBe('Index');
49
+ expect(graph.getNode('algebra\\index.md')?.id).toBe('Index');
50
+ expect(graph.hasNode('INDEX')).toBe(true);
51
+ graph.addEdge('algebra/index.md', 'Limits', 'explicit-next');
52
+ expect(graph.getOutgoingEdges('Index')[0]).toEqual(expect.objectContaining({
53
+ source: 'Index',
54
+ target: 'Limits',
55
+ type: 'explicit-next',
56
+ }));
57
+ });
58
+ test('rejects an alias claimed by two different nodes', () => {
59
+ graph.addNode({
60
+ id: 'A',
61
+ label: 'A',
62
+ inDegree: 0,
63
+ outDegree: 0,
64
+ identityAliases: ['shared/path.md'],
65
+ });
66
+ expect(() => graph.addNode({
67
+ id: 'B',
68
+ label: 'B',
69
+ inDegree: 0,
70
+ outDegree: 0,
71
+ identityAliases: ['shared/path.md'],
72
+ })).toThrow(/alias/i);
73
+ });
74
+ test('replays legacy snapshots without identity metadata and preserves alias-aware edges', () => {
75
+ const restored = Graph_1.Graph.fromJSON({
76
+ nodes: [
77
+ { id: 'A', label: 'A', inDegree: 0, outDegree: 1 },
78
+ { id: 'B', label: 'B', inDegree: 1, outDegree: 0 },
79
+ ],
80
+ edges: [{ source: 'A', target: 'B', type: 'legacy-link', weight: 0.5 }],
81
+ });
82
+ expect(restored.getNode('A')).toEqual(expect.objectContaining({ id: 'A', inDegree: 0, outDegree: 1 }));
83
+ expect(restored.getOutgoingEdges('A')).toEqual([
84
+ { source: 'A', target: 'B', type: 'legacy-link', weight: 0.5 },
85
+ ]);
86
+ });
87
+ test('rejects an edge that references a missing node before restoring', () => {
88
+ expect(() => Graph_1.Graph.fromJSON({
89
+ nodes: [{ id: 'A', label: 'A' }],
90
+ edges: [{ source: 'A', target: 'missing' }],
91
+ })).toThrow(/undeclared node/i);
92
+ });
93
+ test('restores an existing instance atomically', () => {
94
+ graph.addNode({ id: 'old', label: 'old', inDegree: 0, outDegree: 0 });
95
+ expect(() => graph.restore({
96
+ nodes: [{ id: 'new', label: 'new' }],
97
+ edges: [],
98
+ })).not.toThrow();
99
+ expect(graph.hasNode('old')).toBe(false);
100
+ expect(graph.hasNode('new')).toBe(true);
101
+ });
102
+ test('keeps the previous graph when an incoming snapshot fails validation', () => {
103
+ graph.addNode({ id: 'stable', label: 'stable', inDegree: 0, outDegree: 0 });
104
+ expect(() => graph.restore({
105
+ nodes: [{ id: 'candidate', label: 'candidate' }],
106
+ edges: [{ source: 'candidate', target: 'missing' }],
107
+ })).toThrow(/undeclared node/i);
108
+ expect(graph.hasNode('stable')).toBe(true);
109
+ expect(graph.hasNode('candidate')).toBe(false);
110
+ });
37
111
  });
@@ -79,7 +79,7 @@ class NoteConnection {
79
79
  }
80
80
  // 2. Resolve Directory
81
81
  const projectRoot = options.projectRoot || path.resolve(__dirname, '..', '..');
82
- const kbRoot = path.join(projectRoot, 'Knowledge_Base');
82
+ const kbRoot = path.resolve(options.knowledgeBaseRoot || path.join(projectRoot, 'Knowledge_Base'));
83
83
  let conceptDir;
84
84
  if (options.targetPath) {
85
85
  if (path.isAbsolute(options.targetPath)) {
@@ -97,7 +97,7 @@ class NoteConnection {
97
97
  }
98
98
  // 3. Load Files
99
99
  log(`Loading files from: ${conceptDir}`);
100
- const files = await FileLoader_1.FileLoader.loadFiles(conceptDir);
100
+ const files = await FileLoader_1.FileLoader.loadFiles(conceptDir, ['.md'], kbRoot);
101
101
  log(`Loaded ${files.length} files.`);
102
102
  // 4. Load Layout (Optional)
103
103
  // Note: Layout logic is slightly coupled to file system here,