@selesai/code 0.8.6 → 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.
Files changed (192) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/core/agent-session.js +13 -1
  3. package/dist/core/extensions/types.d.ts +2 -0
  4. package/dist/core/provider-composer.js +17 -4
  5. package/dist/core/settings-manager.d.ts +2 -0
  6. package/dist/core/settings-manager.js +3 -0
  7. package/dist/core/system-prompt.js +2 -8
  8. package/dist/core/system-prompt.test.js +6 -0
  9. package/dist/core/tools/index.d.ts +1 -0
  10. package/dist/core/tools/index.js +1 -0
  11. package/dist/core/tools/schema-prune.d.ts +16 -0
  12. package/dist/core/tools/schema-prune.js +39 -0
  13. package/dist/core/tools/schema-prune.test.d.ts +1 -0
  14. package/dist/core/tools/schema-prune.test.js +111 -0
  15. package/dist/defaults/settings.json +1 -0
  16. package/dist/extensions/model-prompt-injector/config.json +10 -0
  17. package/dist/extensions/model-prompt-injector/index.test.ts +112 -0
  18. package/dist/extensions/model-prompt-injector/index.ts +151 -0
  19. package/dist/extensions/package.json +1 -0
  20. package/dist/extensions/pi-subagents/CHANGELOG.md +158 -90
  21. package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +172 -0
  22. package/dist/extensions/pi-subagents/agents/oracle.md +7 -5
  23. package/dist/extensions/pi-subagents/agents/reviewer.md +2 -2
  24. package/dist/extensions/pi-subagents/agents/scout.md +1 -1
  25. package/dist/extensions/pi-subagents/agents/worker.md +1 -1
  26. package/dist/extensions/pi-subagents/docs/configuration.md +78 -19
  27. package/dist/extensions/pi-subagents/docs/extension-api.md +36 -0
  28. package/dist/extensions/pi-subagents/docs/missions.md +5 -3
  29. package/dist/extensions/pi-subagents/docs/models.md +1 -1
  30. package/dist/extensions/pi-subagents/docs/observability.md +42 -2
  31. package/dist/extensions/pi-subagents/docs/tool-reference.md +20 -3
  32. package/dist/extensions/pi-subagents/docs/workflows.md +5 -5
  33. package/dist/extensions/pi-subagents/install.mjs +2 -2
  34. package/dist/extensions/pi-subagents/package-lock.json +1470 -1472
  35. package/dist/extensions/pi-subagents/package.json +1 -1
  36. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +1 -1
  37. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
  38. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +15 -14
  39. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
  40. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +46 -26
  41. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
  42. package/dist/extensions/pi-subagents/src/agents/agents.ts +37 -12
  43. package/dist/extensions/pi-subagents/src/api/external-runs.ts +174 -84
  44. package/dist/extensions/pi-subagents/src/api/preflight.ts +13 -7
  45. package/dist/extensions/pi-subagents/src/extension/config.ts +53 -0
  46. package/dist/extensions/pi-subagents/src/extension/index.ts +67 -13
  47. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +34 -6
  48. package/dist/extensions/pi-subagents/src/extension/rpc.ts +5 -1
  49. package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -30
  50. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +10 -9
  51. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +13 -8
  52. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +16 -3
  53. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -6
  54. package/dist/extensions/pi-subagents/src/inspectors/herdr/shell-command.ts +16 -0
  55. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +5 -4
  56. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +19 -42
  57. package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +3 -1
  58. package/dist/extensions/pi-subagents/src/missions/store.ts +8 -3
  59. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +82 -25
  60. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +71 -1
  61. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +73 -43
  62. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +5 -0
  63. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -6
  64. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +277 -0
  65. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +8 -3
  66. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +2 -2
  67. package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +11 -1
  68. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +21 -6
  69. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +437 -0
  70. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +205 -41
  71. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +27 -7
  72. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +76 -19
  73. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +30 -24
  74. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +101 -4
  75. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +54 -28
  76. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +27 -13
  77. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +298 -33
  78. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +2 -0
  79. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +5 -2
  80. package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +2 -1
  81. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +16 -0
  82. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +219 -15
  83. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +4 -3
  84. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +333 -45
  85. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +107 -1
  86. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +4 -0
  87. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +39 -23
  88. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +16 -2
  89. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +66 -62
  90. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +376 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
  92. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +15 -0
  93. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
  94. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +12 -0
  95. package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +95 -0
  96. package/dist/extensions/pi-subagents/src/shared/agent-stream-options.ts +5 -0
  97. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +0 -4
  98. package/dist/extensions/pi-subagents/src/shared/display-text.ts +50 -0
  99. package/dist/extensions/pi-subagents/src/shared/node-executable.ts +21 -0
  100. package/dist/extensions/pi-subagents/src/shared/types.ts +50 -4
  101. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +34 -25
  102. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +4 -0
  103. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +160 -45
  104. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +1 -48
  105. package/dist/extensions/pi-subagents/src/tui/fleet.ts +128 -16
  106. package/dist/extensions/pi-subagents/src/tui/render.ts +122 -44
  107. package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +2 -1
  108. package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -3
  109. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +10 -2
  110. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +272 -76
  111. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +2 -0
  112. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2 -2
  113. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +68 -9
  114. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +27 -1
  115. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1 -1
  116. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +49 -0
  117. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  118. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +26 -1
  119. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +144 -0
  120. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +26 -0
  121. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +369 -98
  122. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +111 -78
  123. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +49 -1
  124. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +12 -0
  125. package/dist/extensions/pi-subagents/test/support/node-command.ts +15 -0
  126. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +50 -0
  127. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +28 -0
  128. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +61 -0
  129. package/dist/extensions/pi-subagents/test/unit/agent-stream-options.test.ts +14 -0
  130. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +76 -0
  131. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
  132. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +162 -0
  133. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +209 -10
  134. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +41 -1
  135. package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +135 -42
  136. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +252 -1
  137. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +192 -6
  138. package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +32 -0
  139. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +59 -0
  140. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +69 -0
  141. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +23 -4
  142. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +56 -1
  143. package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +67 -3
  144. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  145. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +14 -0
  146. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +18 -0
  147. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +2 -2
  148. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +25 -2
  149. package/dist/extensions/pi-subagents/test/unit/node-executable.test.ts +26 -0
  150. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +8 -0
  151. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +348 -0
  152. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +5 -2
  153. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +1 -1
  154. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +34 -0
  155. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +6 -1
  156. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +92 -0
  157. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +175 -0
  158. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +127 -17
  159. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +44 -0
  160. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +247 -0
  161. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +107 -2
  162. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +5 -8
  163. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +456 -4
  164. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +128 -0
  165. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +28 -0
  166. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +16 -7
  167. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +15 -0
  168. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +79 -15
  169. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +12 -12
  170. package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +109 -0
  171. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +34 -0
  172. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +94 -1
  173. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +42 -2
  174. package/dist/extensions/question/index.ts +21 -6
  175. package/dist/extensions/question/question-list.ts +7 -1
  176. package/dist/extensions/question/row-layout.ts +3 -0
  177. package/dist/extensions/question/tests/exp.test.ts +70 -0
  178. package/dist/extensions/question/tests/exp2.test.ts +59 -0
  179. package/dist/extensions/question/tests/exp3.test.ts +47 -0
  180. package/dist/extensions/question/tests/helpers.test.ts +7 -0
  181. package/dist/extensions/question/tests/question-list.test.ts +140 -0
  182. package/dist/extensions/question/tests/row-layout.test.ts +13 -0
  183. package/dist/extensions/question/tests/selection-mode.test.ts +54 -0
  184. package/dist/extensions/question/tests/zz-ig3.test.ts +20 -0
  185. package/dist/extensions/question/tests/zz-probe.test.ts +29 -0
  186. package/dist/skills/pi-subagents/SKILL.md +1 -1
  187. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
  188. package/dist/skills/pi-subagents/references/execution-controls.md +15 -14
  189. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
  190. package/dist/skills/pi-subagents/references/prompting-and-roles.md +46 -26
  191. package/docs/settings.md +6 -0
  192. package/package.json +3 -3
@@ -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 trackedPromisePatch = Symbol("trackedPromisePatch");
14
- const trackedPromiseObservations = new WeakMap();
15
- let suppressRunObservation = 0;
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
- const frame = stack.split("\n").slice(1).map((line) => line.trim()).find((line) =>
27
- line &&
28
- !line.includes("isDirectWorkflowScriptPromiseHandlerCall") &&
29
- !line.includes("markObserved") &&
30
- !line.includes("promise.then") &&
31
- !line.includes("promise.catch") &&
32
- !line.includes("promise.finally")
33
- );
34
- return frame ? frame.includes("workflow-script.js") && !frame.includes("at async ") : false;
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 trackedObservations(value) {
51
- return value && (typeof value === "object" || typeof value === "function") ? trackedPromiseObservations.get(value) ?? [] : [];
90
+ function trackedObservationTracker(value) {
91
+ return value && (typeof value === "object" || typeof value === "function") ? trackedPromiseTrackers.get(value) : undefined;
52
92
  }
53
93
 
54
- function withSuppressedRunObservation(callback) {
55
- suppressRunObservation += 1;
56
- try {
57
- return callback();
58
- } finally {
59
- suppressRunObservation -= 1;
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 merged = mergeObservations(trackedObservations(promise), observations);
65
- if (merged.length === 0 || !promise || typeof promise.then !== "function") return promise;
66
- trackedPromiseObservations.set(promise, merged);
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 observations = mergeObservations(...values.map(trackedObservations));
100
- const promise = withSuppressedRunObservation(() => createPromise(values));
101
- return observations.length > 0 ? trackRunObservation(observations, promise) : promise;
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, args) {
106
- return new target(...args);
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 observations = trackedObservations(value);
115
- const promise = withSuppressedRunObservation(() => target.resolve(value));
116
- return observations.length > 0 ? trackRunObservation(observations, promise) : promise;
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 entry.reject(new Error(message.error));
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 value = await compiled.runInContext(context);
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 = () => finish({ error: new Error("Workflow script aborted.") });
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") return finish({ error: new Error(typeof message.error === "string" ? message.error : "Workflow script failed.") });
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) throw new Error(`Run '${key}' failed: ${result.error ?? result.output}`);
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(() => options.admit?.(calls));
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(() => options.launch(key, { ...params, async: params.async ?? false }, childController.signal, { admitted: true })).then((result) => {
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
- trace.push({ operation: "run", key, state: normalized.ok ? "completed" : "failed", durationMs: Date.now() - startedAt, ...workflowStringMetadata(params), ...(normalized.agent ? { agent: normalized.agent } : {}), ...(normalized.runId ? { runId: normalized.runId } : {}), ...(!normalized.ok ? { error: normalized.error ?? normalized.output } : {}) });
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
 
@@ -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", "intercom", "contact_supervisor"]);
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", "intercom", "contact_supervisor"]);
796
+ assert.deepEqual(metadata.capabilityAudit?.removedTools, ["write", "contact_supervisor"]);
797
797
  } finally {
798
798
  handle.dispose();
799
799
  }