windmill-components 1.532.0 → 1.537.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 (135) hide show
  1. package/package/components/ArgInput.svelte +25 -18
  2. package/package/components/Auth0Setting.svelte +8 -3
  3. package/package/components/Dev.svelte +5 -4
  4. package/package/components/DiffDrawer.svelte +2 -2
  5. package/package/components/DiffEditor.svelte +34 -37
  6. package/package/components/DiffEditor.svelte.d.ts +23 -39
  7. package/package/components/EditableSchemaForm.svelte +42 -51
  8. package/package/components/EditableSchemaForm.svelte.d.ts +2 -3
  9. package/package/components/Editor.svelte +30 -9
  10. package/package/components/Editor.svelte.d.ts +5 -0
  11. package/package/components/FlowBuilder.svelte +7 -4
  12. package/package/components/FlowPreviewContent.svelte +3 -3
  13. package/package/components/FlowStatusViewer.svelte +28 -0
  14. package/package/components/FlowStatusViewerInner.svelte +72 -20
  15. package/package/components/FlowStatusViewerInner.svelte.d.ts +7 -0
  16. package/package/components/ModulePreview.svelte +2 -1
  17. package/package/components/ModulePreview.svelte.d.ts +1 -0
  18. package/package/components/ModulePreviewForm.svelte +72 -65
  19. package/package/components/ModulePreviewResultViewer.svelte +13 -18
  20. package/package/components/ModuleTest.svelte +6 -5
  21. package/package/components/ModuleTest.svelte.d.ts +1 -0
  22. package/package/components/OktaSetting.svelte +8 -3
  23. package/package/components/Portal.svelte +11 -7
  24. package/package/components/Portal.svelte.d.ts +19 -39
  25. package/package/components/RunForm.svelte +2 -2
  26. package/package/components/RunForm.svelte.d.ts +1 -1
  27. package/package/components/RunFormAdvancedPopup.svelte +13 -1
  28. package/package/components/SchemaForm.svelte +1 -2
  29. package/package/components/ScriptBuilder.svelte +1 -1
  30. package/package/components/ScriptEditor.svelte +21 -7
  31. package/package/components/SimpleEditor.svelte +0 -1
  32. package/package/components/apps/components/layout/AppModal.svelte +2 -2
  33. package/package/components/apps/editor/component/ComponentNavigation.svelte +3 -2
  34. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte +1 -1
  35. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptRunnableByPath.svelte +0 -1
  36. package/package/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte +3 -1
  37. package/package/components/apps/editor/settingsPanel/GridCondition.svelte +3 -1
  38. package/package/components/apps/editor/settingsPanel/GridNavbar.svelte +3 -1
  39. package/package/components/apps/editor/settingsPanel/GridTab.svelte +3 -1
  40. package/package/components/apps/editor/settingsPanel/OneOfInputSpecsEditor.svelte +55 -53
  41. package/package/components/apps/editor/settingsPanel/TableActions.svelte +3 -1
  42. package/package/components/common/button/model.d.ts +1 -1
  43. package/package/components/common/drawer/Disposable.svelte +51 -30
  44. package/package/components/common/drawer/Disposable.svelte.d.ts +12 -44
  45. package/package/components/common/drawer/Drawer.svelte +15 -11
  46. package/package/components/copilot/MetadataGen.svelte +14 -3
  47. package/package/components/copilot/chat/AIChatInput.svelte +0 -1
  48. package/package/components/copilot/chat/AIChatManager.svelte.js +3 -3
  49. package/package/components/copilot/chat/AvailableContextList.svelte +192 -66
  50. package/package/components/copilot/chat/AvailableContextList.svelte.d.ts +2 -2
  51. package/package/components/copilot/chat/ContextElementBadge.svelte +3 -3
  52. package/package/components/copilot/chat/ContextManager.svelte.js +36 -13
  53. package/package/components/copilot/chat/ContextTextarea.svelte +21 -48
  54. package/package/components/copilot/chat/ToolContentDisplay.svelte +10 -1
  55. package/package/components/copilot/chat/ToolExecutionDisplay.svelte +3 -3
  56. package/package/components/copilot/chat/context.d.ts +7 -2
  57. package/package/components/copilot/chat/flow/FlowAIChat.svelte +110 -8
  58. package/package/components/copilot/chat/flow/core.d.ts +11 -0
  59. package/package/components/copilot/chat/flow/core.js +121 -3
  60. package/package/components/copilot/chat/flow/uiIntents.d.ts +8 -0
  61. package/package/components/copilot/chat/flow/uiIntents.js +5 -0
  62. package/package/components/copilot/chat/flow/useUiIntent.d.ts +5 -0
  63. package/package/components/copilot/chat/flow/useUiIntent.js +12 -0
  64. package/package/components/copilot/chat/monaco-adapter.d.ts +22 -4
  65. package/package/components/copilot/chat/monaco-adapter.js +55 -16
  66. package/package/components/copilot/chat/script/core.js +3 -2
  67. package/package/components/copilot/chat/shared.d.ts +3 -2
  68. package/package/components/copilot/chat/shared.js +24 -12
  69. package/package/components/copilot/lib.js +12 -7
  70. package/package/components/copilot/shared.d.ts +1 -1
  71. package/package/components/copilot/shared.js +16 -10
  72. package/package/components/flows/FlowEditor.svelte +4 -2
  73. package/package/components/flows/FlowEditor.svelte.d.ts +1 -0
  74. package/package/components/flows/FlowModuleIcon.svelte +8 -8
  75. package/package/components/flows/common/FlowCardHeader.svelte +4 -1
  76. package/package/components/flows/content/FlowBranchesAllWrapper.svelte +6 -0
  77. package/package/components/flows/content/FlowBranchesOneWrapper.svelte +6 -0
  78. package/package/components/flows/content/FlowEditorPanel.svelte +2 -1
  79. package/package/components/flows/content/FlowEditorPanel.svelte.d.ts +1 -0
  80. package/package/components/flows/content/FlowInput.svelte +31 -34
  81. package/package/components/flows/content/FlowInput.svelte.d.ts +1 -0
  82. package/package/components/flows/content/FlowLoop.svelte +7 -0
  83. package/package/components/flows/content/FlowModuleComponent.svelte +37 -44
  84. package/package/components/flows/content/FlowModuleScript.svelte +1 -1
  85. package/package/components/flows/content/FlowModuleSuspend.svelte +16 -18
  86. package/package/components/flows/content/FlowWhileLoop.svelte +6 -0
  87. package/package/components/flows/content/ScriptEditorDrawer.svelte +9 -11
  88. package/package/components/flows/dfs.d.ts +1 -1
  89. package/package/components/flows/dfs.js +6 -6
  90. package/package/components/flows/flowInfers.js +7 -7
  91. package/package/components/flows/flowStateUtils.svelte.js +1 -2
  92. package/package/components/flows/map/FlowModuleSchemaItem.svelte +12 -26
  93. package/package/components/flows/map/MapItem.svelte +8 -4
  94. package/package/components/flows/map/VirtualItem.svelte +1 -1
  95. package/package/components/flows/pickers/TopLevelNode.svelte +1 -1
  96. package/package/components/flows/propPicker/InputPickerInner.svelte +5 -5
  97. package/package/components/flows/propPicker/OutputPickerInner.svelte +143 -118
  98. package/package/components/flows/propPicker/OutputPickerInner.svelte.d.ts +7 -16
  99. package/package/components/flows/{testSteps.svelte.d.ts → stepsInputArgs.svelte.d.ts} +2 -1
  100. package/package/components/flows/{testSteps.svelte.js → stepsInputArgs.svelte.js} +15 -3
  101. package/package/components/flows/types.d.ts +16 -3
  102. package/package/components/flows/utils.js +3 -0
  103. package/package/components/graph/FlowGraphV2.svelte +1 -1
  104. package/package/components/graph/renderers/nodes/AIToolNode.svelte +4 -4
  105. package/package/components/graph/renderers/nodes/NewAIToolNode.svelte +71 -54
  106. package/package/components/propertyPicker/ObjectViewer.svelte +11 -3
  107. package/package/components/raw_apps/RawAppInlineScriptEditor.svelte +1 -1
  108. package/package/components/schema/AddPropertyV2.svelte +2 -7
  109. package/package/components/schema/AddPropertyV2.svelte.d.ts +3 -20
  110. package/package/components/schema/EditableSchemaDrawer.svelte +109 -115
  111. package/package/components/schema/EditableSchemaDrawer.svelte.d.ts +2 -1
  112. package/package/components/schema/EditableSchemaSdkWrapper.svelte +16 -3
  113. package/package/components/schema/EditableSchemaSdkWrapper.svelte.d.ts +4 -1
  114. package/package/components/schema/EditableSchemaWrapper.svelte +3 -10
  115. package/package/components/schema/FlowPropertyEditor.svelte +9 -41
  116. package/package/components/schema/FlowPropertyEditor.svelte.d.ts +1 -1
  117. package/package/components/schema/SchemaFormDND.svelte +11 -10
  118. package/package/components/schema/SchemaFormDND.svelte.d.ts +3 -2
  119. package/package/components/schema/editable_schema_wrapper.d.ts +0 -3
  120. package/package/components/settings/PremiumInfo.svelte +7 -2
  121. package/package/components/triggers/CaptureWrapper.svelte +2 -13
  122. package/package/components/triggers/CaptureWrapper.svelte.d.ts +1 -1
  123. package/package/components/triggers/TriggersWrapper.svelte +1 -0
  124. package/package/components/triggers/http/RouteEditorInner.svelte +1 -1
  125. package/package/components/triggers/nats/NatsTriggerEditorInner.svelte +23 -20
  126. package/package/components/triggers/nats/NatsTriggersConfigSection.svelte +15 -27
  127. package/package/components/triggers/nats/NatsTriggersConfigSection.svelte.d.ts +7 -5
  128. package/package/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +16 -16
  129. package/package/hubPaths.json +3 -1
  130. package/package/script_helpers.d.ts +2 -2
  131. package/package/script_helpers.js +2 -0
  132. package/package/stores.d.ts +1 -0
  133. package/package/stores.js +8 -1
  134. package/package.json +2 -2
  135. package/package/components/ModulePreviewResultViewer.svelte.d.ts +0 -28
@@ -107,7 +107,7 @@ import AIChatInlineWidget from './copilot/chat/AIChatInlineWidget.svelte';
107
107
  import { writable } from 'svelte/store';
108
108
  import { formatResourceTypes } from './copilot/chat/script/core';
109
109
  import FakeMonacoPlaceHolder from './FakeMonacoPlaceHolder.svelte';
110
- import { editorPositionMap, readFieldsRecursively } from '../utils';
110
+ import { editorPositionMap } from '../utils';
111
111
  import { extToLang, langToExt } from '../editorLangUtils';
112
112
  import { aiChatManager } from './copilot/chat/AIChatManager.svelte';
113
113
  import { getDbSchemas } from './apps/components/display/dbtable/utils';
@@ -406,8 +406,7 @@ function addSqlTypeCompletions() {
406
406
  });
407
407
  }
408
408
  let sqlSchemaCompletor = undefined;
409
- async function updateSchema() {
410
- const newSchemaRes = lang === 'graphql' ? args?.api : args?.database;
409
+ async function updateSchema(newSchemaRes) {
411
410
  if (typeof newSchemaRes === 'string') {
412
411
  const resourcePath = newSchemaRes.replace('$res:', '');
413
412
  dbSchema = $dbSchemas[resourcePath];
@@ -530,7 +529,16 @@ let showInlineAIChat = $state(false);
530
529
  let inlineAIChatSelection = $state(null);
531
530
  let selectedCode = $state('');
532
531
  export function reviewAndApplyCode(code, applyAll = false) {
533
- aiChatEditorHandler?.reviewAndApply(code, applyAll);
532
+ aiChatEditorHandler?.reviewChanges(code, { applyAll, mode: 'apply' });
533
+ }
534
+ export function reviewAppliedCode(originalCode, opts) {
535
+ aiChatEditorHandler?.reviewChanges(originalCode, {
536
+ mode: 'revert',
537
+ onFinishedReview: opts?.onFinishedReview
538
+ });
539
+ }
540
+ export function getAiChatEditorHandler() {
541
+ return aiChatEditorHandler;
534
542
  }
535
543
  function addChatHandler(editor) {
536
544
  try {
@@ -1322,10 +1330,13 @@ $effect(() => {
1322
1330
  ? untrack(() => addSqlTypeCompletions())
1323
1331
  : sqlTypeCompletor?.dispose();
1324
1332
  });
1333
+ let lastArg = undefined;
1325
1334
  $effect(() => {
1326
- console.log('updating schema', lang, $dbSchemas);
1327
- readFieldsRecursively(args);
1328
- lang && $dbSchemas && untrack(() => updateSchema());
1335
+ let newArg = lang === 'graphql' ? args?.api : args?.database;
1336
+ if (newArg !== lastArg) {
1337
+ lastArg = newArg;
1338
+ $dbSchemas && untrack(() => updateSchema(newArg));
1339
+ }
1329
1340
  });
1330
1341
  $effect(() => {
1331
1342
  console.log('updating db schema completions', dbSchema, lang);
@@ -1396,10 +1407,20 @@ $effect(() => {
1396
1407
  {#if $reviewingChanges}
1397
1408
  <GlobalReviewButtons
1398
1409
  onAcceptAll={() => {
1399
- aiChatEditorHandler?.acceptAll()
1410
+ const mode = aiChatEditorHandler?.getReviewMode?.()
1411
+ if (mode === 'revert') {
1412
+ aiChatEditorHandler?.keepAll()
1413
+ } else {
1414
+ aiChatEditorHandler?.acceptAll()
1415
+ }
1400
1416
  }}
1401
1417
  onRejectAll={() => {
1402
- aiChatEditorHandler?.rejectAll()
1418
+ const mode = aiChatEditorHandler?.getReviewMode?.()
1419
+ if (mode === 'revert') {
1420
+ aiChatEditorHandler?.revertAll()
1421
+ } else {
1422
+ aiChatEditorHandler?.rejectAll()
1423
+ }
1403
1424
  }}
1404
1425
  />
1405
1426
  {/if}
@@ -4,6 +4,7 @@ import { type Preview } from '../gen';
4
4
  import type { Text } from 'yjs';
5
5
  import { editor as meditor, type IDisposable } from 'monaco-editor';
6
6
  import { type DepsToGet } from '../ata/index';
7
+ import { AIChatEditorHandler } from './copilot/chat/monaco-adapter';
7
8
  interface Props {
8
9
  code?: string;
9
10
  cmdEnterAction?: (() => void) | undefined;
@@ -73,6 +74,10 @@ declare const Editor: $$__sveltets_2_IsomorphicComponent<Props, {
73
74
  format: () => Promise<void>;
74
75
  getScriptLang: () => string | undefined;
75
76
  reviewAndApplyCode: (code: string, applyAll?: boolean) => void;
77
+ reviewAppliedCode: (originalCode: string, opts?: {
78
+ onFinishedReview?: () => void;
79
+ }) => void;
80
+ getAiChatEditorHandler: () => AIChatEditorHandler | undefined;
76
81
  reloadWebsocket: () => Promise<void>;
77
82
  fetchPackageDeps: (deps: DepsToGet) => Promise<void>;
78
83
  addAction: (id: string, label: string, callback: (editor: meditor.IStandaloneCodeEditor) => void, keybindings?: number[]) => void;
@@ -33,7 +33,7 @@ import DeployButton from './DeployButton.svelte';
33
33
  import { deployTriggers, filterDraftTriggers, handleSelectTriggerFromKind } from './triggers/utils';
34
34
  import DraftTriggersConfirmationModal from './common/confirmationModal/DraftTriggersConfirmationModal.svelte';
35
35
  import { Triggers } from './triggers/triggers.svelte';
36
- import { TestSteps } from './flows/testSteps.svelte';
36
+ import { StepsInputArgs } from './flows/stepsInputArgs.svelte';
37
37
  import { aiChatManager } from './copilot/chat/AIChatManager.svelte';
38
38
  import { setStepHistoryLoaderContext, StepHistoryLoader } from './stepHistoryLoader.svelte';
39
39
  import { ModulesTestStates } from './modulesTest.svelte';
@@ -440,7 +440,7 @@ const flowInputEditorStateStore = writable({
440
440
  editPanelSize: 0,
441
441
  payloadData: undefined
442
442
  });
443
- const testSteps = new TestSteps();
443
+ const stepsInputArgs = new StepsInputArgs();
444
444
  function select(selectedId) {
445
445
  selectedIdStore.set(selectedId);
446
446
  }
@@ -458,7 +458,7 @@ setContext('FlowEditorContext', {
458
458
  flowStateStore,
459
459
  flowStore,
460
460
  pathStore,
461
- testSteps,
461
+ stepsInputArgs,
462
462
  saveDraft,
463
463
  initialPathStore,
464
464
  fakeInitialPath,
@@ -909,6 +909,8 @@ const flowHasChanged = $derived(flowPreviewContent?.flowHasChanged());
909
909
  bind:this={flowPreviewButtons}
910
910
  {loading}
911
911
  onRunPreview={() => {
912
+ // Reset manually edited args inputs when running a preview
913
+ stepsInputArgs.resetManuallyEditedArgs()
912
914
  modulesTestStates.hideJobsInGraph()
913
915
  localModuleStates = {}
914
916
  showJobStatus = true
@@ -950,7 +952,7 @@ const flowHasChanged = $derived(flowPreviewContent?.flowHasChanged());
950
952
  {newFlow}
951
953
  on:applyArgs={(ev) => {
952
954
  if (ev.detail.kind === 'preprocessor') {
953
- testSteps.setStepArgs('preprocessor', ev.detail.args ?? {})
955
+ stepsInputArgs.setStepArgs('preprocessor', ev.detail.args ?? {})
954
956
  $selectedIdStore = 'preprocessor'
955
957
  }
956
958
  }}
@@ -998,6 +1000,7 @@ const flowHasChanged = $derived(flowPreviewContent?.flowHasChanged());
998
1000
  delete modulesTestStates.states[id]
999
1001
  }}
1000
1002
  {flowHasChanged}
1003
+ previewOpen={flowPreviewButtons?.getPreviewOpen()}
1001
1004
  />
1002
1005
  {:else}
1003
1006
  <CenteredPage>Loading...</CenteredPage>
@@ -67,7 +67,7 @@ export async function runPreview(args, restartedFrom) {
67
67
  jobId = await runFlowPreview(args, newFlow, $pathStore, restartedFrom);
68
68
  isRunning = true;
69
69
  if (inputSelected) {
70
- savedArgs = previewArgs.val;
70
+ savedArgs = $state.snapshot(previewArgs.val);
71
71
  inputSelected = undefined;
72
72
  }
73
73
  onRunPreview?.();
@@ -92,7 +92,7 @@ function onKeyDown(event) {
92
92
  if (preventEscape) {
93
93
  selectInput(undefined);
94
94
  event.preventDefault();
95
- event.stopPropagation;
95
+ event.stopPropagation();
96
96
  }
97
97
  break;
98
98
  }
@@ -423,7 +423,7 @@ export function flowHasChanged() {
423
423
  schema={flowStore.val.schema}
424
424
  bind:args={previewArgs.val}
425
425
  on:change={() => {
426
- savedArgs = previewArgs.val
426
+ savedArgs = $state.snapshot(previewArgs.val)
427
427
  }}
428
428
  bind:isValid
429
429
  helperScript={flowStore.val.schema?.['x-windmill-dyn-select-code'] &&
@@ -43,6 +43,8 @@ let refreshGlobal = async (moduleId, clear, root) => {
43
43
  let updateGlobalRefresh = (moduleId, updateFn) => {
44
44
  globalRefreshes[moduleId] = [...(globalRefreshes[moduleId] ?? []), updateFn];
45
45
  };
46
+ let storedToolCallJobs = $state({});
47
+ let toolCallIndicesToLoad = $state([]);
46
48
  </script>
47
49
 
48
50
  <FlowStatusViewerInner
@@ -74,4 +76,30 @@ let updateGlobalRefresh = (moduleId, updateFn) => {
74
76
  isNodeSelected={true}
75
77
  {refreshGlobal}
76
78
  {updateGlobalRefresh}
79
+ toolCallStore={{
80
+ getStoredToolCallJob: (storeKey: string) => storedToolCallJobs[storeKey],
81
+ setStoredToolCallJob: (storeKey: string, job: Job) => {
82
+ storedToolCallJobs[storeKey] = job
83
+ },
84
+ getLocalToolCallJobs: (prefix: string) => {
85
+ // we return a map from tool call index to job
86
+ // to do so, we filter the storedToolCallJobs object by the prefix and we make sure what's left in the key is a tool call index: 2 part of format agentModuleId-toolCallIndex
87
+ // and not a further nested tool call index
88
+ return Object.fromEntries(
89
+ Object.entries(storedToolCallJobs)
90
+ .filter(
91
+ ([key]) => key.startsWith(prefix) && key.replace(prefix, '').split('-').length === 2
92
+ )
93
+ .map(([key, job]) => [Number(key.replace(prefix, '').split('-').pop()), job])
94
+ )
95
+ },
96
+ isToolCallToBeLoaded: (storeKey: string) => {
97
+ return toolCallIndicesToLoad.includes(storeKey)
98
+ },
99
+ addToolCallToLoad: (storeKey: string) => {
100
+ if (!toolCallIndicesToLoad.includes(storeKey)) {
101
+ toolCallIndicesToLoad.push(storeKey)
102
+ }
103
+ }
104
+ }}
77
105
  />
@@ -16,6 +16,7 @@ import { ChevronDown, Hourglass } from 'lucide-svelte';
16
16
  import { deepEqual } from 'fast-equals';
17
17
  import FlowTimeline from './FlowTimeline.svelte';
18
18
  import { dfs } from './flows/dfs';
19
+ import { dfs as dfsPreviousResults } from './flows/previousResults';
19
20
  import Alert from './common/alert/Alert.svelte';
20
21
  import FlowGraphViewerStep from './FlowGraphViewerStep.svelte';
21
22
  import FlowGraphV2 from './graph/FlowGraphV2.svelte';
@@ -28,7 +29,7 @@ import JobLoader from './JobLoader.svelte';
28
29
  import { writable } from 'svelte/store';
29
30
  import { AI_TOOL_CALL_PREFIX, AI_TOOL_MESSAGE_PREFIX, getToolCallId } from './graph/renderers/nodes/AIToolNode.svelte';
30
31
  let { flowStateStore, retryStatus, suspendStatus, hideDownloadInGraph, hideTimeline, hideNodeDefinition, hideDownloadLogs, hideJobId } = getContext('FlowStatusViewer');
31
- let { jobId, initialJob = undefined, workspaceId = undefined, flowJobIds = undefined, innerModule = undefined, render = true, isOwner = false, selectedNode = $bindable(undefined), globalModuleStates, globalDurationStatuses = [], globalIterationBounds, updateRecursiveRefreshFn = undefined, isSelectedBranch = true, isSubflow = false, reducedPolling = false, wideResults = false, hideFlowResult = 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 } = $props();
32
+ let { jobId, initialJob = undefined, workspaceId = undefined, flowJobIds = undefined, innerModule = undefined, render = true, isOwner = false, selectedNode = $bindable(undefined), globalModuleStates, globalDurationStatuses = [], globalIterationBounds, updateRecursiveRefreshFn = undefined, isSelectedBranch = true, isSubflow = false, reducedPolling = false, wideResults = false, hideFlowResult = 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 } = $props();
32
33
  let getTopModuleStates = $derived(topModuleStates ?? localModuleStates);
33
34
  let resultStreams = $state({});
34
35
  if (onResultStreamUpdate == undefined) {
@@ -660,9 +661,7 @@ function loadPreviousIters(innerKey, lenToAdd) {
660
661
  }
661
662
  let stepDetail = $state(undefined);
662
663
  let storedListJobs = $state({});
663
- let storedToolCallJobs = $state({});
664
664
  let selectedToolCall = $state(undefined);
665
- let toolCallIndicesToLoad = $state([]);
666
665
  let wrapperHeight = $state(0);
667
666
  function removeFailureNode(id, parent_module) {
668
667
  if (id?.startsWith('failure-') && parent_module) {
@@ -689,7 +688,7 @@ function allModulesForTimeline(modules, expandedSubflows) {
689
688
  }
690
689
  let nprefix = buildPrefix(prefix, oid);
691
690
  return fms
692
- ? rec(dfs(fms, (x) => x.id.startsWith('subflow:') ? x.id : buildSubflowKey(x.id, nprefix)), nprefix)
691
+ ? rec(dfs(fms, (x) => (x.id.startsWith('subflow:') ? x.id : buildSubflowKey(x.id, nprefix)), { skipToolNodes: true }), nprefix)
693
692
  : [];
694
693
  }));
695
694
  }
@@ -734,6 +733,11 @@ async function onSelectedIteration(detail) {
734
733
  selectedForLoopSetManually: false
735
734
  });
736
735
  }
736
+ if (selectedNode?.startsWith(AI_TOOL_CALL_PREFIX)) {
737
+ const [, agentModuleId, toolCallIndex, _] = selectedNode.split('-');
738
+ const parentLoopsPrefix = getParentLoopsPrefix(agentModuleId);
739
+ toolCallStore?.addToolCallToLoad(parentLoopsPrefix + agentModuleId + '-' + toolCallIndex);
740
+ }
737
741
  }
738
742
  $effect(() => {
739
743
  flowJobIds?.moduleId && untrack(() => onFlowModuleId());
@@ -761,6 +765,27 @@ $effect(() => {
761
765
  let selected = $derived(isListJob ? 'sequence' : 'graph');
762
766
  let animateLogsTab = $state(false);
763
767
  let noLogs = $derived(graphTabOpen && !isNodeSelected);
768
+ /**
769
+ * Returns a string like "forloopmodid1-{iter1}-forloopmodid2-{iter2}-forloopmodid3-{iter3}-"
770
+ * that can be used to prefix tool call store keys for nested tool calls.
771
+ */
772
+ function getParentLoopsPrefix(modId) {
773
+ if (job?.raw_flow) {
774
+ const indices = [];
775
+ const parents = dfsPreviousResults(modId, { value: job?.raw_flow, summary: '' }, true);
776
+ for (const parent of parents) {
777
+ if (parent.value.type === 'forloopflow' || parent.value.type === 'whileloopflow') {
778
+ const state = localModuleStates[parent.id];
779
+ if (state?.selectedForloopIndex !== undefined) {
780
+ indices.push(parent.id + '-' + state.selectedForloopIndex.toString());
781
+ }
782
+ }
783
+ }
784
+ indices.reverse();
785
+ return indices.length > 0 ? indices.join('-') + '-' : '';
786
+ }
787
+ return '';
788
+ }
764
789
  </script>
765
790
 
766
791
  <JobLoader workspaceOverride={workspaceId} {noLogs} noCode bind:this={jobLoader} />
@@ -895,6 +920,10 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
895
920
  {@const forloopIsSelected =
896
921
  forloop_selected == loopJobId ||
897
922
  (innerModule?.type != 'forloopflow' && innerModule?.type != 'whileloopflow')}
923
+ {@const forLoopStoreKeyPrefix =
924
+ innerModule?.type == 'forloopflow' || innerModule?.type == 'whileloopflow'
925
+ ? (flowJobIds?.moduleId ?? '') + '-' + j + '-'
926
+ : ''}
898
927
  <!-- <LogId id={loopJobId} /> -->
899
928
  <div class="border p-6" class:hidden={forloop_selected != loopJobId}>
900
929
  <FlowStatusViewerInner
@@ -929,6 +958,18 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
929
958
  graphTabOpen={selected == 'graph' && graphTabOpen}
930
959
  isNodeSelected={forloop_selected == loopJobId}
931
960
  {globalIterationBounds}
961
+ toolCallStore={{
962
+ getStoredToolCallJob: (storeKey: string) =>
963
+ toolCallStore?.getStoredToolCallJob(forLoopStoreKeyPrefix + storeKey),
964
+ setStoredToolCallJob: (storeKey: string, job: Job) =>
965
+ toolCallStore?.setStoredToolCallJob(forLoopStoreKeyPrefix + storeKey, job),
966
+ getLocalToolCallJobs: (prefix: string) =>
967
+ toolCallStore?.getLocalToolCallJobs(forLoopStoreKeyPrefix + prefix) ?? {},
968
+ addToolCallToLoad: (storeKey: string) =>
969
+ toolCallStore?.addToolCallToLoad(forLoopStoreKeyPrefix + storeKey),
970
+ isToolCallToBeLoaded: (storeKey: string) =>
971
+ toolCallStore?.isToolCallToBeLoaded(forLoopStoreKeyPrefix + storeKey) ?? false
972
+ }}
932
973
  />
933
974
  </div>
934
975
  {/if}
@@ -1088,12 +1129,17 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
1088
1129
  graphTabOpen={selected == 'graph' && graphTabOpen}
1089
1130
  isNodeSelected={localModuleStates?.[selectedNode ?? '']?.job_id == mod.job}
1090
1131
  {globalIterationBounds}
1132
+ {toolCallStore}
1091
1133
  />
1092
- {#if mod.agent_actions && mod.agent_actions.length > 0}
1134
+ {#if mod.agent_actions && mod.agent_actions.length > 0 && mod.id}
1135
+ {@const storeKeyPrefix = getParentLoopsPrefix(mod.id)}
1093
1136
  {#each mod.agent_actions as agentAction, j}
1094
- {#if agentAction.type === 'tool_call' && mod.id}
1137
+ {#if agentAction.type === 'tool_call'}
1095
1138
  {@const toolCallId = getToolCallId(j, mod.id, agentAction.module_id)}
1096
- {@const isSelected = selectedToolCall === j}
1139
+ {@const localToolCallKey = mod.id + '-' + j}
1140
+ {@const storeKey = storeKeyPrefix + localToolCallKey}
1141
+ {@const storedToolCallJob = toolCallStore?.getStoredToolCallJob(storeKey)}
1142
+ {@const isSelected = localToolCallKey === selectedToolCall}
1097
1143
  <Button
1098
1144
  variant={isSelected ? 'contained' : 'border'}
1099
1145
  color={mod.agent_actions_success?.[j] === false
@@ -1103,10 +1149,10 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
1103
1149
  : 'light'}
1104
1150
  btnClasses="w-full flex justify-start"
1105
1151
  on:click={async () => {
1106
- if (selectedToolCall == j) {
1152
+ if (isSelected) {
1107
1153
  selectedToolCall = undefined
1108
1154
  } else {
1109
- selectedToolCall = j
1155
+ selectedToolCall = localToolCallKey
1110
1156
  }
1111
1157
  }}
1112
1158
  endIcon={{
@@ -1118,7 +1164,7 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
1118
1164
  Tool call: {agentAction.function_name}
1119
1165
  </span>
1120
1166
  </Button>
1121
- {#if isSelected || storedToolCallJobs[j] || toolCallIndicesToLoad.includes(j)}
1167
+ {#if isSelected || storedToolCallJob || toolCallStore?.isToolCallToBeLoaded(storeKey)}
1122
1168
  <FlowStatusViewerInner
1123
1169
  topModuleStates={getTopModuleStates}
1124
1170
  {refreshGlobal}
@@ -1136,11 +1182,11 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
1136
1182
  {subflowParentsDurationStatuses}
1137
1183
  {isSelectedBranch}
1138
1184
  jobId={agentAction.job_id}
1139
- job={storedToolCallJobs[j]}
1140
- initialJob={storedToolCallJobs[j]}
1185
+ job={storedToolCallJob}
1186
+ initialJob={storedToolCallJob}
1141
1187
  {reducedPolling}
1142
1188
  onJobsLoaded={({ job, force }) => {
1143
- storedToolCallJobs[j] = job
1189
+ toolCallStore?.setStoredToolCallJob(storeKey, job)
1144
1190
  onJobsLoadedInner({ id: toolCallId } as FlowStatusModule, job, force)
1145
1191
  }}
1146
1192
  loadExtraLogs={(logs) => {
@@ -1231,11 +1277,11 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
1231
1277
  stepDetail = mod
1232
1278
  selectedNode = e
1233
1279
  if (e.startsWith(AI_TOOL_CALL_PREFIX)) {
1234
- const [_prefix, _agentModuleId, j, _toolModuleId] = e.split('-')
1280
+ const [_prefix, agentModuleId, j, _toolModuleId] = e.split('-')
1281
+ const parentLoopsPrefix = getParentLoopsPrefix(agentModuleId)
1235
1282
  const jIdx = Number(j)
1236
- if (!toolCallIndicesToLoad.includes(jIdx)) {
1237
- toolCallIndicesToLoad.push(jIdx)
1238
- }
1283
+ const storeKey = parentLoopsPrefix + agentModuleId + '-' + jIdx
1284
+ toolCallStore?.addToolCallToLoad(storeKey)
1239
1285
  }
1240
1286
  }
1241
1287
  } else {
@@ -1325,6 +1371,7 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
1325
1371
  stepDetail && typeof stepDetail !== 'string' ? stepDetail : undefined}
1326
1372
  {@const agentTools =
1327
1373
  module && module.value.type === 'aiagent' ? module.value.tools : undefined}
1374
+ {@const parentLoopsPrefix = getParentLoopsPrefix(module?.id ?? '')}
1328
1375
  {#if node.flow_jobs_results}
1329
1376
  <span class="pl-1 text-tertiary"
1330
1377
  >Result of step as collection of all subflows</span
@@ -1389,7 +1436,7 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
1389
1436
  logs={node.logs}
1390
1437
  downloadLogs={!hideDownloadLogs}
1391
1438
  aiAgentStatus={agentTools &&
1392
- node.job_id &&
1439
+ node?.job_id &&
1393
1440
  (node.type === 'Success' || node.type === 'Failure')
1394
1441
  ? {
1395
1442
  tools: agentTools,
@@ -1401,9 +1448,14 @@ let noLogs = $derived(graphTabOpen && !isNodeSelected);
1401
1448
  success: node.type === 'Success',
1402
1449
  type: 'CompletedJob'
1403
1450
  },
1404
- storedToolCallJobs,
1451
+ storedToolCallJobs: module
1452
+ ? toolCallStore?.getLocalToolCallJobs(parentLoopsPrefix)
1453
+ : undefined,
1405
1454
  onToolJobLoaded: (job, idx) => {
1406
- storedToolCallJobs[idx] = job
1455
+ if (module) {
1456
+ const storeKey = parentLoopsPrefix + module.id + '-' + idx
1457
+ toolCallStore?.setStoredToolCallJob(storeKey, job)
1458
+ }
1407
1459
  }
1408
1460
  }
1409
1461
  : undefined}
@@ -57,6 +57,13 @@ interface Props {
57
57
  onDone?: ({ job }: {
58
58
  job: CompletedJob;
59
59
  }) => void;
60
+ toolCallStore?: {
61
+ getStoredToolCallJob: (storeKey: string) => Job | undefined;
62
+ setStoredToolCallJob: (storeKey: string, job: Job) => void;
63
+ getLocalToolCallJobs: (prefix: string) => Record<number, Job>;
64
+ isToolCallToBeLoaded: (storeKey: string) => boolean;
65
+ addToolCallToLoad: (storeKey: string) => void;
66
+ };
60
67
  }
61
68
  declare const FlowStatusViewerInner: import("svelte").Component<Props, {}, "job" | "localModuleStates" | "selectedNode" | "rightColumnSelect" | "localDurationStatuses">;
62
69
  type FlowStatusViewerInner = ReturnType<typeof FlowStatusViewerInner>;
@@ -4,7 +4,7 @@ import Button from './common/button/Button.svelte';
4
4
  import ModulePreviewForm from './ModulePreviewForm.svelte';
5
5
  import ModuleTest from './ModuleTest.svelte';
6
6
  import { getContext } from 'svelte';
7
- let { mod, schema, pickableProperties, testJob = $bindable(undefined), testIsLoading = $bindable(false), noEditor = false, scriptProgress = $bindable(undefined), focusArg = undefined } = $props();
7
+ let { mod, schema, pickableProperties, testJob = $bindable(undefined), testIsLoading = $bindable(false), noEditor = false, scriptProgress = $bindable(undefined), focusArg = undefined, onJobDone } = $props();
8
8
  const { flowStore } = getContext('FlowEditorContext');
9
9
  let moduleTest = $state();
10
10
  export function runTestWithStepArgs() {
@@ -19,6 +19,7 @@ export function runTestWithStepArgs() {
19
19
  bind:testIsLoading
20
20
  bind:scriptProgress
21
21
  bind:this={moduleTest}
22
+ {onJobDone}
22
23
  />
23
24
 
24
25
  <div class="p-4">
@@ -12,6 +12,7 @@ interface Props {
12
12
  noEditor?: boolean;
13
13
  scriptProgress?: any;
14
14
  focusArg?: string;
15
+ onJobDone?: () => void;
15
16
  }
16
17
  declare const ModulePreview: import("svelte").Component<Props, {
17
18
  runTestWithStepArgs: () => void;
@@ -1,13 +1,13 @@
1
- <script lang="ts">import { allTrue, sendUserToast } from '../utils';
1
+ <script lang="ts">import { allTrue } from '../utils';
2
2
  import { RefreshCw } from 'lucide-svelte';
3
3
  import ArgInput from './ArgInput.svelte';
4
4
  import { Button } from './common';
5
- import { getContext, onMount, untrack } from 'svelte';
5
+ import { getContext, untrack } from 'svelte';
6
6
  import { evalValue } from './flows/utils';
7
7
  import { getResourceTypes } from './resourceTypesStore';
8
8
  import { twMerge } from 'tailwind-merge';
9
9
  let { schema, mod, pickableProperties, isValid = $bindable(true), autofocus = false, focusArg = undefined } = $props();
10
- const { testSteps, flowStateStore, flowStore, previewArgs } = getContext('FlowEditorContext');
10
+ const { stepsInputArgs, flowStateStore, flowStore, previewArgs } = getContext('FlowEditorContext');
11
11
  let inputCheck = $state({});
12
12
  $effect(() => {
13
13
  isValid = allTrue(inputCheck) ?? false;
@@ -17,11 +17,11 @@ $effect(() => {
17
17
  let lkeys = Object.keys(schema?.properties ?? {});
18
18
  if (schema?.properties && JSON.stringify(lkeys) != JSON.stringify(keys)) {
19
19
  keys = lkeys;
20
- untrack(() => testSteps?.removeExtraKey(mod.id, keys));
20
+ untrack(() => stepsInputArgs?.removeExtraKey(mod.id, keys));
21
21
  }
22
22
  });
23
23
  function plugIt(argName) {
24
- testSteps?.setEvaluatedStepArg(mod.id, argName, $state.snapshot(evalValue(argName, mod, pickableProperties, true)));
24
+ stepsInputArgs?.setEvaluatedStepArg(mod.id, argName, $state.snapshot(evalValue(argName, mod, pickableProperties, true)));
25
25
  }
26
26
  let editor = $state({});
27
27
  // Animation and highlighting for focusArg
@@ -53,70 +53,77 @@ async function loadResourceTypes() {
53
53
  resourceTypes = await getResourceTypes();
54
54
  }
55
55
  loadResourceTypes();
56
- onMount(() => {
57
- if (!testSteps) {
58
- sendUserToast('testSteps module not initialized. Preview will not work.', true);
56
+ let initialized = $state(false);
57
+ $effect.pre(() => {
58
+ if (!initialized) {
59
+ if (stepsInputArgs) {
60
+ stepsInputArgs?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val);
61
+ initialized = true;
62
+ }
59
63
  }
60
- testSteps?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val);
61
64
  });
62
65
  </script>
63
66
 
64
67
  <div class="w-full pt-2" data-popover>
65
- {#if keys.length > 0}
66
- {#each keys as argName, i (argName)}
67
- {#if Object.keys(schema.properties ?? {}).includes(argName)}
68
- <div
69
- class={twMerge(
70
- 'flex gap-2',
71
- animateArg === argName && 'animate-pulse ring-2 ring-offset-2 ring-blue-500 rounded'
72
- )}
73
- data-arg={argName}
74
- >
75
- {#if schema?.properties?.[argName]}
76
- <ArgInput
77
- {resourceTypes}
78
- minW={false}
79
- autofocus={autofocus && !focusArg && i == 0}
80
- label={argName}
81
- description={schema.properties[argName].description}
82
- bind:value={
83
- () => testSteps?.getStepInputArgs(mod.id, argName),
84
- (v) => testSteps?.setStepInputArgs(mod.id, argName, v)
85
- }
86
- type={schema.properties[argName].type}
87
- oneOf={schema.properties[argName].oneOf}
88
- required={schema?.required?.includes(argName)}
89
- pattern={schema.properties[argName].pattern}
90
- bind:editor={editor[argName]}
91
- bind:valid={inputCheck[argName]}
92
- defaultValue={schema.properties[argName].default}
93
- enum_={schema.properties[argName].enum}
94
- format={schema.properties[argName].format}
95
- contentEncoding={schema.properties[argName].contentEncoding}
96
- properties={schema.properties[argName].properties}
97
- nestedRequired={schema.properties[argName].required}
98
- itemsType={schema.properties[argName].items}
99
- extra={schema.properties[argName]}
100
- nullable={schema.properties[argName].nullable}
101
- title={schema.properties[argName].title}
102
- placeholder={schema.properties[argName].placeholder}
103
- />
104
- {/if}
105
- {#if testSteps?.isArgManuallySet(mod.id, argName)}
106
- <div class="pt-6 mt-0.5">
107
- <Button
108
- on:click={() => {
109
- plugIt(argName)
110
- }}
111
- size="sm"
112
- variant="border"
113
- color="light"
114
- title="Re-evaluate input step"><RefreshCw size={14} /></Button
115
- >
116
- </div>
117
- {/if}
118
- </div>
119
- {/if}
120
- {/each}
68
+ {#if initialized}
69
+ {#if keys.length > 0}
70
+ {#each keys as argName, i (argName)}
71
+ {#if Object.keys(schema.properties ?? {}).includes(argName)}
72
+ <div
73
+ class={twMerge(
74
+ 'flex gap-2',
75
+ animateArg === argName && 'animate-pulse ring-2 ring-offset-2 ring-blue-500 rounded'
76
+ )}
77
+ data-arg={argName}
78
+ >
79
+ {#if schema?.properties?.[argName]}
80
+ <ArgInput
81
+ {resourceTypes}
82
+ minW={false}
83
+ autofocus={autofocus && !focusArg && i == 0}
84
+ label={argName}
85
+ description={schema.properties[argName].description}
86
+ bind:value={
87
+ () => stepsInputArgs?.getStepInputArgs(mod.id, argName),
88
+ (v) => stepsInputArgs?.setStepInputArgs(mod.id, argName, v)
89
+ }
90
+ type={schema.properties[argName].type}
91
+ oneOf={schema.properties[argName].oneOf}
92
+ required={schema?.required?.includes(argName)}
93
+ pattern={schema.properties[argName].pattern}
94
+ bind:editor={editor[argName]}
95
+ bind:valid={inputCheck[argName]}
96
+ defaultValue={schema.properties[argName].default}
97
+ enum_={schema.properties[argName].enum}
98
+ format={schema.properties[argName].format}
99
+ contentEncoding={schema.properties[argName].contentEncoding}
100
+ properties={schema.properties[argName].properties}
101
+ nestedRequired={schema.properties[argName].required}
102
+ itemsType={schema.properties[argName].items}
103
+ extra={schema.properties[argName]}
104
+ nullable={schema.properties[argName].nullable}
105
+ title={schema.properties[argName].title}
106
+ placeholder={schema.properties[argName].placeholder}
107
+ />
108
+ {/if}
109
+ {#if stepsInputArgs?.isArgManuallySet(mod.id, argName)}
110
+ <div class="pt-6 mt-0.5">
111
+ <Button
112
+ on:click={() => {
113
+ plugIt(argName)
114
+ }}
115
+ size="sm"
116
+ variant="border"
117
+ color="light"
118
+ title="Re-evaluate input step"><RefreshCw size={14} /></Button
119
+ >
120
+ </div>
121
+ {/if}
122
+ </div>
123
+ {/if}
124
+ {/each}
125
+ {/if}
126
+ {:else}
127
+ <div class="text-center text-sm text-tertiary"> Loading test step arguments... </div>
121
128
  {/if}
122
129
  </div>