pi-subagents 0.70.0 → 0.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +4 -2
  3. package/agents/claude-code-writer.md +1 -0
  4. package/agents/codex-exec-writer.md +1 -0
  5. package/agents/cursor-agent-writer.md +1 -0
  6. package/agents/worker.md +3 -2
  7. package/docs/agents.md +7 -8
  8. package/docs/configuration.md +17 -1
  9. package/docs/extension-api.md +5 -1
  10. package/docs/models.md +1 -1
  11. package/docs/observability.md +17 -13
  12. package/docs/standalone-background.md +4 -4
  13. package/docs/tool-reference.md +9 -11
  14. package/docs/workflows.md +1 -1
  15. package/inspector-runner.mjs +1 -1
  16. package/package.json +2 -2
  17. package/skills/pi-subagents/SKILL.md +1 -0
  18. package/skills/pi-subagents/references/constraints-and-recipes.md +5 -2
  19. package/skills/pi-subagents/references/execution-controls.md +2 -2
  20. package/skills/pi-subagents/references/management-authoring-rpc.md +3 -3
  21. package/skills/pi-subagents/references/multi-lane-orchestration.md +8 -0
  22. package/skills/pi-subagents/references/prompting-and-roles.md +14 -13
  23. package/src/agents/agent-management.d.ts.map +1 -1
  24. package/src/agents/agent-management.js +11 -25
  25. package/src/agents/agent-management.js.map +1 -1
  26. package/src/agents/agent-serializer.d.ts.map +1 -1
  27. package/src/agents/agent-serializer.js +0 -4
  28. package/src/agents/agent-serializer.js.map +1 -1
  29. package/src/agents/agents.d.ts +17 -4
  30. package/src/agents/agents.d.ts.map +1 -1
  31. package/src/agents/agents.js +39 -22
  32. package/src/agents/agents.js.map +1 -1
  33. package/src/agents/proactive-skills.d.ts +1 -0
  34. package/src/agents/proactive-skills.d.ts.map +1 -1
  35. package/src/agents/proactive-skills.js +3 -1
  36. package/src/agents/proactive-skills.js.map +1 -1
  37. package/src/agents/runtime-agent-registry.d.ts +8 -3
  38. package/src/agents/runtime-agent-registry.d.ts.map +1 -1
  39. package/src/agents/runtime-agent-registry.js +14 -9
  40. package/src/agents/runtime-agent-registry.js.map +1 -1
  41. package/src/agents/skills.d.ts +2 -0
  42. package/src/agents/skills.d.ts.map +1 -1
  43. package/src/agents/skills.js +41 -8
  44. package/src/agents/skills.js.map +1 -1
  45. package/src/api/delegation.d.ts +14 -0
  46. package/src/api/delegation.d.ts.map +1 -1
  47. package/src/extension/index.d.ts.map +1 -1
  48. package/src/extension/index.js +5 -1
  49. package/src/extension/index.js.map +1 -1
  50. package/src/extension/rpc.d.ts +1 -1
  51. package/src/extension/rpc.d.ts.map +1 -1
  52. package/src/extension/rpc.js +11 -1
  53. package/src/extension/rpc.js.map +1 -1
  54. package/src/extension/tool-activation.d.ts +9 -0
  55. package/src/extension/tool-activation.d.ts.map +1 -0
  56. package/src/extension/tool-activation.js +172 -0
  57. package/src/extension/tool-activation.js.map +1 -0
  58. package/src/extension/tool-description.d.ts +4 -4
  59. package/src/extension/tool-description.d.ts.map +1 -1
  60. package/src/extension/tool-description.js +1 -1
  61. package/src/intercom/intercom-bridge.d.ts.map +1 -1
  62. package/src/intercom/intercom-bridge.js +2 -0
  63. package/src/intercom/intercom-bridge.js.map +1 -1
  64. package/src/runs/background/active-async-capacity.d.ts.map +1 -1
  65. package/src/runs/background/active-async-capacity.js +4 -33
  66. package/src/runs/background/active-async-capacity.js.map +1 -1
  67. package/src/runs/background/async-execution.d.ts.map +1 -1
  68. package/src/runs/background/async-execution.js +5 -67
  69. package/src/runs/background/async-execution.js.map +1 -1
  70. package/src/runs/background/async-job-tracker.d.ts.map +1 -1
  71. package/src/runs/background/async-job-tracker.js +25 -19
  72. package/src/runs/background/async-job-tracker.js.map +1 -1
  73. package/src/runs/background/async-resume.d.ts +1 -1
  74. package/src/runs/background/async-resume.d.ts.map +1 -1
  75. package/src/runs/background/async-resume.js +8 -6
  76. package/src/runs/background/async-resume.js.map +1 -1
  77. package/src/runs/background/async-status.d.ts +1 -0
  78. package/src/runs/background/async-status.d.ts.map +1 -1
  79. package/src/runs/background/async-status.js +1 -0
  80. package/src/runs/background/async-status.js.map +1 -1
  81. package/src/runs/background/binary-bootstrap.d.ts.map +1 -1
  82. package/src/runs/background/binary-bootstrap.js +28 -6
  83. package/src/runs/background/binary-bootstrap.js.map +1 -1
  84. package/src/runs/background/fleet-view.d.ts.map +1 -1
  85. package/src/runs/background/fleet-view.js +26 -7
  86. package/src/runs/background/fleet-view.js.map +1 -1
  87. package/src/runs/background/notify.d.ts +11 -0
  88. package/src/runs/background/notify.d.ts.map +1 -1
  89. package/src/runs/background/notify.js +71 -3
  90. package/src/runs/background/notify.js.map +1 -1
  91. package/src/runs/background/process-terminal-candidate.d.ts +18 -0
  92. package/src/runs/background/process-terminal-candidate.d.ts.map +1 -0
  93. package/src/runs/background/process-terminal-candidate.js +97 -0
  94. package/src/runs/background/process-terminal-candidate.js.map +1 -0
  95. package/src/runs/background/process-terminal.d.ts +3 -15
  96. package/src/runs/background/process-terminal.d.ts.map +1 -1
  97. package/src/runs/background/process-terminal.js +19 -96
  98. package/src/runs/background/process-terminal.js.map +1 -1
  99. package/src/runs/background/retained-children.d.ts.map +1 -1
  100. package/src/runs/background/retained-children.js +3 -2
  101. package/src/runs/background/retained-children.js.map +1 -1
  102. package/src/runs/background/run-child-session.d.ts +1 -0
  103. package/src/runs/background/run-child-session.d.ts.map +1 -1
  104. package/src/runs/background/run-child-session.js.map +1 -1
  105. package/src/runs/background/run-status.d.ts.map +1 -1
  106. package/src/runs/background/run-status.js +11 -5
  107. package/src/runs/background/run-status.js.map +1 -1
  108. package/src/runs/background/runner-aliases.d.ts.map +1 -1
  109. package/src/runs/background/runner-aliases.js +28 -16
  110. package/src/runs/background/runner-aliases.js.map +1 -1
  111. package/src/runs/background/runner-startup-failure.d.ts +14 -0
  112. package/src/runs/background/runner-startup-failure.d.ts.map +1 -0
  113. package/src/runs/background/runner-startup-failure.js +47 -0
  114. package/src/runs/background/runner-startup-failure.js.map +1 -0
  115. package/src/runs/background/stale-run-reconciler.d.ts.map +1 -1
  116. package/src/runs/background/stale-run-reconciler.js +9 -1
  117. package/src/runs/background/stale-run-reconciler.js.map +1 -1
  118. package/src/runs/background/subagent-runner-bootstrap.d.ts +14 -0
  119. package/src/runs/background/subagent-runner-bootstrap.d.ts.map +1 -0
  120. package/src/runs/background/subagent-runner-bootstrap.js +277 -0
  121. package/src/runs/background/subagent-runner-bootstrap.js.map +1 -0
  122. package/src/runs/background/subagent-runner.d.ts +5 -7
  123. package/src/runs/background/subagent-runner.d.ts.map +1 -1
  124. package/src/runs/background/subagent-runner.js +67 -274
  125. package/src/runs/background/subagent-runner.js.map +1 -1
  126. package/src/runs/background/workflow-terminal-proof.d.ts +18 -0
  127. package/src/runs/background/workflow-terminal-proof.d.ts.map +1 -0
  128. package/src/runs/background/workflow-terminal-proof.js +68 -0
  129. package/src/runs/background/workflow-terminal-proof.js.map +1 -0
  130. package/src/runs/foreground/execution.d.ts.map +1 -1
  131. package/src/runs/foreground/execution.js +43 -111
  132. package/src/runs/foreground/execution.js.map +1 -1
  133. package/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  134. package/src/runs/foreground/subagent-executor.js +108 -17
  135. package/src/runs/foreground/subagent-executor.js.map +1 -1
  136. package/src/runs/shared/acceptance.d.ts.map +1 -1
  137. package/src/runs/shared/acceptance.js +7 -40
  138. package/src/runs/shared/acceptance.js.map +1 -1
  139. package/src/runs/shared/child-hooks.d.ts +1 -3
  140. package/src/runs/shared/child-hooks.d.ts.map +1 -1
  141. package/src/runs/shared/child-hooks.js +1 -16
  142. package/src/runs/shared/child-hooks.js.map +1 -1
  143. package/src/runs/shared/child-launch.d.ts +0 -2
  144. package/src/runs/shared/child-launch.d.ts.map +1 -1
  145. package/src/runs/shared/child-launch.js +4 -2
  146. package/src/runs/shared/child-launch.js.map +1 -1
  147. package/src/runs/shared/child-runtime-config.d.ts +4 -0
  148. package/src/runs/shared/child-runtime-config.d.ts.map +1 -1
  149. package/src/runs/shared/child-runtime-config.js.map +1 -1
  150. package/src/runs/shared/child-session.d.ts +8 -9
  151. package/src/runs/shared/child-session.d.ts.map +1 -1
  152. package/src/runs/shared/child-session.js +67 -2
  153. package/src/runs/shared/child-session.js.map +1 -1
  154. package/src/runs/shared/dynamic-fanout.js +1 -1
  155. package/src/runs/shared/dynamic-fanout.js.map +1 -1
  156. package/src/runs/shared/external-cli-runner.d.ts.map +1 -1
  157. package/src/runs/shared/external-cli-runner.js +3 -1
  158. package/src/runs/shared/external-cli-runner.js.map +1 -1
  159. package/src/runs/shared/git-environment.d.ts +3 -0
  160. package/src/runs/shared/git-environment.d.ts.map +1 -0
  161. package/src/runs/shared/git-environment.js +28 -0
  162. package/src/runs/shared/git-environment.js.map +1 -0
  163. package/src/runs/shared/mcp-config-sources.d.ts +3 -0
  164. package/src/runs/shared/mcp-config-sources.d.ts.map +1 -1
  165. package/src/runs/shared/mcp-config-sources.js.map +1 -1
  166. package/src/runs/shared/mcp-direct-tool-allowlist.js +1 -1
  167. package/src/runs/shared/mcp-direct-tool-allowlist.js.map +1 -1
  168. package/src/runs/shared/mcp-direct-tool-grant.d.ts +3 -1
  169. package/src/runs/shared/mcp-direct-tool-grant.d.ts.map +1 -1
  170. package/src/runs/shared/mcp-direct-tool-grant.js +19 -5
  171. package/src/runs/shared/mcp-direct-tool-grant.js.map +1 -1
  172. package/src/runs/shared/parallel-utils.d.ts +0 -1
  173. package/src/runs/shared/parallel-utils.d.ts.map +1 -1
  174. package/src/runs/shared/parallel-utils.js.map +1 -1
  175. package/src/runs/shared/pi-spawn.d.ts +10 -1
  176. package/src/runs/shared/pi-spawn.d.ts.map +1 -1
  177. package/src/runs/shared/pi-spawn.js +86 -7
  178. package/src/runs/shared/pi-spawn.js.map +1 -1
  179. package/src/runs/shared/single-output.d.ts.map +1 -1
  180. package/src/runs/shared/single-output.js +10 -2
  181. package/src/runs/shared/single-output.js.map +1 -1
  182. package/src/runs/shared/structured-output.d.ts +7 -0
  183. package/src/runs/shared/structured-output.d.ts.map +1 -1
  184. package/src/runs/shared/structured-output.js +74 -0
  185. package/src/runs/shared/structured-output.js.map +1 -1
  186. package/src/runs/shared/subagent-control.d.ts.map +1 -1
  187. package/src/runs/shared/subagent-control.js +6 -19
  188. package/src/runs/shared/subagent-control.js.map +1 -1
  189. package/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
  190. package/src/runs/shared/subagent-prompt-runtime.js +23 -7
  191. package/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
  192. package/src/runs/shared/tool-availability.js +1 -1
  193. package/src/runs/shared/tool-availability.js.map +1 -1
  194. package/src/shared/child-session-name.d.ts.map +1 -1
  195. package/src/shared/child-session-name.js +4 -2
  196. package/src/shared/child-session-name.js.map +1 -1
  197. package/src/shared/fork-context.d.ts +4 -0
  198. package/src/shared/fork-context.d.ts.map +1 -1
  199. package/src/shared/fork-context.js +9 -4
  200. package/src/shared/fork-context.js.map +1 -1
  201. package/src/shared/launch-contract.d.ts.map +1 -1
  202. package/src/shared/launch-contract.js +0 -1
  203. package/src/shared/launch-contract.js.map +1 -1
  204. package/src/shared/pruned-fork.d.ts +1 -1
  205. package/src/shared/pruned-fork.d.ts.map +1 -1
  206. package/src/shared/pruned-fork.js +70 -65
  207. package/src/shared/pruned-fork.js.map +1 -1
  208. package/src/shared/types.d.ts +32 -13
  209. package/src/shared/types.d.ts.map +1 -1
  210. package/src/shared/types.js +2 -0
  211. package/src/shared/types.js.map +1 -1
  212. package/src/slash/delegation-adapters.d.ts +7 -1
  213. package/src/slash/delegation-adapters.d.ts.map +1 -1
  214. package/src/slash/delegation-adapters.js +17 -0
  215. package/src/slash/delegation-adapters.js.map +1 -1
  216. package/src/slash/prompt-template-bridge.d.ts.map +1 -1
  217. package/src/slash/prompt-template-bridge.js +14 -1
  218. package/src/slash/prompt-template-bridge.js.map +1 -1
  219. package/src/slash/prompt-workflows.d.ts.map +1 -1
  220. package/src/slash/prompt-workflows.js +16 -2
  221. package/src/slash/prompt-workflows.js.map +1 -1
  222. package/src/slash/slash-commands.d.ts.map +1 -1
  223. package/src/slash/slash-commands.js +5 -251
  224. package/src/slash/slash-commands.js.map +1 -1
  225. package/src/slash/subagent-cost.d.ts +29 -0
  226. package/src/slash/subagent-cost.d.ts.map +1 -0
  227. package/src/slash/subagent-cost.js +271 -0
  228. package/src/slash/subagent-cost.js.map +1 -0
  229. package/src/slash/subagents-admin.d.ts.map +1 -1
  230. package/src/slash/subagents-admin.js +4 -5
  231. package/src/slash/subagents-admin.js.map +1 -1
  232. package/src/tui/fleet.d.ts.map +1 -1
  233. package/src/tui/fleet.js +4 -0
  234. package/src/tui/fleet.js.map +1 -1
  235. package/src/tui/render.d.ts.map +1 -1
  236. package/src/tui/render.js +16 -11
  237. package/src/tui/render.js.map +1 -1
  238. package/src/watchdog/diff-tool.d.ts +2 -1
  239. package/src/watchdog/diff-tool.d.ts.map +1 -1
  240. package/src/watchdog/diff-tool.js +9 -3
  241. package/src/watchdog/diff-tool.js.map +1 -1
  242. package/src/watchdog/permission-arbiter.d.ts.map +1 -1
  243. package/src/watchdog/permission-arbiter.js +12 -8
  244. package/src/watchdog/permission-arbiter.js.map +1 -1
  245. package/src/watchdog/register-child.d.ts +3 -1
  246. package/src/watchdog/register-child.d.ts.map +1 -1
  247. package/src/watchdog/register-child.js +12 -3
  248. package/src/watchdog/register-child.js.map +1 -1
  249. package/src/watchdog/review.d.ts +1 -0
  250. package/src/watchdog/review.d.ts.map +1 -1
  251. package/src/watchdog/review.js +12 -6
  252. package/src/watchdog/review.js.map +1 -1
  253. package/src/watchdog/runtime.d.ts +1 -1
  254. package/src/watchdog/runtime.d.ts.map +1 -1
  255. package/src/watchdog/runtime.js +2 -1
  256. package/src/watchdog/runtime.js.map +1 -1
  257. package/src/watchdog/turn-delta.d.ts +5 -1
  258. package/src/watchdog/turn-delta.d.ts.map +1 -1
  259. package/src/watchdog/turn-delta.js +9 -5
  260. package/src/watchdog/turn-delta.js.map +1 -1
  261. package/src/workflows/scripted-workflow.d.ts.map +1 -1
  262. package/src/workflows/scripted-workflow.js +1 -3
  263. package/src/workflows/scripted-workflow.js.map +1 -1
  264. package/src/runs/shared/completion-evidence.d.ts +0 -31
  265. package/src/runs/shared/completion-evidence.d.ts.map +0 -1
  266. package/src/runs/shared/completion-evidence.js +0 -54
  267. package/src/runs/shared/completion-evidence.js.map +0 -1
  268. package/src/runs/shared/completion-guard.d.ts +0 -35
  269. package/src/runs/shared/completion-guard.d.ts.map +0 -1
  270. package/src/runs/shared/completion-guard.js +0 -214
  271. package/src/runs/shared/completion-guard.js.map +0 -1
  272. package/src/runs/shared/llm-intent-arbiter.d.ts +0 -48
  273. package/src/runs/shared/llm-intent-arbiter.d.ts.map +0 -1
  274. package/src/runs/shared/llm-intent-arbiter.js +0 -235
  275. package/src/runs/shared/llm-intent-arbiter.js.map +0 -1
  276. package/src/runs/shared/task-intent.d.ts +0 -38
  277. package/src/runs/shared/task-intent.d.ts.map +0 -1
  278. package/src/runs/shared/task-intent.js +0 -213
  279. package/src/runs/shared/task-intent.js.map +0 -1
@@ -0,0 +1,18 @@
1
+ import type { AsyncStatus, ProcessTerminal, WorkflowChildSummary, WorkflowTerminalProof } from "../../shared/types.ts";
2
+ export declare function isTerminalAsyncState(state: AsyncStatus["state"]): boolean;
3
+ export type WorkflowChildProcessEvidence = {
4
+ state: "observed";
5
+ children: ProcessTerminal[];
6
+ } | {
7
+ state: "pending" | "unknown";
8
+ reason: string;
9
+ };
10
+ /**
11
+ * Process evidence for a workflow's async children. Status proofs and capacity
12
+ * release both use this so they cannot disagree about the same workflow.
13
+ * Synchronous children run inside the workflow host and have no process of their own.
14
+ */
15
+ export declare function readWorkflowChildProcessEvidence(workflowAsyncDir: string, steps: AsyncStatus["steps"]): WorkflowChildProcessEvidence;
16
+ /** A persistent workflow host is terminal only after dispatch closes and every async child has process evidence. */
17
+ export declare function readWorkflowTerminalProof(asyncDir: string, steps: AsyncStatus["steps"], summary: WorkflowChildSummary, hostCommandCount: number, closedAt: number): WorkflowTerminalProof;
18
+ //# sourceMappingURL=workflow-terminal-proof.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-terminal-proof.d.ts","sourceRoot":"","sources":["../../../../src/runs/background/workflow-terminal-proof.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAMvH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,OAAO,CAEzE;AAED,MAAM,MAAM,4BAA4B,GACrC;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,eAAe,EAAE,CAAA;CAAE,GAClD;IAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD;;;;GAIG;AACH,wBAAgB,gCAAgC,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,4BAA4B,CA2BpI;AAMD,oHAAoH;AACpH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,qBAAqB,CAYzL"}
@@ -0,0 +1,68 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { readStatus } from "../../shared/utils.js";
4
+ import { readProcessTerminal } from "./process-terminal.js";
5
+ const TERMINAL_WORKFLOW_STATES = new Set(["completed", "failed", "stopped"]);
6
+ export function isTerminalAsyncState(state) {
7
+ return state !== "queued" && state !== "running" && state !== "paused";
8
+ }
9
+ /**
10
+ * Process evidence for a workflow's async children. Status proofs and capacity
11
+ * release both use this so they cannot disagree about the same workflow.
12
+ * Synchronous children run inside the workflow host and have no process of their own.
13
+ */
14
+ export function readWorkflowChildProcessEvidence(workflowAsyncDir, steps) {
15
+ if (!steps)
16
+ return { state: "unknown", reason: "workflow child roster is missing" };
17
+ const children = [];
18
+ for (const step of steps) {
19
+ const label = step.workflowKey ?? step.agent;
20
+ if (typeof step.async !== "boolean")
21
+ return { state: "unknown", reason: `workflow child ${label} is missing async classification` };
22
+ if (!step.async)
23
+ continue;
24
+ if (!step.runId || path.basename(step.runId) !== step.runId)
25
+ return { state: "unknown", reason: `async workflow child ${label} is missing run id` };
26
+ const childDir = path.join(path.dirname(workflowAsyncDir), step.runId);
27
+ if (!fs.existsSync(childDir))
28
+ return { state: "unknown", reason: `async workflow child ${label} directory is missing` };
29
+ const childStatus = readStatus(childDir);
30
+ if (!childStatus)
31
+ return { state: "unknown", reason: `async workflow child ${label} status is missing or unreadable` };
32
+ if (!isTerminalAsyncState(childStatus.state))
33
+ return { state: "pending", reason: `async workflow child ${label} is still ${childStatus.state}` };
34
+ const recorded = childStatus.processTerminal;
35
+ if (!recorded?.runnerProcessInstanceId)
36
+ return { state: "unknown", reason: `async workflow child ${label} has no runner process identity` };
37
+ // A runner startup failure records `not-started` in status and never writes a process-terminal sidecar.
38
+ if (recorded.state === "not-started" && recorded.runId === step.runId && typeof childStatus.error === "string" && childStatus.error) {
39
+ children.push(recorded);
40
+ continue;
41
+ }
42
+ const proof = readProcessTerminal(childDir, { runId: step.runId, runnerProcessInstanceId: recorded.runnerProcessInstanceId });
43
+ if (proof?.state !== "observed" || proof.runId !== step.runId) {
44
+ return { state: !proof || proof.state === "pending" ? "pending" : "unknown", reason: `async workflow child ${label} process-terminal proof is ${proof?.state ?? "missing"}` };
45
+ }
46
+ children.push(proof);
47
+ }
48
+ return { state: "observed", children };
49
+ }
50
+ function unresolved(runId, state, dispatchClosed, reason) {
51
+ return { version: 1, kind: "workflow", runId, state, dispatchClosed, reason };
52
+ }
53
+ /** A persistent workflow host is terminal only after dispatch closes and every async child has process evidence. */
54
+ export function readWorkflowTerminalProof(asyncDir, steps, summary, hostCommandCount, closedAt) {
55
+ const runId = summary.workflowRunId;
56
+ if (!summary.inventoryComplete || !TERMINAL_WORKFLOW_STATES.has(summary.workflowState)) {
57
+ return unresolved(runId, "pending", false, "Workflow dispatch is still open.");
58
+ }
59
+ if (hostCommandCount > 0) {
60
+ return unresolved(runId, "unknown", true, "Workflow host commands have no process-terminal proof.");
61
+ }
62
+ const evidence = readWorkflowChildProcessEvidence(asyncDir, steps);
63
+ if (evidence.state !== "observed")
64
+ return unresolved(runId, evidence.state, true, evidence.reason);
65
+ const observedAt = Math.max(closedAt, ...evidence.children.map((child) => child.state === "observed" ? child.observedAt : 0));
66
+ return { version: 1, kind: "workflow", runId, state: "observed", dispatchClosed: true, observedAt, children: evidence.children };
67
+ }
68
+ //# sourceMappingURL=workflow-terminal-proof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-terminal-proof.js","sourceRoot":"","sources":["../../../../src/runs/background/workflow-terminal-proof.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../../../src/runs/foreground/execution.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAA0E,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAWlI,OAAO,EAIN,KAAK,cAAc,EACnB,KAAK,YAAY,EAQjB,MAAM,uBAAuB,CAAC;AA+jE/B;;;GAGG;AACH,wBAAsB,OAAO,CAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC,YAAY,CAAC,CAwHvB"}
1
+ {"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../../../src/runs/foreground/execution.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAA0E,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAWlI,OAAO,EAIN,KAAK,cAAc,EACnB,KAAK,YAAY,EAQjB,MAAM,uBAAuB,CAAC;AAo/D/B;;;GAGG;AACH,wBAAsB,OAAO,CAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC,YAAY,CAAC,CAwHvB"}
@@ -13,9 +13,6 @@ import { DEFAULT_CONTROL_CONFIG, buildControlEvent, claimControlNotification, de
13
13
  import { getFinalOutput, findLatestSessionFile, detectSubagentError, hasEmptyTerminalAssistantResponse, formatEmptyTerminalAssistantResponseError, extractToolArgsPreview, extractTextFromContent, MAX_STREAMED_RECENT_TOOLS, boundStreamedRecentTools, boundStreamedRecentOutput, boundStreamedToolCalls, } from "../../shared/utils.js";
14
14
  import { resolveSkillsWithFallback } from "../../agents/skills.js";
15
15
  import { effectiveToolTimeoutMs, formatToolTimeoutMessage, resolveToolTimeoutMs, toolTimeoutCallKey, toolTimeoutFromEnv } from "../shared/tool-timeout.js";
16
- import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMutationToolCapability, validateImplementationToolContract } from "../shared/completion-guard.js";
17
- import { planCompletionEvidence } from "../shared/completion-evidence.js";
18
- import { arbitrateCompletionGuardRescue } from "../shared/llm-intent-arbiter.js";
19
16
  import { preflightLaunchCwd } from "../shared/launch-cwd.js";
20
17
  import { createJsonlWriter } from "../../shared/jsonl-writer.js";
21
18
  import { createOrcaProgressTab } from "../shared/orca-progress-tabs.js";
@@ -25,7 +22,7 @@ import { deriveChildSessionName } from "../../shared/child-session-name.js";
25
22
  import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling } from "../shared/capability-ceiling.js";
26
23
  import { resolveEffectiveThinking } from "../../shared/model-info.js";
27
24
  import { assertThinkingWithinCeiling, intersectThinkingCeilings } from "../../shared/thinking-ceiling.js";
28
- import { MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR, MISSING_STRUCTURED_OUTPUT_CALL_ERROR } from "../shared/structured-output.js";
25
+ import { formatStructuredOutputRejectionError, MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR, MISSING_STRUCTURED_OUTPUT_CALL_ERROR } from "../shared/structured-output.js";
29
26
  import { formatMidToolExitError, isOrdinaryToolForMidToolExit } from "../shared/process-signal.js";
30
27
  import { formatChildToolDiagnostic } from "../shared/tool-availability.js";
31
28
  import { formatChildModelResolutionDiagnostic, isChildModelResolutionFailure } from "../shared/model-resolution-diagnostic.js";
@@ -210,6 +207,7 @@ function captureStructuredDelegationProgressState(progress, result) {
210
207
  model: progress.model ?? result.model,
211
208
  toolCount: progress.toolCount,
212
209
  tokens: progress.tokens,
210
+ turnCount: progress.turnCount,
213
211
  };
214
212
  }
215
213
  function structuredDelegationProgressChanged(previous, progress, result) {
@@ -219,6 +217,7 @@ function structuredDelegationProgressChanged(previous, progress, result) {
219
217
  || previous.model !== (progress.model ?? result.model)
220
218
  || previous.toolCount !== progress.toolCount
221
219
  || previous.tokens !== progress.tokens
220
+ || previous.turnCount !== progress.turnCount
222
221
  || previous.recentOutput.length !== progress.recentOutput.length)
223
222
  return true;
224
223
  for (let index = 0; index < progress.recentOutput.length; index++) {
@@ -236,6 +235,9 @@ function structuredDelegationProgressChanged(previous, progress, result) {
236
235
  }
237
236
  return false;
238
237
  }
238
+ function isCompleteUsageCounter(value) {
239
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
240
+ }
239
241
  const STOPPED_BEFORE_COMPLETION_ERROR = "Subagent stopped before completion.";
240
242
  const AFTER_COMPACTION_SETTLEMENT = Symbol("afterCompactionSettlement");
241
243
  async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared) {
@@ -323,34 +325,6 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
323
325
  shared.launchWarnings.emitted = true;
324
326
  }
325
327
  const effectiveSystemPrompt = shared.systemPrompt;
326
- const contractTools = toolPlan.explicitToolAllowlist ? toolPlan.effectiveToolAllowlist : undefined;
327
- const contractError = validateImplementationToolContract({
328
- agent: agent.name,
329
- task: shared.originalTask ?? task,
330
- tools: contractTools,
331
- mcpDirectTools: toolPlan.effectiveMcpTools,
332
- configuredExtensions: toolPlan.configuredExtensions,
333
- requestedTools: toolPlan.requestedBuiltinTools,
334
- acceptanceRole: agent.acceptanceRole,
335
- completionGuard: agent.completionGuard,
336
- });
337
- if (contractError) {
338
- return {
339
- index: options.index ?? 0,
340
- agent: agent.name,
341
- task,
342
- ...(childSessionName ? { sessionName: childSessionName } : {}),
343
- messages: [],
344
- finalOutput: "",
345
- exitCode: 1,
346
- error: contractError,
347
- usage: emptyUsage(),
348
- model: modelArg,
349
- progressSummary: { status: "failed", toolCount: 0, tokens: 0, durationMs: 0 },
350
- ...(toolPlan.capabilityCeiling ? { capabilityCeiling: toolPlan.capabilityCeiling } : {}),
351
- ...(toolPlan.capabilityAudit ? { capabilityAudit: toolPlan.capabilityAudit } : {}),
352
- };
353
- }
354
328
  const fast = options.fast ?? agent.fast;
355
329
  const { launchContractDigest } = resolveLaunchBinding({
356
330
  agent,
@@ -403,6 +377,10 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
403
377
  pendingControlEvents.push(event);
404
378
  options.onControlEvent?.(event);
405
379
  };
380
+ let inputUsageComplete = true;
381
+ let outputUsageComplete = true;
382
+ let cacheReadUsageComplete = true;
383
+ let cacheWriteUsageComplete = true;
406
384
  const progress = {
407
385
  index: options.index ?? 0,
408
386
  agent: agent.name,
@@ -416,12 +394,28 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
416
394
  tokens: 0,
417
395
  ...(modelArg ? { model: modelArg } : {}),
418
396
  ...(resolvedThinking ? { thinking: resolvedThinking } : {}),
419
- inputTokens: 0,
420
- outputTokens: 0,
421
397
  durationMs: 0,
422
398
  lastActivityAt: startTime,
423
399
  };
424
400
  result.progress = progress;
401
+ const projectCompleteUsage = () => {
402
+ if (inputUsageComplete)
403
+ progress.inputTokens = result.usage.input;
404
+ else
405
+ delete progress.inputTokens;
406
+ if (outputUsageComplete)
407
+ progress.outputTokens = result.usage.output;
408
+ else
409
+ delete progress.outputTokens;
410
+ if (cacheReadUsageComplete)
411
+ progress.cacheRead = result.usage.cacheRead;
412
+ else
413
+ delete progress.cacheRead;
414
+ if (cacheWriteUsageComplete)
415
+ progress.cacheWrite = result.usage.cacheWrite;
416
+ else
417
+ delete progress.cacheWrite;
418
+ };
425
419
  const attemptTimeout = resolveAttemptTimeout(options);
426
420
  if (attemptTimeout?.remainingMs === 0) {
427
421
  result.exitCode = 1;
@@ -438,7 +432,6 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
438
432
  return result;
439
433
  }
440
434
  const mutationSnapshot = options.machine ? { source: "tracked-files", trackedOnly: true, cwd: options.cwd ?? runtimeCwd, dirtyFiles: [], fingerprints: {}, unavailable: "Local Git evidence is not authoritative for a pane-native remote run." } : snapshotTrackedMutations(options.cwd ?? runtimeCwd);
441
- let observedMutationAttempt = false;
442
435
  let structuredOutputToolInvoked = false;
443
436
  let structuredOutputMessageStartIndex;
444
437
  let toolAvailabilityError;
@@ -968,7 +961,6 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
968
961
  result.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount);
969
962
  }
970
963
  const mutates = isMutatingTool(evt.toolName, toolArgs, agent.mutationTools);
971
- observedMutationAttempt = observedMutationAttempt || mutates;
972
964
  pendingToolResult = { tool: evt.toolName ?? "tool", path: activeTool?.path, mutates, startedAt: now };
973
965
  fireUpdate();
974
966
  }
@@ -1001,6 +993,10 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
1001
993
  const hasToolCall = toolCalls.length > 0;
1002
994
  const terminalAssistantStop = evt.message.stopReason === "stop" && !hasToolCall;
1003
995
  const u = evt.message.usage;
996
+ inputUsageComplete &&= isCompleteUsageCounter(u?.input);
997
+ outputUsageComplete &&= isCompleteUsageCounter(u?.output);
998
+ cacheReadUsageComplete &&= isCompleteUsageCounter(u?.cacheRead);
999
+ cacheWriteUsageComplete &&= isCompleteUsageCounter(u?.cacheWrite);
1004
1000
  if (u) {
1005
1001
  const window = (u.input || 0) + (u.cacheRead || 0);
1006
1002
  result.usage.input += u.input || 0;
@@ -1009,11 +1005,10 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
1009
1005
  result.usage.cacheWrite += u.cacheWrite || 0;
1010
1006
  result.usage.cost += u.cost?.total || 0;
1011
1007
  progress.tokens = result.usage.input + result.usage.output;
1012
- progress.inputTokens = result.usage.input;
1013
- progress.outputTokens = result.usage.output;
1014
1008
  progress.window = window;
1015
1009
  progress.windowPeak = Math.max(progress.windowPeak ?? 0, window);
1016
1010
  }
1011
+ projectCompleteUsage();
1017
1012
  if (evt.message.model) {
1018
1013
  progress.model = evt.message.model;
1019
1014
  if (!result.model)
@@ -1250,8 +1245,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
1250
1245
  if (session && messageBaseline !== undefined) {
1251
1246
  result.usage = reconcileAttemptUsage(result.usage, session.messages, messageBaseline);
1252
1247
  progress.tokens = result.usage.input + result.usage.output;
1253
- progress.inputTokens = result.usage.input;
1254
- progress.outputTokens = result.usage.output;
1248
+ projectCompleteUsage();
1255
1249
  progress.turnCount = result.usage.turns;
1256
1250
  }
1257
1251
  finish(finalCode);
@@ -1384,16 +1378,11 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
1384
1378
  result.exitCode = 1;
1385
1379
  }
1386
1380
  let validatedStructuredOutput = false;
1387
- if (options.structuredOutput && result.exitCode === 0 && !result.error) {
1381
+ if (options.structuredOutput) {
1388
1382
  result.structuredOutputSchemaPath = options.structuredOutput.schemaPath;
1389
1383
  result.structuredOutputPath = options.structuredOutput.outputPath;
1390
1384
  const structured = capture.structuredOutput();
1391
- if (!structuredOutputToolInvoked || !structured.called) {
1392
- result.exitCode = 1;
1393
- result.error = MISSING_STRUCTURED_OUTPUT_CALL_ERROR;
1394
- result.structuredOutputFailed = true;
1395
- }
1396
- else {
1385
+ if (structuredOutputToolInvoked && structured.called) {
1397
1386
  result.structuredOutput = structured.value;
1398
1387
  const acceptanceMode = options.structuredOutput.acceptanceReportPath
1399
1388
  ? options.structuredOutput.acceptanceReportRequired ? "required" : "optional"
@@ -1406,6 +1395,13 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
1406
1395
  writeStructuredOutputArtifacts(options.structuredOutput, structured.value, acceptanceMode ? structured.acceptanceReport : undefined);
1407
1396
  validatedStructuredOutput = true;
1408
1397
  }
1398
+ else if (result.exitCode === 0 && !result.error) {
1399
+ result.exitCode = 1;
1400
+ result.error = structuredOutputToolInvoked
1401
+ ? formatStructuredOutputRejectionError(result.messages ?? [])
1402
+ : MISSING_STRUCTURED_OUTPUT_CALL_ERROR;
1403
+ result.structuredOutputFailed = true;
1404
+ }
1409
1405
  }
1410
1406
  if (result.exitCode === 0 && !result.error) {
1411
1407
  const messages = result.messages ?? [];
@@ -1475,70 +1471,6 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
1475
1471
  ? `${timeoutMessage}\n\n${result.timeoutRecovery.message}\n\nPartial output before timeout:\n${fullOutput}`
1476
1472
  : `${timeoutMessage}\n\n${result.timeoutRecovery.message}`;
1477
1473
  }
1478
- const completionGuardEnabled = isAgentContract(options.agentContract) ? agent.completionGuard === true : agent.completionGuard !== false;
1479
- const completionGuard = ((result.exitCode === 0 && !result.error) || toolAvailabilityError) && completionGuardEnabled
1480
- ? evaluateCompletionMutationGuard({
1481
- agent: agent.name,
1482
- task: shared.originalTask ?? task,
1483
- messages: result.messages ?? [],
1484
- tools: contractTools,
1485
- mcpDirectTools: toolPlan.effectiveMcpTools,
1486
- mutationTools: agent.mutationTools,
1487
- toolAvailabilityError,
1488
- mutationEvidence,
1489
- })
1490
- : undefined;
1491
- const mutationAttemptObserved = observedMutationAttempt || mutationEvidence.attemptedMutation;
1492
- let completionGuardTriggered = completionGuard?.triggered === true && !mutationAttemptObserved;
1493
- // The classifier is deliberately narrow, so a read-only review task can
1494
- // still be misread as implementation. Arbitrate BEFORE any failure side
1495
- // effect is published (effects, exit code, progress, notifications,
1496
- // acceptance, output persistence): only a confident read-only verdict
1497
- // rescues, and the task text alone is evidence — never the child's own
1498
- // final message.
1499
- let arbiterRescued = false;
1500
- if (completionGuardTriggered) {
1501
- const arbitration = await arbitrateCompletionGuardRescue({
1502
- guardTriggered: true,
1503
- task: shared.originalTask ?? task,
1504
- arbiter: options.llmIntentArbiter,
1505
- });
1506
- completionGuardTriggered = arbitration.triggered;
1507
- arbiterRescued = arbitration.rescued;
1508
- }
1509
- const completionEvidence = planCompletionEvidence({
1510
- guard: completionGuard,
1511
- guardTriggered: completionGuardTriggered,
1512
- completionGuardEnabled,
1513
- mutationCapable: hasMutationToolCapability(contractTools, toolPlan.effectiveMcpTools),
1514
- implementationMutationExpected: expectsImplementationMutation(agent.name, shared.originalTask ?? task),
1515
- mutationAttemptObserved,
1516
- mutationEvidence,
1517
- arbiterRescued,
1518
- agentContractEnabled: isAgentContract(options.agentContract),
1519
- });
1520
- if (completionEvidence.fileMutation) {
1521
- result.effects = {
1522
- ...(result.effects ?? {}),
1523
- fileMutation: completionEvidence.fileMutation,
1524
- };
1525
- }
1526
- if (completionEvidence.legacyFailureError) {
1527
- result.exitCode = 1;
1528
- result.error = completionEvidence.legacyFailureError;
1529
- progress.status = "failed";
1530
- progress.error = result.error;
1531
- emitControlEvent(buildControlEvent({
1532
- from: progress.activityState,
1533
- to: "needs_attention",
1534
- runId: options.runId ?? agent.name,
1535
- agent: agent.name,
1536
- index: options.index,
1537
- ts: Date.now(),
1538
- message: `${agent.name} completed without making edits for an implementation task`,
1539
- reason: "completion_guard",
1540
- }));
1541
- }
1542
1474
  if (options.outputPath && result.exitCode === 0) {
1543
1475
  const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot, options.outputClaimPath);
1544
1476
  fullOutput = stripAcceptanceReport(resolvedOutput.fullOutput);