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
@@ -11,12 +11,7 @@ import * as fs from "node:fs";
11
11
  import * as path from "node:path";
12
12
  import { performance } from "node:perf_hooks";
13
13
  import { pathToFileURL } from "node:url";
14
- import { arbitrateCompletionGuardRescue, createTaskMutationArbiter } from "../shared/llm-intent-arbiter.js";
15
14
  import { installRunnerHttpDispatcher } from "./runner-http-dispatcher.js";
16
- const isRunnerEntrypoint = Boolean(process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href);
17
- // Detached Node runners skip Pi's CLI dispatcher setup; install the runner's own.
18
- if (isRunnerEntrypoint)
19
- installRunnerHttpDispatcher({ agentDir: getAgentDir(), cwd: process.cwd() });
20
15
  import { writeAtomicJson } from "../../shared/atomic-json.js";
21
16
  import { writeAsyncResultFile, writePendingAsyncResultFile } from "./result-files.js";
22
17
  import { createFileCoalescer } from "../../shared/file-coalescer.js";
@@ -42,7 +37,7 @@ import { SUBAGENT_CHILD_ENV } from "../shared/child-runtime-config.js";
42
37
  import { deriveChildSessionName } from "../../shared/child-session-name.js";
43
38
  import { alignForkedSessionCwd } from "../../shared/fork-session-cwd.js";
44
39
  import { outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.js";
45
- import { clearStructuredOutputCaptures, createStructuredOutputFileCapture, createStructuredOutputRuntime, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.js";
40
+ import { clearStructuredOutputCaptures, createStructuredOutputFileCapture, createStructuredOutputRuntime, formatStructuredOutputRejectionError, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.js";
46
41
  import { formatMidToolExitError, isOrdinaryToolForMidToolExit, isUnexplainedProcessSignal } from "../shared/process-signal.js";
47
42
  import { formatChildToolDiagnostic } from "../shared/tool-availability.js";
48
43
  import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.js";
@@ -50,11 +45,10 @@ import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelSt
50
45
  import { claimRunFanoutBatch, getRunFanoutBudgetSnapshot } from "../shared/run-fanout-budget.js";
51
46
  import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent } from "../shared/nested-events.js";
52
47
  import { isContextOverflow } from "../shared/model-resolution.js";
53
- import { markProcessTerminalCandidateLeaseRelease, processTerminalPath, writeProcessTerminalCandidate } from "./process-terminal.js";
48
+ import { processTerminalPath, writeProcessTerminalCandidate } from "./process-terminal.js";
49
+ import { persistRunnerStartupFailure } from "./runner-startup-failure.js";
54
50
  import { createSteeringStatus, recordSteeringRequest, steeringStatus, terminalSteeringNoticeState, unconsumedSteerReason, updateSteeringTarget } from "./steering.js";
55
51
  import { PROMPT_REDACTED, detectSubagentError, extractTextFromContent, extractToolArgsPreview, formatEmptyTerminalAssistantResponseError, getAgentDir, hasEmptyTerminalAssistantResponse, readStatus } from "../../shared/utils.js";
56
- import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMutationToolCapability, validateImplementationToolContract } from "../shared/completion-guard.js";
57
- import { planCompletionEvidence, projectSettlementDiagnostic } from "../shared/completion-evidence.js";
58
52
  import { planAbortRecovery } from "../shared/abort-recovery.js";
59
53
  import { createMutatingFailureState, didMutatingToolFail, isMutatingTool, nextLongRunningTrigger, recordMutatingFailure, resetMutatingFailureState, resolveCurrentPath, shouldEscalateMutatingFailures, summarizeRecentMutatingFailures, } from "../shared/long-running-guard.js";
60
54
  import { parseSessionTokens } from "../../shared/session-tokens.js";
@@ -74,7 +68,6 @@ import { toolTimeoutCallKey } from "../shared/tool-timeout.js";
74
68
  import { usageBudgetExceededMessage, usageBudgetState } from "../shared/usage-budget.js";
75
69
  import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.js";
76
70
  import { resolveWatchdogConfig } from "../../watchdog/settings.js";
77
- import { acquireSessionLease } from "../shared/session-lease.js";
78
71
  import { buildExternalCliPrompt, runExternalCli } from "../shared/external-cli-runner.js";
79
72
  import { resolveClaudeCodeLaunch } from "../shared/claude-code-adapter.js";
80
73
  import { resolveCodexExecLaunch } from "../shared/codex-exec-adapter.js";
@@ -507,7 +500,6 @@ export async function runSingleStepInner(step, ctx) {
507
500
  let task = step.task.replace(placeholderRegex, () => ctx.previousOutput);
508
501
  if (ctx.outputs)
509
502
  task = resolveOutputReferences(task, ctx.outputs);
510
- const taskForCompletionGuard = task;
511
503
  let resolvedTaskToolPlan;
512
504
  if (!step.runner) {
513
505
  resolvedTaskToolPlan = resolvePiLaunchToolPlan(omitUndefinedProperties({
@@ -527,28 +519,6 @@ export async function runSingleStepInner(step, ctx) {
527
519
  requiredExtensions: step.requiredExtensions ?? ctx.inheritedChildRuntime?.requiredExtensions,
528
520
  permissionRules: step.permissionRules,
529
521
  }));
530
- const contractTools = resolvedTaskToolPlan.explicitToolAllowlist ? resolvedTaskToolPlan.effectiveToolAllowlist : undefined;
531
- const contractError = validateImplementationToolContract({
532
- agent: step.agent,
533
- task: taskForCompletionGuard,
534
- tools: contractTools,
535
- mcpDirectTools: resolvedTaskToolPlan.effectiveMcpTools,
536
- configuredExtensions: resolvedTaskToolPlan.configuredExtensions,
537
- requestedTools: resolvedTaskToolPlan.requestedBuiltinTools,
538
- acceptanceRole: step.acceptanceRole,
539
- completionGuard: step.completionGuard,
540
- });
541
- if (contractError) {
542
- return omitUndefinedProperties({
543
- agent: step.agent,
544
- context: step.context,
545
- output: contractError,
546
- error: contractError,
547
- exitCode: 1,
548
- capabilityCeiling: resolvedTaskToolPlan.capabilityCeiling,
549
- capabilityAudit: resolvedTaskToolPlan.capabilityAudit,
550
- });
551
- }
552
522
  }
553
523
  // Derive from the pre-acceptance task so internal acceptance/recovery
554
524
  // instructions never leak into the display name.
@@ -818,7 +788,6 @@ export async function runSingleStepInner(step, ctx) {
818
788
  let finalOutputSnapshot;
819
789
  let structuredAcceptanceReport;
820
790
  let structuredAcceptanceReportError;
821
- let completionGuardTriggeredFinal = false;
822
791
  let toolBudget = step.toolBudget ? initialToolBudgetState(step.toolBudget) : undefined;
823
792
  let toolBudgetBlocked = false;
824
793
  let actualLaunchContractDigest = step.launchContractDigest;
@@ -980,9 +949,9 @@ export async function runSingleStepInner(step, ctx) {
980
949
  aggregateUsage.cacheWrite += run.usage.cacheWrite;
981
950
  aggregateUsage.cost += run.usage.cost;
982
951
  aggregateUsage.turns += run.usage.turns;
983
- // A parked run still owes completion evidence when it actually finishes.
984
- // Stopped/timedOut runs already have terminal failures; checking completion evidence there is meaningless.
985
- const completionDiagnosticsEligible = !run.interrupted && !run.stopped && !run.timedOut;
952
+ // A parked run still owes output diagnostics when it actually finishes.
953
+ // Stopped/timedOut runs already have terminal failures, so terminal output diagnostics are deferred.
954
+ const terminalDiagnosticsEligible = !run.interrupted && !run.stopped && !run.timedOut;
986
955
  const toolDiagnostic = run.exitCode === 0 && !run.error ? launch.capture.toolDiagnostic() : undefined;
987
956
  const toolAvailabilityError = toolDiagnostic ? formatChildToolDiagnostic(toolDiagnostic) : undefined;
988
957
  const runtimeAcknowledgedExtensions = launch.capture.runtimeAcknowledgedExtensions();
@@ -997,18 +966,24 @@ export async function runSingleStepInner(step, ctx) {
997
966
  let structuredOutput;
998
967
  let structuredError;
999
968
  let validatedStructuredOutput = false;
1000
- if (completionDiagnosticsEligible && effectiveStructuredOutput && run.exitCode === 0 && !run.error && !toolAvailabilityError && !midToolExitError) {
1001
- if (!run.structuredOutputToolInvoked) {
969
+ if (effectiveStructuredOutput) {
970
+ const otherwiseSuccessful = terminalDiagnosticsEligible && run.exitCode === 0 && !run.error && !toolAvailabilityError && !midToolExitError;
971
+ if (!run.structuredOutputToolInvoked && otherwiseSuccessful) {
1002
972
  structuredError = MISSING_STRUCTURED_OUTPUT_CALL_ERROR;
1003
973
  }
1004
- else {
974
+ else if (run.structuredOutputToolInvoked) {
1005
975
  const structured = await readStructuredOutput({
1006
976
  schema: effectiveStructuredOutput.schema,
1007
977
  schemaPath: effectiveStructuredOutput.schemaPath,
1008
978
  outputPath: effectiveStructuredOutput.outputPath,
1009
979
  });
1010
- if (structured.error)
1011
- structuredError = structured.error;
980
+ if (structured.error) {
981
+ if (otherwiseSuccessful) {
982
+ structuredError = structured.error === MISSING_STRUCTURED_OUTPUT_CALL_ERROR
983
+ ? formatStructuredOutputRejectionError(run.messages)
984
+ : structured.error;
985
+ }
986
+ }
1012
987
  else {
1013
988
  structuredOutput = structured.value;
1014
989
  const acceptanceReport = readStructuredOutputAcceptanceReport(effectiveStructuredOutput);
@@ -1021,13 +996,13 @@ export async function runSingleStepInner(step, ctx) {
1021
996
  const errorMessages = validatedStructuredOutput
1022
997
  ? run.messages.slice(run.structuredOutputMessageStartIndex ?? run.messages.length)
1023
998
  : run.messages;
1024
- const hiddenError = completionDiagnosticsEligible && run.exitCode === 0 && !run.error && !toolAvailabilityError && !structuredError && !midToolExitError
999
+ const hiddenError = terminalDiagnosticsEligible && run.exitCode === 0 && !run.error && !toolAvailabilityError && !structuredError && !midToolExitError
1025
1000
  ? detectSubagentError(errorMessages)
1026
1001
  : null;
1027
1002
  const terminalEmptyAfterUsefulWork = !validatedStructuredOutput
1028
1003
  && hasEmptyTerminalAssistantResponse(run.messages)
1029
1004
  && (run.toolCount > 0 || Boolean(run.finalOutput.trim()));
1030
- const emptyOutputError = completionDiagnosticsEligible && run.exitCode === 0
1005
+ const emptyOutputError = terminalDiagnosticsEligible && run.exitCode === 0
1031
1006
  && !run.error
1032
1007
  && !toolAvailabilityError
1033
1008
  && !structuredError
@@ -1036,48 +1011,10 @@ export async function runSingleStepInner(step, ctx) {
1036
1011
  && (!hiddenError?.hasError || hasEmptyTerminalAssistantResponse(run.messages))
1037
1012
  ? formatEmptyTerminalAssistantResponseError(run.messages)
1038
1013
  : undefined;
1039
- const completionGuardEnabled = isAgentContract(step.agentContract) ? step.completionGuard === true : step.completionGuard !== false;
1040
- const completionToolPlan = resolvedTaskToolPlan;
1041
- const completionTools = completionToolPlan ? (completionToolPlan.explicitToolAllowlist ? completionToolPlan.effectiveToolAllowlist : undefined) : step.tools;
1042
1014
  const remoteGitChanged = run.nativeMachine?.initialGit && run.nativeMachine.finalGit ? run.nativeMachine.initialGit.head !== run.nativeMachine.finalGit.head || run.nativeMachine.initialGit.dirty !== run.nativeMachine.finalGit.dirty : undefined;
1043
1015
  const mutationEvidence = run.nativeMachine ? { source: "tracked-files", trackedOnly: true, changedFiles: [], attemptedMutation: remoteGitChanged === true, ...(remoteGitChanged === undefined ? { unavailable: "Remote Git before/after evidence was incomplete." } : {}) } : collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
1044
1016
  finalMutationEvidence = mutationEvidence;
1045
- const completionMutationEvidence = ctx.trackedMutationEvidenceForCompletionGuard === false ? undefined : mutationEvidence;
1046
- const completionGuard = completionDiagnosticsEligible && run.exitCode === 0 && !run.error && !structuredError && !hiddenError?.hasError && !midToolExitError && !emptyOutputError && completionGuardEnabled
1047
- ? evaluateCompletionMutationGuard(omitUndefinedProperties({
1048
- agent: step.agent,
1049
- task: taskForCompletionGuard,
1050
- messages: run.messages,
1051
- tools: completionTools,
1052
- mcpDirectTools: completionToolPlan?.effectiveMcpTools ?? step.mcpDirectTools,
1053
- mutationTools: step.mutationTools,
1054
- toolAvailabilityError,
1055
- mutationEvidence: completionMutationEvidence,
1056
- }))
1057
- : undefined;
1058
- const mutationAttemptObserved = run.observedMutationAttempt === true || completionMutationEvidence?.attemptedMutation === true;
1059
- let arbitration = { triggered: completionGuard?.triggered === true && !mutationAttemptObserved, rescued: false };
1060
- if (arbitration.triggered) {
1061
- const modelContext = launch.capture.completionIntentContext?.();
1062
- arbitration = await arbitrateCompletionGuardRescue({
1063
- guardTriggered: true,
1064
- task: taskForCompletionGuard,
1065
- // Construct lazily too: the shared gate refuses overlength tasks before
1066
- // registry/auth/model work. The child has already shut down normally.
1067
- arbiter: modelContext ? async (task) => createTaskMutationArbiter(modelContext)?.(task) ?? "unavailable" : undefined,
1068
- });
1069
- }
1070
- const completionEvidence = planCompletionEvidence({
1071
- guard: completionGuard,
1072
- guardTriggered: arbitration.triggered,
1073
- arbiterRescued: arbitration.rescued,
1074
- completionGuardEnabled,
1075
- mutationCapable: hasMutationToolCapability(completionTools, completionToolPlan?.effectiveMcpTools ?? step.mcpDirectTools),
1076
- implementationMutationExpected: expectsImplementationMutation(step.agent, taskForCompletionGuard),
1077
- mutationAttemptObserved,
1078
- mutationEvidence: completionMutationEvidence,
1079
- agentContractEnabled: isAgentContract(step.agentContract),
1080
- });
1017
+ const mutationAttemptObserved = run.observedMutationAttempt === true || mutationEvidence.attemptedMutation === true;
1081
1018
  const finalOutputHasPersistableFileContent = run.exitCode === 0 && !run.error && !emptyOutputError && Boolean(stripAcceptanceReport(run.finalOutput).trim());
1082
1019
  const requiredOutput = step.outputMode === "file-only" && step.outputPath
1083
1020
  ? { kind: "file-only", path: step.outputPath, missing: !fs.existsSync(step.outputPath) && !finalOutputHasPersistableFileContent }
@@ -1085,9 +1022,9 @@ export async function runSingleStepInner(step, ctx) {
1085
1022
  ? { kind: "structured", path: effectiveStructuredOutput.outputPath, missing: !fs.existsSync(effectiveStructuredOutput.outputPath) }
1086
1023
  : undefined;
1087
1024
  finalRequiredOutputMissing = requiredOutput?.missing;
1088
- const missingRequiredOutputError = completionDiagnosticsEligible ? formatRequiredOutputError(requiredOutput) : undefined;
1025
+ const missingRequiredOutputError = terminalDiagnosticsEligible ? formatRequiredOutputError(requiredOutput) : undefined;
1089
1026
  const missingRequiredOutputAfterMutation = Boolean(missingRequiredOutputError) && (mutationAttemptObserved || Boolean(mutationEvidence.changedFiles.length));
1090
- const effectiveExitCode = toolAvailabilityError || completionEvidence.legacyFailureError || midToolExitError || structuredError || emptyOutputError || missingRequiredOutputError
1027
+ const effectiveExitCode = toolAvailabilityError || midToolExitError || structuredError || emptyOutputError || missingRequiredOutputError
1091
1028
  ? 1
1092
1029
  : hiddenError?.hasError
1093
1030
  ? (hiddenError.exitCode ?? 1)
@@ -1105,8 +1042,7 @@ export async function runSingleStepInner(step, ctx) {
1105
1042
  ? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
1106
1043
  : `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
1107
1044
  : undefined);
1108
- const error = underlyingError ?? missingRequiredOutputError ?? completionEvidence.legacyFailureError;
1109
- completionGuardTriggeredFinal = completionEvidence.guardTriggered && !underlyingError && !missingRequiredOutputError;
1045
+ const error = underlyingError ?? missingRequiredOutputError;
1110
1046
  finalOutputSnapshot = outputSnapshot;
1111
1047
  if (step.toolBudget) {
1112
1048
  const toolMessages = run.messages.filter((message) => message.role === "toolResult");
@@ -1114,21 +1050,18 @@ export async function runSingleStepInner(step, ctx) {
1114
1050
  toolBudgetBlocked = Boolean(blockedMessage);
1115
1051
  toolBudget = toolBudgetState(step.toolBudget, toolMessages.length, blockedMessage ? blockedMessage.toolName : undefined);
1116
1052
  }
1117
- const settlementDiagnostic = projectSettlementDiagnostic(completionEvidence, {
1118
- terminalFailed: effectiveExitCode !== 0,
1053
+ const settlementDiagnostic = effectiveExitCode !== 0 ? {
1119
1054
  finalTextPresent: Boolean(stripAcceptanceReport(run.finalOutput).trim()),
1120
- mutationObserved: mutationEvidence.attemptedMutation,
1121
- requiredOutput,
1055
+ mutation: { attempted: mutationAttemptObserved, observed: mutationEvidence.attemptedMutation },
1056
+ ...(requiredOutput ? { requiredOutput } : {}),
1122
1057
  afterCompactionSettlement: run.afterCompactionSettlement === true,
1123
- });
1124
- const fileMutationEffect = completionEvidence.fileMutation ?? (missingRequiredOutputAfterMutation ? { status: "observed", expected: completionEvidence.mutationExpected, attempted: true, evidence: mutationEvidence } : undefined);
1125
- finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput, runtimeAcknowledgedExtensions, ...(step.agentContract ? { agentContract: step.agentContract } : {}), ...(fileMutationEffect || settlementDiagnostic ? { effects: { ...(fileMutationEffect ? { fileMutation: fileMutationEffect } : {}), ...(settlementDiagnostic ? { settlementDiagnostic } : {}) } } : {}) };
1058
+ } : undefined;
1059
+ const fileMutationEffect = missingRequiredOutputAfterMutation ? { status: "observed", attempted: true, evidence: mutationEvidence } : undefined;
1060
+ finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput, structuredOutputFailed: structuredError ? true : undefined, runtimeAcknowledgedExtensions, ...(step.agentContract ? { agentContract: step.agentContract } : {}), ...(fileMutationEffect || settlementDiagnostic ? { effects: { ...(fileMutationEffect ? { fileMutation: fileMutationEffect } : {}), ...(settlementDiagnostic ? { settlementDiagnostic } : {}) } } : {}) };
1126
1061
  if (run.stopped || run.timedOut || ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.())
1127
1062
  break singleLaunch;
1128
1063
  if (effectiveExitCode === 0 && !error)
1129
1064
  break singleLaunch;
1130
- if (completionEvidence.guardTriggered)
1131
- break singleLaunch;
1132
1065
  const recovery = planAbortRecovery({
1133
1066
  messages: run.messages,
1134
1067
  error,
@@ -1330,11 +1263,11 @@ export async function runSingleStepInner(step, ctx) {
1330
1263
  timeoutRecovery,
1331
1264
  toolBudget,
1332
1265
  toolBudgetBlocked: toolBudgetBlocked || undefined,
1333
- completionGuardTriggered: completionGuardTriggeredFinal,
1334
1266
  ...(finalResult?.effects ? { effects: finalResult.effects } : {}),
1335
- structuredOutput: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : finalResult?.structuredOutput,
1336
- structuredOutputPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.outputPath,
1337
- structuredOutputSchemaPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.schemaPath,
1267
+ structuredOutput: finalResult?.structuredOutput,
1268
+ structuredOutputFailed: finalResult?.structuredOutputFailed,
1269
+ structuredOutputPath: effectiveStructuredOutput?.outputPath,
1270
+ structuredOutputSchemaPath: effectiveStructuredOutput?.schemaPath,
1338
1271
  acceptance: effectiveAcceptance,
1339
1272
  watchdog: finalResult?.watchdog,
1340
1273
  ...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
@@ -3546,7 +3479,6 @@ export async function runSubagent(config, childSessions) {
3546
3479
  onSteerOutcome: (request, delivery) => applySteerDelivery(request.id, fi, delivery),
3547
3480
  timeoutSignal: timeoutAbortController.signal,
3548
3481
  stopSignal: stopAbortController.signal,
3549
- trackedMutationEvidenceForCompletionGuard: false,
3550
3482
  timeoutMessage,
3551
3483
  stopMessage,
3552
3484
  toolTimeoutMs: task.toolTimeoutMs ?? config.toolTimeoutMs,
@@ -3665,6 +3597,7 @@ export async function runSubagent(config, childSessions) {
3665
3597
  review: pr.review,
3666
3598
  timeoutRecovery: pr.timeoutRecovery,
3667
3599
  structuredOutput: pr.structuredOutput,
3600
+ structuredOutputFailed: pr.structuredOutputFailed,
3668
3601
  structuredOutputPath: pr.structuredOutputPath,
3669
3602
  structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
3670
3603
  acceptance: pr.acceptance,
@@ -3969,7 +3902,6 @@ export async function runSubagent(config, childSessions) {
3969
3902
  onSteerOutcome: (request, delivery) => applySteerDelivery(request.id, fi, delivery),
3970
3903
  timeoutSignal: timeoutAbortController.signal,
3971
3904
  stopSignal: stopAbortController.signal,
3972
- trackedMutationEvidenceForCompletionGuard: Boolean(worktreeSetup),
3973
3905
  timeoutMessage,
3974
3906
  stopMessage,
3975
3907
  toolTimeoutMs: taskForRun.toolTimeoutMs ?? config.toolTimeoutMs,
@@ -4048,19 +3980,6 @@ export async function runSubagent(config, childSessions) {
4048
3980
  }));
4049
3981
  if (stopped || childStopped)
4050
3982
  appendTerminalChildStatusEvent(fi, taskEndTime);
4051
- if (singleResult.completionGuardTriggered) {
4052
- const event = buildControlEvent(omitUndefinedProperties({
4053
- from: requiredStatusStep(statusPayload, fi).activityState,
4054
- to: "needs_attention",
4055
- runId: id,
4056
- agent: task.agent,
4057
- index: fi,
4058
- ts: taskEndTime,
4059
- message: `${task.agent} completed without making edits for an implementation task`,
4060
- reason: "completion_guard",
4061
- }));
4062
- appendControlEvent(event);
4063
- }
4064
3983
  if (singleResult.exitCode !== 0 && failFast && !childStopped)
4065
3984
  aborted = true;
4066
3985
  return stopped || childStopped ? { ...singleResult, output: stopMessage, error: stopMessage, exitCode: 1, interrupted: false, timedOut: false, stopped: true, skipped: false } : timedOut ? { ...singleResult, output: singleResult.output || (timeoutMessage ?? "Subagent timed out."), error: singleResult.error ?? timeoutMessage ?? "Subagent timed out.", exitCode: 1, interrupted: false, timedOut: true, skipped: false } : { ...singleResult, skipped: false };
@@ -4128,6 +4047,7 @@ export async function runSubagent(config, childSessions) {
4128
4047
  review: pr.review,
4129
4048
  timeoutRecovery: pr.timeoutRecovery,
4130
4049
  structuredOutput: pr.structuredOutput,
4050
+ structuredOutputFailed: pr.structuredOutputFailed,
4131
4051
  structuredOutputPath: pr.structuredOutputPath,
4132
4052
  structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
4133
4053
  acceptance: pr.acceptance,
@@ -4433,6 +4353,7 @@ export async function runSubagent(config, childSessions) {
4433
4353
  review: singleResult.review,
4434
4354
  timeoutRecovery: singleResult.timeoutRecovery,
4435
4355
  structuredOutput: singleResult.structuredOutput,
4356
+ structuredOutputFailed: singleResult.structuredOutputFailed,
4436
4357
  structuredOutputPath: singleResult.structuredOutputPath,
4437
4358
  structuredOutputSchemaPath: singleResult.structuredOutputSchemaPath,
4438
4359
  acceptance: singleResult.acceptance,
@@ -4595,19 +4516,6 @@ export async function runSubagent(config, childSessions) {
4595
4516
  writeStatusPayload();
4596
4517
  });
4597
4518
  }
4598
- if (singleResult.completionGuardTriggered) {
4599
- const event = buildControlEvent(omitUndefinedProperties({
4600
- from: requiredStatusStep(statusPayload, flatIndex).activityState,
4601
- to: "needs_attention",
4602
- runId: id,
4603
- agent: seqStep.agent,
4604
- index: flatIndex,
4605
- ts: stepEndTime,
4606
- message: `${seqStep.agent} completed without making edits for an implementation task`,
4607
- reason: "completion_guard",
4608
- }));
4609
- appendControlEvent(event);
4610
- }
4611
4519
  flatIndex++;
4612
4520
  if (isAgentContract(seqStep.agentContract) && seqStep.gateOn === "acceptance" && singleResult.acceptance?.status === "rejected") {
4613
4521
  statusPayload.error = acceptanceFailureMessage(singleResult.acceptance) ?? "Chain acceptance gate rejected the step.";
@@ -4856,6 +4764,7 @@ export async function runSubagent(config, childSessions) {
4856
4764
  review: r.review,
4857
4765
  effects: r.effects,
4858
4766
  structuredOutput: r.structuredOutput,
4767
+ structuredOutputFailed: r.structuredOutputFailed,
4859
4768
  structuredOutputPath: r.structuredOutputPath,
4860
4769
  structuredOutputSchemaPath: r.structuredOutputSchemaPath,
4861
4770
  acceptance: r.acceptance,
@@ -4973,162 +4882,46 @@ export async function runSubagent(config, childSessions) {
4973
4882
  }
4974
4883
  }
4975
4884
  }
4976
- async function waitForStartupControl(controlPath, token, action, timeoutMs = 30_000) {
4977
- const deadline = Date.now() + timeoutMs;
4978
- while (Date.now() <= deadline) {
4979
- if (fs.existsSync(controlPath)) {
4980
- let payload;
4981
- try {
4982
- payload = JSON.parse(fs.readFileSync(controlPath, "utf-8"));
4983
- }
4984
- catch (error) {
4985
- throw new Error(`Failed to read runner startup control '${controlPath}': ${error instanceof Error ? error.message : String(error)}`);
4986
- }
4987
- if (payload.token !== token)
4988
- throw new Error("Runner startup control token does not match.");
4989
- if (payload.action === action)
4990
- return;
4991
- if (payload.action !== "ack" && payload.action !== "confirm" && payload.action !== "proceed")
4992
- throw new Error("Runner startup control action is invalid.");
4993
- }
4994
- await new Promise((resolve) => setTimeout(resolve, 20));
4995
- }
4996
- throw new Error(`Timed out after ${timeoutMs}ms waiting for runner startup control '${action}'.`);
4997
- }
4998
- // Both hosts enter here: startup authorization, revival leases and disposal stay shared.
4999
- export async function runConfiguredSubagent(config, options) {
5000
- let lease;
5001
- let startupCommitted = config.revivalLease === undefined && config.launchBarrierToken === undefined;
5002
- const startupPath = path.join(config.asyncDir, "runner-startup.json");
5003
- const startupAckPath = path.join(config.asyncDir, "runner-startup-ack.json");
5004
- const startupConfirmPath = path.join(config.asyncDir, "runner-startup-confirm.json");
5005
- const startupProceedPath = path.join(config.asyncDir, "runner-startup-proceed.json");
5006
- const releaseOnExit = () => {
5007
- try {
5008
- lease?.release();
5009
- }
5010
- catch {
5011
- // Exit cleanup is best effort; a dead-owner lease is reclaimed on the next revival.
5012
- }
5013
- };
5014
- process.once("exit", releaseOnExit);
4885
+ /** Heavy execution entry loaded by the bootstrap only after startup commits. */
4886
+ export async function runConfiguredSubagentExecution(config, options) {
4887
+ let childSessions;
5015
4888
  try {
5016
- if (config.launchBarrierToken) {
5017
- await waitForStartupControl(startupProceedPath, config.launchBarrierToken, "proceed");
5018
- startupCommitted = true;
5019
- try {
5020
- fs.rmSync(startupProceedPath, { force: true });
5021
- }
5022
- catch {
5023
- // Startup control cleanup is best effort after the parent commits the run.
5024
- }
5025
- }
5026
- else if (config.revivalLease) {
5027
- lease = acquireSessionLease(config.revivalLease);
5028
- config.revivalLeaseToken = lease.owner.token;
5029
- writeAtomicJson(startupPath, { state: "ready", token: lease.owner.token, pid: process.pid, owner: lease.owner });
5030
- await waitForStartupControl(startupAckPath, lease.owner.token, "ack");
5031
- writeAtomicJson(startupPath, { state: "acknowledged", token: lease.owner.token, pid: process.pid });
5032
- await waitForStartupControl(startupConfirmPath, lease.owner.token, "confirm");
5033
- writeAtomicJson(startupPath, { state: "confirmed", token: lease.owner.token, pid: process.pid });
5034
- await waitForStartupControl(startupProceedPath, lease.owner.token, "proceed");
5035
- startupCommitted = true;
5036
- for (const controlPath of [startupAckPath, startupConfirmPath, startupProceedPath]) {
5037
- try {
5038
- fs.rmSync(controlPath, { force: true });
5039
- }
5040
- catch {
5041
- // Startup control cleanup is best effort after the parent commits the run.
5042
- }
5043
- }
5044
- }
5045
- const childSessions = await loadRunnerChildSessionFactory(config, options);
5046
- try {
5047
- await runSubagent(config, childSessions);
5048
- }
5049
- finally {
5050
- try {
5051
- await childSessions.dispose();
5052
- }
5053
- catch (error) {
5054
- console.error("Failed to dispose runner child sessions:", error);
5055
- }
5056
- }
4889
+ // Detached Node runners do not receive Pi's CLI dispatcher setup. Binary
4890
+ // hosts install the same dispatcher before entering the shared bootstrap.
4891
+ if (!options?.loadPiCodingAgent)
4892
+ installRunnerHttpDispatcher({ agentDir: getAgentDir(), cwd: process.cwd() });
4893
+ childSessions = await loadRunnerChildSessionFactory(config, options);
5057
4894
  }
5058
4895
  catch (error) {
5059
- if (!startupCommitted) {
5060
- try {
5061
- writeAtomicJson(startupPath, { state: "error", pid: process.pid, error: error instanceof Error ? error.message : String(error) });
5062
- }
5063
- catch {
5064
- // The parent will time out and terminate this runner if the handshake cannot be written.
5065
- }
4896
+ try {
4897
+ persistRunnerStartupFailure({
4898
+ asyncDir: config.asyncDir,
4899
+ runId: config.id,
4900
+ runnerProcessInstanceId: config.runnerProcessInstanceId ?? "unknown-runner-instance",
4901
+ message: `Subagent runner startup failed: ${error instanceof Error ? error.message : String(error)}`,
4902
+ ...(config.sessionId ? { sessionId: config.sessionId } : {}),
4903
+ ...(config.completionOwnerId ? { completionOwnerId: config.completionOwnerId } : {}),
4904
+ candidate: {
4905
+ ...(config.revivalLease?.sessionFile ? { sessionFile: config.revivalLease.sessionFile } : {}),
4906
+ ...(config.revivalLeaseToken ? { revivalLeaseToken: config.revivalLeaseToken } : {}),
4907
+ },
4908
+ });
4909
+ }
4910
+ catch (persistenceError) {
4911
+ console.error("Failed to persist runner setup failure:", persistenceError);
5066
4912
  }
5067
4913
  throw error;
5068
4914
  }
5069
- finally {
5070
- process.off("exit", releaseOnExit);
5071
- if (lease) {
5072
- let acknowledged = false;
5073
- try {
5074
- acknowledged = lease.release();
5075
- }
5076
- catch (error) {
5077
- console.error("Failed to release session revival lease:", error);
5078
- }
5079
- try {
5080
- markProcessTerminalCandidateLeaseRelease(config.asyncDir, lease.owner.token, acknowledged);
5081
- }
5082
- catch (error) {
5083
- console.error("Failed to record session revival lease release:", error);
5084
- }
5085
- }
4915
+ try {
4916
+ await runSubagent(config, childSessions);
5086
4917
  }
5087
- }
5088
- function startConfiguredSubagent(config) {
5089
- // Child sessions and the extensions loaded into them may leave handles
5090
- // behind even after shutdown; the run is fully persisted by now, so exit
5091
- // explicitly instead of waiting for the event loop to drain.
5092
- runConfiguredSubagent(config).then(() => process.exit(0), (runErr) => {
5093
- console.error("Subagent runner error:", runErr);
5094
- process.exit(1);
5095
- });
5096
- }
5097
- if (isRunnerEntrypoint) {
5098
- const configArg = process.argv[2];
5099
- if (configArg) {
4918
+ finally {
5100
4919
  try {
5101
- const configJson = fs.readFileSync(configArg, "utf-8");
5102
- const config = JSON.parse(configJson);
5103
- try {
5104
- fs.unlinkSync(configArg);
5105
- }
5106
- catch {
5107
- // Temp config cleanup is best effort.
5108
- }
5109
- startConfiguredSubagent(config);
4920
+ await childSessions.dispose();
5110
4921
  }
5111
- catch (err) {
5112
- console.error("Subagent runner error:", err);
5113
- process.exit(1);
4922
+ catch (error) {
4923
+ console.error("Failed to dispose runner child sessions:", error);
5114
4924
  }
5115
4925
  }
5116
- else {
5117
- let input = "";
5118
- process.stdin.setEncoding("utf-8");
5119
- process.stdin.on("data", (chunk) => {
5120
- input += chunk;
5121
- });
5122
- process.stdin.on("end", () => {
5123
- try {
5124
- const config = JSON.parse(input);
5125
- startConfiguredSubagent(config);
5126
- }
5127
- catch (err) {
5128
- console.error("Subagent runner error:", err);
5129
- process.exit(1);
5130
- }
5131
- });
5132
- }
5133
4926
  }
5134
4927
  //# sourceMappingURL=subagent-runner.js.map