windmill-components 1.687.0 → 1.695.1

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 (264) hide show
  1. package/package/components/ArgInput.svelte +2 -0
  2. package/package/components/AutoscalingConfigEditor.svelte +18 -4
  3. package/package/components/CompareWorkspaces.svelte +206 -157
  4. package/package/components/DatatableSchemaDiff.svelte +2 -2
  5. package/package/components/Dev.svelte +401 -85
  6. package/package/components/EditableSchemaForm.svelte +4 -0
  7. package/package/components/ErrorOrRecoveryHandler.svelte +2 -2
  8. package/package/components/FlowPreviewContent.svelte +32 -30
  9. package/package/components/FlowRestartButton.svelte +143 -61
  10. package/package/components/FlowRestartButton.svelte.d.ts +37 -0
  11. package/package/components/FlowStatusViewer.svelte +15 -1
  12. package/package/components/FlowStatusViewer.svelte.d.ts +10 -2
  13. package/package/components/FlowStatusViewerInner.svelte +1 -2
  14. package/package/components/FlowStatusViewerInner.svelte.d.ts +6 -2
  15. package/package/components/ForkConflictModal.svelte +57 -0
  16. package/package/components/ForkConflictModal.svelte.d.ts +3 -0
  17. package/package/components/GitRepoViewer.svelte +251 -97
  18. package/package/components/InputTransformSchemaForm.svelte +1 -1
  19. package/package/components/InstanceSettings.svelte +36 -16
  20. package/package/components/Login.svelte +113 -28
  21. package/package/components/Login.svelte.d.ts +1 -0
  22. package/package/components/Path.svelte +7 -1
  23. package/package/components/Path.svelte.d.ts +1 -1
  24. package/package/components/RunsPage.svelte +2 -1
  25. package/package/components/S3FilePickerInner.svelte +89 -89
  26. package/package/components/ScriptEditor.svelte +18 -5
  27. package/package/components/ShareModal.svelte.d.ts +1 -1
  28. package/package/components/apps/components/helpers/RunnableComponent.svelte.d.ts +3 -0
  29. package/package/components/apps/components/helpers/executeRunnable.js +2 -1
  30. package/package/components/apps/editor/AppReportsDrawerInner.svelte +1 -1
  31. package/package/components/apps/editor/appPolicy.js +2 -1
  32. package/package/components/apps/editor/commonAppUtils.d.ts +3 -0
  33. package/package/components/apps/editor/inlineScriptsPanel/CacheTtlPopup.svelte +1 -1
  34. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte +7 -0
  35. package/package/components/apps/editor/inlineScriptsPanel/TagPopup.svelte +49 -0
  36. package/package/components/apps/editor/inlineScriptsPanel/TagPopup.svelte.d.ts +9 -0
  37. package/package/components/apps/inputType.d.ts +1 -0
  38. package/package/components/apps/sharedTypes.d.ts +1 -0
  39. package/package/components/auditLogs/AuditLogsFilters.svelte +8 -3
  40. package/package/components/common/fileUpload/S3ArgInput.svelte +12 -10
  41. package/package/components/common/fileUpload/S3ArgInput.svelte.d.ts +2 -0
  42. package/package/components/copilot/chat/AIChatDisplay.svelte +5 -36
  43. package/package/components/copilot/chat/AIChatInput.svelte +56 -47
  44. package/package/components/copilot/chat/AIChatManager.svelte.js +48 -46
  45. package/package/components/copilot/chat/ContextElementBadge.svelte +6 -4
  46. package/package/components/copilot/chat/app/core.d.ts +12 -20
  47. package/package/components/copilot/chat/app/core.js +103 -160
  48. package/package/components/copilot/chat/app/core.test.js +234 -9
  49. package/package/components/copilot/chat/context.js +44 -0
  50. package/package/components/copilot/chat/flow/FlowAIChat.svelte +5 -3
  51. package/package/components/copilot/chat/flow/core.d.ts +2 -1
  52. package/package/components/copilot/chat/flow/core.js +48 -21
  53. package/package/components/copilot/chat/flow/helperUtils.d.ts +5 -2
  54. package/package/components/copilot/chat/flow/helperUtils.js +33 -1
  55. package/package/components/copilot/chat/flow/helperUtils.test.js +116 -1
  56. package/package/components/copilot/chat/flow/openFlow.json +1 -1
  57. package/package/components/copilot/chat/flow/openFlowZod.gen.js +24 -0
  58. package/package/components/copilot/chat/script/core.js +3 -0
  59. package/package/components/copilot/chat/shared.d.ts +6 -0
  60. package/package/components/copilot/chat/shared.js +22 -1
  61. package/package/components/copilot/chat/shared.test.d.ts +1 -0
  62. package/package/components/copilot/chat/shared.test.js +412 -0
  63. package/package/components/copilot/chat/workspaceTools.d.ts +7 -0
  64. package/package/components/copilot/chat/workspaceTools.js +239 -0
  65. package/package/components/copilot/chat/workspaceToolsZod.gen.d.ts +1295 -0
  66. package/package/components/copilot/chat/workspaceToolsZod.gen.js +424 -0
  67. package/package/components/copilot/lib.js +3 -1
  68. package/package/components/copilot/lib.test.d.ts +1 -0
  69. package/package/components/copilot/lib.test.js +19 -0
  70. package/package/components/copilot/modelConfig.d.ts +3 -0
  71. package/package/components/copilot/modelConfig.js +10 -0
  72. package/package/components/flows/FlowProgressBar.svelte +5 -2
  73. package/package/components/flows/content/FlowModuleComponent.svelte +636 -599
  74. package/package/components/flows/conversations/FlowChatManager.svelte.js +21 -10
  75. package/package/components/flows/flowStateUtils.svelte.js +5 -1
  76. package/package/components/flows/map/FlowModuleSchemaMap.svelte +3 -2
  77. package/package/components/flows/map/FlowModuleSchemaMap.svelte.d.ts +1 -0
  78. package/package/components/git_sync/GitSyncContext.svelte.js +0 -2
  79. package/package/components/graph/FlowGraphV2.svelte +7 -3
  80. package/package/components/graph/FlowGraphV2.svelte.d.ts +1 -0
  81. package/package/components/graph/renderers/triggers/TriggersBadge.svelte +3 -0
  82. package/package/components/home/deploy_ui.js +1 -1
  83. package/package/components/icons/AzureIcon.svelte +12 -25
  84. package/package/components/icons/AzureIcon.svelte.d.ts +3 -2
  85. package/package/components/instanceSettings.js +24 -0
  86. package/package/components/mcp/McpScopeSelector.svelte +119 -9
  87. package/package/components/mcp/McpScopeSelector.svelte.d.ts +1 -0
  88. package/package/components/offboarding-utils.js +2 -0
  89. package/package/components/progressBar/ProgressBar.svelte +9 -5
  90. package/package/components/progressBar/ProgressBar.svelte.d.ts +1 -0
  91. package/package/components/raw_apps/DeleteAfterUsePopup.svelte +52 -0
  92. package/package/components/raw_apps/DeleteAfterUsePopup.svelte.d.ts +9 -0
  93. package/package/components/raw_apps/RawAppBackgroundRunner.svelte +5 -1
  94. package/package/components/raw_apps/RawAppEditor.svelte +159 -102
  95. package/package/components/raw_apps/RawAppInlineScriptEditor.svelte +9 -3
  96. package/package/components/raw_apps/RawAppInlineScriptEditor.svelte.d.ts +2 -1
  97. package/package/components/raw_apps/RawAppInlineScriptRunnable.svelte +1 -0
  98. package/package/components/raw_apps/RawAppInlineScriptRunnable.svelte.d.ts +1 -0
  99. package/package/components/raw_apps/RawAppInputsSpecEditor.svelte +48 -5
  100. package/package/components/raw_apps/RawAppSharedUiDrawer.svelte +129 -0
  101. package/package/components/raw_apps/RawAppSharedUiDrawer.svelte.d.ts +5 -0
  102. package/package/components/raw_apps/RawAppSidebar.svelte +12 -0
  103. package/package/components/raw_apps/dataTableRefUtils.d.ts +7 -0
  104. package/package/components/raw_apps/dataTableRefUtils.js +34 -0
  105. package/package/components/raw_apps/dataTableRefUtils.test.d.ts +1 -0
  106. package/package/components/raw_apps/dataTableRefUtils.test.js +29 -0
  107. package/package/components/raw_apps/rawAppPolicy.d.ts +1 -0
  108. package/package/components/raw_apps/rawAppPolicy.js +17 -2
  109. package/package/components/resources/resourceTypesFilter.d.ts +19 -0
  110. package/package/components/resources/resourceTypesFilter.js +21 -0
  111. package/package/components/restartFromStepPath.d.ts +39 -0
  112. package/package/components/restartFromStepPath.js +89 -0
  113. package/package/components/runs/JobDetailFieldConfig.d.ts +1 -0
  114. package/package/components/runs/JobDetailFieldConfig.js +57 -10
  115. package/package/components/runs/JobDetailHeader.svelte +24 -3
  116. package/package/components/runs/runsFilter.d.ts +1 -1
  117. package/package/components/schema/FlowPropertyEditor.svelte +30 -1
  118. package/package/components/schema/FlowPropertyEditor.svelte.d.ts +5 -2
  119. package/package/components/search/GlobalSearchModal.svelte +8 -1
  120. package/package/components/select/Select.svelte +1 -1
  121. package/package/components/settings/CreateToken.svelte +48 -77
  122. package/package/components/settings/EditTokenScopesModal.svelte +57 -0
  123. package/package/components/settings/EditTokenScopesModal.svelte.d.ts +10 -0
  124. package/package/components/settings/ScopesPicker.svelte +43 -0
  125. package/package/components/settings/ScopesPicker.svelte.d.ts +11 -0
  126. package/package/components/settings/TokensTable.svelte +51 -15
  127. package/package/components/sidebar/OperatorMenu.svelte +6 -0
  128. package/package/components/sidebar/SidebarContent.svelte +11 -1
  129. package/package/components/triggers/AddTriggersButton.svelte +6 -0
  130. package/package/components/triggers/CaptureWrapper.svelte +19 -1
  131. package/package/components/triggers/TriggerEditorToolbar.svelte.d.ts +1 -1
  132. package/package/components/triggers/TriggerModeToggle.svelte +36 -7
  133. package/package/components/triggers/TriggerModeToggle.svelte.d.ts +1 -1
  134. package/package/components/triggers/TriggerSuspendedJobsModal.svelte.d.ts +1 -1
  135. package/package/components/triggers/TriggersEditor.svelte +5 -1
  136. package/package/components/triggers/TriggersWrapper.svelte +10 -0
  137. package/package/components/triggers/azure/AzureCapture.svelte +41 -0
  138. package/package/components/triggers/azure/AzureCapture.svelte.d.ts +44 -0
  139. package/package/components/triggers/azure/AzureTriggerEditor.svelte +20 -0
  140. package/package/components/triggers/azure/AzureTriggerEditor.svelte.d.ts +9 -0
  141. package/package/components/triggers/azure/AzureTriggerEditorConfigSection.svelte +301 -0
  142. package/package/components/triggers/azure/AzureTriggerEditorConfigSection.svelte.d.ts +16 -0
  143. package/package/components/triggers/azure/AzureTriggerEditorInner.svelte +422 -0
  144. package/package/components/triggers/azure/AzureTriggerEditorInner.svelte.d.ts +25 -0
  145. package/package/components/triggers/azure/AzureTriggerPanel.svelte +55 -0
  146. package/package/components/triggers/azure/AzureTriggerPanel.svelte.d.ts +10 -0
  147. package/{dist/sharedUtils/components/triggers/kafka → package/components/triggers/azure}/utils.d.ts +1 -1
  148. package/package/components/triggers/azure/utils.js +56 -0
  149. package/package/components/triggers/email/EmailTriggerEditorInner.svelte +2 -0
  150. package/package/components/triggers/gcp/GcpTriggerEditorInner.svelte +9 -3
  151. package/package/components/triggers/http/RouteEditorInner.svelte +2 -0
  152. package/package/components/triggers/kafka/KafkaTriggerEditorInner.svelte +9 -3
  153. package/package/components/triggers/mqtt/MqttTriggerEditorInner.svelte +9 -3
  154. package/package/components/triggers/nats/NatsTriggerEditorInner.svelte +9 -3
  155. package/package/components/triggers/postgres/PostgresTriggerEditorInner.svelte +9 -3
  156. package/package/components/triggers/schedules/ScheduleEditorInner.svelte +9 -3
  157. package/package/components/triggers/sqs/SqsTriggerEditorInner.svelte +9 -3
  158. package/package/components/triggers/triggers.svelte.d.ts +1 -0
  159. package/package/components/triggers/triggers.svelte.js +23 -1
  160. package/package/components/triggers/utils.js +20 -0
  161. package/package/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +9 -3
  162. package/package/components/triggers.d.ts +1 -1
  163. package/package/components/useNestedRestartState.svelte.d.ts +56 -0
  164. package/package/components/useNestedRestartState.svelte.js +320 -0
  165. package/package/components/workspaceSettings/SharedUiSettings.svelte +175 -0
  166. package/package/components/workspaceSettings/SharedUiSettings.svelte.d.ts +3 -0
  167. package/package/gen/core/OpenAPI.js +1 -1
  168. package/package/gen/schemas.gen.d.ts +294 -24
  169. package/package/gen/schemas.gen.js +297 -25
  170. package/package/gen/services.gen.d.ts +247 -4
  171. package/package/gen/services.gen.js +498 -7
  172. package/package/gen/types.gen.d.ts +990 -37
  173. package/package/hubPaths.json +2 -5
  174. package/package/infer.d.ts +1 -1
  175. package/package/infer.js +37 -51
  176. package/package/mcpEndpointTools.js +60 -4
  177. package/package/script_helpers.js +17 -0
  178. package/package/stores.d.ts +7 -0
  179. package/package/stores.js +6 -1
  180. package/package/system_prompts/index.d.ts +1 -0
  181. package/package/system_prompts/index.js +8 -0
  182. package/package/system_prompts/prompts.d.ts +16 -13
  183. package/package/system_prompts/prompts.js +653 -43
  184. package/package/templates/ci_test_bun.ts.template +8 -0
  185. package/package/templates/ci_test_python.py.template +8 -0
  186. package/package/utils/forkConflict.d.ts +26 -0
  187. package/package/utils/forkConflict.js +56 -0
  188. package/package/utils_deployable.d.ts +164 -121
  189. package/package/utils_deployable.js +61 -11
  190. package/package/utils_workspace_deploy.js +3 -1
  191. package/package.json +29 -5
  192. package/dist/sharedUtils/assets/tokens/colorTokensConfig.d.ts +0 -2
  193. package/dist/sharedUtils/base.d.ts +0 -1
  194. package/dist/sharedUtils/cloud.d.ts +0 -1
  195. package/dist/sharedUtils/common.d.ts +0 -111
  196. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/count.d.ts +0 -5
  197. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/delete.d.ts +0 -5
  198. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/insert.d.ts +0 -5
  199. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/select.d.ts +0 -13
  200. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/update.d.ts +0 -11
  201. package/dist/sharedUtils/components/apps/components/display/dbtable/utils.d.ts +0 -95
  202. package/dist/sharedUtils/components/apps/editor/appPolicy.d.ts +0 -6
  203. package/dist/sharedUtils/components/apps/editor/appUtilsCore.d.ts +0 -7
  204. package/dist/sharedUtils/components/apps/editor/appUtilsS3.d.ts +0 -33
  205. package/dist/sharedUtils/components/apps/editor/commonAppUtils.d.ts +0 -10
  206. package/dist/sharedUtils/components/apps/editor/component/components.d.ts +0 -5371
  207. package/dist/sharedUtils/components/apps/editor/component/default-codes.d.ts +0 -3
  208. package/dist/sharedUtils/components/apps/editor/component/index.d.ts +0 -3
  209. package/dist/sharedUtils/components/apps/editor/component/sets.d.ts +0 -7
  210. package/dist/sharedUtils/components/apps/editor/componentsPanel/componentDefaultProps.d.ts +0 -3
  211. package/dist/sharedUtils/components/apps/gridUtils.d.ts +0 -14
  212. package/dist/sharedUtils/components/apps/inputType.d.ts +0 -178
  213. package/dist/sharedUtils/components/apps/rx.d.ts +0 -29
  214. package/dist/sharedUtils/components/apps/sharedTypes.d.ts +0 -21
  215. package/dist/sharedUtils/components/apps/types.d.ts +0 -274
  216. package/dist/sharedUtils/components/assets/lib.d.ts +0 -25
  217. package/dist/sharedUtils/components/common/alert/model.d.ts +0 -2
  218. package/dist/sharedUtils/components/common/badge/model.d.ts +0 -8
  219. package/dist/sharedUtils/components/common/button/model.d.ts +0 -45
  220. package/dist/sharedUtils/components/common/fileInput/model.d.ts +0 -1
  221. package/dist/sharedUtils/components/common/index.d.ts +0 -24
  222. package/dist/sharedUtils/components/common/skeleton/model.d.ts +0 -21
  223. package/dist/sharedUtils/components/dbTypes.d.ts +0 -14
  224. package/dist/sharedUtils/components/diff_drawer.d.ts +0 -26
  225. package/dist/sharedUtils/components/ducklake.d.ts +0 -1
  226. package/dist/sharedUtils/components/flows/scheduleUtils.d.ts +0 -7
  227. package/dist/sharedUtils/components/icons/index.d.ts +0 -101
  228. package/dist/sharedUtils/components/random_positive_adjetive.d.ts +0 -1
  229. package/dist/sharedUtils/components/raw_apps/rawAppPolicy.d.ts +0 -10
  230. package/dist/sharedUtils/components/raw_apps/utils.d.ts +0 -15
  231. package/dist/sharedUtils/components/triggers/email/utils.d.ts +0 -4
  232. package/dist/sharedUtils/components/triggers/gcp/utils.d.ts +0 -2
  233. package/dist/sharedUtils/components/triggers/http/utils.d.ts +0 -11
  234. package/dist/sharedUtils/components/triggers/mqtt/utils.d.ts +0 -2
  235. package/dist/sharedUtils/components/triggers/nats/utils.d.ts +0 -2
  236. package/dist/sharedUtils/components/triggers/postgres/utils.d.ts +0 -8
  237. package/dist/sharedUtils/components/triggers/sqs/utils.d.ts +0 -2
  238. package/dist/sharedUtils/components/triggers/triggers.svelte.d.ts +0 -32
  239. package/dist/sharedUtils/components/triggers/utils.d.ts +0 -80
  240. package/dist/sharedUtils/components/triggers/websocket/utils.d.ts +0 -2
  241. package/dist/sharedUtils/components/triggers.d.ts +0 -20
  242. package/dist/sharedUtils/gen/core/ApiError.d.ts +0 -10
  243. package/dist/sharedUtils/gen/core/ApiRequestOptions.d.ts +0 -13
  244. package/dist/sharedUtils/gen/core/ApiResult.d.ts +0 -7
  245. package/dist/sharedUtils/gen/core/CancelablePromise.d.ts +0 -26
  246. package/dist/sharedUtils/gen/core/OpenAPI.d.ts +0 -27
  247. package/dist/sharedUtils/gen/core/request.d.ts +0 -29
  248. package/dist/sharedUtils/gen/index.d.ts +0 -6
  249. package/dist/sharedUtils/gen/schemas.gen.d.ts +0 -7036
  250. package/dist/sharedUtils/gen/services.gen.d.ts +0 -6047
  251. package/dist/sharedUtils/gen/types.gen.d.ts +0 -21881
  252. package/dist/sharedUtils/history.svelte.d.ts +0 -9
  253. package/dist/sharedUtils/hub.d.ts +0 -49
  254. package/dist/sharedUtils/jsr.json +0 -6
  255. package/dist/sharedUtils/lib.d.ts +0 -5
  256. package/dist/sharedUtils/lib.es.js +0 -1588
  257. package/dist/sharedUtils/package.json +0 -12
  258. package/dist/sharedUtils/schema.d.ts +0 -3
  259. package/dist/sharedUtils/stores.d.ts +0 -97
  260. package/dist/sharedUtils/svelte5Utils.svelte.d.ts +0 -80
  261. package/dist/sharedUtils/toast.d.ts +0 -8
  262. package/dist/sharedUtils/utils.d.ts +0 -265
  263. package/package/components/copilot/chat/flow/openFlowZod.js +0 -24
  264. /package/package/components/copilot/chat/flow/{openFlowZod.d.ts → openFlowZod.gen.d.ts} +0 -0
@@ -11,7 +11,7 @@ import FlowProgressBar from './flows/FlowProgressBar.svelte';
11
11
  import FlowExecutionStatus from './runs/FlowExecutionStatus.svelte';
12
12
  import JobDetailHeader from './runs/JobDetailHeader.svelte';
13
13
  import { AlertTriangle, Circle, CornerDownLeft, Download, Loader2, Play, RefreshCw, X } from 'lucide-svelte';
14
- import { emptyString, sendUserToast } from '../utils';
14
+ import { sendUserToast } from '../utils';
15
15
  import { dfs } from './flows/dfs';
16
16
  import { sliceModules } from './flows/flowStateUtils.svelte';
17
17
  import InputSelectedBadge from './schema/InputSelectedBadge.svelte';
@@ -22,9 +22,26 @@ import { getStepHistoryLoaderContext } from './stepHistoryLoader.svelte';
22
22
  import FlowChat from './flows/conversations/FlowChat.svelte';
23
23
  import { stateSnapshot } from '../svelte5Utils.svelte';
24
24
  import FlowRestartButton from './FlowRestartButton.svelte';
25
+ import { useNestedRestartState } from './useNestedRestartState.svelte';
25
26
  import { createFlowRecording, setActiveRecording } from './recording/flowRecording.svelte';
26
27
  let { previewMode = $bindable(), open, preventEscape = $bindable(false), jobId = $bindable(undefined), job = $bindable(undefined), initial = $bindable(false), selectedJobStep = $bindable(undefined), selectedJobStepIsTopLevel = $bindable(undefined), selectedJobStepType = $bindable('single'), rightColumnSelect = $bindable('timeline'), branchOrIterationN = $bindable(0), scrollTop = $bindable(0), localModuleStates = $bindable({}), localDurationStatuses = $bindable({}), onRunPreview, render = false, onJobDone, upToId = undefined, suspendStatus } = $props();
27
- let restartBranchNames = [];
28
+ let previewExpandedSubflows = $state({});
29
+ const restart = useNestedRestartState({
30
+ selectedJobStep: () => selectedJobStep,
31
+ job: () => job,
32
+ graphModuleStates: () => localModuleStates,
33
+ expandedSubflows: () => previewExpandedSubflows
34
+ });
35
+ // `selectedJobStepType` and `selectedJobStepIsTopLevel` are still exposed as
36
+ // `$bindable` props to legacy parents (FlowPreviewButtons binds them but
37
+ // doesn't read them). Sync them out of the composable here so the prop
38
+ // surface stays unchanged.
39
+ $effect(() => {
40
+ selectedJobStepIsTopLevel = restart.selectedJobStepIsTopLevel;
41
+ });
42
+ $effect(() => {
43
+ selectedJobStepType = restart.selectedJobStepType;
44
+ });
28
45
  let isRunning = $state(false);
29
46
  let jsonView = $state(false);
30
47
  let jsonEditor = $state(undefined);
@@ -118,28 +135,6 @@ function onKeyDown(event) {
118
135
  }
119
136
  }
120
137
  }
121
- function onSelectedJobStepChange() {
122
- if (selectedJobStep !== undefined && job?.flow_status?.modules !== undefined) {
123
- selectedJobStepIsTopLevel =
124
- job?.flow_status?.modules.map((m) => m.id).indexOf(selectedJobStep) >= 0;
125
- let moduleDefinition = job?.raw_flow?.modules.find((m) => m.id == selectedJobStep);
126
- if (moduleDefinition?.value.type == 'forloopflow') {
127
- selectedJobStepType = 'forloop';
128
- }
129
- else if (moduleDefinition?.value.type == 'branchall') {
130
- selectedJobStepType = 'branchall';
131
- moduleDefinition?.value.branches.forEach((branch, idx) => {
132
- restartBranchNames.push([
133
- idx,
134
- emptyString(branch.summary) ? `Branch #${idx}` : branch.summary
135
- ]);
136
- });
137
- }
138
- else {
139
- selectedJobStepType = 'single';
140
- }
141
- }
142
- }
143
138
  let savedArgs = $state(previewArgs.val);
144
139
  let inputSelected = $state(undefined);
145
140
  async function selectInput(input, type) {
@@ -219,9 +214,6 @@ function onScrollableDivChange() {
219
214
  scrollableDiv.scrollTop = scrollTop;
220
215
  }
221
216
  }
222
- $effect.pre(() => {
223
- selectedJobStep !== undefined && untrack(() => onSelectedJobStepChange());
224
- });
225
217
  $effect(() => {
226
218
  scrollableDiv && render && untrack(() => onScrollableDivChange());
227
219
  });
@@ -316,12 +308,21 @@ export function flowHasChanged() {
316
308
  </div>
317
309
  {:else}
318
310
  <div class="grow justify-center flex flex-row gap-2 items-center">
319
- {#if jobId !== undefined && selectedJobStep !== undefined && selectedJobStepIsTopLevel}
311
+ {#if jobId !== undefined && selectedJobStep !== undefined && restart.topLevelRestartable}
312
+ <!--
313
+ Nested-step restart is intentionally not surfaced from the editor
314
+ preview: the chain needs `flow_job_id` resolved at every level,
315
+ which only the backend can do (by walking the original
316
+ execution). Users who want nested restart should use the run
317
+ page, which goes through the dedicated restart API.
318
+ -->
320
319
  <FlowRestartButton
321
320
  {jobId}
322
321
  {selectedJobStep}
323
- {selectedJobStepType}
324
- {restartBranchNames}
322
+ selectedJobStepType={restart.selectedJobStepType}
323
+ restartBranchNames={restart.restartBranchNames}
324
+ presetIterationN={restart.topLevelLoopIteration}
325
+ iterationCounts={restart.iterationCounts}
325
326
  onRestart={(stepId, branchOrIterationN) => {
326
327
  runPreview(previewArgs.val, {
327
328
  flow_job_id: jobId,
@@ -544,6 +545,7 @@ export function flowHasChanged() {
544
545
  <FlowStatusViewer
545
546
  bind:job
546
547
  bind:localModuleStates
548
+ bind:expandedSubflows={previewExpandedSubflows}
547
549
  bind:localDurationStatuses
548
550
  {suspendStatus}
549
551
  hideDownloadInGraph={customUi?.downloadLogs === false}
@@ -4,24 +4,82 @@ import { Play, RefreshCw } from 'lucide-svelte';
4
4
  import { FlowService, JobService } from '../gen';
5
5
  import { workspaceStore } from '../stores';
6
6
  import { emptyString, sendUserToast } from '../utils';
7
- let { jobId, selectedJobStep, selectedJobStepType, restartBranchNames = [], flowPath = undefined, flowVersionId = undefined, disabled = false, enterpriseOnly = false, variant = 'default', unifiedSize = 'md', onRestart, onRestartComplete } = $props();
7
+ let { jobId, selectedJobStep, selectedJobStepType, restartBranchNames = [], flowPath = undefined, flowVersionId = undefined, disabled = false, enterpriseOnly = false, variant = 'default', unifiedSize = 'md', nestedPath = undefined, nestedTopStepId = undefined, nestedTopBranchOrIterationN = undefined, presetIterationN = undefined, iterationCounts = undefined, nestedPathIterationCounts = undefined, onRestart, onRestartComplete } = $props();
8
+ const isNested = $derived((nestedPath?.length ?? 0) > 0);
9
+ // Inline-expanded subflow steps come in as `subflow:A:B:leaf` — show only `leaf`.
10
+ const displayStepId = $derived(isNested && nestedPath && nestedPath.length > 0
11
+ ? nestedPath[nestedPath.length - 1].step_id
12
+ : selectedJobStep);
8
13
  // Sentinel value meaning "use the same version as the original run" (backend receives undefined)
9
14
  const RUN_VERSION_SENTINEL = -1;
10
15
  let branchOrIterationN = $state(0);
16
+ let iterationEdits = $state({});
17
+ function resetEditsFromProps() {
18
+ const next = {};
19
+ if (isNested && nestedTopBranchOrIterationN !== undefined) {
20
+ next['top'] = nestedTopBranchOrIterationN;
21
+ }
22
+ if (isNested) {
23
+ nestedPath?.forEach((entry, i) => {
24
+ if (entry.branch_or_iteration_n !== undefined) {
25
+ next[`inner-${i}`] = entry.branch_or_iteration_n;
26
+ }
27
+ });
28
+ }
29
+ iterationEdits = next;
30
+ if (selectedJobStepType === 'forloop' && presetIterationN !== undefined) {
31
+ branchOrIterationN = presetIterationN;
32
+ }
33
+ }
34
+ const iterationFields = $derived.by(() => {
35
+ if (!isNested)
36
+ return [];
37
+ const out = [];
38
+ if (nestedTopBranchOrIterationN !== undefined && nestedTopStepId) {
39
+ out.push({
40
+ key: 'top',
41
+ label: nestedTopStepId,
42
+ value: iterationEdits['top'] ?? nestedTopBranchOrIterationN
43
+ });
44
+ }
45
+ nestedPath?.forEach((entry, i) => {
46
+ if (entry.branch_or_iteration_n !== undefined) {
47
+ const k = `inner-${i}`;
48
+ out.push({
49
+ key: k,
50
+ label: entry.step_id,
51
+ value: iterationEdits[k] ?? entry.branch_or_iteration_n
52
+ });
53
+ }
54
+ });
55
+ return out;
56
+ });
57
+ const needsPopup = $derived(!!flowPath || iterationFields.length > 0 || selectedJobStepType !== 'single');
11
58
  let selectedFlowVersion = $state(RUN_VERSION_SENTINEL);
12
59
  let flowVersions = $state([]);
13
60
  let loadingVersions = $state(false);
14
61
  let versionsLoaded = $state(false);
15
62
  let runVersionInList = $state(false);
16
63
  async function restartFlow(stepId, branchOrIterationN, flowVersion) {
17
- let run = await JobService.restartFlowAtStep({
18
- workspace: $workspaceStore,
19
- id: jobId,
20
- requestBody: {
64
+ const requestBody = isNested
65
+ ? {
66
+ step_id: nestedTopStepId,
67
+ branch_or_iteration_n: iterationEdits['top'] ?? nestedTopBranchOrIterationN,
68
+ flow_version: flowVersion,
69
+ nested_path: nestedPath?.map((entry, i) => ({
70
+ step_id: entry.step_id,
71
+ branch_or_iteration_n: iterationEdits[`inner-${i}`] ?? entry.branch_or_iteration_n
72
+ }))
73
+ }
74
+ : {
21
75
  step_id: stepId,
22
76
  branch_or_iteration_n: branchOrIterationN,
23
77
  flow_version: flowVersion
24
- }
78
+ };
79
+ let run = await JobService.restartFlowAtStep({
80
+ workspace: $workspaceStore,
81
+ id: jobId,
82
+ requestBody
25
83
  });
26
84
  onRestartComplete?.(run);
27
85
  }
@@ -94,23 +152,23 @@ function handleRestart() {
94
152
  {/if}
95
153
  </label>
96
154
  {/snippet}
97
- {#snippet singleRestartButton()}
155
+ {#snippet restartTriggerButton(usePlayIcon: boolean)}
98
156
  <Button
99
- title={`Re-start this flow from step ${selectedJobStep} (included).${enterpriseOnly ? ' This is a feature only available in enterprise edition.' : ''}`}
157
+ title={`Re-start this flow from step ${displayStepId} (included).${enterpriseOnly ? ' This is a feature only available in enterprise edition.' : ''}`}
100
158
  {variant}
101
159
  {unifiedSize}
102
160
  {disabled}
103
- startIcon={{ icon: Play }}
104
- nonCaptureEvent={!!flowPath}
161
+ startIcon={{ icon: usePlayIcon ? Play : RefreshCw }}
162
+ nonCaptureEvent={!usePlayIcon || !!flowPath}
105
163
  onClick={() => {
106
- if (!flowPath) {
164
+ if (usePlayIcon && !flowPath) {
107
165
  handleRestart()
108
166
  }
109
167
  }}
110
168
  >
111
169
  Re-start from
112
170
  <Badge baseClass="ml-1" color="indigo">
113
- {selectedJobStep}
171
+ {displayStepId}
114
172
  </Badge>
115
173
  {#if enterpriseOnly && disabled}
116
174
  (EE)
@@ -118,68 +176,92 @@ function handleRestart() {
118
176
  </Button>
119
177
  {/snippet}
120
178
 
121
- {#if selectedJobStepType === 'single'}
122
- {#if !flowPath}
123
- {@render singleRestartButton()}
124
- {:else}
125
- <Popover
126
- floatingConfig={{ strategy: 'absolute', placement: 'bottom-start' }}
127
- disablePopup={!flowPath}
128
- on:openChange={(e) => { if (e.detail) loadFlowVersions() }}
129
- >
130
- {#snippet trigger()}
131
- {@render singleRestartButton()}
132
- {/snippet}
133
- {#snippet content()}
134
- <div class="flex flex-col gap-4 text-primary p-4 w-80">
135
- {@render flowVersionSelector()}
136
-
137
- <Button variant="accent" onClick={handleRestart}>Restart</Button>
138
- </div>
139
- {/snippet}
140
- </Popover>
141
- {/if}
179
+ {#if !needsPopup}
180
+ {@render restartTriggerButton(true)}
142
181
  {:else}
143
182
  <Popover
144
183
  floatingConfig={{ strategy: 'absolute', placement: 'bottom-start' }}
145
- on:openChange={(e) => { if (e.detail) loadFlowVersions() }}
184
+ disablePopup={!needsPopup}
185
+ on:openChange={(e) => {
186
+ if (e.detail) {
187
+ loadFlowVersions()
188
+ resetEditsFromProps()
189
+ }
190
+ }}
146
191
  >
147
192
  {#snippet trigger()}
148
- <Button
149
- title={`Re-start this flow from step ${selectedJobStep} (included).${enterpriseOnly ? ' This is a feature only available in enterprise edition.' : ''}`}
150
- {variant}
151
- {unifiedSize}
152
- {disabled}
153
- startIcon={{ icon: RefreshCw }}
154
- nonCaptureEvent={true}
155
- >
156
- Re-start from
157
- <Badge baseClass="ml-1" color="indigo">
158
- {selectedJobStep}
159
- </Badge>
160
- {#if enterpriseOnly && disabled}
161
- (EE)
162
- {/if}
163
- </Button>
193
+ {@render restartTriggerButton(selectedJobStepType === 'single')}
164
194
  {/snippet}
165
195
  {#snippet content()}
166
196
  <div class="flex flex-col gap-4 text-primary p-4 w-80">
167
- <label>
168
- <div class="pb-1 text-xs font-semibold text-emphasis"
169
- >{selectedJobStepType == 'forloop' ? 'From iteration #' : 'From branch'}</div
170
- >
171
- <div class="flex w-full gap-2">
172
- {#if selectedJobStepType === 'forloop'}
173
- <input type="number" min="0" bind:value={branchOrIterationN} class="!w-32 grow" />
174
- {:else}
197
+ {#if selectedJobStepType === 'forloop'}
198
+ {@const topCount = iterationCounts?.[selectedJobStep] ?? 0}
199
+ <label>
200
+ <div class="pb-1 text-xs font-semibold text-emphasis">From iteration #</div>
201
+ <div class="flex w-full gap-2">
202
+ {#if topCount > 0}
203
+ <select bind:value={branchOrIterationN} class="!w-32 grow">
204
+ {#each Array.from({ length: topCount }, (_, i) => i) as i (i)}
205
+ <option value={i}>{i + 1}</option>
206
+ {/each}
207
+ </select>
208
+ {:else}
209
+ <input type="number" min="0" bind:value={branchOrIterationN} class="!w-32 grow" />
210
+ {/if}
211
+ </div>
212
+ </label>
213
+ {:else if selectedJobStepType === 'branchall'}
214
+ <label>
215
+ <div class="pb-1 text-xs font-semibold text-emphasis">From branch</div>
216
+ <div class="flex w-full gap-2">
175
217
  <select bind:value={branchOrIterationN} class="!w-32 grow">
176
218
  {#each restartBranchNames as [branchIdx, branchName] (branchIdx)}
177
219
  <option value={branchIdx}>{branchName}</option>
178
220
  {/each}
179
221
  </select>
180
- {/if}
181
- </div>
182
- </label>
222
+ </div>
223
+ </label>
224
+ {/if}
225
+
226
+ {#each iterationFields as field (field.key)}
227
+ {@const count = nestedPathIterationCounts?.[field.key] ?? 0}
228
+ <label>
229
+ <div class="pb-1 text-xs font-semibold text-emphasis">
230
+ From iteration # of <code class="text-xs">{field.label}</code>
231
+ </div>
232
+ <div class="flex w-full gap-2">
233
+ {#if count > 0}
234
+ <select
235
+ value={field.value}
236
+ onchange={(e) => {
237
+ const v = parseInt((e.target as HTMLSelectElement).value, 10)
238
+ if (!Number.isNaN(v)) {
239
+ iterationEdits = { ...iterationEdits, [field.key]: v }
240
+ }
241
+ }}
242
+ class="!w-32 grow"
243
+ >
244
+ {#each Array.from({ length: count }, (_, i) => i) as i (i)}
245
+ <option value={i}>{i + 1}</option>
246
+ {/each}
247
+ </select>
248
+ {:else}
249
+ <input
250
+ type="number"
251
+ min="0"
252
+ value={field.value}
253
+ oninput={(e) => {
254
+ const v = parseInt((e.target as HTMLInputElement).value, 10)
255
+ if (!Number.isNaN(v)) {
256
+ iterationEdits = { ...iterationEdits, [field.key]: v }
257
+ }
258
+ }}
259
+ class="!w-32 grow"
260
+ />
261
+ {/if}
262
+ </div>
263
+ </label>
264
+ {/each}
183
265
 
184
266
  {#if flowPath}
185
267
  {@render flowVersionSelector()}
@@ -10,6 +10,43 @@ interface Props {
10
10
  enterpriseOnly?: boolean;
11
11
  variant?: 'default' | 'accent';
12
12
  unifiedSize?: 'xs' | 'sm' | 'md' | 'lg';
13
+ /**
14
+ * For nested-step restarts: path of ancestor containers from the top-level
15
+ * step down to the leaf. When provided, the LAST entry's step_id is the
16
+ * actual restart point and `selectedJobStep` (the leaf shown in the UI) is
17
+ * informational only — the request is built from `nestedTopStepId`,
18
+ * `nestedTopBranchOrIterationN` and `nestedPath`.
19
+ */
20
+ nestedPath?: Array<{
21
+ step_id: string;
22
+ branch_or_iteration_n?: number;
23
+ }>;
24
+ /** Top-level container step id (only used when nestedPath is set) */
25
+ nestedTopStepId?: string;
26
+ /** Top-level container branch_or_iteration_n (only used when nestedPath is set) */
27
+ nestedTopBranchOrIterationN?: number;
28
+ /**
29
+ * For top-level ForLoop restart: iteration index the user is currently
30
+ * viewing in the graph (read from `selectedForloopIndex` upstream). When
31
+ * provided, the popup pre-fills the iteration selector with this value.
32
+ */
33
+ presetIterationN?: number;
34
+ /**
35
+ * Map from ForLoop step id to the number of iterations that ran in the
36
+ * original execution (i.e. `flow_jobs.length`). When provided for a step,
37
+ * the popup renders a `<select>` of `0..count-1` instead of a free-form
38
+ * number input — same surface as the graph's iteration tabs. Used for the
39
+ * SELECTED step's iteration picker when it is itself a top-level ForLoop.
40
+ */
41
+ iterationCounts?: Record<string, number>;
42
+ /**
43
+ * Iteration counts for nested-path entries, keyed by the popup's field-key
44
+ * (`'top'` for the outer container, `'inner-N'` for nested ancestors).
45
+ * Populated by the composable so each entry uses the *correct* graph-state
46
+ * key (prefixed for in-subflow ancestors), avoiding collisions when the
47
+ * same step id appears at multiple nesting levels.
48
+ */
49
+ nestedPathIterationCounts?: Record<string, number>;
13
50
  /** Called when flow is restarted. If not provided, will navigate to the new run using goto (requires SvelteKit) */
14
51
  onRestart?: (stepId: string, branchOrIterationN: number, flowVersion?: number) => void;
15
52
  /** Called when flow restart completes with the new job ID. Used for navigation in non-SvelteKit contexts */
@@ -2,7 +2,16 @@
2
2
  import { setContext, untrack } from 'svelte';
3
3
  import { isOwner as loadIsOwner } from '../utils';
4
4
  import { userStore, workspaceStore } from '../stores';
5
- let { jobId, initialJob = undefined, workspaceId = undefined, flowState = $bindable({}), selectedJobStep = $bindable(undefined), hideFlowResult = false, hideTimeline = false, hideDownloadInGraph = false, hideNodeDefinition = false, hideJobId = false, hideDownloadLogs = false, rightColumnSelect = $bindable('timeline'), isOwner = $bindable(false), wideResults = false, localModuleStates = $bindable({}), localDurationStatuses = $bindable({}), job = $bindable(undefined), render = true, suspendStatus = $bindable({ val: {} }), customUi, onStart, onJobsLoaded, onDone, showLogsWithResult = false, notes: notesProp = undefined, groups: groupsProp = undefined } = $props();
5
+ let { jobId, initialJob = undefined, workspaceId = undefined, flowState = $bindable({}), selectedJobStep = $bindable(undefined), hideFlowResult = false, hideTimeline = false, hideDownloadInGraph = false, hideNodeDefinition = false, hideJobId = false, hideDownloadLogs = false, rightColumnSelect = $bindable('timeline'), isOwner = $bindable(false), wideResults = false, localModuleStates = $bindable({}),
6
+ // `$bindable({})` is the right shape for this prop despite CLAUDE.md's
7
+ // general guidance against `$bindable(default_value)`: that rule targets
8
+ // props where `undefined` has distinct semantics from "empty default".
9
+ // For a Map-typed cache populated by the inner component, callers that
10
+ // don't bind it should still see a usable empty map (the inner writes to
11
+ // it when the user expands a subflow). Without the `{}` default, those
12
+ // callers would crash at write time. Same reasoning applies to the
13
+ // pre-existing `localModuleStates`/`localDurationStatuses` bindables.
14
+ expandedSubflows = $bindable({}), localDurationStatuses = $bindable({}), job = $bindable(undefined), render = true, suspendStatus = $bindable({ val: {} }), customUi, onStart, onJobsLoaded, onDone, showLogsWithResult = false, notes: notesProp = undefined, groups: groupsProp = undefined } = $props();
6
15
  let lastJobId = untrack(() => jobId);
7
16
  let retryStatus = $state({ val: {} });
8
17
  let globalRefreshes = $state({});
@@ -30,6 +39,10 @@ async function updateJobId() {
30
39
  delete flowState[key];
31
40
  localDurationStatuses = {};
32
41
  localModuleStates = {};
42
+ // Reset the subflow definition cache too — a new run can reference
43
+ // different subflow versions; stale entries would confuse nested
44
+ // restart path-finding.
45
+ expandedSubflows = {};
33
46
  }
34
47
  }
35
48
  let lastScriptPath = $state(undefined);
@@ -61,6 +74,7 @@ let toolCallIndicesToLoad = $state([]);
61
74
  }}
62
75
  globalModuleStates={[]}
63
76
  bind:localModuleStates
77
+ bind:expandedSubflows
64
78
  bind:selectedNode={selectedJobStep}
65
79
  bind:localDurationStatuses
66
80
  {onStart}
@@ -1,7 +1,7 @@
1
1
  import type { FlowState } from './flows/flowState';
2
2
  import type { DurationStatus, GraphModuleState } from './graph';
3
3
  import { type StateStore } from '../utils';
4
- import type { CompletedJob, FlowNote, FlowValue, Job } from '../gen';
4
+ import type { CompletedJob, FlowModule, FlowNote, FlowValue, Job } from '../gen';
5
5
  interface Props {
6
6
  jobId: string;
7
7
  initialJob?: Job | undefined;
@@ -18,6 +18,14 @@ interface Props {
18
18
  isOwner?: boolean;
19
19
  wideResults?: boolean;
20
20
  localModuleStates?: Record<string, GraphModuleState>;
21
+ /** Cached subflow definitions, keyed by graph node id (subflow step id, with
22
+ * `subflow:` prefix for nested subflows). Populated as the user expands a
23
+ * subflow in the graph. Bindable so callers can use it to walk inside
24
+ * subflows (e.g. for nested-restart path-finding). */
25
+ expandedSubflows?: Record<string, {
26
+ modules: FlowModule[];
27
+ groups?: any[];
28
+ }>;
21
29
  localDurationStatuses?: Record<string, DurationStatus>;
22
30
  job?: Job | undefined;
23
31
  render?: boolean;
@@ -40,6 +48,6 @@ interface Props {
40
48
  notes?: FlowNote[];
41
49
  groups?: FlowValue['groups'];
42
50
  }
43
- declare const FlowStatusViewer: import("svelte").Component<Props, {}, "job" | "isOwner" | "suspendStatus" | "localModuleStates" | "localDurationStatuses" | "flowState" | "rightColumnSelect" | "selectedJobStep">;
51
+ declare const FlowStatusViewer: import("svelte").Component<Props, {}, "job" | "isOwner" | "suspendStatus" | "expandedSubflows" | "localModuleStates" | "localDurationStatuses" | "flowState" | "rightColumnSelect" | "selectedJobStep">;
44
52
  type FlowStatusViewer = ReturnType<typeof FlowStatusViewer>;
45
53
  export default FlowStatusViewer;
@@ -38,7 +38,7 @@ import { useThrottle } from 'runed';
38
38
  import { Splitpanes, Pane } from 'svelte-splitpanes';
39
39
  import { getActiveReplay } from './recording/flowRecording.svelte';
40
40
  let { flowState: flowStateStore, retryStatus, suspendStatus, hideDownloadInGraph, hideTimeline, hideNodeDefinition, hideDownloadLogs } = getContext('FlowStatusViewer');
41
- let { jobId, initialJob = undefined, workspaceId = undefined, flowJobIds = undefined, innerModule = undefined, render = true, selectedNode = $bindable(undefined), globalModuleStates, globalDurationStatuses = [], updateRecursiveRefreshFn = undefined, isSelectedBranch = true, isSubflow = false, reducedPolling = false, wideResults = false, workspace = $workspaceStore, prefix = undefined, topModuleStates = undefined, refreshGlobal, updateGlobalRefresh, subflowParentsGlobalModuleStates = [], subflowParentsDurationStatuses = [], isForloopSelected = false, job = $bindable(undefined), rightColumnSelect = $bindable('timeline'), localModuleStates = $bindable({}), localDurationStatuses = $bindable({}), customUi, onResultStreamUpdate = undefined, graphTabOpen, isNodeSelected, loadExtraLogs = undefined, onStart = undefined, onJobsLoaded = undefined, onDone = undefined, toolCallStore, showLogsWithResult = false, showJobDetailHeader = false, hideFlowResult = false, notes: notesProp = undefined, groups: groupsProp = undefined } = $props();
41
+ let { jobId, initialJob = undefined, workspaceId = undefined, flowJobIds = undefined, innerModule = undefined, render = true, selectedNode = $bindable(undefined), globalModuleStates, globalDurationStatuses = [], updateRecursiveRefreshFn = undefined, isSelectedBranch = true, isSubflow = false, reducedPolling = false, wideResults = false, workspace = $workspaceStore, prefix = undefined, topModuleStates = undefined, refreshGlobal, updateGlobalRefresh, subflowParentsGlobalModuleStates = [], subflowParentsDurationStatuses = [], isForloopSelected = false, job = $bindable(undefined), rightColumnSelect = $bindable('timeline'), localModuleStates = $bindable({}), expandedSubflows = $bindable({}), localDurationStatuses = $bindable({}), customUi, onResultStreamUpdate = undefined, graphTabOpen, isNodeSelected, loadExtraLogs = undefined, onStart = undefined, onJobsLoaded = undefined, onDone = undefined, toolCallStore, showLogsWithResult = false, showJobDetailHeader = false, hideFlowResult = false, notes: notesProp = undefined, groups: groupsProp = undefined } = $props();
42
42
  let getTopModuleStates = $derived(topModuleStates ?? localModuleStates);
43
43
  // Cache replay check to avoid repeated function calls in the template
44
44
  let isReplay = $derived(!!getActiveReplay());
@@ -99,7 +99,6 @@ function getStepResults(x) {
99
99
  }
100
100
  let retry_selected = $state('');
101
101
  let timeout = undefined;
102
- let expandedSubflows = $state({});
103
102
  let selectionManager = new SelectionManager();
104
103
  function onFlowModuleId() {
105
104
  let modId = flowJobIds?.moduleId;
@@ -1,5 +1,5 @@
1
1
  import FlowStatusViewerInner from './FlowStatusViewerInner.svelte';
2
- import { type Job, type FlowModuleValue, type CompletedJob, type FlowNote, type FlowValue } from '../gen';
2
+ import { type Job, type FlowModuleValue, type FlowModule, type CompletedJob, type FlowNote, type FlowValue } from '../gen';
3
3
  import { type DurationStatus, type GraphModuleState } from './graph';
4
4
  interface Props {
5
5
  jobId: string;
@@ -39,6 +39,10 @@ interface Props {
39
39
  }) | undefined;
40
40
  rightColumnSelect?: 'timeline' | 'node_status' | 'node_definition' | 'user_states' | 'tracing';
41
41
  localModuleStates?: Record<string, GraphModuleState>;
42
+ expandedSubflows?: Record<string, {
43
+ modules: FlowModule[];
44
+ groups?: any[];
45
+ }>;
42
46
  localDurationStatuses?: Record<string, DurationStatus>;
43
47
  onResultStreamUpdate?: ({ jobId, result_stream }: {
44
48
  jobId: string;
@@ -75,6 +79,6 @@ export type FlowModuleForTimeline = {
75
79
  id: string;
76
80
  type: FlowModuleValue['type'];
77
81
  };
78
- declare const FlowStatusViewerInner: import("svelte").Component<Props, {}, "job" | "localModuleStates" | "localDurationStatuses" | "selectedNode" | "rightColumnSelect">;
82
+ declare const FlowStatusViewerInner: import("svelte").Component<Props, {}, "job" | "expandedSubflows" | "localModuleStates" | "localDurationStatuses" | "selectedNode" | "rightColumnSelect">;
79
83
  type FlowStatusViewerInner = ReturnType<typeof FlowStatusViewerInner>;
80
84
  export default FlowStatusViewerInner;
@@ -0,0 +1,57 @@
1
+ <script lang="ts">import ConfirmationModal from './common/confirmationModal/ConfirmationModal.svelte';
2
+ import { forkConflictModal } from '../stores';
3
+ const state = $derived(forkConflictModal.val);
4
+ // Three failure shapes we want to describe accurately:
5
+ // - split-events: shared upstream subscription/queue, two listeners halve each other's traffic
6
+ // - duplicate-firing: independent triggers on shared input, every event fires twice
7
+ // - slot-takeover: PG replication slot is exclusive AND its position is destructively shared
8
+ const SPLIT_KINDS = new Set(['kafka', 'nats', 'mqtt', 'sqs', 'gcp', 'azure']);
9
+ const DUPLICATE_KINDS = new Set(['websocket', 'schedule']);
10
+ const family = $derived(!state
11
+ ? 'unknown'
12
+ : state.kind === 'postgres'
13
+ ? 'slot'
14
+ : SPLIT_KINDS.has(state.kind)
15
+ ? 'split'
16
+ : DUPLICATE_KINDS.has(state.kind)
17
+ ? 'duplicate'
18
+ : 'unknown');
19
+ function close(confirmed) {
20
+ const resolve = state?.resolve;
21
+ forkConflictModal.val = undefined;
22
+ resolve?.(confirmed);
23
+ }
24
+ </script>
25
+
26
+ <ConfirmationModal
27
+ open={!!state}
28
+ title="Enable in fork conflicts with parent"
29
+ confirmationText="Enable anyway"
30
+ onConfirmed={() => close(true)}
31
+ onCanceled={() => close(false)}
32
+ >
33
+ {#if state}
34
+ <p>
35
+ The parent workspace (<span class="font-mono">{state.parentWorkspaceId}</span>) has the same
36
+ {state.kindLabel} configured at this path. Because this fork's row was cloned from it, the upstream
37
+ identifier is shared.
38
+ </p>
39
+ <p class="mt-2">
40
+ {#if family === 'split'}
41
+ If both are enabled, the two listeners will compete on the same upstream and each side will
42
+ receive only a fraction of its events.
43
+ {:else if family === 'duplicate'}
44
+ If both are enabled, every event will fire the script twice — once in the fork and once in
45
+ the parent.
46
+ {:else if family === 'slot'}
47
+ The cloned <span class="font-mono">replication_slot_name</span> points at the same Postgres slot,
48
+ which only allows one consumer at a time. Enabling here will either fail with "slot already active"
49
+ if the parent is enabled, or hijack the slot's WAL position if it isn't — causing the parent
50
+ to lose events when re-enabled.
51
+ {:else}
52
+ Enabling it here may compete for the same upstream events or duplicate side effects.
53
+ {/if}
54
+ </p>
55
+ <p class="mt-2">Enable in this fork anyway?</p>
56
+ {/if}
57
+ </ConfirmationModal>
@@ -0,0 +1,3 @@
1
+ declare const ForkConflictModal: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ForkConflictModal = ReturnType<typeof ForkConflictModal>;
3
+ export default ForkConflictModal;