@selesai/code 0.8.5 → 0.8.7
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 +28 -0
- package/dist/core/agent-session.js +13 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/provider-composer.js +17 -4
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/system-prompt.js +2 -8
- package/dist/core/system-prompt.test.js +6 -0
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/schema-prune.d.ts +16 -0
- package/dist/core/tools/schema-prune.js +39 -0
- package/dist/core/tools/schema-prune.test.d.ts +1 -0
- package/dist/core/tools/schema-prune.test.js +111 -0
- package/dist/defaults/models.json +5 -0
- package/dist/defaults/settings.json +1 -0
- package/dist/extensions/agent-browser.test.ts +178 -2
- package/dist/extensions/agent-browser.ts +2 -0
- package/dist/extensions/copy-turn.test.ts +229 -0
- package/dist/extensions/copy-turn.ts +11 -0
- package/dist/extensions/grep-app/index.test.ts +356 -1
- package/dist/extensions/grep-app/index.ts +6 -1
- package/dist/extensions/handoff-new.test.ts +351 -161
- package/dist/extensions/handoff-new.ts +3 -0
- package/dist/extensions/inline-skills.test.ts +93 -0
- package/dist/extensions/inline-skills.ts +3 -0
- package/dist/extensions/model-prompt-injector/config.json +10 -0
- package/dist/extensions/model-prompt-injector/index.test.ts +112 -0
- package/dist/extensions/model-prompt-injector/index.ts +151 -0
- package/dist/extensions/package.json +1 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +158 -90
- package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +172 -0
- package/dist/extensions/pi-subagents/agents/builder.md +5 -2
- package/dist/extensions/pi-subagents/agents/commentator.md +1 -1
- package/dist/extensions/pi-subagents/agents/oracle.md +7 -5
- package/dist/extensions/pi-subagents/agents/reviewer.md +2 -2
- package/dist/extensions/pi-subagents/agents/scout.md +1 -1
- package/dist/extensions/pi-subagents/agents/worker.md +1 -1
- package/dist/extensions/pi-subagents/docs/configuration.md +78 -11
- package/dist/extensions/pi-subagents/docs/extension-api.md +36 -0
- package/dist/extensions/pi-subagents/docs/missions.md +5 -3
- package/dist/extensions/pi-subagents/docs/models.md +1 -1
- package/dist/extensions/pi-subagents/docs/observability.md +42 -2
- package/dist/extensions/pi-subagents/docs/tool-reference.md +20 -3
- package/dist/extensions/pi-subagents/docs/workflows.md +4 -4
- package/dist/extensions/pi-subagents/install.mjs +2 -2
- package/dist/extensions/pi-subagents/package-lock.json +1470 -1472
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +16 -15
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +46 -26
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +37 -12
- package/dist/extensions/pi-subagents/src/api/external-runs.ts +174 -84
- package/dist/extensions/pi-subagents/src/api/preflight.ts +13 -7
- package/dist/extensions/pi-subagents/src/extension/config.ts +59 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +146 -23
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +34 -6
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +5 -1
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -30
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +10 -9
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +13 -8
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +16 -3
- package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -6
- package/dist/extensions/pi-subagents/src/inspectors/herdr/shell-command.ts +16 -0
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +5 -4
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +19 -42
- package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +3 -1
- package/dist/extensions/pi-subagents/src/missions/store.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +82 -25
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +71 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +73 -43
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +17 -6
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -6
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +277 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +11 -3
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +11 -1
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +21 -6
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +437 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +224 -42
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +27 -7
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +76 -19
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +35 -26
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +108 -9
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +54 -28
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +27 -13
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +298 -33
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +219 -15
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -4
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +4 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +368 -50
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +107 -1
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +39 -23
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +16 -2
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +66 -62
- package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +376 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +15 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +95 -0
- package/dist/extensions/pi-subagents/src/shared/agent-stream-options.ts +5 -0
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +0 -4
- package/dist/extensions/pi-subagents/src/shared/display-text.ts +50 -0
- package/dist/extensions/pi-subagents/src/shared/node-executable.ts +21 -0
- package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +71 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +66 -4
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +34 -25
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +4 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +160 -45
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +1 -48
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +129 -17
- package/dist/extensions/pi-subagents/src/tui/render.ts +122 -44
- package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +2 -1
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -3
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +10 -2
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +272 -76
- package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +68 -9
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +27 -1
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +49 -0
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +26 -1
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +144 -0
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +369 -98
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +111 -78
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +49 -1
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +12 -0
- package/dist/extensions/pi-subagents/test/support/node-command.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +61 -0
- package/dist/extensions/pi-subagents/test/unit/agent-stream-options.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +76 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +162 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +209 -10
- package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +135 -42
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +252 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +192 -6
- package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +59 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +23 -4
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +56 -1
- package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +67 -3
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +25 -2
- package/dist/extensions/pi-subagents/test/unit/node-executable.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +348 -0
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +5 -2
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +6 -1
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +92 -0
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +127 -17
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +247 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +107 -2
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +5 -8
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +456 -4
- package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +73 -0
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +128 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +16 -7
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +79 -15
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +109 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +45 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +94 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +42 -2
- package/dist/extensions/ponytail/index.js +11 -0
- package/dist/extensions/ponytail/ponytail-config.cjs +2 -0
- package/dist/extensions/ponytail/ponytail-instructions.cjs +6 -0
- package/dist/extensions/ponytail/test/extension.test.js +274 -140
- package/dist/extensions/ponytail/test/helpers.test.js +280 -92
- package/dist/extensions/question/index.ts +33 -0
- package/dist/extensions/question/question-list.ts +28 -0
- package/dist/extensions/question/row-layout.ts +3 -0
- package/dist/extensions/question/tests/batch.test.ts +103 -65
- package/dist/extensions/question/tests/exp.test.ts +70 -0
- package/dist/extensions/question/tests/exp2.test.ts +59 -0
- package/dist/extensions/question/tests/exp3.test.ts +47 -0
- package/dist/extensions/question/tests/helpers.test.ts +75 -35
- package/dist/extensions/question/tests/question-list.test.ts +640 -198
- package/dist/extensions/question/tests/row-layout.test.ts +193 -114
- package/dist/extensions/question/tests/schemas.test.ts +42 -19
- package/dist/extensions/question/tests/selection-mode.test.ts +54 -0
- package/dist/extensions/question/tests/shortcuts.test.ts +106 -84
- package/dist/extensions/question/tests/wizard.test.ts +771 -50
- package/dist/extensions/question/tests/zz-ig3.test.ts +20 -0
- package/dist/extensions/question/tests/zz-probe.test.ts +29 -0
- package/dist/extensions/rtk.test.ts +180 -1
- package/dist/extensions/tokenin-onboarding.ts +3 -0
- package/dist/extensions/undo.test.ts +720 -0
- package/dist/extensions/undo.ts +6 -0
- package/dist/extensions/web-agent-onboarding.test.ts +415 -0
- package/dist/extensions/workflow/extension.ts +3 -3
- package/dist/extensions/workflow/modes.ts +41 -19
- package/dist/skills/pi-subagents/SKILL.md +1 -1
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
- package/dist/skills/pi-subagents/references/execution-controls.md +16 -15
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +46 -26
- package/docs/plans/workflow-autoloop-reference.md +1 -2
- package/docs/plans/workflow-handoff-carryover.md +108 -0
- package/docs/settings.md +6 -0
- package/package.json +4 -1
|
@@ -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" | "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
|
|
|
@@ -492,6 +640,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
492
640
|
const children = new Map<string, WorkflowScriptChildResult>();
|
|
493
641
|
const childOrder: string[] = [];
|
|
494
642
|
const launches = new Map<string, { fingerprint: string; promise: Promise<WorkflowScriptChildResult>; observed: boolean }>();
|
|
643
|
+
const stoppedLaunches = new Set<string>();
|
|
495
644
|
const batchAdmissions = new Map<string, Promise<void>>();
|
|
496
645
|
const observedRunCalls = new Set<number>();
|
|
497
646
|
const childController = new AbortController();
|
|
@@ -504,7 +653,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
504
653
|
const traceChanged = () => options.onTrace?.([...trace]);
|
|
505
654
|
|
|
506
655
|
return await new Promise<WorkflowScriptResult>((resolve, reject) => {
|
|
507
|
-
const finish = (outcome: { value: unknown } | { error: Error }) => {
|
|
656
|
+
const finish = (outcome: { value: unknown } | { error: Error & { workflowErrorKind?: unknown } }) => {
|
|
508
657
|
if (settled) return;
|
|
509
658
|
settled = true;
|
|
510
659
|
if (timer) clearTimeout(timer);
|
|
@@ -515,11 +664,34 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
515
664
|
? new Error(`workflowScript completed with unawaited runs.run launch(es): ${unobservedKeys.map((key) => `'${key}'`).join(", ")}. Await or return each launch.`)
|
|
516
665
|
: undefined;
|
|
517
666
|
childController.abort("error" in outcome ? outcome.error : completionError ?? new Error("Workflow script completed."));
|
|
518
|
-
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));
|
|
519
668
|
else if (completionError) reject(new WorkflowScriptError(completionError.message, partial()));
|
|
520
669
|
else resolve({ value: outcome.value, ...partial() });
|
|
521
670
|
};
|
|
522
|
-
const onAbort = () =>
|
|
671
|
+
const onAbort = () => {
|
|
672
|
+
const signalReason = options.signal?.reason;
|
|
673
|
+
const error = signalReason instanceof Error
|
|
674
|
+
? signalReason
|
|
675
|
+
: typeof signalReason === "string"
|
|
676
|
+
? new Error(signalReason)
|
|
677
|
+
: new Error("Workflow script aborted.");
|
|
678
|
+
for (const key of launches.keys()) {
|
|
679
|
+
if (children.has(key)) continue;
|
|
680
|
+
stoppedLaunches.add(key);
|
|
681
|
+
const started = trace.findLast((entry) => entry.operation === "run" && entry.key === key && entry.state === "started");
|
|
682
|
+
trace.push({
|
|
683
|
+
operation: "run",
|
|
684
|
+
key,
|
|
685
|
+
state: "stopped",
|
|
686
|
+
...(started?.agent ? { agent: started.agent } : {}),
|
|
687
|
+
...(started?.phase ? { phase: started.phase } : {}),
|
|
688
|
+
...(started?.label ? { label: started.label } : {}),
|
|
689
|
+
error: error.message,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
traceChanged();
|
|
693
|
+
finish({ error });
|
|
694
|
+
};
|
|
523
695
|
const timer = options.timeoutMs === undefined
|
|
524
696
|
? undefined
|
|
525
697
|
: setTimeout(() => finish({ error: new Error(`Workflow script timed out after ${options.timeoutMs}ms.`) }), options.timeoutMs);
|
|
@@ -531,6 +703,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
531
703
|
if (!settled && code !== 0) finish({ error: new Error(`Workflow worker exited with code ${code}.`) });
|
|
532
704
|
});
|
|
533
705
|
worker.on("message", (message: Record<string, unknown>) => {
|
|
706
|
+
if (settled) return;
|
|
534
707
|
if (message.type === "emit") {
|
|
535
708
|
try {
|
|
536
709
|
assertWorkflowJsonValue(message.value, "emit");
|
|
@@ -560,7 +733,11 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
560
733
|
}
|
|
561
734
|
return finish({ value: message.value });
|
|
562
735
|
}
|
|
563
|
-
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
|
+
}
|
|
564
741
|
if (message.type === "runObserved" && typeof message.callId === "number") {
|
|
565
742
|
const key = typeof message.key === "string" ? message.key : undefined;
|
|
566
743
|
const launch = key ? launches.get(key) : undefined;
|
|
@@ -576,7 +753,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
576
753
|
if (!settled) worker.postMessage({ type: "response", callId: message.callId, ok: true, value: omitUndefinedWorkflowValues(value) });
|
|
577
754
|
},
|
|
578
755
|
(error: unknown) => {
|
|
579
|
-
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" } : {}) });
|
|
580
757
|
},
|
|
581
758
|
);
|
|
582
759
|
};
|
|
@@ -617,7 +794,9 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
617
794
|
const target = known?.runId ?? keyOrRunId;
|
|
618
795
|
trace.push({ operation: "status", key: keyOrRunId, state: "started", ...(known?.runId ? { runId: known.runId } : {}) });
|
|
619
796
|
traceChanged();
|
|
797
|
+
if (settled) return;
|
|
620
798
|
respond(options.status(target, childController.signal).then((result) => {
|
|
799
|
+
if (settled) return result;
|
|
621
800
|
trace.push({ operation: "status", key: keyOrRunId, state: result.ok ? "completed" : "failed", ...(result.runId ? { runId: result.runId } : {}), ...(!result.ok ? { error: result.output } : {}) });
|
|
622
801
|
traceChanged();
|
|
623
802
|
if (!result.ok) throw new Error(`Status '${keyOrRunId}' failed: ${result.output}`);
|
|
@@ -666,7 +845,11 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
666
845
|
const deliver = (promise: Promise<WorkflowScriptChildResult>) => collectFailure
|
|
667
846
|
? promise
|
|
668
847
|
: promise.then((result) => {
|
|
669
|
-
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
|
+
}
|
|
670
853
|
return result;
|
|
671
854
|
});
|
|
672
855
|
const fingerprint = stableJson(params);
|
|
@@ -680,9 +863,6 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
680
863
|
}
|
|
681
864
|
|
|
682
865
|
const startedAt = Date.now();
|
|
683
|
-
childOrder.push(key);
|
|
684
|
-
trace.push({ operation: "run", key, state: "started", ...workflowStringMetadata(params) });
|
|
685
|
-
traceChanged();
|
|
686
866
|
const batch = isRecord(message.args.batch) && typeof message.args.batch.id === "string" && Array.isArray(message.args.batch.calls)
|
|
687
867
|
? { id: message.args.batch.id, calls: message.args.batch.calls.filter((call): call is { key: string; params: Record<string, unknown> } => isRecord(call) && typeof call.key === "string" && isRecord(call.params)) }
|
|
688
868
|
: undefined;
|
|
@@ -694,24 +874,40 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
694
874
|
seenKeys.add(call.key);
|
|
695
875
|
return true;
|
|
696
876
|
});
|
|
697
|
-
admission = Promise.resolve().then(() =>
|
|
877
|
+
admission = Promise.resolve().then(() => {
|
|
878
|
+
if (settled) return;
|
|
879
|
+
return options.admit?.(calls);
|
|
880
|
+
});
|
|
698
881
|
if (batch) batchAdmissions.set(batch.id, admission);
|
|
699
882
|
}
|
|
700
|
-
const promise = admission.then(() =>
|
|
883
|
+
const promise = admission.then(() => {
|
|
884
|
+
if (settled || stoppedLaunches.has(key)) {
|
|
885
|
+
const reason = childController.signal.reason;
|
|
886
|
+
const text = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "Workflow script aborted.";
|
|
887
|
+
return { key, ok: false, output: text, error: text, artifactPaths: [] };
|
|
888
|
+
}
|
|
889
|
+
return options.launch(key, { ...params, async: params.async ?? false }, childController.signal, { admitted: true });
|
|
890
|
+
}).then((result) => {
|
|
701
891
|
const normalized = !result.ok && !result.error ? { ...result, error: result.output } : result;
|
|
892
|
+
if (stoppedLaunches.has(key)) return normalized;
|
|
702
893
|
children.set(key, normalized);
|
|
703
|
-
|
|
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 } : {}) });
|
|
704
896
|
traceChanged();
|
|
705
897
|
return normalized;
|
|
706
898
|
}, (error: unknown) => {
|
|
707
899
|
const text = error instanceof Error ? error.message : String(error);
|
|
708
900
|
const failure: WorkflowScriptChildResult = { key, ok: false, output: text, error: text, artifactPaths: [] };
|
|
901
|
+
if (stoppedLaunches.has(key)) return failure;
|
|
709
902
|
children.set(key, failure);
|
|
710
903
|
trace.push({ operation: "run", key, state: "failed", durationMs: Date.now() - startedAt, ...workflowStringMetadata(params), error: text });
|
|
711
904
|
traceChanged();
|
|
712
905
|
return failure;
|
|
713
906
|
});
|
|
714
907
|
launches.set(key, { fingerprint, promise, observed: callObserved });
|
|
908
|
+
childOrder.push(key);
|
|
909
|
+
trace.push({ operation: "run", key, state: "started", ...workflowStringMetadata(params) });
|
|
910
|
+
traceChanged();
|
|
715
911
|
respond(deliver(promise));
|
|
716
912
|
});
|
|
717
913
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-relaunch policy for scripted workflows that end before the goal is clean
|
|
3
|
+
* because the per-run fan-out budget was exhausted.
|
|
4
|
+
*
|
|
5
|
+
* A `budget` result means the goal was demonstrably not reached, so the only
|
|
6
|
+
* decision is whether to relaunch. `clean` and failures always surface to the
|
|
7
|
+
* parent — the parent remains the authority on whether the goal is met.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_MAX_WORKFLOW_AUTO_RELAUNCHES = 12;
|
|
11
|
+
|
|
12
|
+
export interface AutoRelaunchDecision {
|
|
13
|
+
relaunch: boolean;
|
|
14
|
+
capped: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resolveMaxWorkflowAutoRelaunches(raw: unknown): number | undefined {
|
|
18
|
+
if (raw === undefined) return DEFAULT_MAX_WORKFLOW_AUTO_RELAUNCHES;
|
|
19
|
+
if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 0) return DEFAULT_MAX_WORKFLOW_AUTO_RELAUNCHES;
|
|
20
|
+
return raw === 0 ? undefined : raw;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resolveAutoRelaunchDecision(value: unknown, relaunched: number, cap: number | undefined): AutoRelaunchDecision {
|
|
24
|
+
const budgetExhausted = !!value && typeof value === "object" && !Array.isArray(value) && (value as Record<string, unknown>).result === "budget";
|
|
25
|
+
if (!budgetExhausted) return { relaunch: false, capped: false };
|
|
26
|
+
if (cap === undefined) return { relaunch: true, capped: false };
|
|
27
|
+
return relaunched >= cap ? { relaunch: false, capped: true } : { relaunch: true, capped: false };
|
|
28
|
+
}
|
|
@@ -347,6 +347,8 @@ export interface ToolDefinition<TParams extends TSchema = TSchema, TDetails = un
|
|
|
347
347
|
promptGuidelines?: string[];
|
|
348
348
|
/** Parameter schema (TypeBox) */
|
|
349
349
|
parameters: TParams;
|
|
350
|
+
/** When global pruneToolDescriptions is enabled, keep this tool's per-field parameter descriptions on the wire (already pruned by the tool itself). */
|
|
351
|
+
keepParameterDescriptions?: boolean;
|
|
350
352
|
/** Controls whether ToolExecutionComponent renders the standard colored shell or the tool renders its own framing. */
|
|
351
353
|
renderShell?: "default" | "self";
|
|
352
354
|
/** Optional compatibility shim to prepare raw tool call arguments before schema validation. Must return an object conforming to TParams. */
|
|
@@ -784,7 +784,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
784
784
|
assert.deepEqual(status.capabilityCeiling, payload.capabilityCeiling);
|
|
785
785
|
assert.deepEqual(status.steps?.[0]?.capabilityCeiling, payload.capabilityCeiling);
|
|
786
786
|
assert.deepEqual(payload.capabilityAudit?.effectiveTools, ["read"]);
|
|
787
|
-
assert.deepEqual(payload.capabilityAudit?.removedTools, ["write", "
|
|
787
|
+
assert.deepEqual(payload.capabilityAudit?.removedTools, ["write", "contact_supervisor"]);
|
|
788
788
|
assert.equal(payload.capabilityAudit?.extensionsDenied, true);
|
|
789
789
|
const events = fs.readFileSync(path.join(ASYNC_DIR, asyncId, "events.jsonl"), "utf-8").trim().split("\n").map((line) => JSON.parse(line));
|
|
790
790
|
assert.ok(events.some((event) => event.type === "subagent.capability-ceiling.applied" && event.stepIndex === 0 && event.capabilityAudit?.removedTools?.includes("write")));
|
|
@@ -793,7 +793,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
793
793
|
const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8")) as { launchContractDigest?: string; capabilityCeiling?: unknown; capabilityAudit?: { removedTools?: string[] } };
|
|
794
794
|
assert.equal(metadata.launchContractDigest, payload.results[0]?.launchContractDigest);
|
|
795
795
|
assert.deepEqual(metadata.capabilityCeiling, payload.capabilityCeiling);
|
|
796
|
-
assert.deepEqual(metadata.capabilityAudit?.removedTools, ["write", "
|
|
796
|
+
assert.deepEqual(metadata.capabilityAudit?.removedTools, ["write", "contact_supervisor"]);
|
|
797
797
|
} finally {
|
|
798
798
|
handle.dispose();
|
|
799
799
|
}
|