Skip to content

Commit d98545d

Browse files
authored
fix(terminal): thread executionOrder through child workflow SSE events for loop support (#3346)
* fix(terminal): thread executionOrder through child workflow SSE events for loop support * ran lint * fix(terminal): render iteration children through EntryNodeRow for workflow block expansion IterationNodeRow was rendering all children as flat BlockRow components, ignoring nodeType. Workflow blocks inside loop iterations were never rendered as WorkflowNodeRow, so they had no expand chevron or child tree. * fix(terminal): add childWorkflowBlockId to matchesEntryForUpdate Sub-executors reset executionOrderCounter, so child blocks across loop iterations share the same blockId + executionOrder. Without checking childWorkflowBlockId, updateConsole for iteration N overwrites entries from iterations 0..N-1, causing all child blocks to be grouped under the last iteration's workflow instance.
1 parent fadbad4 commit d98545d

File tree

9 files changed

+49
-11
lines changed

9 files changed

+49
-11
lines changed

apps/sim/app/api/workflows/[id]/execute/route.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
987987
const onChildWorkflowInstanceReady = (
988988
blockId: string,
989989
childWorkflowInstanceId: string,
990-
iterationContext?: IterationContext
990+
iterationContext?: IterationContext,
991+
executionOrder?: number
991992
) => {
992993
sendEvent({
993994
type: 'block:childWorkflowStarted',
@@ -1001,6 +1002,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
10011002
iterationCurrent: iterationContext.iterationCurrent,
10021003
iterationContainerId: iterationContext.iterationContainerId,
10031004
}),
1005+
...(executionOrder !== undefined && { executionOrder }),
10041006
},
10051007
})
10061008
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,16 @@ const IterationNodeRow = memo(function IterationNodeRow({
160160
onSelectEntry,
161161
isExpanded,
162162
onToggle,
163+
expandedNodes,
164+
onToggleNode,
163165
}: {
164166
node: EntryNode
165167
selectedEntryId: string | null
166168
onSelectEntry: (entry: ConsoleEntry) => void
167169
isExpanded: boolean
168170
onToggle: () => void
171+
expandedNodes: Set<string>
172+
onToggleNode: (nodeId: string) => void
169173
}) {
170174
const { entry, children, iterationInfo } = node
171175
const hasError = Boolean(entry.error) || children.some((c) => c.entry.error)
@@ -226,11 +230,13 @@ const IterationNodeRow = memo(function IterationNodeRow({
226230
{isExpanded && hasChildren && (
227231
<div className={ROW_STYLES.nested}>
228232
{children.map((child) => (
229-
<BlockRow
233+
<EntryNodeRow
230234
key={child.entry.id}
231-
entry={child.entry}
232-
isSelected={selectedEntryId === child.entry.id}
233-
onSelect={onSelectEntry}
235+
node={child}
236+
selectedEntryId={selectedEntryId}
237+
onSelectEntry={onSelectEntry}
238+
expandedNodes={expandedNodes}
239+
onToggleNode={onToggleNode}
234240
/>
235241
))}
236242
</div>
@@ -346,6 +352,8 @@ const SubflowNodeRow = memo(function SubflowNodeRow({
346352
onSelectEntry={onSelectEntry}
347353
isExpanded={expandedNodes.has(iterNode.entry.id)}
348354
onToggle={() => onToggleNode(iterNode.entry.id)}
355+
expandedNodes={expandedNodes}
356+
onToggleNode={onToggleNode}
349357
/>
350358
))}
351359
</div>
@@ -520,6 +528,8 @@ const EntryNodeRow = memo(function EntryNodeRow({
520528
onSelectEntry={onSelectEntry}
521529
isExpanded={expandedNodes.has(node.entry.id)}
522530
onToggle={() => onToggleNode(node.entry.id)}
531+
expandedNodes={expandedNodes}
532+
onToggleNode={onToggleNode}
523533
/>
524534
)
525535
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ export function useWorkflowExecution() {
554554
childWorkflowInstanceId: string
555555
iterationCurrent?: number
556556
iterationContainerId?: string
557+
executionOrder?: number
557558
}) => {
558559
if (isStaleExecution()) return
559560
updateConsole(
@@ -564,6 +565,7 @@ export function useWorkflowExecution() {
564565
...(data.iterationContainerId !== undefined && {
565566
iterationContainerId: data.iterationContainerId,
566567
}),
568+
...(data.executionOrder !== undefined && { executionOrder: data.executionOrder }),
567569
},
568570
executionIdRef.current
569571
)

apps/sim/executor/execution/block-executor.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ export class BlockExecutor {
8080
const startTime = performance.now()
8181
let resolvedInputs: Record<string, any> = {}
8282

83-
const nodeMetadata = this.buildNodeMetadata(node)
83+
const nodeMetadata = {
84+
...this.buildNodeMetadata(node),
85+
executionOrder: blockLog?.executionOrder,
86+
}
8487
let cleanupSelfReference: (() => void) | undefined
8588

8689
if (block.metadata?.id === BlockType.HUMAN_IN_THE_LOOP) {

apps/sim/executor/execution/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ export interface ExecutionCallbacks {
8989
onChildWorkflowInstanceReady?: (
9090
blockId: string,
9191
childWorkflowInstanceId: string,
92-
iterationContext?: IterationContext
92+
iterationContext?: IterationContext,
93+
executionOrder?: number
9394
) => void
9495
}
9596

@@ -155,7 +156,8 @@ export interface ContextExtensions {
155156
onChildWorkflowInstanceReady?: (
156157
blockId: string,
157158
childWorkflowInstanceId: string,
158-
iterationContext?: IterationContext
159+
iterationContext?: IterationContext,
160+
executionOrder?: number
159161
) => void
160162

161163
/**

apps/sim/executor/handlers/workflow/workflow-handler.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export class WorkflowBlockHandler implements BlockHandler {
6262
branchTotal?: number
6363
originalBlockId?: string
6464
isLoopNode?: boolean
65+
executionOrder?: number
6566
}
6667
): Promise<BlockOutput | StreamingExecution> {
6768
return this._executeCore(ctx, block, inputs, nodeMetadata)
@@ -79,6 +80,7 @@ export class WorkflowBlockHandler implements BlockHandler {
7980
branchTotal?: number
8081
originalBlockId?: string
8182
isLoopNode?: boolean
83+
executionOrder?: number
8284
}
8385
): Promise<BlockOutput | StreamingExecution> {
8486
logger.info(`Executing workflow block: ${block.id}`)
@@ -169,7 +171,12 @@ export class WorkflowBlockHandler implements BlockHandler {
169171
const iterationContext = nodeMetadata
170172
? this.getIterationContext(ctx, nodeMetadata)
171173
: undefined
172-
ctx.onChildWorkflowInstanceReady?.(effectiveBlockId, instanceId, iterationContext)
174+
ctx.onChildWorkflowInstanceReady?.(
175+
effectiveBlockId,
176+
instanceId,
177+
iterationContext,
178+
nodeMetadata?.executionOrder
179+
)
173180
}
174181

175182
const subExecutor = new Executor({

apps/sim/executor/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,8 @@ export interface ExecutionContext {
264264
onChildWorkflowInstanceReady?: (
265265
blockId: string,
266266
childWorkflowInstanceId: string,
267-
iterationContext?: IterationContext
267+
iterationContext?: IterationContext,
268+
executionOrder?: number
268269
) => void
269270

270271
/**
@@ -377,6 +378,7 @@ export interface BlockHandler {
377378
branchTotal?: number
378379
originalBlockId?: string
379380
isLoopNode?: boolean
381+
executionOrder?: number
380382
}
381383
) => Promise<BlockOutput | StreamingExecution>
382384
}

apps/sim/lib/workflows/executor/execution-events.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ export interface BlockChildWorkflowStartedEvent extends BaseExecutionEvent {
155155
childWorkflowInstanceId: string
156156
iterationCurrent?: number
157157
iterationContainerId?: string
158+
executionOrder?: number
158159
}
159160
}
160161

@@ -396,7 +397,8 @@ export function createSSECallbacks(options: SSECallbackOptions) {
396397
const onChildWorkflowInstanceReady = (
397398
blockId: string,
398399
childWorkflowInstanceId: string,
399-
iterationContext?: IterationContext
400+
iterationContext?: IterationContext,
401+
executionOrder?: number
400402
) => {
401403
sendEvent({
402404
type: 'block:childWorkflowStarted',
@@ -410,6 +412,7 @@ export function createSSECallbacks(options: SSECallbackOptions) {
410412
iterationCurrent: iterationContext.iterationCurrent,
411413
iterationContainerId: iterationContext.iterationContainerId,
412414
}),
415+
...(executionOrder !== undefined && { executionOrder }),
413416
},
414417
})
415418
}

apps/sim/stores/terminal/console/store.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ const matchesEntryForUpdate = (
9191
return false
9292
}
9393

94+
if (
95+
update.childWorkflowBlockId !== undefined &&
96+
entry.childWorkflowBlockId !== update.childWorkflowBlockId
97+
) {
98+
return false
99+
}
100+
94101
return true
95102
}
96103

0 commit comments

Comments
 (0)