pi-subagents 0.83.1 → 0.84.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 +17 -0
- package/bundle/{chunk-5YIITCPD.js → chunk-235AWJUQ.js} +24 -21
- package/bundle/{chunk-YXFJVCOL.js → chunk-4E7BKDTX.js} +3 -3
- package/bundle/{chunk-UFL7IYCW.js → chunk-7BWGNDNK.js} +2 -2
- package/bundle/{chunk-ZVOAMTUQ.js → chunk-AKOIVLA5.js} +3 -3
- package/bundle/{chunk-RNIZLSEA.js → chunk-CGN2TVE7.js} +1 -0
- package/bundle/{chunk-F5DBXJ2G.js → chunk-DZDETGRJ.js} +8 -5
- package/bundle/{chunk-C7JMPU5F.js → chunk-J5YL7LYR.js} +1 -1
- package/bundle/{chunk-UUP372DR.js → chunk-JG4QKI5M.js} +22 -8
- package/bundle/{chunk-UYK5MZWE.js → chunk-L3WFKBEI.js} +1 -1
- package/bundle/{chunk-VLFZIN6D.js → chunk-NENDSQUY.js} +2 -2
- package/bundle/{chunk-MABL3FUM.js → chunk-NKQ6JNAS.js} +1 -1
- package/bundle/{chunk-QP5ZWNIS.js → chunk-PALJGGYC.js} +1 -1
- package/bundle/{chunk-IPGZMQXV.js → chunk-PV63UFUR.js} +5 -5
- package/bundle/{chunk-AHIFYGR3.js → chunk-VQVVHSY6.js} +5 -5
- package/bundle/{chunk-NPIYWVOD.js → chunk-XDQORLZ5.js} +3 -3
- package/bundle/index.js +1 -1
- package/bundle/preflight.js +2 -2
- package/package.json +1 -1
- package/src/runs/background/async-execution.d.ts +3 -0
- package/src/runs/background/async-execution.js +15 -0
- package/src/runs/background/async-retention.d.ts +1 -0
- package/src/runs/background/async-retention.js +16 -10
- package/src/runs/background/run-child-session.d.ts +2 -0
- package/src/runs/background/run-child-session.js +9 -1
- package/src/runs/background/subagent-runner.js +1 -0
- package/src/runs/foreground/subagent-executor.js +5 -0
- package/src/runs/shared/child-session.d.ts +2 -0
- package/src/runs/shared/child-session.js +1 -0
- package/src/runs/shared/model-resolution-diagnostic.d.ts +11 -0
- package/src/runs/shared/model-resolution-diagnostic.js +25 -0
- package/src/runs/shared/parallel-utils.d.ts +2 -0
- package/src/workflows/scripted-workflow.js +2 -0
- package/workflow-script-worker.cjs +449 -175
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
1
3
|
const { parentPort, workerData } = require("node:worker_threads");
|
|
2
4
|
const vm = require("node:vm");
|
|
3
5
|
const { inspect } = require("node:util");
|
|
@@ -18,6 +20,72 @@ function createWorkflowPromiseHook(callbacks) {
|
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
// Workflow scripts run in their own vm context (the script realm). The API a script can reach (runs,
|
|
24
|
+
// state, emit, console, Promise, results, errors) is built by workflowRuntime inside a second vm
|
|
25
|
+
// context (the runtime realm), compiled from this function's source. Neither realm can compile code
|
|
26
|
+
// from strings, and no worker-realm object may ever become reachable from either: one would hand the
|
|
27
|
+
// script the worker's Function, and with it process. The runtime talks to the worker only through
|
|
28
|
+
// `bridge`, which it keeps in this closure, passes only strings and numbers, and calls in try/catch.
|
|
29
|
+
// Engine paths can still throw a worker-realm error into script code (a stack overflow inside Node's
|
|
30
|
+
// stack formatter, for one), so lockWorkerRealm also makes every worker value useless before a script runs.
|
|
31
|
+
function workflowRuntime(bridge, scriptRealm, stateEnabled) {
|
|
32
|
+
"use strict";
|
|
33
|
+
|
|
34
|
+
// Promise hooks report every promise in the isolate, the worker's own included. Captured before any
|
|
35
|
+
// script runs, so a script that replaces Object.getPrototypeOf cannot receive a worker promise.
|
|
36
|
+
const getPrototypeOf = Object.getPrototypeOf;
|
|
37
|
+
const runtimePromisePrototype = Promise.prototype;
|
|
38
|
+
const nativePromisePrototype = scriptRealm.promisePrototype;
|
|
39
|
+
|
|
40
|
+
function isWorkflowRealmPromise(value) {
|
|
41
|
+
if (!value || typeof value !== "object") return false;
|
|
42
|
+
let prototype = getPrototypeOf(value);
|
|
43
|
+
for (let depth = 0; prototype && depth < 16; depth++) {
|
|
44
|
+
if (prototype === nativePromisePrototype || prototype === runtimePromisePrototype) return true;
|
|
45
|
+
prototype = getPrototypeOf(prototype);
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function post(message) {
|
|
51
|
+
const json = JSON.stringify(message);
|
|
52
|
+
let posted = false;
|
|
53
|
+
try {
|
|
54
|
+
posted = bridge.post(json);
|
|
55
|
+
} catch {}
|
|
56
|
+
if (posted !== true) throw new Error("Workflow runtime could not send a message to the host.");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function inspectValue(value) {
|
|
60
|
+
let encoded;
|
|
61
|
+
try {
|
|
62
|
+
encoded = bridge.inspect(value);
|
|
63
|
+
} catch {}
|
|
64
|
+
if (typeof encoded !== "string") throw new Error("console could not format a value.");
|
|
65
|
+
const decoded = JSON.parse(encoded);
|
|
66
|
+
if (decoded[0] !== true) throw new Error(String(decoded[1]));
|
|
67
|
+
return decoded[1];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function utf8ByteLength(value) {
|
|
71
|
+
if (typeof value !== "string") throw new TypeError("Expected a string to measure.");
|
|
72
|
+
let length;
|
|
73
|
+
try {
|
|
74
|
+
length = bridge.utf8Length(value);
|
|
75
|
+
} catch {}
|
|
76
|
+
if (typeof length !== "number" || length < 0) throw new Error("Workflow runtime could not measure a string.");
|
|
77
|
+
return length;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function utf8Prefix(value, maxBytes) {
|
|
81
|
+
let prefix;
|
|
82
|
+
try {
|
|
83
|
+
prefix = bridge.utf8Prefix(value, maxBytes);
|
|
84
|
+
} catch {}
|
|
85
|
+
if (typeof prefix !== "string") throw new Error("Workflow runtime could not truncate a string.");
|
|
86
|
+
return prefix;
|
|
87
|
+
}
|
|
88
|
+
|
|
21
89
|
let nextCallId = 0;
|
|
22
90
|
let topLevelWorkflowPromise;
|
|
23
91
|
let suppressNativePromiseConsumption = 0;
|
|
@@ -26,7 +94,7 @@ const pending = new Map();
|
|
|
26
94
|
const workflowErrorKinds = new WeakMap();
|
|
27
95
|
const runKeyPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
28
96
|
function validGitRef(ref) {
|
|
29
|
-
if (typeof ref !== "string" || !ref || ref === "@" ||
|
|
97
|
+
if (typeof ref !== "string" || !ref || ref === "@" || utf8ByteLength(ref) > 1024 || ref.startsWith("/") || ref.endsWith("/") || ref.includes("//") || ref.includes("..") || ref.includes("@{")) return false;
|
|
30
98
|
if (/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/iu.test(ref)) return false;
|
|
31
99
|
if (/[[\]\\~^:?*\u0000-\u0020\u007f]/u.test(ref) || ref.endsWith(".") || ref.endsWith(".lock")) return false;
|
|
32
100
|
return ref.split("/").every((component) => component.length > 0 && component !== "." && component !== ".." && !component.startsWith(".") && !component.endsWith(".") && !component.endsWith(".lock"));
|
|
@@ -50,7 +118,13 @@ function canonicalRunParams(params) {
|
|
|
50
118
|
}
|
|
51
119
|
|
|
52
120
|
function isDirectWorkflowScriptPromiseHandlerCall() {
|
|
53
|
-
|
|
121
|
+
let stack;
|
|
122
|
+
try {
|
|
123
|
+
stack = new Error().stack;
|
|
124
|
+
} catch {
|
|
125
|
+
// Near the stack limit the host's formatter overflows and throws its own realm's RangeError.
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
54
128
|
if (typeof stack !== "string") return false;
|
|
55
129
|
return stack.split("\n").some((line) => line.includes("workflow-script.js") && !line.includes("at async "));
|
|
56
130
|
}
|
|
@@ -122,7 +196,7 @@ function addTrackedObservations(tracker, observations) {
|
|
|
122
196
|
for (const observation of tracker.observations) {
|
|
123
197
|
if (observedCallIds.has(observation.callId)) continue;
|
|
124
198
|
observedCallIds.add(observation.callId);
|
|
125
|
-
|
|
199
|
+
post({ type: "callObserved", callId: observation.callId, key: observation.key, operation: observation.operation });
|
|
126
200
|
}
|
|
127
201
|
}
|
|
128
202
|
|
|
@@ -247,7 +321,7 @@ function hostCall(method, args, observation) {
|
|
|
247
321
|
const callId = ++nextCallId;
|
|
248
322
|
const promise = new Promise((resolve, reject) => {
|
|
249
323
|
pending.set(callId, { resolve, reject });
|
|
250
|
-
|
|
324
|
+
post({ type: "call", callId, method, args });
|
|
251
325
|
});
|
|
252
326
|
return observation && typeof observation.key === "string" && typeof observation.operation === "string"
|
|
253
327
|
? trackRunObservation([{ key: observation.key, operation: observation.operation, callId }], promise)
|
|
@@ -258,7 +332,7 @@ function runHostCall(key, params, collectFailure, batch, generatedLaneKey) {
|
|
|
258
332
|
const callId = ++nextCallId;
|
|
259
333
|
const promise = new Promise((resolve, reject) => {
|
|
260
334
|
pending.set(callId, { resolve, reject });
|
|
261
|
-
|
|
335
|
+
post({ type: "call", callId, method: "run", args: { key, params, ...(collectFailure ? { collectFailure: true } : {}), ...(batch ? { batch } : {}), ...(generatedLaneKey ? { generatedLaneKey } : {}) } });
|
|
262
336
|
});
|
|
263
337
|
return { key, callId, promise };
|
|
264
338
|
}
|
|
@@ -300,14 +374,13 @@ function wrapRunsAllResults(results, keys) {
|
|
|
300
374
|
}
|
|
301
375
|
|
|
302
376
|
function laneByteLength(value) {
|
|
303
|
-
return
|
|
377
|
+
return utf8ByteLength(value);
|
|
304
378
|
}
|
|
305
379
|
|
|
306
380
|
function laneBoardText(value) {
|
|
307
381
|
if (typeof value !== "string" || !value) return undefined;
|
|
308
382
|
if (laneByteLength(value) <= MAX_LANE_BOARD_TEXT_BYTES) return value;
|
|
309
|
-
|
|
310
|
-
return new TextDecoder().decode(bytes, { stream: true }) + "...";
|
|
383
|
+
return utf8Prefix(value, MAX_LANE_BOARD_TEXT_BYTES - 3) + "...";
|
|
311
384
|
}
|
|
312
385
|
|
|
313
386
|
function laneBoardPath(value) {
|
|
@@ -508,7 +581,7 @@ function workflowPlanStringMetadata(params) {
|
|
|
508
581
|
|
|
509
582
|
function runLanes(laneSpecs) {
|
|
510
583
|
const lanes = validateLaneSpecs(laneSpecs);
|
|
511
|
-
|
|
584
|
+
post({ type: "lanePlan", lanes: lanes.map((lane) => ({
|
|
512
585
|
key: lane.key,
|
|
513
586
|
stages: lane.stages.map((stage) => ({
|
|
514
587
|
key: stage.key,
|
|
@@ -576,7 +649,7 @@ function validateExtensionBindings(value, label) {
|
|
|
576
649
|
}
|
|
577
650
|
}
|
|
578
651
|
visit(value, 0);
|
|
579
|
-
if (
|
|
652
|
+
if (utf8ByteLength(stableRunJson(value)) > 16384) throw new Error(label + " extensionBindings canonical JSON exceeds 16384 bytes.");
|
|
580
653
|
}
|
|
581
654
|
|
|
582
655
|
function validateLaneMetadata(value, label, workflowKey) {
|
|
@@ -590,7 +663,7 @@ function validateLaneMetadata(value, label, workflowKey) {
|
|
|
590
663
|
if (typeof value.key !== "string" || !value.key.trim() || !runKeyPattern.test(value.key.trim())) throw new Error(label + ".key is invalid.");
|
|
591
664
|
if (workflowKey !== undefined && value.key.trim() !== workflowKey) throw new Error(label + ".key must match workflow key '" + workflowKey + "'.");
|
|
592
665
|
if (value.mode !== undefined && !["mutation", "review", "scout", "gate"].includes(value.mode)) throw new Error(label + ".mode is invalid.");
|
|
593
|
-
const bounded = (entry, maxBytes) => typeof entry === "string" && entry.trim() &&
|
|
666
|
+
const bounded = (entry, maxBytes) => typeof entry === "string" && entry.trim() && utf8ByteLength(entry.trim()) <= maxBytes && !/[\r\n\u0000]/.test(entry);
|
|
594
667
|
if (value.sourceRef !== undefined && !bounded(value.sourceRef, 128)) throw new Error(label + ".sourceRef is invalid.");
|
|
595
668
|
for (const [name, maxItems, maxLength] of [["claims", 20, 160], ["outputPaths", 10, 256]]) {
|
|
596
669
|
if (value[name] === undefined) continue;
|
|
@@ -682,11 +755,11 @@ function validateHostCommand(key, params) {
|
|
|
682
755
|
throw new Error("runs.host('" + key + "') params have unsupported fields: " + unknown.join(", ") + "." + cwdHint);
|
|
683
756
|
}
|
|
684
757
|
if (params.kind !== "command") throw new Error("runs.host('" + key + "') kind must be 'command'.");
|
|
685
|
-
if (typeof params.command !== "string" || !params.command.trim() || params.command.includes("\u0000") ||
|
|
758
|
+
if (typeof params.command !== "string" || !params.command.trim() || params.command.includes("\u0000") || utf8ByteLength(params.command.trim()) > 16384) throw new Error("runs.host('" + key + "') command must be a non-empty string of at most 16384 bytes without NUL.");
|
|
686
759
|
if (!Number.isInteger(params.timeoutMs) || params.timeoutMs < 1 || params.timeoutMs > 86400000) throw new Error("runs.host('" + key + "') timeoutMs must be an integer from 1 to 86400000.");
|
|
687
|
-
if (params.output !== undefined && (typeof params.output !== "string" || !params.output.trim() || /^[/\\]|(?:^|[/\\])\.\.(?:[/\\]|$)/.test(params.output) || /[\u0000-\u001f\u007f]/.test(params.output) ||
|
|
760
|
+
if (params.output !== undefined && (typeof params.output !== "string" || !params.output.trim() || /^[/\\]|(?:^|[/\\])\.\.(?:[/\\]|$)/.test(params.output) || /[\u0000-\u001f\u007f]/.test(params.output) || utf8ByteLength(params.output.trim()) > 240)) throw new Error("runs.host('" + key + "') output must be a bounded relative path without traversal or control characters.");
|
|
688
761
|
if (params.role !== undefined && params.role !== "ci" && params.role !== "gate") throw new Error("runs.host('" + key + "') role must be 'ci' or 'gate'.");
|
|
689
|
-
if (params.provider !== undefined && (typeof params.provider !== "string" || !params.provider.trim() || /[\r\n\u0000]/.test(params.provider) ||
|
|
762
|
+
if (params.provider !== undefined && (typeof params.provider !== "string" || !params.provider.trim() || /[\r\n\u0000]/.test(params.provider) || utf8ByteLength(params.provider.trim()) > 64)) throw new Error("runs.host('" + key + "') provider must be a non-empty single-line string of at most 64 bytes.");
|
|
690
763
|
assertJsonValue(params, "runs.host('" + key + "') params");
|
|
691
764
|
if (hostKeys.has(key) || runFingerprints.has(key)) throw new Error("Workflow key '" + key + "' is already in use.");
|
|
692
765
|
if (hostKeys.size >= 32) throw new Error("workflowScript supports at most 32 runs.host calls.");
|
|
@@ -807,14 +880,221 @@ const state = Object.freeze({
|
|
|
807
880
|
},
|
|
808
881
|
});
|
|
809
882
|
|
|
810
|
-
|
|
883
|
+
const contextObjectPrototype = scriptRealm.objectPrototype;
|
|
811
884
|
|
|
812
885
|
const capturedConsole = Object.freeze(Object.fromEntries(
|
|
813
886
|
["log", "info", "warn", "error"].map((level) => [level, (...args) => {
|
|
814
|
-
|
|
887
|
+
post({ type: "console", level, text: args.map((value) => typeof value === "string" ? value : inspectValue(value)).join(" ") });
|
|
815
888
|
}]),
|
|
816
889
|
));
|
|
817
890
|
|
|
891
|
+
function postWorkflowError(error) {
|
|
892
|
+
// Pre-run syntax and portability errors are tagged "validation" where the script is compiled;
|
|
893
|
+
// anything reaching here was thrown while the script ran, including a runtime SyntaxError.
|
|
894
|
+
const taggedKind = error && (typeof error === "object" || typeof error === "function") ? workflowErrorKinds.get(error) : undefined;
|
|
895
|
+
post({ type: "error", error: isSyntaxError(error) ? formatWorkflowScriptSyntaxError(error) : formatWorkflowScriptError(error), errorKind: taggedKind ?? "script" });
|
|
896
|
+
}
|
|
897
|
+
function assertJsonValue(value, path = "emit", seen = new Set()) {
|
|
898
|
+
|
|
899
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return;
|
|
900
|
+
if (typeof value === "number") {
|
|
901
|
+
if (!Number.isFinite(value)) throw new Error(path + " must contain only finite JSON numbers.");
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
if (typeof value !== "object") throw new Error(path + " must be a JSON value; received " + typeof value + ".");
|
|
905
|
+
if (seen.has(value)) throw new Error(path + " must not contain cycles.");
|
|
906
|
+
seen.add(value);
|
|
907
|
+
if (Array.isArray(value)) {
|
|
908
|
+
for (let index = 0; index < value.length; index++) {
|
|
909
|
+
if (!Object.prototype.hasOwnProperty.call(value, index)) throw new Error(path + " must not contain sparse array entries.");
|
|
910
|
+
assertJsonValue(value[index], path + "[" + index + "]", seen);
|
|
911
|
+
}
|
|
912
|
+
} else {
|
|
913
|
+
const prototype = Object.getPrototypeOf(value);
|
|
914
|
+
if (prototype !== null && prototype !== Object.prototype && prototype !== contextObjectPrototype) throw new Error(path + " must contain only plain JSON objects.");
|
|
915
|
+
if (Object.getOwnPropertySymbols(value).length > 0) throw new Error(path + " must not contain symbol keys.");
|
|
916
|
+
for (const [key, entry] of Object.entries(value)) assertJsonValue(entry, path + "." + key, seen);
|
|
917
|
+
}
|
|
918
|
+
seen.delete(value);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function isPlainWorkflowObject(value) {
|
|
922
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
923
|
+
const prototype = Object.getPrototypeOf(value);
|
|
924
|
+
return prototype === null || prototype === Object.prototype || prototype === contextObjectPrototype;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function unwrapRunsAllResults(value, seen = new Map()) {
|
|
928
|
+
if (value === null || typeof value !== "object") return value;
|
|
929
|
+
const runsAllTarget = runsAllResultTargets.get(value);
|
|
930
|
+
const target = runsAllTarget || value;
|
|
931
|
+
if (seen.has(target)) return seen.get(target);
|
|
932
|
+
if (Array.isArray(target)) {
|
|
933
|
+
const copy = [];
|
|
934
|
+
seen.set(target, copy);
|
|
935
|
+
let changed = !!runsAllTarget;
|
|
936
|
+
for (let index = 0; index < target.length; index++) {
|
|
937
|
+
copy[index] = unwrapRunsAllResults(target[index], seen);
|
|
938
|
+
changed ||= copy[index] !== target[index];
|
|
939
|
+
}
|
|
940
|
+
return changed ? copy : target;
|
|
941
|
+
}
|
|
942
|
+
if (!isPlainWorkflowObject(target) || Object.getOwnPropertySymbols(target).length > 0) return target;
|
|
943
|
+
let changed = false;
|
|
944
|
+
const entries = Object.entries(target).map(([key, entry]) => {
|
|
945
|
+
const unwrapped = unwrapRunsAllResults(entry, seen);
|
|
946
|
+
changed ||= unwrapped !== entry;
|
|
947
|
+
return [key, unwrapped];
|
|
948
|
+
});
|
|
949
|
+
return changed ? Object.fromEntries(entries) : target;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function omitUndefinedWorkflowValues(value, seen = new Set()) {
|
|
953
|
+
if (value === null || typeof value !== "object") return value;
|
|
954
|
+
if (seen.has(value)) return value;
|
|
955
|
+
seen.add(value);
|
|
956
|
+
const normalized = Array.isArray(value)
|
|
957
|
+
? value.map((entry) => entry === undefined ? null : omitUndefinedWorkflowValues(entry, seen))
|
|
958
|
+
: isPlainWorkflowObject(value) && Object.getOwnPropertySymbols(value).length === 0
|
|
959
|
+
? Object.fromEntries(Object.entries(value).flatMap(([key, entry]) => entry === undefined ? [] : [[key, omitUndefinedWorkflowValues(entry, seen)]]))
|
|
960
|
+
: value;
|
|
961
|
+
seen.delete(value);
|
|
962
|
+
return normalized;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
const scriptGlobals = { runs, Promise: workflowPromise, emit(value) { const unwrapped = unwrapRunsAllResults(value); const emittedValue = unwrapped === undefined ? null : omitUndefinedWorkflowValues(unwrapped); assertJsonValue(emittedValue); post({ type: "emit", value: emittedValue }); }, console: capturedConsole };
|
|
966
|
+
if (stateEnabled) scriptGlobals.state = state;
|
|
967
|
+
|
|
968
|
+
function deliver(callId, ok, encodedValue, errorText, errorKind) {
|
|
969
|
+
const entry = pending.get(callId);
|
|
970
|
+
if (!entry) return;
|
|
971
|
+
pending.delete(callId);
|
|
972
|
+
if (ok) entry.resolve(JSON.parse(encodedValue).value);
|
|
973
|
+
else {
|
|
974
|
+
const error = new Error(errorText);
|
|
975
|
+
if (errorKind === "child" || errorKind === "detached-child") workflowErrorKinds.set(error, errorKind);
|
|
976
|
+
entry.reject(error);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
let nativeThenDescriptor;
|
|
981
|
+
|
|
982
|
+
function beginWorkflowScript() {
|
|
983
|
+
try {
|
|
984
|
+
nativeThenDescriptor = Object.getOwnPropertyDescriptor(nativePromisePrototype, "then");
|
|
985
|
+
if (!nativeThenDescriptor || typeof nativeThenDescriptor.value !== "function") throw new Error("workflowScript could not inspect the VM Promise.prototype.then method.");
|
|
986
|
+
const nativeThen = nativeThenDescriptor.value;
|
|
987
|
+
// Some hosts freeze built-ins in every realm, which makes then read-only. The promise hooks
|
|
988
|
+
// still track observation, so skip the wrapper rather than fail every workflow.
|
|
989
|
+
if (nativeThenDescriptor.configurable) Object.defineProperty(nativePromisePrototype, "then", {
|
|
990
|
+
...nativeThenDescriptor,
|
|
991
|
+
value: function workflowPromiseThen(...args) {
|
|
992
|
+
if (isDirectWorkflowScriptPromiseHandlerCall() || suppressNativePromiseConsumption > 0) {
|
|
993
|
+
return withSuppressedNativePromiseConsumption(() => Reflect.apply(nativeThen, this, args));
|
|
994
|
+
}
|
|
995
|
+
return Reflect.apply(nativeThen, this, args);
|
|
996
|
+
},
|
|
997
|
+
});
|
|
998
|
+
return true;
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
postWorkflowError(error);
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
function hookBefore(promise) {
|
|
1006
|
+
if (!isWorkflowRealmPromise(promise)) return;
|
|
1007
|
+
activeNativePromises.push(promise);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function hookAfter(promise) {
|
|
1011
|
+
// Compare by identity rather than lastIndexOf: this may be a worker promise, and a script can replace Array methods.
|
|
1012
|
+
for (let index = activeNativePromises.length - 1; index >= 0; index--) {
|
|
1013
|
+
if (activeNativePromises[index] !== promise) continue;
|
|
1014
|
+
activeNativePromises.splice(index, 1);
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function hookInit(promise, parent) {
|
|
1020
|
+
if (!isWorkflowRealmPromise(promise)) return;
|
|
1021
|
+
const childTracker = nativePromiseTracker(promise);
|
|
1022
|
+
if (!parent || !isWorkflowRealmPromise(parent)) return;
|
|
1023
|
+
nativePromiseParents.set(promise, parent);
|
|
1024
|
+
const parentTracker = nativePromiseTracker(parent);
|
|
1025
|
+
addTrackerDependency(childTracker, parentTracker);
|
|
1026
|
+
const activePromise = activeNativePromises.at(-1);
|
|
1027
|
+
if (activePromise && activePromise !== parent) {
|
|
1028
|
+
addTrackerDependency(nativePromiseTracker(activePromise), parentTracker);
|
|
1029
|
+
} else if (!activePromise && suppressNativePromiseConsumption === 0) {
|
|
1030
|
+
markTrackedObservationsConsumed(parentTracker);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
function endWorkflowScript() {
|
|
1035
|
+
try {
|
|
1036
|
+
let stopped = false;
|
|
1037
|
+
try {
|
|
1038
|
+
stopped = bridge.stopPromiseHooks();
|
|
1039
|
+
} catch {}
|
|
1040
|
+
if (stopped !== true) throw new Error("Workflow runtime could not stop its promise hooks.");
|
|
1041
|
+
} finally {
|
|
1042
|
+
try {
|
|
1043
|
+
if (nativeThenDescriptor.configurable) Object.defineProperty(nativePromisePrototype, "then", nativeThenDescriptor);
|
|
1044
|
+
} finally {
|
|
1045
|
+
topLevelWorkflowPromise = undefined;
|
|
1046
|
+
activeNativePromises.length = 0;
|
|
1047
|
+
suppressNativePromiseConsumption = 0;
|
|
1048
|
+
nativePromiseTrackers = new WeakMap();
|
|
1049
|
+
nativePromiseParents = new WeakMap();
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
function completeWorkflowScript(value) {
|
|
1055
|
+
const persistedValue = value === undefined ? null : omitUndefinedWorkflowValues(value);
|
|
1056
|
+
try {
|
|
1057
|
+
assertJsonValue(persistedValue, "return");
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
post({ type: "error", errorKind: "return-serialization", errorPhase: "return-serialization", error: formatWorkflowScriptError(error) });
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
post({ type: "complete", value: persistedValue });
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// The worker ran the compiled script: `result` is its completion value, or what it threw synchronously.
|
|
1066
|
+
function awaitWorkflowScript(threw, result) {
|
|
1067
|
+
let settled;
|
|
1068
|
+
try {
|
|
1069
|
+
if (threw) throw result;
|
|
1070
|
+
topLevelWorkflowPromise = result;
|
|
1071
|
+
markTrackedObservationsConsumed(nativePromiseTracker(result));
|
|
1072
|
+
settled = Promise.resolve(result);
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
settled = Promise.reject(error);
|
|
1075
|
+
}
|
|
1076
|
+
settled.then((value) => {
|
|
1077
|
+
endWorkflowScript();
|
|
1078
|
+
completeWorkflowScript(value);
|
|
1079
|
+
}, (error) => {
|
|
1080
|
+
endWorkflowScript();
|
|
1081
|
+
postWorkflowError(error);
|
|
1082
|
+
}).catch(postWorkflowError);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
return Object.freeze({
|
|
1086
|
+
__proto__: null,
|
|
1087
|
+
scriptGlobals: Object.freeze(scriptGlobals),
|
|
1088
|
+
deliver,
|
|
1089
|
+
beginWorkflowScript,
|
|
1090
|
+
hookBefore,
|
|
1091
|
+
hookAfter,
|
|
1092
|
+
hookInit,
|
|
1093
|
+
awaitWorkflowScript,
|
|
1094
|
+
reportError: postWorkflowError,
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
|
|
818
1098
|
function formatWorkflowScriptSyntaxError(error) {
|
|
819
1099
|
const details = formatWorkflowScriptError(error);
|
|
820
1100
|
return [
|
|
@@ -837,17 +1117,11 @@ function isSyntaxError(error) {
|
|
|
837
1117
|
return error instanceof SyntaxError || error?.name === "SyntaxError";
|
|
838
1118
|
}
|
|
839
1119
|
|
|
840
|
-
function postWorkflowError(error) {
|
|
841
|
-
// Pre-run syntax and portability errors are tagged "validation" where the script is compiled;
|
|
842
|
-
// anything reaching here was thrown while the script ran, including a runtime SyntaxError.
|
|
843
|
-
const taggedKind = error && (typeof error === "object" || typeof error === "function") ? workflowErrorKinds.get(error) : undefined;
|
|
844
|
-
parentPort.postMessage({ type: "error", error: isSyntaxError(error) ? formatWorkflowScriptSyntaxError(error) : formatWorkflowScriptError(error), errorKind: taggedKind ?? "script" });
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
process.on("unhandledRejection", postWorkflowError);
|
|
848
|
-
|
|
849
1120
|
const NESTED_ASYNC_WORKFLOW_ERROR = "workflowScript validation failed before child launch; no children launched. workflowScript does not support nested async functions. Use top-level await, plain helper functions that return runs.run(...), or explicit Promise chains so workflows stay portable across Node and Bun. Parallel plus sequential rewrite: const a = runs.run(\"a\", { agent: \"worker\", task: \"A\" }); const writer = await runs.run(\"writer\", { agent: \"worker\", task: \"Write\" }); const review = await runs.run(\"review\", { agent: \"reviewer\", task: writer.output }); const [aResult] = await Promise.all([a]); return { a: aResult.output, issue: { writerRunId: writer.runId, reviewRunId: review.runId } };";
|
|
850
1121
|
const AST_SCALAR_KEYS = new Set(["type", "start", "end"]);
|
|
1122
|
+
// A script context has no module loader, and Node and Bun reject import() there with an error built in
|
|
1123
|
+
// the worker realm, so the call is refused before the script runs.
|
|
1124
|
+
const DYNAMIC_IMPORT_WORKFLOW_ERROR = "workflowScript validation failed before child launch; no children launched. workflowScript cannot load modules with import().";
|
|
851
1125
|
|
|
852
1126
|
function assertPortableWorkflowScript(source) {
|
|
853
1127
|
const wrapped = "(async () => {\n" + source + "\n})()";
|
|
@@ -864,7 +1138,8 @@ function assertPortableWorkflowScript(source) {
|
|
|
864
1138
|
throw error;
|
|
865
1139
|
}
|
|
866
1140
|
const wrapper = workflowWrapperFunction(ast);
|
|
867
|
-
|
|
1141
|
+
// Walk the whole program, not just the wrapper body: a script can close the wrapper early.
|
|
1142
|
+
walkWorkflowAst(ast, wrapper);
|
|
868
1143
|
}
|
|
869
1144
|
|
|
870
1145
|
function workflowWrapperFunction(ast) {
|
|
@@ -886,183 +1161,182 @@ function walkWorkflowAst(node, allowedAsyncFunction) {
|
|
|
886
1161
|
if (node !== allowedAsyncFunction && isAsyncFunctionNode(node)) {
|
|
887
1162
|
throw new Error(NESTED_ASYNC_WORKFLOW_ERROR);
|
|
888
1163
|
}
|
|
1164
|
+
if (node.type === "ImportExpression") throw new Error(DYNAMIC_IMPORT_WORKFLOW_ERROR);
|
|
889
1165
|
for (const [key, child] of Object.entries(node)) {
|
|
890
1166
|
if (AST_SCALAR_KEYS.has(key)) continue;
|
|
891
1167
|
walkWorkflowAst(child, allowedAsyncFunction);
|
|
892
1168
|
}
|
|
893
1169
|
}
|
|
894
1170
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1171
|
+
// The error-format helpers run in both realms; the runtime realm compiles its own copies from their source.
|
|
1172
|
+
const WORKFLOW_RUNTIME_SOURCE = [
|
|
1173
|
+
"(function (bridge, scriptRealm, stateEnabled) {",
|
|
1174
|
+
"\"use strict\";",
|
|
1175
|
+
formatWorkflowScriptSyntaxError,
|
|
1176
|
+
formatWorkflowScriptError,
|
|
1177
|
+
isSyntaxError,
|
|
1178
|
+
"return (" + workflowRuntime + ")(bridge, scriptRealm, stateEnabled);",
|
|
1179
|
+
"})",
|
|
1180
|
+
].join("\n");
|
|
1181
|
+
|
|
1182
|
+
const VM_CONTEXT_OPTIONS = { codeGeneration: { strings: false, wasm: false } };
|
|
1183
|
+
const LOCK_PREPARE_STACK_TRACE_SOURCE = "Object.defineProperty(Error, \"prepareStackTrace\", { value: undefined, writable: false, enumerable: false, configurable: false });";
|
|
1184
|
+
|
|
1185
|
+
// A null-prototype sandbox keeps globalThis.constructor out of the worker realm. The sandbox itself is
|
|
1186
|
+
// still a worker-realm object, and Bun forwards a failed global redefinition to it as the worker's
|
|
1187
|
+
// TypeError; lockWorkerRealm makes that error useless. vm.constants.DONT_CONTEXTIFY would avoid the
|
|
1188
|
+
// sandbox, but Bun 1.3 ignores properties defined on such a global, so the script API could not be installed.
|
|
1189
|
+
function createVmRealm() {
|
|
1190
|
+
return vm.createContext(Object.create(null), VM_CONTEXT_OPTIONS);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// Runs once the worker has compiled everything it needs, right before the script starts. A worker-realm
|
|
1194
|
+
// value can still reach a script through engine paths (see workflowRuntime), so take away what such a
|
|
1195
|
+
// value would be used for: no worker function's constructor compiles code, and no script can install a
|
|
1196
|
+
// stack formatter that hands it worker frames.
|
|
1197
|
+
function lockWorkerRealm() {
|
|
1198
|
+
const refuse = function Function() {
|
|
1199
|
+
throw new TypeError("Code generation is disabled in the workflow worker.");
|
|
1200
|
+
};
|
|
1201
|
+
const functionPrototypes = [
|
|
1202
|
+
Function.prototype,
|
|
1203
|
+
Object.getPrototypeOf(async function () {}),
|
|
1204
|
+
Object.getPrototypeOf(function* () {}),
|
|
1205
|
+
Object.getPrototypeOf(async function* () {}),
|
|
1206
|
+
];
|
|
1207
|
+
// A host that froze these already chose their values; redefining them would throw and fail every workflow.
|
|
1208
|
+
const lock = (target, key, value) => {
|
|
1209
|
+
if (Object.getOwnPropertyDescriptor(target, key)?.configurable === false) return;
|
|
1210
|
+
Object.defineProperty(target, key, { value, writable: false, enumerable: false, configurable: false });
|
|
1211
|
+
};
|
|
1212
|
+
for (const prototype of functionPrototypes) lock(prototype, "constructor", refuse);
|
|
1213
|
+
lock(Error, "prepareStackTrace", Error.prepareStackTrace);
|
|
916
1214
|
}
|
|
1215
|
+
const INSPECT_OPTIONS = { depth: 4, breakLength: 120, customInspect: false };
|
|
917
1216
|
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
const prototype = Object.getPrototypeOf(value);
|
|
921
|
-
return prototype === null || prototype === Object.prototype || prototype === contextObjectPrototype;
|
|
922
|
-
}
|
|
1217
|
+
let runtime;
|
|
1218
|
+
let stopWorkflowPromiseHook;
|
|
923
1219
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
changed ||= copy[index] !== target[index];
|
|
1220
|
+
// Each method takes and returns only strings, numbers and booleans, and catches its own failures.
|
|
1221
|
+
// inspect is the one exception on input: it formats a script value without calling script-defined
|
|
1222
|
+
// custom inspectors, which would receive worker-realm arguments.
|
|
1223
|
+
const bridge = Object.freeze({
|
|
1224
|
+
__proto__: null,
|
|
1225
|
+
post(json) {
|
|
1226
|
+
try {
|
|
1227
|
+
parentPort.postMessage(JSON.parse(json));
|
|
1228
|
+
return true;
|
|
1229
|
+
} catch {
|
|
1230
|
+
return false;
|
|
936
1231
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
|
|
1232
|
+
},
|
|
1233
|
+
inspect(value) {
|
|
1234
|
+
try {
|
|
1235
|
+
return JSON.stringify([true, inspect(value, INSPECT_OPTIONS)]);
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
const message = error && typeof error.message === "string" ? error.message : "console could not format a value.";
|
|
1238
|
+
return JSON.stringify([false, message]);
|
|
1239
|
+
}
|
|
1240
|
+
},
|
|
1241
|
+
utf8Length(value) {
|
|
1242
|
+
try {
|
|
1243
|
+
return typeof value === "string" ? new TextEncoder().encode(value).byteLength : -1;
|
|
1244
|
+
} catch {
|
|
1245
|
+
return -1;
|
|
1246
|
+
}
|
|
1247
|
+
},
|
|
1248
|
+
utf8Prefix(value, maxBytes) {
|
|
1249
|
+
try {
|
|
1250
|
+
if (typeof value !== "string" || typeof maxBytes !== "number") return undefined;
|
|
1251
|
+
return new TextDecoder().decode(new TextEncoder().encode(value).subarray(0, maxBytes), { stream: true });
|
|
1252
|
+
} catch {
|
|
1253
|
+
return undefined;
|
|
1254
|
+
}
|
|
1255
|
+
},
|
|
1256
|
+
stopPromiseHooks() {
|
|
1257
|
+
const stop = stopWorkflowPromiseHook;
|
|
1258
|
+
stopWorkflowPromiseHook = undefined;
|
|
1259
|
+
try {
|
|
1260
|
+
stop?.();
|
|
1261
|
+
return true;
|
|
1262
|
+
} catch {
|
|
1263
|
+
return false;
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
});
|
|
948
1267
|
|
|
949
|
-
function
|
|
950
|
-
|
|
951
|
-
if (seen.has(value)) return value;
|
|
952
|
-
seen.add(value);
|
|
953
|
-
const normalized = Array.isArray(value)
|
|
954
|
-
? value.map((entry) => entry === undefined ? null : omitUndefinedWorkflowValues(entry, seen))
|
|
955
|
-
: isPlainWorkflowObject(value) && Object.getOwnPropertySymbols(value).length === 0
|
|
956
|
-
? Object.fromEntries(Object.entries(value).flatMap(([key, entry]) => entry === undefined ? [] : [[key, omitUndefinedWorkflowValues(entry, seen)]]))
|
|
957
|
-
: value;
|
|
958
|
-
seen.delete(value);
|
|
959
|
-
return normalized;
|
|
1268
|
+
function postWorkerError(error) {
|
|
1269
|
+
parentPort.postMessage({ type: "error", error: isSyntaxError(error) ? formatWorkflowScriptSyntaxError(error) : formatWorkflowScriptError(error), errorKind: "script" });
|
|
960
1270
|
}
|
|
961
1271
|
|
|
1272
|
+
process.on("unhandledRejection", (reason) => {
|
|
1273
|
+
// A worker-realm reason is the worker's own failure; it must not enter the runtime realm.
|
|
1274
|
+
if (runtime && !(reason instanceof Object)) runtime.reportError(reason);
|
|
1275
|
+
else postWorkerError(reason);
|
|
1276
|
+
});
|
|
1277
|
+
|
|
962
1278
|
function deepFreezeWorkflowArgs(value) {
|
|
963
1279
|
if (value === null || typeof value !== "object" || Object.isFrozen(value)) return value;
|
|
964
1280
|
for (const entry of Object.values(value)) deepFreezeWorkflowArgs(entry);
|
|
965
1281
|
return Object.freeze(value);
|
|
966
1282
|
}
|
|
967
1283
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1284
|
+
function startWorkflow(message) {
|
|
1285
|
+
const context = createVmRealm();
|
|
1286
|
+
const runtimeContext = createVmRealm();
|
|
1287
|
+
// Bun hands Error.prepareStackTrace call sites (and the array holding them) from the worker's realm,
|
|
1288
|
+
// whatever context the error came from. Lock it in both realms before any code runs there.
|
|
1289
|
+
for (const realm of [context, runtimeContext]) vm.runInContext(LOCK_PREPARE_STACK_TRACE_SOURCE, realm);
|
|
1290
|
+
const scriptRealm = vm.runInContext("({ __proto__: null, objectPrototype: Object.prototype, promisePrototype: (async () => {})().constructor.prototype })", context);
|
|
1291
|
+
const createRuntime = new vm.Script(WORKFLOW_RUNTIME_SOURCE, { filename: "pi-workflow-runtime.js" }).runInContext(runtimeContext);
|
|
1292
|
+
runtime = createRuntime(bridge, scriptRealm, Boolean(message.stateEnabled));
|
|
1293
|
+
Object.assign(context, runtime.scriptGlobals);
|
|
1294
|
+
// Rebuild args inside the VM realm: a worker-realm object would expose the worker's unrestricted
|
|
1295
|
+
// Function through args.constructor.constructor, bypassing codeGeneration.strings: false.
|
|
1296
|
+
context.args = deepFreezeWorkflowArgs(vm.runInContext("JSON.parse", context)(JSON.stringify(message.args ?? {})));
|
|
1297
|
+
let compiled;
|
|
1298
|
+
try {
|
|
1299
|
+
assertPortableWorkflowScript(message.script);
|
|
1300
|
+
compiled = new vm.Script("(async () => {\n" + message.script + "\n})()", { filename: "workflow-script.js", lineOffset: -1 });
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
parentPort.postMessage({ type: "error", errorKind: "validation", error: isSyntaxError(error) ? formatWorkflowScriptSyntaxError(error) : formatWorkflowScriptError(error) });
|
|
979
1303
|
return;
|
|
980
1304
|
}
|
|
981
|
-
if (
|
|
1305
|
+
if (!runtime.beginWorkflowScript()) return;
|
|
1306
|
+
stopWorkflowPromiseHook = createWorkflowPromiseHook({ init: runtime.hookInit, before: runtime.hookBefore, after: runtime.hookAfter });
|
|
1307
|
+
lockWorkerRealm();
|
|
1308
|
+
let result;
|
|
1309
|
+
let threw = false;
|
|
982
1310
|
try {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1311
|
+
result = compiled.runInContext(context);
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
threw = true;
|
|
1314
|
+
result = error;
|
|
1315
|
+
}
|
|
1316
|
+
runtime.awaitWorkflowScript(threw, result);
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
parentPort.on("message", (message) => {
|
|
1320
|
+
if (message.type === "response") {
|
|
1321
|
+
if (!runtime || typeof message.callId !== "number") return;
|
|
1322
|
+
if (!message.ok) {
|
|
1323
|
+
runtime.deliver(message.callId, false, undefined, message.error === undefined ? undefined : String(message.error), typeof message.errorKind === "string" ? message.errorKind : undefined);
|
|
996
1324
|
return;
|
|
997
1325
|
}
|
|
998
|
-
|
|
999
|
-
const nativeThenDescriptor = Object.getOwnPropertyDescriptor(nativePromisePrototype, "then");
|
|
1000
|
-
if (!nativeThenDescriptor || typeof nativeThenDescriptor.value !== "function") throw new Error("workflowScript could not inspect the VM Promise.prototype.then method.");
|
|
1001
|
-
const nativeThen = nativeThenDescriptor.value;
|
|
1002
|
-
let stopWorkflowPromiseHook;
|
|
1003
|
-
let value;
|
|
1004
|
-
try {
|
|
1005
|
-
// Some hosts freeze built-ins in every realm, which makes then read-only. The promise hooks
|
|
1006
|
-
// below still track observation, so skip the wrapper rather than fail every workflow.
|
|
1007
|
-
if (nativeThenDescriptor.configurable) Object.defineProperty(nativePromisePrototype, "then", {
|
|
1008
|
-
...nativeThenDescriptor,
|
|
1009
|
-
value: function workflowPromiseThen(...args) {
|
|
1010
|
-
if (isDirectWorkflowScriptPromiseHandlerCall() || suppressNativePromiseConsumption > 0) {
|
|
1011
|
-
return withSuppressedNativePromiseConsumption(() => Reflect.apply(nativeThen, this, args));
|
|
1012
|
-
}
|
|
1013
|
-
return Reflect.apply(nativeThen, this, args);
|
|
1014
|
-
},
|
|
1015
|
-
});
|
|
1016
|
-
stopWorkflowPromiseHook = createWorkflowPromiseHook({
|
|
1017
|
-
before(promise) {
|
|
1018
|
-
activeNativePromises.push(promise);
|
|
1019
|
-
},
|
|
1020
|
-
after(promise) {
|
|
1021
|
-
const index = activeNativePromises.lastIndexOf(promise);
|
|
1022
|
-
if (index !== -1) activeNativePromises.splice(index, 1);
|
|
1023
|
-
},
|
|
1024
|
-
init(promise, parent) {
|
|
1025
|
-
const childTracker = nativePromiseTracker(promise);
|
|
1026
|
-
if (!parent) return;
|
|
1027
|
-
nativePromiseParents.set(promise, parent);
|
|
1028
|
-
const parentTracker = nativePromiseTracker(parent);
|
|
1029
|
-
addTrackerDependency(childTracker, parentTracker);
|
|
1030
|
-
const activePromise = activeNativePromises.at(-1);
|
|
1031
|
-
if (activePromise && activePromise !== parent) {
|
|
1032
|
-
addTrackerDependency(nativePromiseTracker(activePromise), parentTracker);
|
|
1033
|
-
} else if (!activePromise && suppressNativePromiseConsumption === 0) {
|
|
1034
|
-
markTrackedObservationsConsumed(parentTracker);
|
|
1035
|
-
}
|
|
1036
|
-
},
|
|
1037
|
-
});
|
|
1038
|
-
const workflowResultPromise = compiled.runInContext(context);
|
|
1039
|
-
topLevelWorkflowPromise = workflowResultPromise;
|
|
1040
|
-
markTrackedObservationsConsumed(nativePromiseTracker(workflowResultPromise));
|
|
1041
|
-
value = await workflowResultPromise;
|
|
1042
|
-
} finally {
|
|
1043
|
-
try {
|
|
1044
|
-
stopWorkflowPromiseHook?.();
|
|
1045
|
-
} finally {
|
|
1046
|
-
try {
|
|
1047
|
-
if (nativeThenDescriptor.configurable) Object.defineProperty(nativePromisePrototype, "then", nativeThenDescriptor);
|
|
1048
|
-
} finally {
|
|
1049
|
-
topLevelWorkflowPromise = undefined;
|
|
1050
|
-
activeNativePromises.length = 0;
|
|
1051
|
-
suppressNativePromiseConsumption = 0;
|
|
1052
|
-
nativePromiseTrackers = new WeakMap();
|
|
1053
|
-
nativePromiseParents = new WeakMap();
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
const persistedValue = value === undefined ? null : omitUndefinedWorkflowValues(value);
|
|
1326
|
+
let encodedValue;
|
|
1058
1327
|
try {
|
|
1059
|
-
|
|
1328
|
+
encodedValue = JSON.stringify({ value: message.value });
|
|
1060
1329
|
} catch (error) {
|
|
1061
|
-
|
|
1330
|
+
runtime.deliver(message.callId, false, undefined, "Workflow host response is not JSON: " + error.message, undefined);
|
|
1062
1331
|
return;
|
|
1063
1332
|
}
|
|
1064
|
-
|
|
1333
|
+
runtime.deliver(message.callId, true, encodedValue, undefined, undefined);
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
if (message.type !== "start") return;
|
|
1337
|
+
try {
|
|
1338
|
+
startWorkflow(message);
|
|
1065
1339
|
} catch (error) {
|
|
1066
|
-
|
|
1340
|
+
postWorkerError(error);
|
|
1067
1341
|
}
|
|
1068
1342
|
});
|