@@ -6,6 +6,7 @@ import type { BlockOutput } from '@/blocks/types'
66import { Executor } from '@/executor'
77import { BlockType , DEFAULTS , HTTP } from '@/executor/constants'
88import { ChildWorkflowError } from '@/executor/errors/child-workflow-error'
9+ import type { IterationContext } from '@/executor/execution/types'
910import type {
1011 BlockHandler ,
1112 ExecutionContext ,
@@ -44,6 +45,40 @@ export class WorkflowBlockHandler implements BlockHandler {
4445 ctx : ExecutionContext ,
4546 block : SerializedBlock ,
4647 inputs : Record < string , any >
48+ ) : Promise < BlockOutput | StreamingExecution > {
49+ return this . _executeCore ( ctx , block , inputs )
50+ }
51+
52+ async executeWithNode (
53+ ctx : ExecutionContext ,
54+ block : SerializedBlock ,
55+ inputs : Record < string , any > ,
56+ nodeMetadata : {
57+ nodeId : string
58+ loopId ?: string
59+ parallelId ?: string
60+ branchIndex ?: number
61+ branchTotal ?: number
62+ originalBlockId ?: string
63+ isLoopNode ?: boolean
64+ }
65+ ) : Promise < BlockOutput | StreamingExecution > {
66+ return this . _executeCore ( ctx , block , inputs , nodeMetadata )
67+ }
68+
69+ private async _executeCore (
70+ ctx : ExecutionContext ,
71+ block : SerializedBlock ,
72+ inputs : Record < string , any > ,
73+ nodeMetadata ?: {
74+ nodeId : string
75+ loopId ?: string
76+ parallelId ?: string
77+ branchIndex ?: number
78+ branchTotal ?: number
79+ originalBlockId ?: string
80+ isLoopNode ?: boolean
81+ }
4782 ) : Promise < BlockOutput | StreamingExecution > {
4883 logger . info ( `Executing workflow block: ${ block . id } ` )
4984
@@ -122,6 +157,12 @@ export class WorkflowBlockHandler implements BlockHandler {
122157 const childDepth = ( ctx . childWorkflowContext ?. depth ?? 0 ) + 1
123158 const shouldPropagateCallbacks = childDepth <= DEFAULTS . MAX_SSE_CHILD_DEPTH
124159
160+ if ( nodeMetadata && shouldPropagateCallbacks ) {
161+ const effectiveBlockId = nodeMetadata . originalBlockId ?? nodeMetadata . nodeId
162+ const iterationContext = this . getIterationContext ( ctx , nodeMetadata )
163+ ctx . onChildWorkflowInstanceReady ?.( effectiveBlockId , instanceId , iterationContext )
164+ }
165+
125166 const subExecutor = new Executor ( {
126167 workflow : childWorkflow . serializedState ,
127168 workflowInput : childWorkflowInput ,
@@ -138,6 +179,7 @@ export class WorkflowBlockHandler implements BlockHandler {
138179 onBlockStart : ctx . onBlockStart ,
139180 onBlockComplete : ctx . onBlockComplete ,
140181 onStream : ctx . onStream as ( ( streamingExecution : unknown ) => Promise < void > ) | undefined ,
182+ onChildWorkflowInstanceReady : ctx . onChildWorkflowInstanceReady ,
141183 childWorkflowContext : {
142184 parentBlockId : instanceId ,
143185 workflowName : childWorkflowName ,
@@ -208,6 +250,40 @@ export class WorkflowBlockHandler implements BlockHandler {
208250 }
209251 }
210252
253+ private getIterationContext (
254+ ctx : ExecutionContext ,
255+ nodeMetadata : {
256+ loopId ?: string
257+ parallelId ?: string
258+ branchIndex ?: number
259+ branchTotal ?: number
260+ isLoopNode ?: boolean
261+ }
262+ ) : IterationContext | undefined {
263+ if ( nodeMetadata . branchIndex !== undefined && nodeMetadata . parallelId !== undefined ) {
264+ return {
265+ iterationCurrent : nodeMetadata . branchIndex ,
266+ iterationTotal : nodeMetadata . branchTotal ,
267+ iterationType : 'parallel' ,
268+ iterationContainerId : nodeMetadata . parallelId ,
269+ }
270+ }
271+
272+ if ( nodeMetadata . isLoopNode && nodeMetadata . loopId ) {
273+ const loopScope = ctx . loopExecutions ?. get ( nodeMetadata . loopId )
274+ if ( loopScope && loopScope . iteration !== undefined ) {
275+ return {
276+ iterationCurrent : loopScope . iteration ,
277+ iterationTotal : loopScope . maxIterations ,
278+ iterationType : 'loop' ,
279+ iterationContainerId : nodeMetadata . loopId ,
280+ }
281+ }
282+ }
283+
284+ return undefined
285+ }
286+
211287 /**
212288 * Builds a cleaner error message for nested workflow errors.
213289 * Parses nested error messages to extract workflow chain and root error.
0 commit comments