pi-subagents 0.49.0 → 0.50.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 +35 -0
- package/agents/oracle.md +7 -5
- package/agents/researcher.md +1 -1
- package/agents/reviewer.md +2 -2
- package/agents/scout.md +1 -1
- package/agents/worker.md +1 -1
- package/docs/agents.md +2 -0
- package/docs/configuration.md +47 -3
- package/docs/extension-api.md +36 -0
- package/docs/missions.md +1 -1
- package/docs/observability.md +16 -0
- package/docs/tool-reference.md +19 -2
- package/docs/workflows.md +2 -2
- package/package.json +1 -1
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +5 -4
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +31 -15
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +10 -0
- package/src/api/external-runs.ts +174 -84
- package/src/api/preflight.ts +12 -6
- package/src/extension/config.ts +33 -0
- package/src/extension/index.ts +48 -5
- package/src/extension/public-execution.ts +3 -1
- package/src/extension/rpc.ts +5 -1
- package/src/extension/schemas.ts +2 -1
- package/src/extension/tool-description.ts +4 -2
- package/src/inspectors/herdr/actions.ts +3 -7
- package/src/inspectors/herdr/project-panes.ts +2 -6
- package/src/inspectors/herdr/shell-command.ts +16 -0
- package/src/intercom/intercom-bridge.ts +2 -3
- package/src/intercom/native-supervisor-channel.ts +19 -42
- package/src/missions/goal-driver.ts +3 -1
- package/src/runs/background/active-run-index.ts +71 -1
- package/src/runs/background/async-execution.ts +49 -7
- package/src/runs/background/async-job-tracker.ts +1 -0
- package/src/runs/background/async-resume.ts +3 -5
- package/src/runs/background/async-status-snapshot.ts +277 -0
- package/src/runs/background/async-status.ts +8 -3
- package/src/runs/background/chain-root-attachment.ts +2 -2
- package/src/runs/background/completion-replay.ts +11 -1
- package/src/runs/background/fleet-view.ts +3 -1
- package/src/runs/background/result-files.ts +437 -0
- package/src/runs/background/result-watcher.ts +188 -41
- package/src/runs/background/retained-children.ts +69 -20
- package/src/runs/background/run-id-resolver.ts +30 -24
- package/src/runs/background/run-status.ts +7 -2
- package/src/runs/background/scheduled-runs.ts +54 -28
- package/src/runs/background/stale-run-reconciler.ts +27 -13
- package/src/runs/background/subagent-runner.ts +294 -32
- package/src/runs/background/subagent-wait.ts +2 -0
- package/src/runs/background/wait-completions.ts +5 -2
- package/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/src/runs/foreground/chain-execution.ts +16 -0
- package/src/runs/foreground/execution.ts +219 -15
- package/src/runs/foreground/subagent-executor.ts +114 -28
- package/src/runs/shared/completion-guard.ts +96 -6
- package/src/runs/shared/external-cli-runner.ts +4 -0
- package/src/runs/shared/model-fallback.ts +16 -2
- package/src/runs/shared/nested-events.ts +66 -62
- package/src/runs/shared/orca-progress-tabs.ts +375 -0
- package/src/runs/shared/parallel-utils.ts +2 -0
- package/src/runs/shared/subagent-control.ts +15 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +1 -9
- package/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/src/runs/shared/tool-timeout.ts +93 -0
- package/src/shared/types.ts +29 -2
- package/src/slash/slash-commands.ts +34 -25
- package/src/slash/slash-live-state.ts +3 -0
- package/src/tui/fleet-status.ts +160 -45
- package/src/tui/fleet.ts +125 -15
- package/src/tui/render.ts +41 -10
- package/src/workflows/chat-progress.ts +6 -4
- package/src/workflows/scripted-workflow.ts +227 -70
|
@@ -4,15 +4,23 @@ const KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
|
4
4
|
|
|
5
5
|
const WORKER_SOURCE = String.raw`
|
|
6
6
|
const { parentPort } = require("node:worker_threads");
|
|
7
|
+
const { promiseHooks } = require("node:v8");
|
|
7
8
|
const vm = require("node:vm");
|
|
8
9
|
const { inspect } = require("node:util");
|
|
9
10
|
|
|
11
|
+
if (!promiseHooks || typeof promiseHooks.createHook !== "function") throw new Error("workflowScript requires node:v8 promiseHooks.createHook support.");
|
|
12
|
+
|
|
10
13
|
let nextCallId = 0;
|
|
14
|
+
let topLevelWorkflowPromise;
|
|
15
|
+
let suppressNativePromiseConsumption = 0;
|
|
16
|
+
const activeNativePromises = [];
|
|
11
17
|
const pending = new Map();
|
|
12
18
|
const runKeyPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
let
|
|
19
|
+
const trackedPromiseTrackers = new WeakMap();
|
|
20
|
+
const trackedPromiseTargets = new WeakMap();
|
|
21
|
+
let nativePromiseTrackers = new WeakMap();
|
|
22
|
+
let nativePromiseParents = new WeakMap();
|
|
23
|
+
const observedRunCallIds = new Set();
|
|
16
24
|
|
|
17
25
|
function stableRunJson(value) {
|
|
18
26
|
if (Array.isArray(value)) return "[" + value.map(stableRunJson).join(",") + "]";
|
|
@@ -23,15 +31,47 @@ function stableRunJson(value) {
|
|
|
23
31
|
function isDirectWorkflowScriptPromiseHandlerCall() {
|
|
24
32
|
const stack = new Error().stack;
|
|
25
33
|
if (typeof stack !== "string") return false;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
return stack.split("\n").some((line) => line.includes("workflow-script.js") && !line.includes("at async "));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function nativePromiseTracker(promise) {
|
|
38
|
+
if (!promise || (typeof promise !== "object" && typeof promise !== "function")) return undefined;
|
|
39
|
+
let tracker = nativePromiseTrackers.get(promise);
|
|
40
|
+
if (!tracker) {
|
|
41
|
+
tracker = { observations: [], consumed: false, dependencies: [] };
|
|
42
|
+
nativePromiseTrackers.set(promise, tracker);
|
|
43
|
+
}
|
|
44
|
+
return tracker;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function promiseObservationTracker(value) {
|
|
48
|
+
if (!value || (typeof value !== "object" && typeof value !== "function")) return undefined;
|
|
49
|
+
return trackedPromiseTrackers.get(value) ?? nativePromiseTrackers.get(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function addTrackerDependency(tracker, dependency) {
|
|
53
|
+
if (!dependency) return;
|
|
54
|
+
tracker.dependencies ??= [];
|
|
55
|
+
if (!tracker.dependencies.includes(dependency)) tracker.dependencies.push(dependency);
|
|
56
|
+
if (tracker.consumed) markTrackedObservationsConsumed(dependency);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function descendsFromTopLevelWorkflow(promise) {
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
for (let current = promise; current && !seen.has(current); current = nativePromiseParents.get(current)) {
|
|
62
|
+
if (current === topLevelWorkflowPromise) return true;
|
|
63
|
+
seen.add(current);
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function withSuppressedNativePromiseConsumption(callback) {
|
|
69
|
+
suppressNativePromiseConsumption++;
|
|
70
|
+
try {
|
|
71
|
+
return callback();
|
|
72
|
+
} finally {
|
|
73
|
+
suppressNativePromiseConsumption--;
|
|
74
|
+
}
|
|
35
75
|
}
|
|
36
76
|
|
|
37
77
|
function mergeObservations(...groups) {
|
|
@@ -47,63 +87,107 @@ function mergeObservations(...groups) {
|
|
|
47
87
|
return merged;
|
|
48
88
|
}
|
|
49
89
|
|
|
50
|
-
function
|
|
51
|
-
return value && (typeof value === "object" || typeof value === "function") ?
|
|
90
|
+
function trackedObservationTracker(value) {
|
|
91
|
+
return value && (typeof value === "object" || typeof value === "function") ? trackedPromiseTrackers.get(value) : undefined;
|
|
52
92
|
}
|
|
53
93
|
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
94
|
+
function trackedPromiseTarget(value) {
|
|
95
|
+
return value && (typeof value === "object" || typeof value === "function") ? trackedPromiseTargets.get(value) ?? value : value;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function addTrackedObservations(tracker, observations) {
|
|
99
|
+
tracker.observations = mergeObservations(tracker.observations, observations);
|
|
100
|
+
if (!tracker.consumed) return;
|
|
101
|
+
for (const observation of tracker.observations) {
|
|
102
|
+
if (observedRunCallIds.has(observation.callId)) continue;
|
|
103
|
+
observedRunCallIds.add(observation.callId);
|
|
104
|
+
parentPort.postMessage({ type: "runObserved", callId: observation.callId, key: observation.key });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function markTrackedObservationsConsumed(tracker, seen = new Set()) {
|
|
109
|
+
if (seen.has(tracker)) return;
|
|
110
|
+
seen.add(tracker);
|
|
111
|
+
tracker.consumed = true;
|
|
112
|
+
addTrackedObservations(tracker, []);
|
|
113
|
+
for (const dependency of tracker.dependencies ?? []) markTrackedObservationsConsumed(dependency, seen);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function consumeTrackedObservations(tracker) {
|
|
117
|
+
if (isDirectWorkflowScriptPromiseHandlerCall()) return;
|
|
118
|
+
const activePromise = activeNativePromises.at(-1);
|
|
119
|
+
if (activePromise === topLevelWorkflowPromise || descendsFromTopLevelWorkflow(activePromise)) {
|
|
120
|
+
markTrackedObservationsConsumed(tracker);
|
|
121
|
+
} else if (activePromise) {
|
|
122
|
+
addTrackerDependency(nativePromiseTracker(activePromise), tracker);
|
|
123
|
+
} else {
|
|
124
|
+
markTrackedObservationsConsumed(tracker);
|
|
60
125
|
}
|
|
61
126
|
}
|
|
62
127
|
|
|
128
|
+
function trackObservationTracker(tracker, promise, allowFutureObservations = false) {
|
|
129
|
+
const target = trackedPromiseTarget(promise);
|
|
130
|
+
if ((!allowFutureObservations && tracker.observations.length === 0) || !target || typeof target.then !== "function") return promise;
|
|
131
|
+
|
|
132
|
+
const tracked = new Proxy(target, {
|
|
133
|
+
get(promiseTarget, prop) {
|
|
134
|
+
if (prop === "then") return function promiseThen(onFulfilled, onRejected) {
|
|
135
|
+
consumeTrackedObservations(tracker);
|
|
136
|
+
return trackObservationTracker({ observations: tracker.observations, consumed: false, dependencies: [tracker] }, promiseTarget.then(onFulfilled, onRejected), true);
|
|
137
|
+
};
|
|
138
|
+
if (prop === "catch") return function promiseCatch(onRejected) {
|
|
139
|
+
consumeTrackedObservations(tracker);
|
|
140
|
+
return trackObservationTracker({ observations: tracker.observations, consumed: false, dependencies: [tracker] }, promiseTarget.catch(onRejected), true);
|
|
141
|
+
};
|
|
142
|
+
if (prop === "finally") return function promiseFinally(onFinally) {
|
|
143
|
+
consumeTrackedObservations(tracker);
|
|
144
|
+
return trackObservationTracker({ observations: tracker.observations, consumed: false, dependencies: [tracker] }, promiseTarget.finally(onFinally), true);
|
|
145
|
+
};
|
|
146
|
+
return Reflect.get(promiseTarget, prop, promiseTarget);
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
trackedPromiseTrackers.set(tracked, tracker);
|
|
150
|
+
trackedPromiseTargets.set(tracked, target);
|
|
151
|
+
return tracked;
|
|
152
|
+
}
|
|
153
|
+
|
|
63
154
|
function trackRunObservation(observations, promise) {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (promise[trackedPromisePatch]) return promise;
|
|
68
|
-
|
|
69
|
-
const observedCallIds = new Set();
|
|
70
|
-
const markObserved = () => {
|
|
71
|
-
if (suppressRunObservation > 0 || isDirectWorkflowScriptPromiseHandlerCall()) return;
|
|
72
|
-
for (const observation of trackedObservations(promise)) {
|
|
73
|
-
if (observedCallIds.has(observation.callId)) continue;
|
|
74
|
-
observedCallIds.add(observation.callId);
|
|
75
|
-
parentPort.postMessage({ type: "runObserved", callId: observation.callId, key: observation.key });
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
const originalThen = promise.then.bind(promise);
|
|
79
|
-
const originalCatch = promise.catch.bind(promise);
|
|
80
|
-
const originalFinally = promise.finally.bind(promise);
|
|
81
|
-
Object.defineProperty(promise, trackedPromisePatch, { value: true });
|
|
82
|
-
promise.then = (onFulfilled, onRejected) => {
|
|
83
|
-
markObserved();
|
|
84
|
-
return trackRunObservation(trackedObservations(promise), originalThen(onFulfilled, onRejected));
|
|
85
|
-
};
|
|
86
|
-
promise.catch = (onRejected) => {
|
|
87
|
-
markObserved();
|
|
88
|
-
return trackRunObservation(trackedObservations(promise), originalCatch(onRejected));
|
|
89
|
-
};
|
|
90
|
-
promise.finally = (onFinally) => {
|
|
91
|
-
markObserved();
|
|
92
|
-
return trackRunObservation(trackedObservations(promise), originalFinally(onFinally));
|
|
93
|
-
};
|
|
94
|
-
return promise;
|
|
155
|
+
const tracker = trackedObservationTracker(promise) ?? { observations: [], consumed: false };
|
|
156
|
+
addTrackedObservations(tracker, observations);
|
|
157
|
+
return trackObservationTracker(tracker, promise);
|
|
95
158
|
}
|
|
96
159
|
|
|
97
160
|
function trackPromiseCombinator(items, createPromise) {
|
|
98
161
|
const values = Array.from(items);
|
|
99
|
-
const
|
|
100
|
-
const promise =
|
|
101
|
-
|
|
162
|
+
const dependencies = [...new Set(values.map(promiseObservationTracker).filter(Boolean))];
|
|
163
|
+
const promise = withSuppressedNativePromiseConsumption(() => createPromise(values.map(trackedPromiseTarget)));
|
|
164
|
+
if (dependencies.length === 0) return promise;
|
|
165
|
+
return trackObservationTracker({ observations: [], consumed: false, dependencies }, promise, true);
|
|
102
166
|
}
|
|
103
167
|
|
|
104
168
|
const workflowPromise = new Proxy(Promise, {
|
|
105
|
-
construct(target,
|
|
106
|
-
return new target(
|
|
169
|
+
construct(target, [executor]) {
|
|
170
|
+
if (typeof executor !== "function") return new target(executor);
|
|
171
|
+
const tracker = { observations: [], consumed: false };
|
|
172
|
+
const promise = new target((resolve, reject) => {
|
|
173
|
+
let settled = false;
|
|
174
|
+
try {
|
|
175
|
+
executor((value) => {
|
|
176
|
+
if (settled) return;
|
|
177
|
+
settled = true;
|
|
178
|
+
addTrackerDependency(tracker, promiseObservationTracker(value));
|
|
179
|
+
resolve(trackedPromiseTarget(value));
|
|
180
|
+
}, (reason) => {
|
|
181
|
+
if (settled) return;
|
|
182
|
+
settled = true;
|
|
183
|
+
reject(reason);
|
|
184
|
+
});
|
|
185
|
+
} catch (error) {
|
|
186
|
+
settled = true;
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
return trackObservationTracker(tracker, promise, true);
|
|
107
191
|
},
|
|
108
192
|
get(target, prop) {
|
|
109
193
|
if (prop === "all") return (items) => trackPromiseCombinator(items, (values) => target.all(values));
|
|
@@ -111,9 +195,10 @@ const workflowPromise = new Proxy(Promise, {
|
|
|
111
195
|
if (prop === "race") return (items) => trackPromiseCombinator(items, (values) => target.race(values));
|
|
112
196
|
if (prop === "any") return (items) => trackPromiseCombinator(items, (values) => target.any(values));
|
|
113
197
|
if (prop === "resolve") return (value) => {
|
|
114
|
-
const
|
|
115
|
-
const promise =
|
|
116
|
-
|
|
198
|
+
const dependency = promiseObservationTracker(value);
|
|
199
|
+
const promise = withSuppressedNativePromiseConsumption(() => target.resolve(trackedPromiseTarget(value)));
|
|
200
|
+
if (!dependency) return promise;
|
|
201
|
+
return trackObservationTracker({ observations: [], consumed: false, dependencies: [dependency] }, promise, true);
|
|
117
202
|
};
|
|
118
203
|
const value = target[prop];
|
|
119
204
|
return typeof value === "function" ? value.bind(target) : value;
|
|
@@ -289,7 +374,11 @@ parentPort.on("message", async (message) => {
|
|
|
289
374
|
if (!entry) return;
|
|
290
375
|
pending.delete(message.callId);
|
|
291
376
|
if (message.ok) entry.resolve(message.value);
|
|
292
|
-
else
|
|
377
|
+
else {
|
|
378
|
+
const error = new Error(message.error);
|
|
379
|
+
if (message.errorKind === "detached-child") error.workflowErrorKind = "detached-child";
|
|
380
|
+
entry.reject(error);
|
|
381
|
+
}
|
|
293
382
|
return;
|
|
294
383
|
}
|
|
295
384
|
if (message.type !== "start") return;
|
|
@@ -306,12 +395,68 @@ parentPort.on("message", async (message) => {
|
|
|
306
395
|
parentPort.postMessage({ type: "error", error: formatWorkflowScriptSyntaxError(error) });
|
|
307
396
|
return;
|
|
308
397
|
}
|
|
309
|
-
const
|
|
398
|
+
const nativePromisePrototype = vm.runInContext("(async () => {})().constructor.prototype", context);
|
|
399
|
+
const nativeThenDescriptor = Object.getOwnPropertyDescriptor(nativePromisePrototype, "then");
|
|
400
|
+
if (!nativeThenDescriptor || typeof nativeThenDescriptor.value !== "function") throw new Error("workflowScript could not inspect the VM Promise.prototype.then method.");
|
|
401
|
+
const nativeThen = nativeThenDescriptor.value;
|
|
402
|
+
let stopWorkflowPromiseHook;
|
|
403
|
+
let value;
|
|
404
|
+
try {
|
|
405
|
+
Object.defineProperty(nativePromisePrototype, "then", {
|
|
406
|
+
...nativeThenDescriptor,
|
|
407
|
+
value: function workflowPromiseThen(...args) {
|
|
408
|
+
if (isDirectWorkflowScriptPromiseHandlerCall() || suppressNativePromiseConsumption > 0) {
|
|
409
|
+
return withSuppressedNativePromiseConsumption(() => Reflect.apply(nativeThen, this, args));
|
|
410
|
+
}
|
|
411
|
+
return Reflect.apply(nativeThen, this, args);
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
stopWorkflowPromiseHook = promiseHooks.createHook({
|
|
415
|
+
before(promise) {
|
|
416
|
+
activeNativePromises.push(promise);
|
|
417
|
+
},
|
|
418
|
+
after(promise) {
|
|
419
|
+
const index = activeNativePromises.lastIndexOf(promise);
|
|
420
|
+
if (index !== -1) activeNativePromises.splice(index, 1);
|
|
421
|
+
},
|
|
422
|
+
init(promise, parent) {
|
|
423
|
+
const childTracker = nativePromiseTracker(promise);
|
|
424
|
+
if (!parent) return;
|
|
425
|
+
nativePromiseParents.set(promise, parent);
|
|
426
|
+
const parentTracker = nativePromiseTracker(parent);
|
|
427
|
+
addTrackerDependency(childTracker, parentTracker);
|
|
428
|
+
const activePromise = activeNativePromises.at(-1);
|
|
429
|
+
if (activePromise && activePromise !== parent) {
|
|
430
|
+
addTrackerDependency(nativePromiseTracker(activePromise), parentTracker);
|
|
431
|
+
} else if (!activePromise && suppressNativePromiseConsumption === 0) {
|
|
432
|
+
markTrackedObservationsConsumed(parentTracker);
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
const workflowResultPromise = compiled.runInContext(context);
|
|
437
|
+
topLevelWorkflowPromise = workflowResultPromise;
|
|
438
|
+
markTrackedObservationsConsumed(nativePromiseTracker(workflowResultPromise));
|
|
439
|
+
value = await workflowResultPromise;
|
|
440
|
+
} finally {
|
|
441
|
+
try {
|
|
442
|
+
stopWorkflowPromiseHook?.();
|
|
443
|
+
} finally {
|
|
444
|
+
try {
|
|
445
|
+
Object.defineProperty(nativePromisePrototype, "then", nativeThenDescriptor);
|
|
446
|
+
} finally {
|
|
447
|
+
topLevelWorkflowPromise = undefined;
|
|
448
|
+
activeNativePromises.length = 0;
|
|
449
|
+
suppressNativePromiseConsumption = 0;
|
|
450
|
+
nativePromiseTrackers = new WeakMap();
|
|
451
|
+
nativePromiseParents = new WeakMap();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
310
455
|
const persistedValue = value === undefined ? null : omitUndefinedWorkflowValues(value);
|
|
311
456
|
assertJsonValue(persistedValue, "return");
|
|
312
457
|
parentPort.postMessage({ type: "complete", value: persistedValue });
|
|
313
458
|
} catch (error) {
|
|
314
|
-
parentPort.postMessage({ type: "error", error: error && error.stack ? error.stack : String(error) });
|
|
459
|
+
parentPort.postMessage({ type: "error", error: error && error.stack ? error.stack : String(error), ...(error && error.workflowErrorKind === "detached-child" ? { errorKind: "detached-child" } : {}) });
|
|
315
460
|
}
|
|
316
461
|
});
|
|
317
462
|
`;
|
|
@@ -324,6 +469,7 @@ export interface WorkflowScriptChildResult {
|
|
|
324
469
|
runId?: string;
|
|
325
470
|
output: string;
|
|
326
471
|
error?: string;
|
|
472
|
+
detached?: boolean;
|
|
327
473
|
structuredOutput?: unknown;
|
|
328
474
|
artifactPaths: string[];
|
|
329
475
|
results?: unknown[];
|
|
@@ -332,7 +478,7 @@ export interface WorkflowScriptChildResult {
|
|
|
332
478
|
export interface WorkflowScriptTraceEntry {
|
|
333
479
|
operation: "run" | "status";
|
|
334
480
|
key: string;
|
|
335
|
-
state: "started" | "completed" | "failed" | "stopped" | "reused";
|
|
481
|
+
state: "started" | "completed" | "failed" | "detached" | "stopped" | "reused";
|
|
336
482
|
/** Canonical child agent name when resolved launch or result data is available. */
|
|
337
483
|
agent?: string;
|
|
338
484
|
runId?: string;
|
|
@@ -352,11 +498,13 @@ export interface WorkflowScriptResult {
|
|
|
352
498
|
|
|
353
499
|
export class WorkflowScriptError extends Error {
|
|
354
500
|
readonly partial: Omit<WorkflowScriptResult, "value">;
|
|
501
|
+
readonly errorKind?: "detached-child";
|
|
355
502
|
|
|
356
|
-
constructor(message: string, partial: Omit<WorkflowScriptResult, "value"
|
|
503
|
+
constructor(message: string, partial: Omit<WorkflowScriptResult, "value">, errorKind?: "detached-child") {
|
|
357
504
|
super(message);
|
|
358
505
|
this.name = "WorkflowScriptError";
|
|
359
506
|
this.partial = partial;
|
|
507
|
+
this.errorKind = errorKind;
|
|
360
508
|
}
|
|
361
509
|
}
|
|
362
510
|
|
|
@@ -505,7 +653,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
505
653
|
const traceChanged = () => options.onTrace?.([...trace]);
|
|
506
654
|
|
|
507
655
|
return await new Promise<WorkflowScriptResult>((resolve, reject) => {
|
|
508
|
-
const finish = (outcome: { value: unknown } | { error: Error }) => {
|
|
656
|
+
const finish = (outcome: { value: unknown } | { error: Error & { workflowErrorKind?: unknown } }) => {
|
|
509
657
|
if (settled) return;
|
|
510
658
|
settled = true;
|
|
511
659
|
if (timer) clearTimeout(timer);
|
|
@@ -516,7 +664,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
516
664
|
? new Error(`workflowScript completed with unawaited runs.run launch(es): ${unobservedKeys.map((key) => `'${key}'`).join(", ")}. Await or return each launch.`)
|
|
517
665
|
: undefined;
|
|
518
666
|
childController.abort("error" in outcome ? outcome.error : completionError ?? new Error("Workflow script completed."));
|
|
519
|
-
if ("error" in outcome) reject(new WorkflowScriptError(outcome.error.message, partial()));
|
|
667
|
+
if ("error" in outcome) reject(new WorkflowScriptError(outcome.error.message, partial(), outcome.error.workflowErrorKind === "detached-child" ? "detached-child" : undefined));
|
|
520
668
|
else if (completionError) reject(new WorkflowScriptError(completionError.message, partial()));
|
|
521
669
|
else resolve({ value: outcome.value, ...partial() });
|
|
522
670
|
};
|
|
@@ -585,7 +733,11 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
585
733
|
}
|
|
586
734
|
return finish({ value: message.value });
|
|
587
735
|
}
|
|
588
|
-
if (message.type === "error")
|
|
736
|
+
if (message.type === "error") {
|
|
737
|
+
const workflowError = new Error(typeof message.error === "string" ? message.error : "Workflow script failed.") as Error & { workflowErrorKind?: "detached-child" };
|
|
738
|
+
if (message.errorKind === "detached-child") workflowError.workflowErrorKind = "detached-child";
|
|
739
|
+
return finish({ error: workflowError });
|
|
740
|
+
}
|
|
589
741
|
if (message.type === "runObserved" && typeof message.callId === "number") {
|
|
590
742
|
const key = typeof message.key === "string" ? message.key : undefined;
|
|
591
743
|
const launch = key ? launches.get(key) : undefined;
|
|
@@ -601,7 +753,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
601
753
|
if (!settled) worker.postMessage({ type: "response", callId: message.callId, ok: true, value: omitUndefinedWorkflowValues(value) });
|
|
602
754
|
},
|
|
603
755
|
(error: unknown) => {
|
|
604
|
-
if (!settled) worker.postMessage({ type: "response", callId: message.callId, ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
756
|
+
if (!settled) worker.postMessage({ type: "response", callId: message.callId, ok: false, error: error instanceof Error ? error.message : String(error), ...(error instanceof Error && (error as { workflowErrorKind?: unknown }).workflowErrorKind === "detached-child" ? { errorKind: "detached-child" } : {}) });
|
|
605
757
|
},
|
|
606
758
|
);
|
|
607
759
|
};
|
|
@@ -693,7 +845,11 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
693
845
|
const deliver = (promise: Promise<WorkflowScriptChildResult>) => collectFailure
|
|
694
846
|
? promise
|
|
695
847
|
: promise.then((result) => {
|
|
696
|
-
if (!result.ok)
|
|
848
|
+
if (!result.ok) {
|
|
849
|
+
const childError = new Error(result.detached ? `Run '${key}' detached: ${result.error ?? result.output}` : `Run '${key}' failed: ${result.error ?? result.output}`) as Error & { workflowErrorKind?: "detached-child" };
|
|
850
|
+
if (result.detached) childError.workflowErrorKind = "detached-child";
|
|
851
|
+
throw childError;
|
|
852
|
+
}
|
|
697
853
|
return result;
|
|
698
854
|
});
|
|
699
855
|
const fingerprint = stableJson(params);
|
|
@@ -735,7 +891,8 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
735
891
|
const normalized = !result.ok && !result.error ? { ...result, error: result.output } : result;
|
|
736
892
|
if (stoppedLaunches.has(key)) return normalized;
|
|
737
893
|
children.set(key, normalized);
|
|
738
|
-
|
|
894
|
+
const state = normalized.ok ? "completed" : normalized.detached ? "detached" : "failed";
|
|
895
|
+
trace.push({ operation: "run", key, state, durationMs: Date.now() - startedAt, ...workflowStringMetadata(params), ...(normalized.agent ? { agent: normalized.agent } : {}), ...(normalized.runId ? { runId: normalized.runId } : {}), ...(!normalized.ok ? { error: normalized.error ?? normalized.output } : {}) });
|
|
739
896
|
traceChanged();
|
|
740
897
|
return normalized;
|
|
741
898
|
}, (error: unknown) => {
|