@hexabot-ai/agentic 3.1.2-alpha.1 → 3.1.2-alpha.3
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.
- package/dist/cjs/step-executors/loop-executor.js +26 -39
- package/dist/cjs/step-executors/parallel-executor.js +9 -15
- package/dist/cjs/step-executors/suspension-continuation.js +27 -0
- package/dist/cjs/suspension-rebuilder.js +61 -74
- package/dist/cjs/workflow-runner.js +2 -10
- package/dist/esm/step-executors/loop-executor.js +26 -39
- package/dist/esm/step-executors/parallel-executor.js +9 -15
- package/dist/esm/step-executors/suspension-continuation.js +24 -0
- package/dist/esm/suspension-rebuilder.js +61 -74
- package/dist/esm/workflow-runner.js +2 -10
- package/dist/types/step-executors/loop-executor.d.ts.map +1 -1
- package/dist/types/step-executors/parallel-executor.d.ts.map +1 -1
- package/dist/types/step-executors/suspension-continuation.d.ts +10 -0
- package/dist/types/step-executors/suspension-continuation.d.ts.map +1 -0
- package/dist/types/suspension-rebuilder.d.ts.map +1 -1
- package/dist/types/workflow-runner.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/suspension-rebuilder.test.ts +43 -9
- package/src/__tests__/workflow-runner.test.ts +119 -1
- package/src/step-executors/loop-executor.ts +49 -64
- package/src/step-executors/parallel-executor.ts +13 -20
- package/src/step-executors/suspension-continuation.ts +31 -0
- package/src/suspension-rebuilder.ts +94 -100
- package/src/workflow-runner.ts +4 -11
|
@@ -9,6 +9,7 @@ exports.executeLoop = executeLoop;
|
|
|
9
9
|
exports.updateAccumulator = updateAccumulator;
|
|
10
10
|
exports.shouldStopLoop = shouldStopLoop;
|
|
11
11
|
const workflow_values_1 = require("../workflow-values");
|
|
12
|
+
const suspension_continuation_1 = require("./suspension-continuation");
|
|
12
13
|
/**
|
|
13
14
|
* Execute a loop step by iterating over input items and executing child steps.
|
|
14
15
|
*
|
|
@@ -49,30 +50,23 @@ async function executeForEachLoop(env, step, state, path, startIndex) {
|
|
|
49
50
|
index,
|
|
50
51
|
]);
|
|
51
52
|
if (suspension) {
|
|
52
|
-
return {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(suspension, async () => {
|
|
54
|
+
const postScope = buildScope(env, iterationState, { item, index }, accumulator);
|
|
55
|
+
accumulator = await updateAccumulator(step, postScope, accumulator);
|
|
56
|
+
const shouldStop = await shouldStopLoop(step, postScope);
|
|
57
|
+
if (shouldStop) {
|
|
58
|
+
state.accumulator = accumulator;
|
|
59
|
+
if (step.accumulate && step.name) {
|
|
60
|
+
state.output[step.name] = { [step.accumulate.as]: accumulator };
|
|
58
61
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return undefined;
|
|
68
|
-
}
|
|
69
|
-
return executeForEachLoop(env, step, {
|
|
70
|
-
...iterationState,
|
|
71
|
-
accumulator,
|
|
72
|
-
iterationStack: state.iterationStack,
|
|
73
|
-
}, path, index + 1);
|
|
74
|
-
},
|
|
75
|
-
};
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return executeForEachLoop(env, step, {
|
|
65
|
+
...iterationState,
|
|
66
|
+
accumulator,
|
|
67
|
+
iterationStack: state.iterationStack,
|
|
68
|
+
}, path, index + 1);
|
|
69
|
+
});
|
|
76
70
|
}
|
|
77
71
|
const postScope = buildScope(env, iterationState, { item, index }, accumulator);
|
|
78
72
|
accumulator = await updateAccumulator(step, postScope, accumulator);
|
|
@@ -104,22 +98,15 @@ async function executeWhileLoop(env, step, state, path, startIndex) {
|
|
|
104
98
|
index,
|
|
105
99
|
]);
|
|
106
100
|
if (suspension) {
|
|
107
|
-
return {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return executeWhileLoop(env, step, {
|
|
117
|
-
...iterationState,
|
|
118
|
-
accumulator,
|
|
119
|
-
iterationStack: state.iterationStack,
|
|
120
|
-
}, path, index + 1);
|
|
121
|
-
},
|
|
122
|
-
};
|
|
101
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(suspension, async () => {
|
|
102
|
+
const postScope = buildScope(env, iterationState, { item: undefined, index }, accumulator);
|
|
103
|
+
accumulator = await updateAccumulator(step, postScope, accumulator);
|
|
104
|
+
return executeWhileLoop(env, step, {
|
|
105
|
+
...iterationState,
|
|
106
|
+
accumulator,
|
|
107
|
+
iterationStack: state.iterationStack,
|
|
108
|
+
}, path, index + 1);
|
|
109
|
+
});
|
|
123
110
|
}
|
|
124
111
|
const postScope = buildScope(env, iterationState, { item: undefined, index }, accumulator);
|
|
125
112
|
accumulator = await updateAccumulator(step, postScope, accumulator);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.executeParallel = executeParallel;
|
|
9
9
|
const skip_helpers_1 = require("./skip-helpers");
|
|
10
|
+
const suspension_continuation_1 = require("./suspension-continuation");
|
|
10
11
|
/**
|
|
11
12
|
* Execute a set of steps in parallel order, respecting the configured strategy.
|
|
12
13
|
*
|
|
@@ -23,22 +24,15 @@ async function executeParallel(env, step, state, path, startIndex = 0) {
|
|
|
23
24
|
const childPath = [...path, 'parallel', index];
|
|
24
25
|
const suspension = await env.executeStep(child, state, childPath);
|
|
25
26
|
if (suspension) {
|
|
26
|
-
return {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (next) {
|
|
31
|
-
return next;
|
|
27
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(suspension, async () => {
|
|
28
|
+
if (step.strategy === 'wait_any') {
|
|
29
|
+
if (index + 1 < step.steps.length) {
|
|
30
|
+
(0, skip_helpers_1.markStepsSkipped)(env, step.steps.slice(index + 1), state.iterationStack);
|
|
32
31
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
return executeParallel(env, step, state, path, index + 1);
|
|
40
|
-
},
|
|
41
|
-
};
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return executeParallel(env, step, state, path, index + 1);
|
|
35
|
+
});
|
|
42
36
|
}
|
|
43
37
|
if (step.strategy === 'wait_any') {
|
|
44
38
|
if (index + 1 < step.steps.length) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
4
|
+
* Copyright (c) 2025 Hexastack.
|
|
5
|
+
* Full terms: see LICENSE.md.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.wrapSuspensionContinuation = wrapSuspensionContinuation;
|
|
9
|
+
/**
|
|
10
|
+
* Compose a suspension with the continuation that should run once it completes.
|
|
11
|
+
*
|
|
12
|
+
* When resuming a suspension yields another suspension from the same logical
|
|
13
|
+
* step, the follow-up suspension still needs to remember how to continue the
|
|
14
|
+
* surrounding flow after that step eventually finishes.
|
|
15
|
+
*/
|
|
16
|
+
function wrapSuspensionContinuation(suspension, onComplete) {
|
|
17
|
+
return {
|
|
18
|
+
...suspension,
|
|
19
|
+
continue: async (resumeData) => {
|
|
20
|
+
const next = await suspension.continue(resumeData);
|
|
21
|
+
if (next) {
|
|
22
|
+
return wrapSuspensionContinuation(next, onComplete);
|
|
23
|
+
}
|
|
24
|
+
return onComplete();
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -11,6 +11,7 @@ exports.buildSuspensionForPath = buildSuspensionForPath;
|
|
|
11
11
|
const loop_executor_1 = require("./step-executors/loop-executor");
|
|
12
12
|
const parallel_executor_1 = require("./step-executors/parallel-executor");
|
|
13
13
|
const skip_helpers_1 = require("./step-executors/skip-helpers");
|
|
14
|
+
const suspension_continuation_1 = require("./step-executors/suspension-continuation");
|
|
14
15
|
const workflow_event_emitter_1 = require("./workflow-event-emitter");
|
|
15
16
|
/**
|
|
16
17
|
* Parse a suspended step id into its execution path and iteration stack.
|
|
@@ -154,16 +155,7 @@ function buildSuspensionForPath(deps, steps, state, targetPath, pathPrefix, iter
|
|
|
154
155
|
}
|
|
155
156
|
const resumed = await executeStep(step, state, currentPath);
|
|
156
157
|
if (resumed) {
|
|
157
|
-
return
|
|
158
|
-
...resumed,
|
|
159
|
-
continue: async (nextResumeData) => {
|
|
160
|
-
const next = await resumed.continue(nextResumeData);
|
|
161
|
-
if (next) {
|
|
162
|
-
return next;
|
|
163
|
-
}
|
|
164
|
-
return deps.executeFlow(steps, state, pathPrefix, current + 1);
|
|
165
|
-
},
|
|
166
|
-
};
|
|
158
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(resumed, () => deps.executeFlow(steps, state, pathPrefix, current + 1));
|
|
167
159
|
}
|
|
168
160
|
return deps.executeFlow(steps, state, pathPrefix, current + 1);
|
|
169
161
|
}
|
|
@@ -188,22 +180,19 @@ function buildSuspensionForPath(deps, steps, state, targetPath, pathPrefix, iter
|
|
|
188
180
|
return null;
|
|
189
181
|
}
|
|
190
182
|
const childIndex = typeof childPath[0] === 'number' ? childPath[0] : 0;
|
|
191
|
-
return {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (next) {
|
|
196
|
-
return next;
|
|
183
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(childSuspension, async () => {
|
|
184
|
+
if (step.strategy === 'wait_any') {
|
|
185
|
+
if (childIndex + 1 < step.steps.length) {
|
|
186
|
+
(0, skip_helpers_1.markStepsSkipped)(env, step.steps.slice(childIndex + 1), state.iterationStack ?? []);
|
|
197
187
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
};
|
|
188
|
+
return deps.executeFlow(steps, state, pathPrefix, current + 1);
|
|
189
|
+
}
|
|
190
|
+
const next = await (0, parallel_executor_1.executeParallel)(env, step, state, currentPath, childIndex + 1);
|
|
191
|
+
if (next) {
|
|
192
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(next, () => deps.executeFlow(steps, state, pathPrefix, current + 1));
|
|
193
|
+
}
|
|
194
|
+
return deps.executeFlow(steps, state, pathPrefix, current + 1);
|
|
195
|
+
});
|
|
207
196
|
}
|
|
208
197
|
if (step.type === workflow_event_emitter_1.StepType.Conditional) {
|
|
209
198
|
if (rest[0] !== 'branch' || typeof rest[1] !== 'number') {
|
|
@@ -219,16 +208,7 @@ function buildSuspensionForPath(deps, steps, state, targetPath, pathPrefix, iter
|
|
|
219
208
|
if (!branchSuspension) {
|
|
220
209
|
return null;
|
|
221
210
|
}
|
|
222
|
-
return
|
|
223
|
-
...branchSuspension,
|
|
224
|
-
continue: async (resumeData) => {
|
|
225
|
-
const next = await branchSuspension.continue(resumeData);
|
|
226
|
-
if (next) {
|
|
227
|
-
return next;
|
|
228
|
-
}
|
|
229
|
-
return deps.executeFlow(steps, state, pathPrefix, current + 1);
|
|
230
|
-
},
|
|
231
|
-
};
|
|
211
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(branchSuspension, () => deps.executeFlow(steps, state, pathPrefix, current + 1));
|
|
232
212
|
}
|
|
233
213
|
if (step.type === workflow_event_emitter_1.StepType.Loop) {
|
|
234
214
|
if (rest.length < 1 || typeof rest[0] !== 'string') {
|
|
@@ -256,46 +236,53 @@ function buildSuspensionForPath(deps, steps, state, targetPath, pathPrefix, iter
|
|
|
256
236
|
return null;
|
|
257
237
|
}
|
|
258
238
|
const accumulator = iterationState.accumulator ?? state.accumulator ?? undefined;
|
|
259
|
-
return {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const updatedAccumulator = await (0, loop_executor_1.updateAccumulator)(step, scope, accumulator);
|
|
277
|
-
const shouldStop = await (0, loop_executor_1.shouldStopLoop)(step, scope);
|
|
278
|
-
state.output = iterationState.output;
|
|
279
|
-
if (step.accumulate && step.name) {
|
|
280
|
-
state.output[step.name] = {
|
|
281
|
-
[step.accumulate.as]: updatedAccumulator,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
if (step.accumulate) {
|
|
285
|
-
state.accumulator = updatedAccumulator;
|
|
286
|
-
}
|
|
287
|
-
if (shouldStop) {
|
|
288
|
-
return undefined;
|
|
289
|
-
}
|
|
290
|
-
const baseState = {
|
|
291
|
-
...iterationState,
|
|
292
|
-
iterationStack: ancestorIterationStack,
|
|
293
|
-
accumulator: updatedAccumulator,
|
|
294
|
-
output: state.output,
|
|
239
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(childSuspension, async () => {
|
|
240
|
+
const scope = {
|
|
241
|
+
input: iterationState.input,
|
|
242
|
+
context: env.context.state,
|
|
243
|
+
output: iterationState.output,
|
|
244
|
+
iteration: iterationState.iteration ?? {
|
|
245
|
+
item: undefined,
|
|
246
|
+
index: iterationIndex,
|
|
247
|
+
},
|
|
248
|
+
accumulator,
|
|
249
|
+
};
|
|
250
|
+
const updatedAccumulator = await (0, loop_executor_1.updateAccumulator)(step, scope, accumulator);
|
|
251
|
+
const shouldStop = await (0, loop_executor_1.shouldStopLoop)(step, scope);
|
|
252
|
+
state.output = iterationState.output;
|
|
253
|
+
if (step.accumulate && step.name) {
|
|
254
|
+
state.output[step.name] = {
|
|
255
|
+
[step.accumulate.as]: updatedAccumulator,
|
|
295
256
|
};
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
257
|
+
}
|
|
258
|
+
if (step.accumulate) {
|
|
259
|
+
state.accumulator = updatedAccumulator;
|
|
260
|
+
}
|
|
261
|
+
if (shouldStop) {
|
|
262
|
+
return deps.executeFlow(steps, state, pathPrefix, current + 1);
|
|
263
|
+
}
|
|
264
|
+
const baseState = {
|
|
265
|
+
...iterationState,
|
|
266
|
+
iterationStack: ancestorIterationStack,
|
|
267
|
+
accumulator: updatedAccumulator,
|
|
268
|
+
output: state.output,
|
|
269
|
+
};
|
|
270
|
+
const next = await (0, loop_executor_1.executeLoop)(env, step, baseState, currentPath, iterationIndex + 1);
|
|
271
|
+
state.output = baseState.output;
|
|
272
|
+
if (step.accumulate) {
|
|
273
|
+
state.accumulator = baseState.accumulator;
|
|
274
|
+
}
|
|
275
|
+
if (next) {
|
|
276
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(next, async () => {
|
|
277
|
+
state.output = baseState.output;
|
|
278
|
+
if (step.accumulate) {
|
|
279
|
+
state.accumulator = baseState.accumulator;
|
|
280
|
+
}
|
|
281
|
+
return deps.executeFlow(steps, state, pathPrefix, current + 1);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
return deps.executeFlow(steps, state, pathPrefix, current + 1);
|
|
285
|
+
});
|
|
299
286
|
}
|
|
300
287
|
return null;
|
|
301
288
|
}
|
|
@@ -11,6 +11,7 @@ const runner_runtime_control_1 = require("./runner-runtime-control");
|
|
|
11
11
|
const conditional_executor_1 = require("./step-executors/conditional-executor");
|
|
12
12
|
const loop_executor_1 = require("./step-executors/loop-executor");
|
|
13
13
|
const parallel_executor_1 = require("./step-executors/parallel-executor");
|
|
14
|
+
const suspension_continuation_1 = require("./step-executors/suspension-continuation");
|
|
14
15
|
const task_executor_1 = require("./step-executors/task-executor");
|
|
15
16
|
const suspension_rebuilder_1 = require("./suspension-rebuilder");
|
|
16
17
|
const workflow_event_emitter_1 = require("./workflow-event-emitter");
|
|
@@ -408,16 +409,7 @@ class WorkflowRunner {
|
|
|
408
409
|
const stepPath = [...path, index];
|
|
409
410
|
const suspension = await this.executeStep(step, state, stepPath);
|
|
410
411
|
if (suspension) {
|
|
411
|
-
return
|
|
412
|
-
...suspension,
|
|
413
|
-
continue: async (resumeData) => {
|
|
414
|
-
const next = await suspension.continue(resumeData);
|
|
415
|
-
if (next) {
|
|
416
|
-
return next;
|
|
417
|
-
}
|
|
418
|
-
return this.executeFlow(steps, state, path, index + 1);
|
|
419
|
-
},
|
|
420
|
-
};
|
|
412
|
+
return (0, suspension_continuation_1.wrapSuspensionContinuation)(suspension, () => this.executeFlow(steps, state, path, index + 1));
|
|
421
413
|
}
|
|
422
414
|
}
|
|
423
415
|
return undefined;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Full terms: see LICENSE.md.
|
|
5
5
|
*/
|
|
6
6
|
import { evaluateValue } from '../workflow-values';
|
|
7
|
+
import { wrapSuspensionContinuation } from './suspension-continuation';
|
|
7
8
|
/**
|
|
8
9
|
* Execute a loop step by iterating over input items and executing child steps.
|
|
9
10
|
*
|
|
@@ -44,30 +45,23 @@ async function executeForEachLoop(env, step, state, path, startIndex) {
|
|
|
44
45
|
index,
|
|
45
46
|
]);
|
|
46
47
|
if (suspension) {
|
|
47
|
-
return {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
return wrapSuspensionContinuation(suspension, async () => {
|
|
49
|
+
const postScope = buildScope(env, iterationState, { item, index }, accumulator);
|
|
50
|
+
accumulator = await updateAccumulator(step, postScope, accumulator);
|
|
51
|
+
const shouldStop = await shouldStopLoop(step, postScope);
|
|
52
|
+
if (shouldStop) {
|
|
53
|
+
state.accumulator = accumulator;
|
|
54
|
+
if (step.accumulate && step.name) {
|
|
55
|
+
state.output[step.name] = { [step.accumulate.as]: accumulator };
|
|
53
56
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return undefined;
|
|
63
|
-
}
|
|
64
|
-
return executeForEachLoop(env, step, {
|
|
65
|
-
...iterationState,
|
|
66
|
-
accumulator,
|
|
67
|
-
iterationStack: state.iterationStack,
|
|
68
|
-
}, path, index + 1);
|
|
69
|
-
},
|
|
70
|
-
};
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return executeForEachLoop(env, step, {
|
|
60
|
+
...iterationState,
|
|
61
|
+
accumulator,
|
|
62
|
+
iterationStack: state.iterationStack,
|
|
63
|
+
}, path, index + 1);
|
|
64
|
+
});
|
|
71
65
|
}
|
|
72
66
|
const postScope = buildScope(env, iterationState, { item, index }, accumulator);
|
|
73
67
|
accumulator = await updateAccumulator(step, postScope, accumulator);
|
|
@@ -99,22 +93,15 @@ async function executeWhileLoop(env, step, state, path, startIndex) {
|
|
|
99
93
|
index,
|
|
100
94
|
]);
|
|
101
95
|
if (suspension) {
|
|
102
|
-
return {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return executeWhileLoop(env, step, {
|
|
112
|
-
...iterationState,
|
|
113
|
-
accumulator,
|
|
114
|
-
iterationStack: state.iterationStack,
|
|
115
|
-
}, path, index + 1);
|
|
116
|
-
},
|
|
117
|
-
};
|
|
96
|
+
return wrapSuspensionContinuation(suspension, async () => {
|
|
97
|
+
const postScope = buildScope(env, iterationState, { item: undefined, index }, accumulator);
|
|
98
|
+
accumulator = await updateAccumulator(step, postScope, accumulator);
|
|
99
|
+
return executeWhileLoop(env, step, {
|
|
100
|
+
...iterationState,
|
|
101
|
+
accumulator,
|
|
102
|
+
iterationStack: state.iterationStack,
|
|
103
|
+
}, path, index + 1);
|
|
104
|
+
});
|
|
118
105
|
}
|
|
119
106
|
const postScope = buildScope(env, iterationState, { item: undefined, index }, accumulator);
|
|
120
107
|
accumulator = await updateAccumulator(step, postScope, accumulator);
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Full terms: see LICENSE.md.
|
|
5
5
|
*/
|
|
6
6
|
import { markStepsSkipped } from './skip-helpers';
|
|
7
|
+
import { wrapSuspensionContinuation } from './suspension-continuation';
|
|
7
8
|
/**
|
|
8
9
|
* Execute a set of steps in parallel order, respecting the configured strategy.
|
|
9
10
|
*
|
|
@@ -20,22 +21,15 @@ export async function executeParallel(env, step, state, path, startIndex = 0) {
|
|
|
20
21
|
const childPath = [...path, 'parallel', index];
|
|
21
22
|
const suspension = await env.executeStep(child, state, childPath);
|
|
22
23
|
if (suspension) {
|
|
23
|
-
return {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (next) {
|
|
28
|
-
return next;
|
|
24
|
+
return wrapSuspensionContinuation(suspension, async () => {
|
|
25
|
+
if (step.strategy === 'wait_any') {
|
|
26
|
+
if (index + 1 < step.steps.length) {
|
|
27
|
+
markStepsSkipped(env, step.steps.slice(index + 1), state.iterationStack);
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
return executeParallel(env, step, state, path, index + 1);
|
|
37
|
-
},
|
|
38
|
-
};
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return executeParallel(env, step, state, path, index + 1);
|
|
32
|
+
});
|
|
39
33
|
}
|
|
40
34
|
if (step.strategy === 'wait_any') {
|
|
41
35
|
if (index + 1 < step.steps.length) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2025 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Compose a suspension with the continuation that should run once it completes.
|
|
8
|
+
*
|
|
9
|
+
* When resuming a suspension yields another suspension from the same logical
|
|
10
|
+
* step, the follow-up suspension still needs to remember how to continue the
|
|
11
|
+
* surrounding flow after that step eventually finishes.
|
|
12
|
+
*/
|
|
13
|
+
export function wrapSuspensionContinuation(suspension, onComplete) {
|
|
14
|
+
return {
|
|
15
|
+
...suspension,
|
|
16
|
+
continue: async (resumeData) => {
|
|
17
|
+
const next = await suspension.continue(resumeData);
|
|
18
|
+
if (next) {
|
|
19
|
+
return wrapSuspensionContinuation(next, onComplete);
|
|
20
|
+
}
|
|
21
|
+
return onComplete();
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|