@kortyx/hooks 0.23.1 → 0.24.0
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/CHANGELOG.md +7 -0
- package/README.md +14 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +220 -17
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.24.0](https://github.com/kortyx-io/kortyx/compare/hooks-v0.23.1...hooks-v0.24.0) (2026-09-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **hooks:** support durable parallel child workflows ([#171](https://github.com/kortyx-io/kortyx/issues/171)) ([fdf2d56](https://github.com/kortyx-io/kortyx/commit/fdf2d564d88e921988e8f3e20da9fe0c30bbcdf9))
|
|
9
|
+
|
|
3
10
|
## [0.23.1](https://github.com/kortyx-io/kortyx/compare/hooks-v0.23.0...hooks-v0.23.1) (2026-09-11)
|
|
4
11
|
|
|
5
12
|
|
package/README.md
CHANGED
|
@@ -78,6 +78,19 @@ string ids. Register the same definitions with the agent.
|
|
|
78
78
|
Calls need stable ids. A child interrupt pauses the chain; resuming replays
|
|
79
79
|
the enclosing node and reuses saved child results. Keep effects before calls
|
|
80
80
|
idempotent. Fork/rollback retain nested checkpoints; use Redis for persistence
|
|
81
|
-
across server restarts.
|
|
81
|
+
across server restarts. Join concurrent children with `parallel`:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const [first, second] = await parallel([
|
|
85
|
+
useWorkflow({ id: "first", workflow: researchWorkflow, input: { topic: "AI" } }),
|
|
86
|
+
useWorkflow({ id: "second", workflow: researchWorkflow, input: { topic: "Robotics" } }),
|
|
87
|
+
]);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The tuple follows input order. The join preserves all siblings before pausing and
|
|
91
|
+
presents waiting questions individually through the parent handle. Terminal group
|
|
92
|
+
failures throw `ParallelError`, whose `results` exposes each settled outcome.
|
|
93
|
+
Await groups sequentially within one node; children may contain nested groups.
|
|
94
|
+
Native `Promise.all` calls and parallel graph edges remain unsupported.
|
|
82
95
|
|
|
83
96
|
See [the execution and replay contract](../../docs/design-specs/child-workflows.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,16 @@ type CompleteResponseOptions = {
|
|
|
13
13
|
/** Finish client delivery, leaving the node and workflow execution running. */
|
|
14
14
|
declare function completeResponse(options?: CompleteResponseOptions): Promise<void>;
|
|
15
15
|
|
|
16
|
+
/** All siblings have settled; results retain the order passed to parallel(). */
|
|
17
|
+
declare class ParallelError extends AggregateError {
|
|
18
|
+
readonly results: readonly PromiseSettledResult<unknown>[];
|
|
19
|
+
constructor(results: readonly PromiseSettledResult<unknown>[]);
|
|
20
|
+
}
|
|
21
|
+
/** Join concurrent child calls, preserving their snapshots before suspending. */
|
|
22
|
+
declare function parallel<const T extends readonly unknown[]>(values: T): Promise<{
|
|
23
|
+
-readonly [K in keyof T]: Awaited<T[K]>;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
16
26
|
type WorkflowCallOutcome = {
|
|
17
27
|
status: "completed";
|
|
18
28
|
data: Record<string, unknown>;
|
|
@@ -236,4 +246,4 @@ declare function useWorkflowState<T>(key: string, ...rest: [] | [T]): [T, StateS
|
|
|
236
246
|
/** Best effort lifecycle facts. Engine snapshots and interrupt tokens never leave the runtime. */
|
|
237
247
|
declare function emitWorkflowCall(telemetry: KortyxTelemetryConfig | undefined, type: KortyxTelemetryEventType, payload: Record<string, unknown>): void;
|
|
238
248
|
|
|
239
|
-
export { type CompleteResponseOptions, type HookRuntimeContext, KortyxTelemetryConfig, KortyxTelemetryEventType, KortyxTraceMetadata, ReasonTraceAdapter, type UseInterruptArgs, type UseReasonArgs, type UseReasonResult, type UseReasonStep, type UseReasonToolExecution, type UseStructuredDataArgs, WorkflowCallError, type WorkflowCallOutcome, type WorkflowCallService, completeResponse, createWorkflowHooks, emitWorkflowCall, runWithHookContext, useAbortSignal, useInterrupt, useNodeState, useReason, useRuntimeContext, useStructuredData, useWorkflow, useWorkflowState, workflowCallFingerprint };
|
|
249
|
+
export { type CompleteResponseOptions, type HookRuntimeContext, KortyxTelemetryConfig, KortyxTelemetryEventType, KortyxTraceMetadata, ParallelError, ReasonTraceAdapter, type UseInterruptArgs, type UseReasonArgs, type UseReasonResult, type UseReasonStep, type UseReasonToolExecution, type UseStructuredDataArgs, WorkflowCallError, type WorkflowCallOutcome, type WorkflowCallService, completeResponse, createWorkflowHooks, emitWorkflowCall, parallel, runWithHookContext, useAbortSignal, useInterrupt, useNodeState, useReason, useRuntimeContext, useStructuredData, useWorkflow, useWorkflowState, workflowCallFingerprint };
|
package/dist/index.js
CHANGED
|
@@ -19,10 +19,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
// src/index.ts
|
|
20
20
|
var index_exports = {};
|
|
21
21
|
__export(index_exports, {
|
|
22
|
+
ParallelError: () => ParallelError,
|
|
22
23
|
WorkflowCallError: () => WorkflowCallError,
|
|
23
24
|
completeResponse: () => completeResponse,
|
|
24
25
|
createWorkflowHooks: () => createWorkflowHooks,
|
|
25
26
|
emitWorkflowCall: () => emitWorkflowCall,
|
|
27
|
+
parallel: () => parallel,
|
|
26
28
|
runWithHookContext: () => runWithHookContext,
|
|
27
29
|
useAbortSignal: () => useAbortSignal,
|
|
28
30
|
useInterrupt: () => useInterrupt,
|
|
@@ -90,6 +92,11 @@ var createInternalContext = (ctx) => {
|
|
|
90
92
|
...ctx,
|
|
91
93
|
workflowCallActive: false,
|
|
92
94
|
workflowCallIds: /* @__PURE__ */ new Set(),
|
|
95
|
+
workflowTasks: [],
|
|
96
|
+
parallelJoins: [],
|
|
97
|
+
parallelGroup: void 0,
|
|
98
|
+
parallelIndex: 0,
|
|
99
|
+
workflowContextClosed: false,
|
|
93
100
|
nodeStateIndex: 0,
|
|
94
101
|
reasonCallIndex: 0,
|
|
95
102
|
currentNodeState,
|
|
@@ -161,10 +168,29 @@ function getReasonTraceAdapter() {
|
|
|
161
168
|
async function runWithHookContext(ctx, fn) {
|
|
162
169
|
const internal = createInternalContext(ctx);
|
|
163
170
|
try {
|
|
164
|
-
const result = await storage.run(internal,
|
|
171
|
+
const result = await storage.run(internal, async () => {
|
|
172
|
+
const drain = async () => {
|
|
173
|
+
let drained = -1;
|
|
174
|
+
while (drained !== internal.parallelJoins.length + internal.workflowTasks.length) {
|
|
175
|
+
drained = internal.parallelJoins.length + internal.workflowTasks.length;
|
|
176
|
+
await Promise.allSettled(internal.parallelJoins);
|
|
177
|
+
await Promise.all(internal.workflowTasks.map((task) => task.done));
|
|
178
|
+
}
|
|
179
|
+
internal.workflowContextClosed = true;
|
|
180
|
+
};
|
|
181
|
+
try {
|
|
182
|
+
const result2 = await fn();
|
|
183
|
+
await drain();
|
|
184
|
+
if (internal.workflowControlError) throw internal.workflowControlError;
|
|
185
|
+
return result2;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
await drain();
|
|
188
|
+
throw internal.workflowControlError ?? error;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
165
191
|
cleanupCompletedReasonCheckpoints(internal);
|
|
166
192
|
for (const key of Object.keys(internal.currentNodeState.byKey)) {
|
|
167
|
-
if (key.startsWith("__useWorkflow:") || key === "__useWorkflowActivation") {
|
|
193
|
+
if (key.startsWith("__useWorkflow:") || key.startsWith("__parallel:") || key === "__useWorkflowActivation") {
|
|
168
194
|
internal.currentNodeState.byKey[key] = null;
|
|
169
195
|
internal.stateDirty = true;
|
|
170
196
|
}
|
|
@@ -2662,9 +2688,161 @@ function useWorkflowState(key, ...rest) {
|
|
|
2662
2688
|
return [getValue(), setValue];
|
|
2663
2689
|
}
|
|
2664
2690
|
|
|
2691
|
+
// src/parallel.ts
|
|
2692
|
+
var import_core6 = require("@kortyx/core");
|
|
2693
|
+
var ParallelChildWaiting = class extends Error {
|
|
2694
|
+
};
|
|
2695
|
+
var ParallelError = class extends AggregateError {
|
|
2696
|
+
constructor(results) {
|
|
2697
|
+
super(
|
|
2698
|
+
results.flatMap(
|
|
2699
|
+
(result) => result.status === "rejected" ? [result.reason] : []
|
|
2700
|
+
),
|
|
2701
|
+
"One or more parallel operations failed."
|
|
2702
|
+
);
|
|
2703
|
+
this.results = results;
|
|
2704
|
+
this.name = "ParallelError";
|
|
2705
|
+
}
|
|
2706
|
+
results;
|
|
2707
|
+
};
|
|
2708
|
+
function registerWorkflowTask(id) {
|
|
2709
|
+
const ctx = getHookContext();
|
|
2710
|
+
if (ctx.workflowControlError) throw ctx.workflowControlError;
|
|
2711
|
+
if (ctx.workflowContextClosed)
|
|
2712
|
+
throw new Error("The workflow node activation has finished.");
|
|
2713
|
+
const group = ctx.parallelGroup;
|
|
2714
|
+
if (group?.restored && !group.record.calls.includes(id)) {
|
|
2715
|
+
throw new Error(`Parallel call membership changed during replay: '${id}'.`);
|
|
2716
|
+
}
|
|
2717
|
+
let finish;
|
|
2718
|
+
const task = {
|
|
2719
|
+
id,
|
|
2720
|
+
...group ? { group } : {},
|
|
2721
|
+
completed: false,
|
|
2722
|
+
done: new Promise((resolve) => {
|
|
2723
|
+
finish = resolve;
|
|
2724
|
+
}),
|
|
2725
|
+
finish: () => {
|
|
2726
|
+
task.completed = true;
|
|
2727
|
+
finish();
|
|
2728
|
+
}
|
|
2729
|
+
};
|
|
2730
|
+
ctx.workflowTasks.push(task);
|
|
2731
|
+
if (group && !group.record.calls.includes(id)) group.record.calls.push(id);
|
|
2732
|
+
return task;
|
|
2733
|
+
}
|
|
2734
|
+
function parallel(values) {
|
|
2735
|
+
const settled = Promise.allSettled(values);
|
|
2736
|
+
let ctx;
|
|
2737
|
+
try {
|
|
2738
|
+
ctx = getHookContext();
|
|
2739
|
+
} catch (error) {
|
|
2740
|
+
return Promise.reject(error);
|
|
2741
|
+
}
|
|
2742
|
+
const join = async () => {
|
|
2743
|
+
if (ctx.workflowControlError) throw ctx.workflowControlError;
|
|
2744
|
+
if (ctx.parallelGroup)
|
|
2745
|
+
throw new Error(
|
|
2746
|
+
"Await each parallel group before starting another in the same node."
|
|
2747
|
+
);
|
|
2748
|
+
const key = `__parallel:${ctx.parallelIndex++}`;
|
|
2749
|
+
const saved = ctx.currentNodeState.byKey[key];
|
|
2750
|
+
const tasks = ctx.workflowTasks.filter(
|
|
2751
|
+
(task) => !task.completed && !task.group
|
|
2752
|
+
);
|
|
2753
|
+
const ids = tasks.map((task) => task.id);
|
|
2754
|
+
if (saved && (saved.version !== 1 || ids.some((id) => !saved.calls.includes(id)))) {
|
|
2755
|
+
throw new Error("Parallel call membership changed during replay.");
|
|
2756
|
+
}
|
|
2757
|
+
const group = {
|
|
2758
|
+
key,
|
|
2759
|
+
record: saved ?? { version: 1, calls: ids, interrupts: [] },
|
|
2760
|
+
restored: Boolean(saved),
|
|
2761
|
+
ready: Promise.resolve()
|
|
2762
|
+
};
|
|
2763
|
+
ctx.currentNodeState.byKey[key] = group.record;
|
|
2764
|
+
ctx.stateDirty = true;
|
|
2765
|
+
ctx.parallelGroup = group;
|
|
2766
|
+
for (const task of tasks) task.group = group;
|
|
2767
|
+
const callRecord = (id) => ctx.currentNodeState.byKey[`__useWorkflow:${id}`];
|
|
2768
|
+
const bridge = async (callId, index) => {
|
|
2769
|
+
const interruption = callRecord(callId)?.interrupts[index];
|
|
2770
|
+
if (!interruption)
|
|
2771
|
+
throw new Error(
|
|
2772
|
+
"Parallel interrupt history does not match the saved child."
|
|
2773
|
+
);
|
|
2774
|
+
try {
|
|
2775
|
+
interruption.response = await awaitInterruptInternal({
|
|
2776
|
+
request: interruption.request,
|
|
2777
|
+
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
2778
|
+
});
|
|
2779
|
+
} catch (error) {
|
|
2780
|
+
ctx.workflowControlError = error;
|
|
2781
|
+
throw error;
|
|
2782
|
+
}
|
|
2783
|
+
};
|
|
2784
|
+
group.ready = (async () => {
|
|
2785
|
+
for (const entry of group.record.interrupts)
|
|
2786
|
+
await bridge(entry.callId, entry.index);
|
|
2787
|
+
})();
|
|
2788
|
+
try {
|
|
2789
|
+
const [ready, outcomes] = await Promise.all([
|
|
2790
|
+
Promise.allSettled([group.ready]),
|
|
2791
|
+
settled
|
|
2792
|
+
]);
|
|
2793
|
+
let drained = -1;
|
|
2794
|
+
while (drained !== ctx.workflowTasks.length) {
|
|
2795
|
+
drained = ctx.workflowTasks.length;
|
|
2796
|
+
await Promise.all(
|
|
2797
|
+
ctx.workflowTasks.filter((task) => task.group === group).map((task) => task.done)
|
|
2798
|
+
);
|
|
2799
|
+
}
|
|
2800
|
+
if (ready[0]?.status === "rejected") throw ready[0].reason;
|
|
2801
|
+
(0, import_core6.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2802
|
+
if (ctx.workflowControlError) throw ctx.workflowControlError;
|
|
2803
|
+
const rejected = outcomes.filter(
|
|
2804
|
+
(outcome) => outcome.status === "rejected"
|
|
2805
|
+
);
|
|
2806
|
+
const control = rejected.find(
|
|
2807
|
+
(outcome) => (0, import_core6.isExecutionCancelled)(outcome.reason) || (0, import_core6.isExecutionLimitReached)(outcome.reason)
|
|
2808
|
+
);
|
|
2809
|
+
if (control) {
|
|
2810
|
+
ctx.workflowControlError = control.reason;
|
|
2811
|
+
throw control.reason;
|
|
2812
|
+
}
|
|
2813
|
+
const actual = ctx.workflowTasks.filter((task) => task.group === group).map((task) => task.id);
|
|
2814
|
+
if (group.record.calls.length !== actual.length || group.record.calls.some((id) => !actual.includes(id))) {
|
|
2815
|
+
throw new Error("Parallel call membership changed during replay.");
|
|
2816
|
+
}
|
|
2817
|
+
for (const callId of group.record.calls) {
|
|
2818
|
+
const record = callRecord(callId);
|
|
2819
|
+
if (record?.status !== "interrupted") continue;
|
|
2820
|
+
const index = record.interrupts.length - 1;
|
|
2821
|
+
if (record.interrupts[index]?.response !== void 0) continue;
|
|
2822
|
+
group.record.interrupts.push({ callId, index });
|
|
2823
|
+
await bridge(callId, index);
|
|
2824
|
+
throw new Error(
|
|
2825
|
+
"Parallel interrupt unexpectedly resumed without replay."
|
|
2826
|
+
);
|
|
2827
|
+
}
|
|
2828
|
+
if (rejected.length) throw new ParallelError(outcomes);
|
|
2829
|
+
return outcomes.map(
|
|
2830
|
+
(outcome) => outcome.value
|
|
2831
|
+
);
|
|
2832
|
+
} finally {
|
|
2833
|
+
ctx.parallelGroup = void 0;
|
|
2834
|
+
}
|
|
2835
|
+
};
|
|
2836
|
+
const promise = join();
|
|
2837
|
+
ctx.parallelJoins.push(promise);
|
|
2838
|
+
void promise.catch(() => {
|
|
2839
|
+
});
|
|
2840
|
+
return promise;
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2665
2843
|
// src/workflow.ts
|
|
2666
2844
|
var import_node_crypto5 = require("crypto");
|
|
2667
|
-
var
|
|
2845
|
+
var import_core7 = require("@kortyx/core");
|
|
2668
2846
|
|
|
2669
2847
|
// src/workflow-telemetry.ts
|
|
2670
2848
|
var import_node_crypto4 = require("crypto");
|
|
@@ -2750,16 +2928,30 @@ function createWorkflowHooks(workflows) {
|
|
|
2750
2928
|
};
|
|
2751
2929
|
}
|
|
2752
2930
|
async function useWorkflow(args) {
|
|
2931
|
+
const task = registerWorkflowTask(args.id);
|
|
2932
|
+
try {
|
|
2933
|
+
await Promise.resolve();
|
|
2934
|
+
if (task.group) await task.group.ready;
|
|
2935
|
+
return await runWorkflowCall(args, task);
|
|
2936
|
+
} catch (error) {
|
|
2937
|
+
if ((0, import_core7.isExecutionCancelled)(error) || (0, import_core7.isExecutionLimitReached)(error))
|
|
2938
|
+
getHookContext().workflowControlError = error;
|
|
2939
|
+
throw error;
|
|
2940
|
+
} finally {
|
|
2941
|
+
task.finish();
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
async function runWorkflowCall(args, task) {
|
|
2753
2945
|
const ctx = getHookContext();
|
|
2754
|
-
(0,
|
|
2946
|
+
(0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2755
2947
|
const service = ctx.node.callWorkflow;
|
|
2756
2948
|
if (!service)
|
|
2757
2949
|
throw new Error("useWorkflow requires an agent workflow registry.");
|
|
2758
2950
|
if (!args.id.trim())
|
|
2759
2951
|
throw new Error("useWorkflow requires a stable, nonempty id.");
|
|
2760
|
-
if (ctx.workflowCallActive)
|
|
2952
|
+
if (ctx.workflowCallActive && !task.group)
|
|
2761
2953
|
throw new Error(
|
|
2762
|
-
"Concurrent workflow calls
|
|
2954
|
+
"Concurrent workflow calls require parallel([...]); otherwise await each call."
|
|
2763
2955
|
);
|
|
2764
2956
|
if (ctx.workflowCallIds.has(args.id))
|
|
2765
2957
|
throw new Error(
|
|
@@ -2780,6 +2972,11 @@ async function useWorkflow(args) {
|
|
|
2780
2972
|
});
|
|
2781
2973
|
const key = `__useWorkflow:${args.id}`;
|
|
2782
2974
|
let record = ctx.currentNodeState.byKey[key];
|
|
2975
|
+
if (record && record.parallelKey !== task.group?.key)
|
|
2976
|
+
throw new WorkflowCallError(
|
|
2977
|
+
workflow,
|
|
2978
|
+
"parallel call membership changed during replay"
|
|
2979
|
+
);
|
|
2783
2980
|
if (record && record.fingerprint !== fingerprint)
|
|
2784
2981
|
throw new WorkflowCallError(
|
|
2785
2982
|
workflow,
|
|
@@ -2790,11 +2987,12 @@ async function useWorkflow(args) {
|
|
|
2790
2987
|
fingerprint,
|
|
2791
2988
|
invocationId: (0, import_node_crypto5.randomUUID)(),
|
|
2792
2989
|
status: "running",
|
|
2793
|
-
interrupts: []
|
|
2990
|
+
interrupts: [],
|
|
2991
|
+
...task.group ? { parallelKey: task.group.key } : {}
|
|
2794
2992
|
};
|
|
2795
2993
|
ctx.currentNodeState.byKey[key] = record;
|
|
2796
2994
|
ctx.stateDirty = true;
|
|
2797
|
-
ctx.workflowCallActive = true;
|
|
2995
|
+
if (!task.group) ctx.workflowCallActive = true;
|
|
2798
2996
|
const telemetry = ctx.node.workflowCallTelemetry;
|
|
2799
2997
|
const activationKey = "__useWorkflowActivation";
|
|
2800
2998
|
ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto5.randomUUID)();
|
|
@@ -2830,7 +3028,7 @@ async function useWorkflow(args) {
|
|
|
2830
3028
|
};
|
|
2831
3029
|
const cached = record.status === "completed";
|
|
2832
3030
|
try {
|
|
2833
|
-
for (const interruption of record.interrupts) {
|
|
3031
|
+
for (const interruption of task.group ? [] : record.interrupts) {
|
|
2834
3032
|
interruption.response = await awaitInterruptInternal({
|
|
2835
3033
|
request: interruption.request,
|
|
2836
3034
|
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
@@ -2838,6 +3036,8 @@ async function useWorkflow(args) {
|
|
|
2838
3036
|
}
|
|
2839
3037
|
if (record.status === "failed")
|
|
2840
3038
|
throw new WorkflowCallError(workflow, record.error ?? "child failed");
|
|
3039
|
+
if (task.group && record.status === "interrupted" && record.interrupts.at(-1)?.response === void 0)
|
|
3040
|
+
throw new ParallelChildWaiting();
|
|
2841
3041
|
while (record.status !== "completed") {
|
|
2842
3042
|
let outcome;
|
|
2843
3043
|
try {
|
|
@@ -2856,13 +3056,13 @@ async function useWorkflow(args) {
|
|
|
2856
3056
|
...record.interrupts.length ? { response: record.interrupts.at(-1)?.response } : {}
|
|
2857
3057
|
});
|
|
2858
3058
|
} catch (error) {
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
3059
|
+
const child = error;
|
|
3060
|
+
recordUsage(child?.__kortyxChildUsage);
|
|
3061
|
+
(0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
3062
|
+
if ((0, import_core7.isExecutionCancelled)(error)) throw error;
|
|
3063
|
+
if ((0, import_core7.isExecutionLimitReached)(error)) {
|
|
3064
|
+
if (child?.__kortyxChildSnapshot)
|
|
2864
3065
|
record.snapshot = child.__kortyxChildSnapshot;
|
|
2865
|
-
recordUsage(child.__kortyxChildUsage);
|
|
2866
3066
|
report("suspended", {
|
|
2867
3067
|
reason: "limit_reached",
|
|
2868
3068
|
limit: error.limitReached
|
|
@@ -2874,7 +3074,7 @@ async function useWorkflow(args) {
|
|
|
2874
3074
|
report("failed", { error: "Child workflow failed" });
|
|
2875
3075
|
throw new WorkflowCallError(workflow, record.error);
|
|
2876
3076
|
}
|
|
2877
|
-
(0,
|
|
3077
|
+
(0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2878
3078
|
recordUsage(outcome.usage);
|
|
2879
3079
|
if (outcome.status === "completed") {
|
|
2880
3080
|
record.data = outcome.data;
|
|
@@ -2895,6 +3095,7 @@ async function useWorkflow(args) {
|
|
|
2895
3095
|
request: outcome.request
|
|
2896
3096
|
};
|
|
2897
3097
|
record.interrupts.push(interruption);
|
|
3098
|
+
if (task.group) throw new ParallelChildWaiting();
|
|
2898
3099
|
interruption.response = await awaitInterruptInternal({
|
|
2899
3100
|
request: interruption.request,
|
|
2900
3101
|
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
@@ -2908,15 +3109,17 @@ async function useWorkflow(args) {
|
|
|
2908
3109
|
data: JSON.parse(JSON.stringify(data))
|
|
2909
3110
|
};
|
|
2910
3111
|
} finally {
|
|
2911
|
-
ctx.workflowCallActive = false;
|
|
3112
|
+
if (!task.group) ctx.workflowCallActive = false;
|
|
2912
3113
|
}
|
|
2913
3114
|
}
|
|
2914
3115
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2915
3116
|
0 && (module.exports = {
|
|
3117
|
+
ParallelError,
|
|
2916
3118
|
WorkflowCallError,
|
|
2917
3119
|
completeResponse,
|
|
2918
3120
|
createWorkflowHooks,
|
|
2919
3121
|
emitWorkflowCall,
|
|
3122
|
+
parallel,
|
|
2920
3123
|
runWithHookContext,
|
|
2921
3124
|
useAbortSignal,
|
|
2922
3125
|
useInterrupt,
|