@@ -8,8 +8,10 @@ import { parseWorkflowScript, type ParsedWorkflowScript } from "./workflow-scrip
88import { runWorkflowSandbox } from "./workflow-sandbox.js" ;
99import {
1010 createWorkflowApi ,
11+ createWorkflowApiRuntime ,
1112 type CreateAgentWorktree ,
1213 type WorkflowApi ,
14+ type WorkflowApiRuntime ,
1315 type WorkflowJournal ,
1416 type WorkflowReplay ,
1517 type WorkflowRunProvider ,
@@ -46,6 +48,8 @@ export interface ExecuteWorkflowOptions {
4648 resolveNestedSource ?: ( nameOrRef : string | { scriptPath : string } ) => string | Promise < string > ;
4749 nestDepth ?: number ;
4850 timeoutMs ?: number ;
51+ /** Shared call counter/semaphore for nested workflow execution. */
52+ runtime ?: WorkflowApiRuntime ;
4953 /** Optional hooks after API construction (tests). */
5054 onApi ?: ( api : WorkflowApi ) => void ;
5155}
@@ -73,6 +77,7 @@ export async function executeWorkflow(
7377 resolveWorkflowConcurrency ( parsed . meta . concurrency , availableParallelism ( ) ) ;
7478
7579 const resolveNestedSource = options . resolveNestedSource ;
80+ const runtime = options . runtime ?? createWorkflowApiRuntime ( concurrency ) ;
7681
7782 // Shared callIndex/semaphore for nested scripts via parent API path.
7883 const api = createWorkflowApi ( {
@@ -89,17 +94,25 @@ export async function executeWorkflow(
8994 runProvider : options . runProvider ,
9095 createWorktree : options . createWorktree ,
9196 replay : options . replay ,
97+ runtime,
9298 nestDepth,
9399 resolveNestedSource,
94100 executeNested : resolveNestedSource
95101 ? async ( input ) =>
96- executeNestedOnApi ( {
97- parentOptions : options ,
98- parentApi : api ,
99- source : input . source ,
100- args : input . args ,
101- nestDepth : input . nestDepth ,
102- } )
102+ (
103+ await executeWorkflow ( {
104+ ...options ,
105+ parsed : undefined ,
106+ source : input . source ,
107+ filename : "workflow:nested" ,
108+ args : input . args ,
109+ signal,
110+ concurrency,
111+ runtime,
112+ nestDepth : input . nestDepth ,
113+ onApi : undefined ,
114+ } )
115+ ) . result
103116 : undefined ,
104117 } ) ;
105118 options . onApi ?.( api ) ;
@@ -136,62 +149,6 @@ export async function executeWorkflow(
136149 }
137150}
138151
139- /**
140- * Nested script execution reusing parent's agent() call counter + semaphore
141- * by constructing a child API that shares internal state via re-entry.
142- *
143- * Implementation: run child sandbox with a new API that has nestDepth+1 but
144- * delegates agent/parallel/pipeline to the parent API (same callIndex).
145- */
146- async function executeNestedOnApi ( input : {
147- parentOptions : ExecuteWorkflowOptions ;
148- parentApi : WorkflowApi ;
149- source : string ;
150- args : JsonValue | undefined ;
151- nestDepth : number ;
152- } ) : Promise < unknown > {
153- if ( input . nestDepth > WORKFLOW_MAX_NEST_DEPTH_LOCAL ) {
154- throw new WorkflowEngineError (
155- "nest_depth" ,
156- `workflow() nesting limited to ${ WORKFLOW_MAX_NEST_DEPTH_LOCAL } level` ,
157- ) ;
158- }
159- const parsed = parseWorkflowScript ( input . source , {
160- filename : "workflow:nested" ,
161- } ) ;
162-
163- // Child surface: reuse parent agent/parallel/pipeline/phase/log/budget/workflow
164- // so callIndex + semaphore stay shared. Override args + meta for the child body.
165- const childApi : WorkflowApi = {
166- agent : input . parentApi . agent ,
167- parallel : input . parentApi . parallel ,
168- pipeline : input . parentApi . pipeline ,
169- phase : input . parentApi . phase ,
170- log : input . parentApi . log ,
171- args : input . args ,
172- budget : input . parentApi . budget ,
173- // Child workflow() must see nestDepth via a wrapper that throws at depth>1.
174- workflow : async ( ...args : unknown [ ] ) => {
175- throw new WorkflowEngineError (
176- "nest_depth" ,
177- `workflow() nesting limited to ${ WORKFLOW_MAX_NEST_DEPTH_LOCAL } level` ,
178- ) ;
179- } ,
180- meta : parsed . meta ,
181- getCallCount : ( ) => input . parentApi . getCallCount ( ) ,
182- getNestDepth : ( ) => input . nestDepth ,
183- } ;
184-
185- return runWorkflowSandbox ( {
186- parsed,
187- api : childApi ,
188- timeoutMs : input . parentOptions . timeoutMs ?? WORKFLOW_HOST_TIMEOUT_MS ,
189- signal : input . parentOptions . signal ,
190- } ) ;
191- }
192-
193- const WORKFLOW_MAX_NEST_DEPTH_LOCAL = 1 ;
194-
195152export function mapEngineErrorKind ( error : unknown ) : WorkflowErrorKind {
196153 if ( error instanceof WorkflowEngineError ) {
197154 return error . kind ;
0 commit comments