@flowdrop/flowdrop 1.15.0 → 2.0.0-beta.2

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 (235) hide show
  1. package/CHANGELOG.md +508 -0
  2. package/MIGRATION-2.0.md +629 -0
  3. package/README.md +23 -23
  4. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  5. package/dist/adapters/WorkflowAdapter.js +14 -8
  6. package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
  7. package/dist/api/enhanced-client.js +6 -11
  8. package/dist/chat/batchFeedback.d.ts +39 -0
  9. package/dist/chat/batchFeedback.js +51 -0
  10. package/dist/commands/executor.js +15 -1
  11. package/dist/commands/storeIntegration.svelte.d.ts +4 -1
  12. package/dist/commands/storeIntegration.svelte.js +26 -21
  13. package/dist/commands/types.d.ts +2 -0
  14. package/dist/components/App.svelte +163 -192
  15. package/dist/components/App.svelte.d.ts +47 -8
  16. package/dist/components/ConfigForm.svelte +77 -49
  17. package/dist/components/ConfigModal.svelte +7 -2
  18. package/dist/components/ConnectionLine.svelte +4 -2
  19. package/dist/components/Navbar.svelte +61 -1
  20. package/dist/components/NodeSidebar.svelte +27 -45
  21. package/dist/components/NodeStatusOverlay.svelte +94 -6
  22. package/dist/components/NodeSwapPicker.svelte +10 -8
  23. package/dist/components/PipelineStatus.svelte +22 -68
  24. package/dist/components/PipelineStatus.svelte.d.ts +3 -0
  25. package/dist/components/PortCoordinateTracker.svelte +5 -6
  26. package/dist/components/SchemaForm.stories.svelte +1 -3
  27. package/dist/components/SchemaForm.svelte +22 -27
  28. package/dist/components/SchemaForm.svelte.d.ts +0 -8
  29. package/dist/components/SettingsModal.svelte +8 -3
  30. package/dist/components/SettingsPanel.svelte +20 -4
  31. package/dist/components/SwapMappingEditor.svelte +67 -49
  32. package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
  33. package/dist/components/UniversalNode.svelte +9 -7
  34. package/dist/components/WorkflowEditor.svelte +121 -111
  35. package/dist/components/WorkflowEditor.svelte.d.ts +21 -10
  36. package/dist/components/chat/AIChatPanel.svelte +98 -89
  37. package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
  38. package/dist/components/chat/CommandPreview.svelte +2 -1
  39. package/dist/components/console/CommandConsole.svelte +7 -5
  40. package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
  41. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
  42. package/dist/components/console/ConsoleInput.svelte +15 -6
  43. package/dist/components/console/ConsoleOutput.svelte +2 -1
  44. package/dist/components/form/FormArray.svelte +5 -9
  45. package/dist/components/form/FormArray.svelte.d.ts +2 -1
  46. package/dist/components/form/FormAutocomplete.svelte +16 -15
  47. package/dist/components/form/FormField.svelte +4 -2
  48. package/dist/components/form/FormFieldLight.svelte +34 -3
  49. package/dist/components/form/FormFieldLight.svelte.d.ts +12 -0
  50. package/dist/components/form/FormMarkdownEditor.svelte +9 -4
  51. package/dist/components/form/FormRangeField.svelte +1 -0
  52. package/dist/components/form/FormTemplateEditor.svelte +11 -3
  53. package/dist/components/form/FormToggle.svelte +5 -12
  54. package/dist/components/form/FormToggle.svelte.d.ts +4 -2
  55. package/dist/components/form/FormUISchemaRenderer.svelte +3 -1
  56. package/dist/components/form/templateAutocomplete.js +1 -5
  57. package/dist/components/form/types.d.ts +1 -14
  58. package/dist/components/interrupt/FormPrompt.svelte +3 -2
  59. package/dist/components/interrupt/InterruptBubble.svelte +25 -17
  60. package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
  61. package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
  62. package/dist/components/layouts/MainLayout.svelte +20 -13
  63. package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
  64. package/dist/components/nodes/AtomNode.svelte +17 -5
  65. package/dist/components/nodes/GatewayNode.svelte +19 -10
  66. package/dist/components/nodes/IdeaNode.svelte +7 -0
  67. package/dist/components/nodes/SimpleNode.svelte +11 -6
  68. package/dist/components/nodes/SquareNode.svelte +15 -8
  69. package/dist/components/nodes/TerminalNode.svelte +9 -4
  70. package/dist/components/nodes/ToolNode.svelte +7 -1
  71. package/dist/components/nodes/WorkflowNode.svelte +16 -7
  72. package/dist/components/playground/ChatInput.svelte +11 -14
  73. package/dist/components/playground/ChatPanel.svelte +6 -49
  74. package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
  75. package/dist/components/playground/ControlPanel.svelte +134 -123
  76. package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
  77. package/dist/components/playground/ExecutionLogs.svelte +11 -9
  78. package/dist/components/playground/InputCollector.svelte +11 -9
  79. package/dist/components/playground/MessageStream.svelte +17 -23
  80. package/dist/components/playground/PipelineKanbanView.svelte +69 -8
  81. package/dist/components/playground/PipelineKanbanView.svelte.d.ts +2 -0
  82. package/dist/components/playground/PipelinePanel.svelte +31 -8
  83. package/dist/components/playground/PipelinePanel.svelte.d.ts +2 -0
  84. package/dist/components/playground/PipelineTableView.svelte +188 -44
  85. package/dist/components/playground/PipelineTableView.svelte.d.ts +2 -0
  86. package/dist/components/playground/Playground.svelte +154 -105
  87. package/dist/components/playground/Playground.svelte.d.ts +5 -0
  88. package/dist/components/playground/PlaygroundApp.svelte +11 -1
  89. package/dist/components/playground/PlaygroundApp.svelte.d.ts +6 -0
  90. package/dist/components/playground/PlaygroundModal.svelte +18 -3
  91. package/dist/components/playground/PlaygroundModal.svelte.d.ts +6 -0
  92. package/dist/components/playground/PlaygroundStudio.svelte +40 -32
  93. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +6 -0
  94. package/dist/components/playground/SessionManager.svelte +9 -12
  95. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +30 -1
  96. package/dist/components/playground/pipelineViewUtils.svelte.js +40 -3
  97. package/dist/config/endpoints.d.ts +23 -7
  98. package/dist/config/endpoints.js +30 -10
  99. package/dist/core/index.d.ts +5 -6
  100. package/dist/core/index.js +8 -12
  101. package/dist/display/index.d.ts +6 -3
  102. package/dist/display/index.js +7 -5
  103. package/dist/editor/index.d.ts +20 -21
  104. package/dist/editor/index.js +26 -36
  105. package/dist/form/code.d.ts +25 -15
  106. package/dist/form/code.js +44 -41
  107. package/dist/form/fieldRegistry.d.ts +17 -13
  108. package/dist/form/fieldRegistry.js +32 -12
  109. package/dist/form/full.d.ts +19 -14
  110. package/dist/form/full.js +26 -28
  111. package/dist/form/index.d.ts +3 -4
  112. package/dist/form/index.js +6 -5
  113. package/dist/form/markdown.d.ts +13 -8
  114. package/dist/form/markdown.js +22 -23
  115. package/dist/helpers/proximityConnect.d.ts +3 -2
  116. package/dist/helpers/proximityConnect.js +2 -5
  117. package/dist/helpers/workflowEditorHelper.d.ts +14 -5
  118. package/dist/helpers/workflowEditorHelper.js +28 -25
  119. package/dist/index.d.ts +28 -24
  120. package/dist/index.js +27 -50
  121. package/dist/messages/defaults.d.ts +2 -5
  122. package/dist/messages/defaults.js +3 -6
  123. package/dist/messages/index.d.ts +0 -1
  124. package/dist/messages/index.js +0 -1
  125. package/dist/mocks/app-forms.d.ts +6 -2
  126. package/dist/mocks/app-forms.js +11 -4
  127. package/dist/openapi/v1/openapi.yaml +3 -3
  128. package/dist/playground/index.d.ts +4 -5
  129. package/dist/playground/index.js +4 -32
  130. package/dist/playground/mount.d.ts +25 -0
  131. package/dist/playground/mount.js +50 -20
  132. package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
  133. package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
  134. package/dist/registry/builtinFormats.d.ts +9 -18
  135. package/dist/registry/builtinFormats.js +9 -39
  136. package/dist/registry/builtinNodeTypes.d.ts +53 -0
  137. package/dist/registry/builtinNodeTypes.js +67 -0
  138. package/dist/registry/builtinNodes.d.ts +2 -64
  139. package/dist/registry/builtinNodes.js +7 -103
  140. package/dist/registry/index.d.ts +3 -4
  141. package/dist/registry/index.js +4 -6
  142. package/dist/registry/nodeComponentRegistry.d.ts +182 -15
  143. package/dist/registry/nodeComponentRegistry.js +235 -17
  144. package/dist/registry/workflowFormatRegistry.d.ts +14 -9
  145. package/dist/registry/workflowFormatRegistry.js +24 -8
  146. package/dist/{schema → schemas}/index.d.ts +2 -2
  147. package/dist/{schema → schemas}/index.js +2 -2
  148. package/dist/schemas/v1/workflow.schema.json +3 -3
  149. package/dist/services/agentSpecExecutionService.d.ts +0 -2
  150. package/dist/services/agentSpecExecutionService.js +0 -3
  151. package/dist/services/apiVariableService.d.ts +2 -1
  152. package/dist/services/apiVariableService.js +16 -47
  153. package/dist/services/autoSaveService.d.ts +7 -0
  154. package/dist/services/autoSaveService.js +6 -4
  155. package/dist/services/categoriesApi.js +3 -6
  156. package/dist/services/chatService.d.ts +9 -4
  157. package/dist/services/chatService.js +23 -28
  158. package/dist/services/draftStorage.d.ts +129 -13
  159. package/dist/services/draftStorage.js +185 -37
  160. package/dist/services/dynamicSchemaService.d.ts +2 -1
  161. package/dist/services/dynamicSchemaService.js +5 -22
  162. package/dist/services/globalSave.d.ts +13 -12
  163. package/dist/services/globalSave.js +29 -51
  164. package/dist/services/historyService.d.ts +9 -3
  165. package/dist/services/historyService.js +9 -3
  166. package/dist/services/interruptService.d.ts +15 -9
  167. package/dist/services/interruptService.js +35 -37
  168. package/dist/services/nodeExecutionService.d.ts +18 -3
  169. package/dist/services/nodeExecutionService.js +71 -45
  170. package/dist/services/playgroundService.d.ts +16 -10
  171. package/dist/services/playgroundService.js +42 -43
  172. package/dist/services/portConfigApi.js +3 -6
  173. package/dist/services/settingsService.d.ts +9 -4
  174. package/dist/services/settingsService.js +23 -12
  175. package/dist/services/variableService.d.ts +2 -1
  176. package/dist/services/variableService.js +2 -2
  177. package/dist/services/workflowStorage.js +6 -6
  178. package/dist/stores/apiContext.d.ts +56 -0
  179. package/dist/stores/apiContext.js +80 -0
  180. package/dist/stores/categoriesStore.svelte.d.ts +28 -23
  181. package/dist/stores/categoriesStore.svelte.js +69 -64
  182. package/dist/stores/getInstance.svelte.d.ts +39 -0
  183. package/dist/stores/getInstance.svelte.js +65 -0
  184. package/dist/stores/historyStore.svelte.d.ts +77 -93
  185. package/dist/stores/historyStore.svelte.js +134 -160
  186. package/dist/stores/instanceContainer.svelte.d.ts +111 -0
  187. package/dist/stores/instanceContainer.svelte.js +114 -0
  188. package/dist/stores/interruptStore.svelte.d.ts +112 -82
  189. package/dist/stores/interruptStore.svelte.js +253 -226
  190. package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
  191. package/dist/stores/pipelinePanelStore.svelte.js +61 -14
  192. package/dist/stores/playgroundStore.svelte.d.ts +169 -222
  193. package/dist/stores/playgroundStore.svelte.js +513 -580
  194. package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
  195. package/dist/stores/portCoordinateStore.svelte.js +109 -98
  196. package/dist/stores/settingsStore.svelte.d.ts +4 -1
  197. package/dist/stores/settingsStore.svelte.js +47 -12
  198. package/dist/stores/workflowStore.svelte.d.ts +178 -213
  199. package/dist/stores/workflowStore.svelte.js +449 -501
  200. package/dist/stories/EdgeDecorator.svelte +5 -2
  201. package/dist/stories/NodeDecorator.svelte +5 -3
  202. package/dist/svelte-app.d.ts +60 -10
  203. package/dist/svelte-app.js +159 -54
  204. package/dist/types/auth.d.ts +9 -51
  205. package/dist/types/auth.js +4 -54
  206. package/dist/types/events.d.ts +6 -3
  207. package/dist/types/index.d.ts +37 -5
  208. package/dist/types/index.js +0 -1
  209. package/dist/types/navbar.d.ts +7 -0
  210. package/dist/types/playground.d.ts +18 -3
  211. package/dist/types/settings.d.ts +13 -0
  212. package/dist/types/settings.js +1 -0
  213. package/dist/utils/colors.d.ts +47 -21
  214. package/dist/utils/colors.js +69 -68
  215. package/dist/utils/connections.d.ts +9 -15
  216. package/dist/utils/connections.js +13 -32
  217. package/dist/utils/duration.d.ts +13 -0
  218. package/dist/utils/duration.js +45 -0
  219. package/dist/utils/edgeStyling.js +9 -5
  220. package/dist/utils/fetchWithAuth.d.ts +36 -15
  221. package/dist/utils/fetchWithAuth.js +53 -23
  222. package/dist/utils/icons.d.ts +5 -2
  223. package/dist/utils/icons.js +6 -5
  224. package/dist/utils/nodeSwap.d.ts +6 -2
  225. package/dist/utils/nodeSwap.js +62 -126
  226. package/dist/utils/nodeTypes.d.ts +17 -8
  227. package/dist/utils/nodeTypes.js +27 -20
  228. package/dist/utils/performanceUtils.js +7 -0
  229. package/package.json +7 -5
  230. package/dist/messages/deprecation.d.ts +0 -20
  231. package/dist/messages/deprecation.js +0 -33
  232. package/dist/registry/plugin.d.ts +0 -215
  233. package/dist/registry/plugin.js +0 -249
  234. package/dist/services/api.d.ts +0 -129
  235. package/dist/services/api.js +0 -217
package/README.md CHANGED
@@ -50,7 +50,7 @@ No vendor lock-in. No data leaving your walls. No surprise bills.
50
50
  npm install @flowdrop/flowdrop
51
51
  ```
52
52
 
53
- > **Note:** FlowDrop supports **one editor instance per page** (module-level singleton stores). See [Architecture Notes](#architecture-notes) for details.
53
+ > **Note:** FlowDrop supports **multiple editor instances per page** each mount gets its own isolated state container. See [Architecture Notes](#architecture-notes) for details.
54
54
 
55
55
  You get a production-ready workflow UI. You keep full control of everything else.
56
56
 
@@ -58,11 +58,11 @@ You get a production-ready workflow UI. You keep full control of everything else
58
58
 
59
59
  ```svelte
60
60
  <script lang="ts">
61
- import { WorkflowEditor } from '@flowdrop/flowdrop';
61
+ import { App } from '@flowdrop/flowdrop';
62
62
  import '@flowdrop/flowdrop/styles/base.css';
63
63
  </script>
64
64
 
65
- <WorkflowEditor />
65
+ <App />
66
66
  ```
67
67
 
68
68
  **5 lines. One fully-functional workflow editor.**
@@ -80,7 +80,7 @@ You get a production-ready workflow UI. You keep full control of everything else
80
80
 
81
81
  ## Architecture Notes
82
82
 
83
- - **Single instance per page.** FlowDrop uses module-level singleton stores for state management. Only one FlowDrop editor instance can exist on a page at a time.
83
+ - **Multiple instances per page.** Each mount gets its own `FlowDropInstance` state container (workflow, undo/redo history, playground sessions, registries, API context, drafts), so editors on the same page are fully isolated. In 2.0 there are no module-level store APIs resolve state with `getInstance()` inside the component tree or the mount handle's `.instance` outside it. The first mount without an `instanceId` becomes the page-default instance; pass `instanceId` to additional mounts to scope their draft/panel storage keys. Theme and settings remain page-global by design.
84
84
  - **Svelte 5 required.** FlowDrop uses Svelte 5 runes (`$state`, `$derived`, `$effect`) throughout. Svelte 4 is not supported.
85
85
  - **Modern browsers only.** The library targets ES2020+ and does not include polyfills for older browsers.
86
86
 
@@ -112,12 +112,12 @@ FlowDrop includes a theme system with built-in light/dark support:
112
112
 
113
113
  ```svelte
114
114
  <script lang="ts">
115
- import { WorkflowEditor } from '@flowdrop/flowdrop';
115
+ import { App } from '@flowdrop/flowdrop';
116
116
  import '@flowdrop/flowdrop/styles';
117
117
  </script>
118
118
 
119
119
  <!-- Built-in themes: 'default' or 'minimal' -->
120
- <WorkflowEditor theme="minimal" />
120
+ <App theme="minimal" />
121
121
  ```
122
122
 
123
123
  Themes bundle a visual skin (CSS token palette) with behavioral UI defaults. You can also pass a custom theme object with your own skin tokens for full control over the light and dark palettes.
@@ -136,7 +136,7 @@ const app = await mountFlowDropApp(container, {
136
136
  Every user-facing string flows through a typed `Messages` tree. Pass a callback to override any subset:
137
137
 
138
138
  ```svelte
139
- <FlowDrop messages={() => ({ form: { schema: { save: 'Apply' } } })} />
139
+ <App messages={() => ({ form: { schema: { save: 'Apply' } } })} />
140
140
  ```
141
141
 
142
142
  Wire the callback to your i18n library (paraglide-js, sveltekit-i18n, etc.) — locale changes propagate automatically. See the [i18n & Custom Messages guide](https://docs.flowdrop.io/guides/i18n) for the full shape and a paraglide-js worked example.
@@ -145,20 +145,20 @@ Wire the callback to your i18n library (paraglide-js, sveltekit-i18n, etc.) —
145
145
 
146
146
  FlowDrop provides tree-shakeable sub-module exports so you can import only what you need:
147
147
 
148
- | Export Path | Contents |
149
- | ---------------------------------- | -------------------------------------------------- |
150
- | `@flowdrop/flowdrop` | Full library (components, stores, services, types) |
151
- | `@flowdrop/flowdrop/core` | Types and utilities only (no heavy dependencies) |
152
- | `@flowdrop/flowdrop/editor` | WorkflowEditor, stores, services |
153
- | `@flowdrop/flowdrop/form` | SchemaForm, form fields, registry |
154
- | `@flowdrop/flowdrop/form/code` | Code editor field (CodeMirror) |
155
- | `@flowdrop/flowdrop/form/markdown` | Markdown editor field |
156
- | `@flowdrop/flowdrop/display` | MarkdownDisplay component |
157
- | `@flowdrop/flowdrop/playground` | Playground components and services |
158
- | `@flowdrop/flowdrop/settings` | SettingsPanel, stores, services |
159
- | `@flowdrop/flowdrop/styles` | Base CSS stylesheet |
160
- | `@flowdrop/flowdrop/schema` | Workflow JSON schema |
161
- | `@flowdrop/flowdrop/openapi` | OpenAPI spec (YAML) for the FlowDrop backend API |
148
+ | Export Path | Contents |
149
+ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
150
+ | `@flowdrop/flowdrop` | Slim front door: `App`, mount functions, `createFlowDropInstance`, `getInstance`, auth providers, core bootstrap types |
151
+ | `@flowdrop/flowdrop/core` | Types and utilities only (no heavy dependencies) |
152
+ | `@flowdrop/flowdrop/editor` | WorkflowEditor, stores, services |
153
+ | `@flowdrop/flowdrop/form` | SchemaForm, form fields, registry |
154
+ | `@flowdrop/flowdrop/form/code` | Code editor field (CodeMirror) |
155
+ | `@flowdrop/flowdrop/form/markdown` | Markdown editor field |
156
+ | `@flowdrop/flowdrop/display` | MarkdownDisplay component |
157
+ | `@flowdrop/flowdrop/playground` | Playground components and services |
158
+ | `@flowdrop/flowdrop/settings` | SettingsPanel, stores, services |
159
+ | `@flowdrop/flowdrop/styles` | Base CSS stylesheet |
160
+ | `@flowdrop/flowdrop/schema` | Workflow JSON schema |
161
+ | `@flowdrop/flowdrop/openapi` | OpenAPI spec (YAML) for the FlowDrop backend API |
162
162
 
163
163
  ### OpenAPI spec
164
164
 
@@ -170,12 +170,12 @@ The full OpenAPI spec for the FlowDrop backend API ships with the package, versi
170
170
 
171
171
  ```svelte
172
172
  <script>
173
- import { WorkflowEditor, NodeSidebar } from '@flowdrop/flowdrop';
173
+ import { WorkflowEditor, NodeSidebar } from '@flowdrop/flowdrop/editor';
174
174
  </script>
175
175
 
176
176
  <div class="flex h-screen">
177
177
  <NodeSidebar {nodes} />
178
- <WorkflowEditor {nodes} />
178
+ <WorkflowEditor />
179
179
  </div>
180
180
  ```
181
181
 
@@ -50,7 +50,7 @@ export interface StandardWorkflow {
50
50
  nodes: StandardNode[];
51
51
  edges: StandardEdge[];
52
52
  metadata?: {
53
- version: string;
53
+ schemaVersion: string;
54
54
  createdAt: string;
55
55
  updatedAt: string;
56
56
  author?: string;
@@ -13,6 +13,7 @@
13
13
  * - External applications that want to integrate with FlowDrop
14
14
  * - Systems that need to generate or modify workflows programmatically
15
15
  */
16
+ import { WORKFLOW_SCHEMA_VERSION } from '../schemas/index.js';
16
17
  import { v4 as uuidv4 } from 'uuid';
17
18
  import { generateNodeId } from '../utils/nodeIds.js';
18
19
  /**
@@ -35,7 +36,7 @@ export class WorkflowAdapter {
35
36
  nodes: [],
36
37
  edges: [],
37
38
  metadata: {
38
- version: '1.0.0',
39
+ schemaVersion: '1.0.0',
39
40
  createdAt: new Date().toISOString(),
40
41
  updatedAt: new Date().toISOString()
41
42
  }
@@ -222,13 +223,14 @@ export class WorkflowAdapter {
222
223
  if (!validation.valid) {
223
224
  throw new Error(`Invalid workflow: ${validation.errors.join(', ')}`);
224
225
  }
225
- // Update metadata
226
+ // Update metadata — heal legacy 1.x `version` key into `schemaVersion`.
227
+ const incoming = workflow.metadata;
226
228
  workflow.metadata = {
227
- version: workflow.metadata?.version || '1.0.0',
228
- createdAt: workflow.metadata?.createdAt || new Date().toISOString(),
229
+ schemaVersion: incoming?.schemaVersion || incoming?.version || '1.0.0',
230
+ createdAt: incoming?.createdAt || new Date().toISOString(),
229
231
  updatedAt: new Date().toISOString(),
230
- author: workflow.metadata?.author,
231
- tags: workflow.metadata?.tags
232
+ author: incoming?.author,
233
+ tags: incoming?.tags
232
234
  };
233
235
  return workflow;
234
236
  }
@@ -291,7 +293,11 @@ export class WorkflowAdapter {
291
293
  sourceHandle: edge.sourceHandle,
292
294
  targetHandle: edge.targetHandle
293
295
  })),
294
- metadata: workflow.metadata
296
+ metadata: workflow.metadata ?? {
297
+ schemaVersion: WORKFLOW_SCHEMA_VERSION,
298
+ createdAt: new Date().toISOString(),
299
+ updatedAt: new Date().toISOString()
300
+ }
295
301
  };
296
302
  }
297
303
  /**
@@ -337,7 +343,7 @@ export class WorkflowAdapter {
337
343
  cloned.id = uuidv4();
338
344
  cloned.name = newName || `${workflow.name} (Copy)`;
339
345
  cloned.metadata = {
340
- version: cloned.metadata?.version || '1.0.0',
346
+ schemaVersion: cloned.metadata?.schemaVersion || '1.0.0',
341
347
  createdAt: new Date().toISOString(),
342
348
  updatedAt: new Date().toISOString(),
343
349
  author: cloned.metadata?.author,
@@ -13,7 +13,7 @@ import { getComponentTypeDefaults, extractComponentType, AGENTSPEC_NAMESPACE } f
13
13
  import { computeAutoLayout } from './autoLayout.js';
14
14
  import { v4 as uuidv4 } from 'uuid';
15
15
  import { logger } from '../../utils/logger.js';
16
- import { buildHandleId, extractPortId, extractDirection } from '../../utils/handleIds.js';
16
+ import { buildHandleId, extractPortId } from '../../utils/handleIds.js';
17
17
  // ============================================================================
18
18
  // Property ↔ Port Conversion
19
19
  // ============================================================================
@@ -138,7 +138,7 @@ export class AgentSpecAdapter {
138
138
  }
139
139
  }
140
140
  // Find start node
141
- const startNodeName = this.findStartNodeName(agentSpecNodes, nodeIdToName);
141
+ const startNodeName = this.findStartNodeName(agentSpecNodes);
142
142
  return {
143
143
  component_type: 'flow',
144
144
  name: workflow.name,
@@ -149,7 +149,7 @@ export class AgentSpecAdapter {
149
149
  data_flow_connections: dataFlowEdges.length > 0 ? dataFlowEdges : null,
150
150
  metadata: {
151
151
  'flowdrop:workflow_id': workflow.id,
152
- 'flowdrop:version': workflow.metadata?.version,
152
+ 'flowdrop:version': workflow.metadata?.schemaVersion,
153
153
  ...(workflow.metadata?.author ? { 'flowdrop:author': workflow.metadata.author } : {}),
154
154
  ...(workflow.metadata?.tags ? { 'flowdrop:tags': workflow.metadata.tags } : {})
155
155
  }
@@ -196,7 +196,7 @@ export class AgentSpecAdapter {
196
196
  const edges = [];
197
197
  // Control-flow edges → trigger port connections
198
198
  for (const cfEdge of agentSpecFlow.control_flow_connections) {
199
- const edge = this.convertFromControlFlowEdge(cfEdge, nameToNodeId, nodes);
199
+ const edge = this.convertFromControlFlowEdge(cfEdge, nameToNodeId);
200
200
  if (edge)
201
201
  edges.push(edge);
202
202
  }
@@ -215,7 +215,7 @@ export class AgentSpecAdapter {
215
215
  nodes,
216
216
  edges,
217
217
  metadata: {
218
- version: agentSpecFlow.metadata?.['flowdrop:version'] || '1.0.0',
218
+ schemaVersion: agentSpecFlow.metadata?.['flowdrop:version'] || '1.0.0',
219
219
  createdAt: new Date().toISOString(),
220
220
  updatedAt: new Date().toISOString(),
221
221
  author: agentSpecFlow.metadata?.['flowdrop:author'],
@@ -578,7 +578,7 @@ export class AgentSpecAdapter {
578
578
  /**
579
579
  * Convert an Agent Spec ControlFlowEdge to a FlowDrop edge.
580
580
  */
581
- convertFromControlFlowEdge(cfEdge, nameToNodeId, nodes) {
581
+ convertFromControlFlowEdge(cfEdge, nameToNodeId) {
582
582
  const sourceId = nameToNodeId.get(cfEdge.from_node);
583
583
  const targetId = nameToNodeId.get(cfEdge.to_node);
584
584
  if (!sourceId || !targetId)
@@ -615,7 +615,7 @@ export class AgentSpecAdapter {
615
615
  /**
616
616
  * Find the start node name from converted Agent Spec nodes.
617
617
  */
618
- findStartNodeName(nodes, nodeIdToName) {
618
+ findStartNodeName(nodes) {
619
619
  // Look for an explicit start_node
620
620
  const startNode = nodes.find((n) => n.component_type === 'start_node');
621
621
  if (startNode)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @module api/enhanced-client
7
7
  */
8
- import { buildEndpointUrl, getEndpointMethod, getEndpointHeaders } from '../config/endpoints.js';
8
+ import { buildEndpointUrl, getEndpointMethod, getRequestHeaders } from '../config/endpoints.js';
9
9
  import { NoAuthProvider } from '../types/auth.js';
10
10
  import { getApiSettings } from '../stores/settingsStore.svelte.js';
11
11
  import { logger } from '../utils/logger.js';
@@ -56,15 +56,12 @@ export class EnhancedFlowDropApiClient {
56
56
  async request(endpointKey, endpointPath, params, options = {}, operation = 'API request') {
57
57
  const url = buildEndpointUrl(this.config, endpointPath, params);
58
58
  const method = options.method ?? getEndpointMethod(this.config, endpointKey);
59
- const configHeaders = getEndpointHeaders(this.config, endpointKey);
60
59
  // Get user settings for timeout and retry
61
60
  const userApiSettings = getApiSettings();
62
- // Get auth headers from provider
63
- const authHeaders = await this.authProvider.getAuthHeaders();
64
- // Merge headers: config headers < auth headers < request-specific headers
61
+ // Merge headers via the shared path: static endpoint headers < auth headers
62
+ // < request-specific headers.
65
63
  const headers = {
66
- ...configHeaders,
67
- ...authHeaders,
64
+ ...(await getRequestHeaders(this.config, endpointKey, this.authProvider)),
68
65
  ...options.headers
69
66
  };
70
67
  // Create AbortController for timeout
@@ -93,11 +90,9 @@ export class EnhancedFlowDropApiClient {
93
90
  if (this.authProvider.onUnauthorized) {
94
91
  const refreshed = await this.authProvider.onUnauthorized();
95
92
  if (refreshed && attempt < maxAttempts) {
96
- // Get new auth headers and retry
97
- const newAuthHeaders = await this.authProvider.getAuthHeaders();
93
+ // Rebuild headers via the shared path to pick up the fresh token.
98
94
  fetchConfig.headers = {
99
- ...configHeaders,
100
- ...newAuthHeaders,
95
+ ...(await getRequestHeaders(this.config, endpointKey, this.authProvider)),
101
96
  ...options.headers
102
97
  };
103
98
  continue; // Retry with new headers
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Batch retry feedback for the AI Assistant.
3
+ *
4
+ * When a batch of DSL commands finishes with failures — some couldn't be
5
+ * parsed, or one failed to execute and the batch rolled back — the assistant
6
+ * needs concrete, specific feedback to self-correct. This module builds that
7
+ * message.
8
+ *
9
+ * Specificity is the point: the parse-error feedback names the offending text
10
+ * and the exact parser reason. The old generic "provide corrected commands"
11
+ * ask is what made auto-retry on parse errors loop on the same malformed shape
12
+ * — so it was disabled. Pinpointing the failure makes re-enabling it safe.
13
+ *
14
+ * @module chat/batchFeedback
15
+ */
16
+ /** A command that could not be parsed, with the reason and its raw text. */
17
+ export interface ParseFailure {
18
+ raw: string;
19
+ error: string;
20
+ }
21
+ /** Summary of how a batch finished, used to build retry feedback. */
22
+ export interface BatchOutcome {
23
+ /** Number of commands that executed successfully before stopping. */
24
+ completedCount: number;
25
+ /** Error from the first command that failed to execute, if any. */
26
+ executionError?: string;
27
+ /** Whether the executed commands were rolled back (atomic execution failure). */
28
+ rolledBack: boolean;
29
+ /** Commands that could not be parsed and were skipped. */
30
+ parseErrors: ParseFailure[];
31
+ }
32
+ /**
33
+ * Build feedback for the assistant after a batch finished with failures.
34
+ *
35
+ * Pinpoints *what* went wrong — the specific parser error and offending text
36
+ * for unparseable commands, and/or the execution error — so the assistant has
37
+ * a concrete, different target to correct.
38
+ */
39
+ export declare function buildRetryFeedback(outcome: BatchOutcome): string;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Batch retry feedback for the AI Assistant.
3
+ *
4
+ * When a batch of DSL commands finishes with failures — some couldn't be
5
+ * parsed, or one failed to execute and the batch rolled back — the assistant
6
+ * needs concrete, specific feedback to self-correct. This module builds that
7
+ * message.
8
+ *
9
+ * Specificity is the point: the parse-error feedback names the offending text
10
+ * and the exact parser reason. The old generic "provide corrected commands"
11
+ * ask is what made auto-retry on parse errors loop on the same malformed shape
12
+ * — so it was disabled. Pinpointing the failure makes re-enabling it safe.
13
+ *
14
+ * @module chat/batchFeedback
15
+ */
16
+ /**
17
+ * Build feedback for the assistant after a batch finished with failures.
18
+ *
19
+ * Pinpoints *what* went wrong — the specific parser error and offending text
20
+ * for unparseable commands, and/or the execution error — so the assistant has
21
+ * a concrete, different target to correct.
22
+ */
23
+ export function buildRetryFeedback(outcome) {
24
+ const { completedCount, executionError, rolledBack, parseErrors } = outcome;
25
+ const lines = [];
26
+ if (executionError) {
27
+ lines.push(`A command failed to execute: ${executionError}`);
28
+ lines.push(rolledBack && completedCount > 0
29
+ ? `The ${completedCount} command(s) that had succeeded were rolled back, so no changes were applied.`
30
+ : 'No changes were applied.');
31
+ }
32
+ else if (completedCount > 0) {
33
+ lines.push(`${completedCount} command(s) were applied successfully.`);
34
+ }
35
+ if (parseErrors.length > 0) {
36
+ lines.push(`\n${parseErrors.length} command(s) could not be parsed and were skipped:`);
37
+ for (const pe of parseErrors) {
38
+ const firstLine = pe.raw.split('\n')[0];
39
+ const preview = pe.raw.includes('\n') ? `${firstLine} …` : firstLine;
40
+ lines.push(` • ${preview}\n reason: ${pe.error}`);
41
+ }
42
+ lines.push('\nNote: multiline values must close with """ on its own line.');
43
+ }
44
+ if (rolledBack) {
45
+ lines.push('\nPlease re-send the full corrected batch to achieve the original goal.');
46
+ }
47
+ else {
48
+ lines.push('\nThe successful changes are reflected in the current workflow state. Re-send only corrected versions of the skipped command(s).');
49
+ }
50
+ return lines.join('\n');
51
+ }
@@ -888,17 +888,31 @@ function executeSwapNode(command, context) {
888
888
  edges: swapResult.updatedEdges
889
889
  });
890
890
  }
891
+ // Name each dropped edge so the user can see exactly what was lost
892
+ const droppedEdgeDetails = preview.droppedEdges.map(({ edge }) => {
893
+ const sourcePort = extractPortId(edge.sourceHandle ?? undefined) ?? '?';
894
+ const targetPort = extractPortId(edge.targetHandle ?? undefined) ?? '?';
895
+ return `${toShortId(edge.source)}:${sourcePort} → ${toShortId(edge.target)}:${targetPort}`;
896
+ });
891
897
  const resultData = {
892
898
  oldNodeId: toShortId(node.id),
893
899
  newNodeId: toShortId(preview.newNodeId),
894
900
  newType: command.newTypeId,
895
901
  keptEdges: preview.keptEdges.length,
896
902
  droppedEdges: preview.droppedEdges.length,
903
+ droppedEdgeDetails,
897
904
  hasDataLoss: preview.hasDataLoss,
898
905
  configCarriedOver: preview.configCarriedOver,
899
906
  configReset: preview.configReset
900
907
  };
901
- const droppedMsg = preview.droppedEdges.length > 0 ? ` (${preview.droppedEdges.length} edge(s) dropped)` : '';
908
+ // Cap the inline list a badly mismatched swap can drop dozens of edges,
909
+ // and the full list is always available in resultData.droppedEdgeDetails
910
+ const MAX_NAMED_DROPPED_EDGES = 5;
911
+ const namedDropped = droppedEdgeDetails.slice(0, MAX_NAMED_DROPPED_EDGES);
912
+ const unnamedCount = droppedEdgeDetails.length - namedDropped.length;
913
+ const droppedMsg = preview.droppedEdges.length > 0
914
+ ? ` (${preview.droppedEdges.length} edge(s) dropped: ${namedDropped.join(', ')}${unnamedCount > 0 ? `, … and ${unnamedCount} more` : ''})`
915
+ : '';
902
916
  return {
903
917
  ok: true,
904
918
  message: `Swapped ${toShortId(node.id)} → ${toShortId(preview.newNodeId)} (${command.newTypeId})${droppedMsg}`,
@@ -4,6 +4,7 @@
4
4
  * Bridges the command system to the live Svelte stores.
5
5
  * This is the only Svelte-coupled file in commands/.
6
6
  */
7
+ import { type FlowDropInstance } from '../stores/instanceContainer.svelte.js';
7
8
  import { type CommandContext, type UIAction } from './types.js';
8
9
  import type { NodeMetadata } from '../types/index.js';
9
10
  /**
@@ -11,6 +12,8 @@ import type { NodeMetadata } from '../types/index.js';
11
12
  *
12
13
  * @param nodeTypes - Available node type definitions
13
14
  * @param onUIAction - Optional callback for UI-side actions (open config panel, select node)
15
+ * @param instance - The FlowDrop instance to operate on; defaults to the
16
+ * page-default instance when omitted
14
17
  * @returns CommandContext connected to live stores, or null if no workflow is loaded
15
18
  */
16
- export declare function createStoreCommandContext(nodeTypes: NodeMetadata[], onUIAction?: (action: UIAction) => void): CommandContext | null;
19
+ export declare function createStoreCommandContext(nodeTypes: NodeMetadata[], onUIAction?: (action: UIAction) => void, instance?: FlowDropInstance): CommandContext | null;
@@ -4,62 +4,67 @@
4
4
  * Bridges the command system to the live Svelte stores.
5
5
  * This is the only Svelte-coupled file in commands/.
6
6
  */
7
- import { getWorkflowStore, workflowActions } from '../stores/workflowStore.svelte.js';
8
- import { historyService } from '../services/historyService.js';
7
+ import { getDefaultInstance } from '../stores/instanceContainer.svelte.js';
9
8
  import { buildTypeMap } from './types.js';
10
9
  /**
11
10
  * Creates a CommandContext that bridges the command system to the live Svelte stores.
12
11
  *
13
12
  * @param nodeTypes - Available node type definitions
14
13
  * @param onUIAction - Optional callback for UI-side actions (open config panel, select node)
14
+ * @param instance - The FlowDrop instance to operate on; defaults to the
15
+ * page-default instance when omitted
15
16
  * @returns CommandContext connected to live stores, or null if no workflow is loaded
16
17
  */
17
- export function createStoreCommandContext(nodeTypes, onUIAction) {
18
- const workflow = getWorkflowStore();
18
+ export function createStoreCommandContext(nodeTypes, onUIAction, instance) {
19
+ const fd = instance ?? getDefaultInstance();
20
+ const readWorkflow = () => fd.workflow.current;
21
+ const actions = fd.workflow.actions;
22
+ const history = fd.history;
23
+ const workflow = readWorkflow();
19
24
  if (!workflow) {
20
25
  return null;
21
26
  }
22
27
  const dispatch = {
23
- addNode: (node) => workflowActions.addNode(node),
24
- removeNode: (nodeId) => workflowActions.removeNode(nodeId),
25
- updateNode: (nodeId, updates) => workflowActions.updateNode(nodeId, updates),
26
- addEdge: (edge) => workflowActions.addEdge(edge),
27
- removeEdge: (edgeId) => workflowActions.removeEdge(edgeId),
28
- batchUpdate: (updates) => workflowActions.batchUpdate(updates),
28
+ addNode: (node) => actions.addNode(node),
29
+ removeNode: (nodeId) => actions.removeNode(nodeId),
30
+ updateNode: (nodeId, updates) => actions.updateNode(nodeId, updates),
31
+ addEdge: (edge) => actions.addEdge(edge),
32
+ removeEdge: (edgeId) => actions.removeEdge(edgeId),
33
+ batchUpdate: (updates) => actions.batchUpdate(updates),
29
34
  undo: () => {
30
- const previousState = historyService.undo();
35
+ const previousState = history.undo();
31
36
  if (previousState) {
32
- workflowActions.restoreFromHistory(previousState);
37
+ actions.restoreFromHistory(previousState);
33
38
  return true;
34
39
  }
35
40
  return false;
36
41
  },
37
42
  redo: () => {
38
- const nextState = historyService.redo();
43
+ const nextState = history.redo();
39
44
  if (nextState) {
40
- workflowActions.restoreFromHistory(nextState);
45
+ actions.restoreFromHistory(nextState);
41
46
  return true;
42
47
  }
43
48
  return false;
44
49
  },
45
50
  startTransaction: (description) => {
46
- const currentWorkflow = getWorkflowStore();
51
+ const currentWorkflow = readWorkflow();
47
52
  if (currentWorkflow) {
48
- historyService.startTransaction(currentWorkflow, description);
53
+ history.startTransaction(currentWorkflow, description);
49
54
  }
50
55
  },
51
- commitTransaction: () => historyService.commitTransaction(),
56
+ commitTransaction: () => history.commitTransaction(),
52
57
  cancelTransaction: () => {
53
- const snapshot = historyService.cancelTransaction();
58
+ const snapshot = history.cancelTransaction();
54
59
  if (snapshot) {
55
- workflowActions.restoreFromHistory(snapshot);
60
+ actions.restoreFromHistory(snapshot);
56
61
  }
57
62
  },
58
63
  emitUIAction: onUIAction,
59
- swapNode: (updates) => workflowActions.swapNode(updates)
64
+ swapNode: (updates) => actions.swapNode(updates)
60
65
  };
61
66
  return {
62
- getWorkflow: () => getWorkflowStore(),
67
+ getWorkflow: () => readWorkflow(),
63
68
  nodeTypes,
64
69
  typeMap: buildTypeMap(nodeTypes),
65
70
  dispatch
@@ -244,6 +244,8 @@ export interface SwapNodeResultData {
244
244
  newType: string;
245
245
  keptEdges: number;
246
246
  droppedEdges: number;
247
+ /** Human-readable descriptions of each dropped edge, e.g. "node.1:text → node.2:message" */
248
+ droppedEdgeDetails: string[];
247
249
  hasDataLoss: boolean;
248
250
  configCarriedOver: string[];
249
251
  configReset: string[];