noteconnection 1.6.8 → 1.8.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 (212) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +258 -64
  3. package/dist/src/agent_workspace.contract.parity.test.js +475 -0
  4. package/dist/src/agent_workspace.frontend.test.js +9989 -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/copy.assets.contract.test.js +130 -0
  8. package/dist/src/core/PathBridge.js +28 -3
  9. package/dist/src/export/WorkspaceExportBundle.js +1130 -0
  10. package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
  11. package/dist/src/export/index.js +18 -0
  12. package/dist/src/export/types.js +2 -0
  13. package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
  14. package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
  15. package/dist/src/foundation.release.evidence.contract.test.js +406 -0
  16. package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
  17. package/dist/src/frontend/README.md +258 -64
  18. package/dist/src/frontend/agent_workspace.js +5353 -0
  19. package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
  20. package/dist/src/frontend/analysis.js +31 -4
  21. package/dist/src/frontend/app.js +3597 -141
  22. package/dist/src/frontend/focus_mode_interactions.js +33 -0
  23. package/dist/src/frontend/godot_future_path_renderer.js +540 -0
  24. package/dist/src/frontend/godot_tree_interactions.js +178 -0
  25. package/dist/src/frontend/graph_state.mjs +105 -0
  26. package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
  27. package/dist/src/frontend/i18n.mjs +186 -0
  28. package/dist/src/frontend/index.html +641 -266
  29. package/dist/src/frontend/layout_gpu.js +12 -4
  30. package/dist/src/frontend/locales/en.json +721 -5
  31. package/dist/src/frontend/locales/zh.json +721 -5
  32. package/dist/src/frontend/main.mjs +60 -0
  33. package/dist/src/frontend/markdown_runtime.js +827 -0
  34. package/dist/src/frontend/notemd.css +49 -0
  35. package/dist/src/frontend/notemd.html +7 -1
  36. package/dist/src/frontend/notemd.js +64 -0
  37. package/dist/src/frontend/path.html +107 -0
  38. package/dist/src/frontend/path_app.js +2189 -150
  39. package/dist/src/frontend/path_layout.mjs +143 -0
  40. package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
  41. package/dist/src/frontend/path_modules_bridge.js +486 -0
  42. package/dist/src/frontend/path_state.mjs +118 -0
  43. package/dist/src/frontend/path_styles.css +146 -0
  44. package/dist/src/frontend/path_worker_bridge.mjs +85 -0
  45. package/dist/src/frontend/reader.js +522 -27
  46. package/dist/src/frontend/runtime_bridge.js +67 -54
  47. package/dist/src/frontend/runtime_bridge.mjs +279 -0
  48. package/dist/src/frontend/settings.js +130 -12
  49. package/dist/src/frontend/simulationWorker.js +241 -6
  50. package/dist/src/frontend/source_manager.js +190 -21
  51. package/dist/src/frontend/styles.css +2853 -72
  52. package/dist/src/frontend/workbench_state.mjs +101 -0
  53. package/dist/src/frontend/workspace_panes.js +10168 -0
  54. package/dist/src/frontend.locale.contract.test.js +62 -0
  55. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  56. package/dist/src/indexing/IndexLifecycle.js +195 -0
  57. package/dist/src/indexing/IndexLifecycle.test.js +49 -0
  58. package/dist/src/indexing/SegmentBuilder.js +64 -0
  59. package/dist/src/indexing/UnitBuilder.js +48 -0
  60. package/dist/src/indexing/types.js +2 -0
  61. package/dist/src/knowledge.api.contract.test.js +170 -0
  62. package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
  64. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  65. package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
  68. package/dist/src/learning/answerReleaseReview.js +4319 -0
  69. package/dist/src/learning/answerReleaseReview.test.js +2888 -0
  70. package/dist/src/learning/api.js +2 -0
  71. package/dist/src/learning/conversationComposer.js +1480 -0
  72. package/dist/src/learning/conversationComposer.test.js +1817 -0
  73. package/dist/src/learning/domains/ConversationManager.js +53 -0
  74. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  75. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  76. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  77. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  78. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  79. package/dist/src/learning/domains/TutorRouter.js +313 -0
  80. package/dist/src/learning/domains/index.js +33 -0
  81. package/dist/src/learning/domains/types.js +7 -0
  82. package/dist/src/learning/errors.js +29 -0
  83. package/dist/src/learning/evidenceContextAssembler.js +1176 -0
  84. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  85. package/dist/src/learning/graphContextAssembler.js +870 -0
  86. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  87. package/dist/src/learning/index.js +28 -0
  88. package/dist/src/learning/queryBackend.js +1898 -0
  89. package/dist/src/learning/queryBackend.test.js +955 -0
  90. package/dist/src/learning/ragContextPack.js +257 -0
  91. package/dist/src/learning/ragContextPack.test.js +160 -0
  92. package/dist/src/learning/ragPublicText.js +38 -0
  93. package/dist/src/learning/ragSufficiencyJudge.js +161 -0
  94. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  95. package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
  96. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
  97. package/dist/src/learning/requestNormalization.js +198 -0
  98. package/dist/src/learning/runtimeCapability.js +4677 -0
  99. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  100. package/dist/src/learning/store.js +1240 -0
  101. package/dist/src/learning/store.test.js +1126 -0
  102. package/dist/src/learning/tutorAdapter.js +2 -0
  103. package/dist/src/learning/types.js +2 -0
  104. package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
  105. package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
  106. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  107. package/dist/src/license.policy.contract.test.js +66 -0
  108. package/dist/src/memory/MemoryGovernance.js +74 -0
  109. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  110. package/dist/src/memory/types.js +2 -0
  111. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  112. package/dist/src/middleware/auth.js +17 -0
  113. package/dist/src/middleware/body-parser.js +45 -0
  114. package/dist/src/middleware/cors.js +44 -0
  115. package/dist/src/middleware/index.js +21 -0
  116. package/dist/src/middleware/request-trace.js +96 -0
  117. package/dist/src/notemd/AppConfigToml.js +6 -4
  118. package/dist/src/notemd/MermaidProcessor.js +400 -50
  119. package/dist/src/notemd/NotemdService.js +498 -13
  120. package/dist/src/notemd/PromptManager.js +15 -0
  121. package/dist/src/notemd/cli/commands.js +357 -0
  122. package/dist/src/notemd/cli/dispatcher.js +225 -0
  123. package/dist/src/notemd/cli/index.js +169 -0
  124. package/dist/src/notemd/cli/parser.js +68 -0
  125. package/dist/src/notemd/cli/types.js +2 -0
  126. package/dist/src/notemd/constants.js +43 -0
  127. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  128. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  129. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  130. package/dist/src/notemd/diagram/intent.js +95 -0
  131. package/dist/src/notemd/diagram/planner.js +71 -0
  132. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  133. package/dist/src/notemd/diagram/types.js +18 -0
  134. package/dist/src/notemd/index.js +26 -0
  135. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  136. package/dist/src/notemd/operations/cliContracts.js +17 -0
  137. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  138. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  139. package/dist/src/notemd/operations/registry.js +991 -0
  140. package/dist/src/notemd/operations/types.js +2 -0
  141. package/dist/src/notemd/providerDiagnostics.js +220 -0
  142. package/dist/src/notemd/providerProfiles.js +42 -0
  143. package/dist/src/notemd/providerTemplates.js +231 -0
  144. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  145. package/dist/src/notemd/search/SearchManager.js +13 -0
  146. package/dist/src/notemd/search/SearchProvider.js +2 -0
  147. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  148. package/dist/src/notemd.agent.manifest.test.js +85 -0
  149. package/dist/src/notemd.api.contract.test.js +14 -1
  150. package/dist/src/notemd.app_config_toml.test.js +2 -1
  151. package/dist/src/notemd.batch.workflow.test.js +117 -0
  152. package/dist/src/notemd.cli.e2e.test.js +136 -0
  153. package/dist/src/notemd.core.test.js +51 -0
  154. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  155. package/dist/src/notemd.providerTemplates.test.js +34 -0
  156. package/dist/src/notemd.server.integration.test.js +143 -35
  157. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  158. package/dist/src/pathbridge.handshake.contract.test.js +16 -2
  159. package/dist/src/pathmode.background.contract.test.js +69 -0
  160. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  161. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  162. package/dist/src/platform/ExportProfile.js +58 -0
  163. package/dist/src/platform/PlatformCapabilities.js +45 -0
  164. package/dist/src/platform/PlatformCapabilities.test.js +30 -0
  165. package/dist/src/platform/RenderMaterializer.js +33 -0
  166. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  167. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  168. package/dist/src/reader_renderer.js +404 -3
  169. package/dist/src/reader_renderer.test.js +87 -0
  170. package/dist/src/release.godot.mirror.contract.test.js +73 -0
  171. package/dist/src/resources/ResourceRegistry.js +223 -0
  172. package/dist/src/resources/ResourceRegistry.test.js +61 -0
  173. package/dist/src/resources/types.js +2 -0
  174. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  175. package/dist/src/routes/data.js +267 -0
  176. package/dist/src/routes/diagnostics.js +51 -0
  177. package/dist/src/routes/index.js +23 -0
  178. package/dist/src/routes/knowledge.js +968 -0
  179. package/dist/src/routes/markdown.js +287 -0
  180. package/dist/src/routes/notemd.js +565 -0
  181. package/dist/src/routes/registry.contract.test.js +130 -0
  182. package/dist/src/routes/render.js +285 -0
  183. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  184. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  185. package/dist/src/routes/settings.js +6 -0
  186. package/dist/src/routes/staticFiles.js +94 -0
  187. package/dist/src/routes/types.js +2 -0
  188. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  189. package/dist/src/server.js +11090 -1549
  190. package/dist/src/server.migration.test.js +193 -21
  191. package/dist/src/server.port.fallback.contract.test.js +63 -0
  192. package/dist/src/session/SessionStateStore.js +81 -0
  193. package/dist/src/session/SessionStateStore.test.js +58 -0
  194. package/dist/src/session/types.js +2 -0
  195. package/dist/src/settings.runtime.contract.test.js +50 -0
  196. package/dist/src/shared/types.contract.test.js +107 -0
  197. package/dist/src/shared/types.js +22 -0
  198. package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
  199. package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
  200. package/dist/src/source_manager.loadflow.test.js +46 -0
  201. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  202. package/dist/src/tauri.frontend.build.contract.test.js +60 -0
  203. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  204. package/dist/src/utils/RuntimePaths.js +4 -13
  205. package/dist/src/utils/platform.js +153 -0
  206. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  207. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  208. package/dist/src/workflows/types.js +2 -0
  209. package/dist/src/workspace/WorkspaceRegistry.js +122 -0
  210. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  211. package/dist/src/workspace/types.js +2 -0
  212. package/package.json +61 -10
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Diagram Pipeline unit tests — validates intent inference, planner,
5
+ * spec validation, and response parser (all pure functions).
6
+ */
7
+ const intent_1 = require("./notemd/diagram/intent");
8
+ const planner_1 = require("./notemd/diagram/planner");
9
+ const diagramSpec_1 = require("./notemd/diagram/diagramSpec");
10
+ const diagramSpecResponseParser_1 = require("./notemd/diagram/diagramSpecResponseParser");
11
+ const diagramSpecPrompt_1 = require("./notemd/diagram/prompts/diagramSpecPrompt");
12
+ // ── Intent Inference ──
13
+ describe('Diagram Intent Inference', () => {
14
+ test('empty content defaults to mindmap', () => {
15
+ const result = (0, intent_1.inferDiagramIntent)('');
16
+ expect(result.intent).toBe('mindmap');
17
+ expect(result.confidence).toBeLessThan(0.5);
18
+ });
19
+ test('whitespace-only defaults to mindmap', () => {
20
+ const result = (0, intent_1.inferDiagramIntent)(' \n ');
21
+ expect(result.intent).toBe('mindmap');
22
+ });
23
+ test('data table with numeric values infers dataChart', () => {
24
+ const md = [
25
+ '| Metric | Value |',
26
+ '| ------ | ----- |',
27
+ '| CPU | 85% |',
28
+ '| Memory | 72% |',
29
+ 'Monthly growth trend: 12% increase.',
30
+ ].join('\n');
31
+ const result = (0, intent_1.inferDiagramIntent)(md);
32
+ expect(result.intent).toBe('dataChart');
33
+ expect(result.confidence).toBeGreaterThan(0.8);
34
+ });
35
+ test('request-response vocabulary infers sequence', () => {
36
+ const md = 'Client sends request to server. Server processes and returns response. Gateway routes the request. Service handles the response.';
37
+ const result = (0, intent_1.inferDiagramIntent)(md);
38
+ expect(result.intent).toBe('sequence');
39
+ });
40
+ test('arrow markers infers sequence', () => {
41
+ const result = (0, intent_1.inferDiagramIntent)('User -> API -> Database -> Response');
42
+ expect(result.intent).toBe('sequence');
43
+ });
44
+ test('database schema vocabulary infers erDiagram', () => {
45
+ const md = 'The entity has a primary key. The table references a foreign key. The schema defines the column.';
46
+ const result = (0, intent_1.inferDiagramIntent)(md);
47
+ expect(result.intent).toBe('erDiagram');
48
+ });
49
+ test('state transition vocabulary infers stateDiagram', () => {
50
+ const md = 'The state machine transitions from pending to running. Completed state follows. Failed state triggers retry.';
51
+ const result = (0, intent_1.inferDiagramIntent)(md);
52
+ expect(result.intent).toBe('stateDiagram');
53
+ });
54
+ test('workflow/process vocabulary infers flowchart', () => {
55
+ const md = 'Step 1: Validate input. If valid then process, else stop. Continue to next step.';
56
+ const result = (0, intent_1.inferDiagramIntent)(md);
57
+ expect(result.intent).toBe('flowchart');
58
+ });
59
+ test('numbered steps infers flowchart', () => {
60
+ const md = '1. First step\n2. Second step\n3. Third step';
61
+ const result = (0, intent_1.inferDiagramIntent)(md);
62
+ expect(result.intent).toBe('flowchart');
63
+ });
64
+ test('concept map keywords infers canvasMap', () => {
65
+ const md = 'This is a concept map showing the knowledge graph of spatial clusters.';
66
+ const result = (0, intent_1.inferDiagramIntent)(md);
67
+ expect(result.intent).toBe('canvasMap');
68
+ });
69
+ test('general content defaults to mindmap', () => {
70
+ const result = (0, intent_1.inferDiagramIntent)('The quick brown fox jumps over the lazy dog.');
71
+ expect(result.intent).toBe('mindmap');
72
+ expect(result.confidence).toBeGreaterThan(0.4);
73
+ });
74
+ });
75
+ // ── Planner ──
76
+ describe('Diagram Planner', () => {
77
+ test('best-fit mode routes dataChart to vega-lite', () => {
78
+ const md = '| A | B |\n| - | - |\n| 1 | 2 |\n| 3 | 4 |\nchart';
79
+ const plan = (0, planner_1.buildDiagramPlan)(md, { compatibilityMode: 'best-fit' });
80
+ expect(plan.renderTarget).toBe('vega-lite');
81
+ });
82
+ test('best-fit mode routes sequence to mermaid', () => {
83
+ const md = 'Client sends request. Server returns response. Gateway routes. Service processes.';
84
+ const plan = (0, planner_1.buildDiagramPlan)(md, { compatibilityMode: 'best-fit' });
85
+ expect(plan.intent).toBe('sequence');
86
+ expect(plan.renderTarget).toBe('mermaid');
87
+ expect(plan.mermaidDiagramType).toBe('sequenceDiagram');
88
+ });
89
+ test('legacy-mermaid mode forces mermaid render target', () => {
90
+ const md = '| A | B |\n| - | - |\n| 1 | 2 |\n| 3 | 4 |\nchart share distribution';
91
+ const plan = (0, planner_1.buildDiagramPlan)(md, { compatibilityMode: 'legacy-mermaid' });
92
+ expect(plan.renderTarget).toBe('mermaid');
93
+ expect(plan.legacyCompatibilityMode).toBe(true);
94
+ });
95
+ test('explicit intent overrides inference', () => {
96
+ const plan = (0, planner_1.buildDiagramPlan)('any content', {
97
+ requestedIntent: 'flowchart',
98
+ compatibilityMode: 'best-fit'
99
+ });
100
+ expect(plan.intent).toBe('flowchart');
101
+ expect(plan.confidence).toBeGreaterThan(0.9);
102
+ });
103
+ test('mindmap intent resolves to mindmap mermaid type', () => {
104
+ const plan = (0, planner_1.buildDiagramPlan)('# Just a regular note about topics', { compatibilityMode: 'best-fit' });
105
+ expect(plan.intent).toBe('mindmap');
106
+ expect(plan.mermaidDiagramType).toBe('mindmap');
107
+ });
108
+ test('fallback targets include html for dataChart (non-mermaid)', () => {
109
+ const md = '| A | B |\n| - | - |\n| 1 | 2 |\n| 3 | 4 |\nchart share breakdown';
110
+ const plan = (0, planner_1.buildDiagramPlan)(md, { compatibilityMode: 'best-fit' });
111
+ expect(plan.renderTarget).toBe('vega-lite');
112
+ // dataChart has no mermaidDiagramType, so only html is a valid fallback
113
+ expect(plan.fallbackTargets).toContain('html');
114
+ });
115
+ });
116
+ // ── Spec Validation ──
117
+ describe('Diagram Spec Validation', () => {
118
+ const validMindmapSpec = {
119
+ intent: 'mindmap',
120
+ title: 'Test Diagram',
121
+ nodes: [{ id: 'n1', label: 'Node 1' }, { id: 'n2', label: 'Node 2' }],
122
+ edges: [{ from: 'n1', to: 'n2' }],
123
+ };
124
+ test('valid mindmap spec passes validation', () => {
125
+ const result = (0, diagramSpec_1.validateDiagramSpec)(validMindmapSpec);
126
+ expect(result.valid).toBe(true);
127
+ expect(result.errors).toHaveLength(0);
128
+ });
129
+ test('missing title fails validation', () => {
130
+ const result = (0, diagramSpec_1.validateDiagramSpec)({ ...validMindmapSpec, title: '' });
131
+ expect(result.valid).toBe(false);
132
+ expect(result.errors.some(e => e.includes('title'))).toBe(true);
133
+ });
134
+ test('duplicate node ids fail validation', () => {
135
+ const result = (0, diagramSpec_1.validateDiagramSpec)({
136
+ ...validMindmapSpec,
137
+ nodes: [{ id: 'n1', label: 'A' }, { id: 'n1', label: 'B' }],
138
+ });
139
+ expect(result.valid).toBe(false);
140
+ expect(result.errors.some(e => e.includes('duplicate'))).toBe(true);
141
+ });
142
+ test('edge to missing node fails validation', () => {
143
+ const result = (0, diagramSpec_1.validateDiagramSpec)({
144
+ ...validMindmapSpec,
145
+ edges: [{ from: 'n1', to: 'missing' }],
146
+ });
147
+ expect(result.valid).toBe(false);
148
+ expect(result.errors.some(e => e.includes('missing'))).toBe(true);
149
+ });
150
+ test('unsupported intent fails validation', () => {
151
+ const result = (0, diagramSpec_1.validateDiagramSpec)({ ...validMindmapSpec, intent: 'bogus' });
152
+ expect(result.valid).toBe(false);
153
+ expect(result.errors.some(e => e.includes('unsupported'))).toBe(true);
154
+ });
155
+ test('assertValidDiagramSpec throws on invalid spec', () => {
156
+ expect(() => (0, diagramSpec_1.assertValidDiagramSpec)({ ...validMindmapSpec, title: '' })).toThrow();
157
+ });
158
+ test('dataChart intent requires data series', () => {
159
+ const result = (0, diagramSpec_1.validateDiagramSpec)({ ...validMindmapSpec, intent: 'dataChart', nodes: [] });
160
+ expect(result.valid).toBe(false);
161
+ expect(result.errors.some(e => e.includes('data series'))).toBe(true);
162
+ });
163
+ });
164
+ // ── Response Parser ──
165
+ describe('Diagram Spec Response Parser', () => {
166
+ test('parses valid JSON DiagramSpec', () => {
167
+ const json = JSON.stringify({
168
+ intent: 'mindmap',
169
+ title: 'Test',
170
+ nodes: [{ id: 'a', label: 'A' }],
171
+ });
172
+ const spec = (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)(json);
173
+ expect(spec.intent).toBe('mindmap');
174
+ expect(spec.title).toBe('Test');
175
+ expect(spec.nodes).toHaveLength(1);
176
+ expect(spec.nodes[0].id).toBe('a');
177
+ });
178
+ test('strips code fences from response', () => {
179
+ const json = '```json\n' + JSON.stringify({
180
+ intent: 'flowchart',
181
+ title: 'Test',
182
+ nodes: [{ id: 'a', label: 'A' }],
183
+ }) + '\n```';
184
+ const spec = (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)(json);
185
+ expect(spec.intent).toBe('flowchart');
186
+ expect(spec.title).toBe('Test');
187
+ });
188
+ test('handles diagramSpec wrapper key', () => {
189
+ const json = JSON.stringify({
190
+ diagramSpec: {
191
+ intent: 'sequence',
192
+ title: 'Test',
193
+ nodes: [{ id: 'a', label: 'A' }],
194
+ }
195
+ });
196
+ const spec = (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)(json);
197
+ expect(spec.intent).toBe('sequence');
198
+ });
199
+ test('throws on non-JSON input', () => {
200
+ expect(() => (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)('not json at all')).toThrow();
201
+ });
202
+ test('normalizes edge fields (source→from, target→to)', () => {
203
+ const json = JSON.stringify({
204
+ intent: 'mindmap', title: 'E',
205
+ nodes: [{ id: 'a', label: 'A' }, { id: 'b', label: 'B' }],
206
+ edges: [{ source: 'a', target: 'b', label: 'connects' }],
207
+ });
208
+ const spec = (0, diagramSpecResponseParser_1.parseDiagramSpecResponse)(json);
209
+ expect(spec.edges[0].from).toBe('a');
210
+ expect(spec.edges[0].to).toBe('b');
211
+ });
212
+ });
213
+ // ── Prompt Builder ──
214
+ describe('Diagram Spec Prompt', () => {
215
+ test('includes required intent in output', () => {
216
+ const prompt = (0, diagramSpecPrompt_1.buildDiagramSpecPrompt)({ requiredIntent: 'flowchart' });
217
+ expect(prompt).toContain('REQUIRED');
218
+ expect(prompt).toContain('flowchart');
219
+ });
220
+ test('includes preferred intent', () => {
221
+ const prompt = (0, diagramSpecPrompt_1.buildDiagramSpecPrompt)({ preferredIntent: 'sequence' });
222
+ expect(prompt).toContain('Preferred');
223
+ expect(prompt).toContain('sequence');
224
+ });
225
+ test('includes target language', () => {
226
+ const prompt = (0, diagramSpecPrompt_1.buildDiagramSpecPrompt)({ targetLanguage: 'zh' });
227
+ expect(prompt).toContain('zh');
228
+ });
229
+ test('output rules forbid code fences', () => {
230
+ const prompt = (0, diagramSpecPrompt_1.buildDiagramSpecPrompt)();
231
+ expect(prompt).toContain('Do not wrap');
232
+ });
233
+ });
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const providerTemplates_1 = require("./notemd/providerTemplates");
4
+ const constants_1 = require("./notemd/constants");
5
+ describe('notemd provider templates', () => {
6
+ test('exposes curated templates inspired by Cherry Studio and DeepTutor', () => {
7
+ expect(providerTemplates_1.NOTEMD_PROVIDER_TEMPLATES.length).toBeGreaterThanOrEqual(6);
8
+ expect((0, providerTemplates_1.getNotemdProviderTemplate)('openai-official')?.providerName).toBe('OpenAI');
9
+ expect((0, providerTemplates_1.getNotemdProviderTemplate)('lmstudio-local')?.inspiredBy).toContain('DeepTutor');
10
+ });
11
+ test('applies a template to the matching provider and switches activeProvider', () => {
12
+ const next = (0, providerTemplates_1.applyProviderTemplateToSettings)(constants_1.DEFAULT_SETTINGS, 'openrouter-gateway');
13
+ const provider = next.providers.find((item) => item.name === 'OpenRouter');
14
+ expect(next.activeProvider).toBe('OpenRouter');
15
+ expect(provider).toBeTruthy();
16
+ expect(provider?.baseUrl).toContain('openrouter.ai');
17
+ expect(provider?.model).toContain('openai/');
18
+ });
19
+ test('merges provider_templates into the notemd TOML section without dropping existing overrides', () => {
20
+ const next = (0, providerTemplates_1.mergeProviderTemplatesIntoNotemdSection)({
21
+ active_provider: 'DeepSeek',
22
+ provider_templates: {
23
+ custom_keep: {
24
+ label: 'Keep Me',
25
+ },
26
+ },
27
+ });
28
+ const templates = next.provider_templates;
29
+ expect(templates).toBeTruthy();
30
+ expect(templates.custom_keep.label).toBe('Keep Me');
31
+ expect(templates['openai-official']).toBeTruthy();
32
+ expect(templates['openai-official'].provider_name).toBe('OpenAI');
33
+ });
34
+ });
@@ -141,11 +141,35 @@ describe('NoteMD server integration', () => {
141
141
  let temp;
142
142
  let envRestorers;
143
143
  let server;
144
+ let serverModule;
144
145
  let port;
145
146
  let kbFilePath;
146
147
  let runtimeDataDir;
147
148
  let appConfigPath;
148
149
  let originalArgv;
150
+ function loadFreshServerModule() {
151
+ jest.resetModules();
152
+ process.argv = process.argv.slice(0, 2);
153
+ jest.doMock('./index', () => ({
154
+ buildGraph: jest.fn().mockResolvedValue(undefined),
155
+ }));
156
+ jest.doMock('./core/PathBridge', () => ({
157
+ PathBridge: jest.fn().mockImplementation(() => ({})),
158
+ }));
159
+ jest.doMock('./reader_renderer', () => ({
160
+ renderMathPng: jest.fn().mockResolvedValue({
161
+ pngBase64: 'math',
162
+ width: 100,
163
+ height: 50,
164
+ }),
165
+ renderMermaidPng: jest.fn().mockResolvedValue({
166
+ pngBase64: 'mermaid',
167
+ width: 100,
168
+ height: 50,
169
+ }),
170
+ }));
171
+ return require('./server');
172
+ }
149
173
  beforeAll(async () => {
150
174
  temp = new TempDir('noteconnection-notemd');
151
175
  const projectRoot = temp.mkdir('project');
@@ -186,45 +210,29 @@ describe('NoteMD server integration', () => {
186
210
  envRestorers.push(setEnv('npm_config_workers', undefined));
187
211
  envRestorers.push(setEnv('npm_config_static', undefined));
188
212
  port = await getFreePort();
189
- jest.resetModules();
190
213
  originalArgv = [...process.argv];
191
- process.argv = process.argv.slice(0, 2);
192
- jest.doMock('./index', () => ({
193
- buildGraph: jest.fn().mockResolvedValue(undefined),
194
- }));
195
- jest.doMock('./core/PathBridge', () => ({
196
- PathBridge: jest.fn().mockImplementation(() => ({})),
197
- }));
198
- jest.doMock('./reader_renderer', () => ({
199
- renderMathPng: jest.fn().mockResolvedValue({
200
- pngBase64: 'math',
201
- width: 100,
202
- height: 50,
203
- }),
204
- renderMermaidPng: jest.fn().mockResolvedValue({
205
- pngBase64: 'mermaid',
206
- width: 100,
207
- height: 50,
208
- }),
209
- }));
210
- const serverModule = require('./server');
214
+ serverModule = loadFreshServerModule();
211
215
  server = await serverModule.startServer({ port });
212
216
  });
213
- afterAll(async () => {
214
- if (server) {
215
- if (typeof server.closeAllConnections === 'function') {
216
- server.closeAllConnections();
217
- }
218
- await new Promise((resolve, reject) => {
219
- server.close((err) => {
220
- if (err) {
221
- reject(err);
222
- return;
223
- }
224
- resolve();
225
- });
226
- });
217
+ async function shutdownServerInstance(instance) {
218
+ if (!instance) {
219
+ return;
220
+ }
221
+ if (typeof instance.closeAllConnections === 'function') {
222
+ instance.closeAllConnections();
227
223
  }
224
+ await new Promise((resolve, reject) => {
225
+ instance.close((err) => {
226
+ if (err) {
227
+ reject(err);
228
+ return;
229
+ }
230
+ resolve();
231
+ });
232
+ });
233
+ }
234
+ afterAll(async () => {
235
+ await shutdownServerInstance(server);
228
236
  envRestorers.reverse().forEach((restore) => restore());
229
237
  process.argv = originalArgv;
230
238
  jest.dontMock('./index');
@@ -462,4 +470,104 @@ describe('NoteMD server integration', () => {
462
470
  expect(fs.existsSync(appConfigPath)).toBe(true);
463
471
  expect(fs.existsSync(path.join(runtimeDataDir, 'notemd_config.json'))).toBe(false);
464
472
  });
473
+ test('foundation readiness and backend sufficiency reflect embedded sqlite graph runtime after ingest', async () => {
474
+ const ingestResponse = await requestJson(port, 'POST', '/api/knowledge/ingest', {
475
+ incremental: true,
476
+ documents: [
477
+ {
478
+ documentId: 'doc_foundation_runtime',
479
+ sourcePath: path.join(path.dirname(kbFilePath), 'foundation_runtime.md'),
480
+ language: 'en',
481
+ content: '# Foundation Runtime\n\nEmbedded graph backend readiness should be measurable after ingest.',
482
+ },
483
+ ],
484
+ });
485
+ expect(ingestResponse.status).toBe(200);
486
+ expect(ingestResponse.body.success).toBe(true);
487
+ const readinessResponse = await requestJson(port, 'GET', '/api/knowledge/foundation/readiness');
488
+ expect(readinessResponse.status).toBe(200);
489
+ expect(readinessResponse.body.success).toBe(true);
490
+ expect(readinessResponse.body.readiness).toEqual(expect.objectContaining({
491
+ status: 'integrated',
492
+ decision: 'go',
493
+ ready: true,
494
+ }));
495
+ expect(readinessResponse.body.readiness.baseline).toEqual(expect.objectContaining({
496
+ storeType: 'sqlite',
497
+ graphBackendStatus: 'independent',
498
+ graphBackendSignalKind: 'embedded_graphdb',
499
+ graphBackendIndependent: true,
500
+ queryBackendDefaultMode: 'local_hybrid',
501
+ vectorAdapterStatus: 'independent',
502
+ vectorAdapterSignalKind: 'embedding_ann',
503
+ }));
504
+ expect(readinessResponse.body.readiness.promotionCriteriaPassed).toBe(readinessResponse.body.readiness.promotionCriteriaTotal);
505
+ const sufficiencyResponse = await requestJson(port, 'GET', '/api/knowledge/backend/sufficiency');
506
+ expect(sufficiencyResponse.status).toBe(200);
507
+ expect(sufficiencyResponse.body.success).toBe(true);
508
+ expect(sufficiencyResponse.body.sufficiency).toEqual(expect.objectContaining({
509
+ sufficient: true,
510
+ }));
511
+ expect(sufficiencyResponse.body.sufficiency.checks.knowledgeGraph).toEqual(expect.objectContaining({
512
+ passed: true,
513
+ reason: 'embedded_graphdb',
514
+ }));
515
+ expect(sufficiencyResponse.body.sufficiency.checks.queryBackend).toEqual(expect.objectContaining({
516
+ passed: true,
517
+ }));
518
+ expect(sufficiencyResponse.body.sufficiency.checks.vectorIndex).toEqual(expect.objectContaining({
519
+ passed: true,
520
+ reason: 'embedding_ann',
521
+ }));
522
+ });
523
+ test('embedded sqlite graph runtime survives server restart and preserves query/store diagnostics', async () => {
524
+ const sqliteStorePath = path.join(runtimeDataDir, 'knowledge_graph_store.graphdb.v1.sqlite');
525
+ const ingestResponse = await requestJson(port, 'POST', '/api/knowledge/ingest', {
526
+ incremental: true,
527
+ documents: [
528
+ {
529
+ documentId: 'doc_restart_runtime',
530
+ sourcePath: path.join(path.dirname(kbFilePath), 'restart_runtime.md'),
531
+ language: 'en',
532
+ content: '# Restart Runtime\n\nPersist this graph content across server restart for sqlite proof.',
533
+ },
534
+ ],
535
+ });
536
+ expect(ingestResponse.status).toBe(200);
537
+ expect(ingestResponse.body.success).toBe(true);
538
+ expect(fs.existsSync(sqliteStorePath)).toBe(true);
539
+ await shutdownServerInstance(server);
540
+ port = await getFreePort();
541
+ serverModule = loadFreshServerModule();
542
+ server = await serverModule.startServer({ port });
543
+ const diagnosticsResponse = await requestJson(port, 'GET', '/api/knowledge/store-diagnostics');
544
+ expect(diagnosticsResponse.status).toBe(200);
545
+ expect(diagnosticsResponse.body.success).toBe(true);
546
+ expect(diagnosticsResponse.body.store).toEqual(expect.objectContaining({
547
+ storeType: 'graphdb',
548
+ storageEngine: 'sqlite',
549
+ backendReady: true,
550
+ usingFallback: false,
551
+ }));
552
+ const queryResponse = await requestJson(port, 'POST', '/api/knowledge/query', {
553
+ query: 'persist graph content restart sqlite proof',
554
+ topK: 3,
555
+ });
556
+ expect(queryResponse.status).toBe(200);
557
+ expect(queryResponse.body.success).toBe(true);
558
+ expect(Array.isArray(queryResponse.body.result.items)).toBe(true);
559
+ expect(queryResponse.body.result.items.length).toBeGreaterThan(0);
560
+ expect(String(queryResponse.body.result.items[0]?.atom?.documentId || '')).toBe('doc_restart_runtime');
561
+ const readinessResponse = await requestJson(port, 'GET', '/api/knowledge/foundation/readiness');
562
+ expect(readinessResponse.status).toBe(200);
563
+ expect(readinessResponse.body.readiness).toEqual(expect.objectContaining({
564
+ status: 'integrated',
565
+ decision: 'go',
566
+ }));
567
+ expect(readinessResponse.body.readiness.baseline).toEqual(expect.objectContaining({
568
+ storeType: 'sqlite',
569
+ graphBackendSignalKind: 'embedded_graphdb',
570
+ graphBackendIndependent: true,
571
+ }));
572
+ });
465
573
  });
@@ -0,0 +1,162 @@
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
+ /**
37
+ * Workflow Pipeline contract test — validates the 4-stage
38
+ * runWorkflow pipeline (extract → wikilinks → generate → mermaid).
39
+ */
40
+ const fs = __importStar(require("fs"));
41
+ const os = __importStar(require("os"));
42
+ const path = __importStar(require("path"));
43
+ const NotemdService_1 = require("./notemd/NotemdService");
44
+ class TempDir {
45
+ constructor(prefix) { this.root = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), prefix)); }
46
+ file(rel, content) {
47
+ const p = path.join(this.root, rel);
48
+ fs.mkdirSync(path.dirname(p), { recursive: true });
49
+ fs.writeFileSync(p, content, 'utf8');
50
+ return p;
51
+ }
52
+ cleanup() { fs.rmSync(this.root, { recursive: true, force: true }); }
53
+ }
54
+ function defaultSettings(overrides = {}) {
55
+ return {
56
+ activeProvider: 'OpenAI',
57
+ searchProvider: 'duckduckgo',
58
+ language: 'en',
59
+ providers: [],
60
+ enableStableApiCall: true,
61
+ enableApiErrorDebugMode: false,
62
+ maxRetries: 0,
63
+ retryDelayMs: 0,
64
+ maxTokens: 256,
65
+ chunkWordCount: 100,
66
+ enableDuplicateDetection: false,
67
+ enableExperimentalDiagramPipeline: false,
68
+ enableBatchParallelism: false,
69
+ developerMode: false,
70
+ developerDiagnosticCallMode: 'runtime-stable',
71
+ developerDiagnosticTimeoutMs: 60000,
72
+ developerDiagnosticStabilityRuns: 3,
73
+ experimentalDiagramCompatibilityMode: 'legacy-mermaid',
74
+ autoMermaidFixAfterGenerate: true,
75
+ tavilyApiKey: '',
76
+ tavilyMaxResults: 5,
77
+ tavilySearchDepth: 'basic',
78
+ ddgFetchTimeout: 10000,
79
+ useCustomConceptNoteFolder: false,
80
+ conceptNoteFolder: '',
81
+ ...overrides,
82
+ };
83
+ }
84
+ describe('Notemd Workflow Pipeline', () => {
85
+ let temp;
86
+ let service;
87
+ beforeEach(() => { temp = new TempDir('notemd-workflow-'); service = new NotemdService_1.NotemdService(); });
88
+ afterEach(() => { temp.cleanup(); });
89
+ test('runWorkflow executes all 3 stages (extract → generate → mermaid) on a simple file', async () => {
90
+ const src = temp.file('notes/topic.md', '# Graph Theory\n\nGraph theory studies **nodes** and **edges**.\n\n```mermaid\ngraph TD\nA-->B\n```');
91
+ const result = await service.runWorkflow({ filePath: src }, defaultSettings());
92
+ expect(result.sourceFilePath).toBe(src);
93
+ expect(result.outputFolderPath).toContain('topic_notemd_output');
94
+ expect(fs.existsSync(result.outputFolderPath)).toBe(true);
95
+ // All 3 stages should be present (no wiki-links)
96
+ expect(result.stages.length).toBeGreaterThanOrEqual(3);
97
+ const stageNames = result.stages.map(s => s.stage);
98
+ expect(stageNames).toContain('extract-concepts');
99
+ expect(stageNames).toContain('generate-titles');
100
+ expect(stageNames).toContain('mermaid-fix');
101
+ // Extract stage should have completed
102
+ const extractStage = result.stages.find(s => s.stage === 'extract-concepts');
103
+ expect(['completed', 'error']).toContain(extractStage.status);
104
+ // Summary should have valid numbers
105
+ expect(typeof result.summary.conceptsExtracted).toBe('number');
106
+ expect(typeof result.summary.totalElapsedMs).toBe('number');
107
+ expect(result.summary.totalElapsedMs).toBeGreaterThan(0);
108
+ });
109
+ test('runWorkflow with addWikiLinks injects wiki-links and writes _wikified file', async () => {
110
+ const src = temp.file('notes/test.md', '# Machine Learning\n\nMachine learning uses neural networks and deep learning.');
111
+ const result = await service.runWorkflow({ filePath: src, addWikiLinks: true, skipGenerate: true, skipMermaidFix: true }, defaultSettings());
112
+ // Wiki-links stage should be present and completed
113
+ const wikiStage = result.stages.find(s => s.stage === 'add-wikilinks');
114
+ expect(wikiStage).toBeDefined();
115
+ // May be 'completed' with links or 'skipped' if LLM returned no concepts
116
+ expect(['completed', 'skipped']).toContain(wikiStage.status);
117
+ // Add stage names check
118
+ const stageNames = result.stages.map(s => s.stage);
119
+ expect(stageNames).toContain('add-wikilinks');
120
+ expect(stageNames).not.toContain('generate-titles');
121
+ expect(stageNames).not.toContain('mermaid-fix');
122
+ });
123
+ test('runWorkflow with skipGenerate and skipMermaidFix only runs extract', async () => {
124
+ const src = temp.file('notes/simple.md', '# Simple\n\nJust a simple note.');
125
+ const result = await service.runWorkflow({ filePath: src, skipGenerate: true, skipMermaidFix: true }, defaultSettings());
126
+ // Only 1 stage should run (extract) since generate + mermaid are skipped
127
+ const runningStages = result.stages.filter(s => s.status !== 'skipped');
128
+ expect(runningStages.length).toBeGreaterThanOrEqual(1);
129
+ const stageNames = result.stages.map(s => s.stage);
130
+ expect(stageNames).toContain('extract-concepts');
131
+ });
132
+ test('runWorkflow with wikiLinksInPlace modifies source file directly', async () => {
133
+ const src = temp.file('notes/inplace.md', '# AI\n\nArtificial intelligence uses machine learning and deep learning algorithms.');
134
+ const originalContent = fs.readFileSync(src, 'utf8');
135
+ const result = await service.runWorkflow({ filePath: src, addWikiLinks: true, wikiLinksInPlace: true, skipGenerate: true, skipMermaidFix: true }, defaultSettings());
136
+ const wikiStage = result.stages.find(s => s.stage === 'add-wikilinks');
137
+ expect(wikiStage).toBeDefined();
138
+ // If wikilinks were injected in-place, file content should differ
139
+ if (wikiStage.status === 'completed') {
140
+ const newContent = fs.readFileSync(src, 'utf8');
141
+ // Either content changed or no concepts were found
142
+ expect(typeof newContent).toBe('string');
143
+ }
144
+ });
145
+ test('runWorkflow errors are collected per-stage without blocking other stages', async () => {
146
+ const src = temp.file('notes/errors.md', 'test content');
147
+ const result = await service.runWorkflow({ filePath: src }, defaultSettings());
148
+ // Each stage should report its status
149
+ result.stages.forEach(stage => {
150
+ expect(['completed', 'error', 'skipped', 'running']).toContain(stage.status);
151
+ });
152
+ // Result should always have a valid structure even on errors
153
+ expect(result.sourceFilePath).toBe(src);
154
+ expect(Array.isArray(result.errors)).toBe(true);
155
+ expect(typeof result.summary.totalElapsedMs).toBe('number');
156
+ });
157
+ test('runWorkflow output folder is created', async () => {
158
+ const src = temp.file('notes/output.md', '# Test\n\nJust testing.');
159
+ const result = await service.runWorkflow({ filePath: src }, defaultSettings());
160
+ expect(fs.existsSync(result.outputFolderPath)).toBe(true);
161
+ });
162
+ });