pi-subagents 0.74.0 → 0.76.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 (237) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/docs/agents.md +15 -5
  3. package/docs/configuration.md +14 -2
  4. package/docs/extension-api.md +2 -2
  5. package/docs/missions.md +27 -3
  6. package/docs/models.md +6 -2
  7. package/docs/observability.md +8 -3
  8. package/docs/tool-reference.md +48 -8
  9. package/docs/workflows.md +3 -3
  10. package/package.json +2 -1
  11. package/src/agents/agent-memory.js +3 -3
  12. package/src/agents/agent-memory.js.map +1 -1
  13. package/src/agents/agent-serializer.d.ts.map +1 -1
  14. package/src/agents/agent-serializer.js +8 -1
  15. package/src/agents/agent-serializer.js.map +1 -1
  16. package/src/agents/agents.d.ts +1 -0
  17. package/src/agents/agents.d.ts.map +1 -1
  18. package/src/agents/agents.js +137 -95
  19. package/src/agents/agents.js.map +1 -1
  20. package/src/api/preflight.d.ts +1 -1
  21. package/src/api/preflight.d.ts.map +1 -1
  22. package/src/api/preflight.js +44 -1
  23. package/src/api/preflight.js.map +1 -1
  24. package/src/extension/config.d.ts.map +1 -1
  25. package/src/extension/config.js +4 -1
  26. package/src/extension/config.js.map +1 -1
  27. package/src/extension/index.d.ts.map +1 -1
  28. package/src/extension/index.js +62 -22
  29. package/src/extension/index.js.map +1 -1
  30. package/src/extension/public-execution.d.ts.map +1 -1
  31. package/src/extension/public-execution.js +4 -0
  32. package/src/extension/public-execution.js.map +1 -1
  33. package/src/extension/reply-workflow-script.d.ts.map +1 -1
  34. package/src/extension/reply-workflow-script.js +21 -12
  35. package/src/extension/reply-workflow-script.js.map +1 -1
  36. package/src/extension/rpc.js +1 -1
  37. package/src/extension/rpc.js.map +1 -1
  38. package/src/extension/schemas.d.ts +2 -1
  39. package/src/extension/schemas.d.ts.map +1 -1
  40. package/src/extension/schemas.js +13 -11
  41. package/src/extension/schemas.js.map +1 -1
  42. package/src/extension/tool-description.js +2 -2
  43. package/src/extension/tool-description.js.map +1 -1
  44. package/src/intercom/result-intercom.d.ts.map +1 -1
  45. package/src/intercom/result-intercom.js +2 -1
  46. package/src/intercom/result-intercom.js.map +1 -1
  47. package/src/missions/goal-driver.d.ts +1 -0
  48. package/src/missions/goal-driver.d.ts.map +1 -1
  49. package/src/missions/goal-driver.js +49 -34
  50. package/src/missions/goal-driver.js.map +1 -1
  51. package/src/missions/store.d.ts.map +1 -1
  52. package/src/missions/store.js +11 -1
  53. package/src/missions/store.js.map +1 -1
  54. package/src/profiles/profiles.d.ts +1 -1
  55. package/src/profiles/profiles.d.ts.map +1 -1
  56. package/src/profiles/profiles.js +29 -22
  57. package/src/profiles/profiles.js.map +1 -1
  58. package/src/runs/background/active-async-capacity.d.ts +15 -1
  59. package/src/runs/background/active-async-capacity.d.ts.map +1 -1
  60. package/src/runs/background/active-async-capacity.js +51 -6
  61. package/src/runs/background/active-async-capacity.js.map +1 -1
  62. package/src/runs/background/async-execution.d.ts +13 -0
  63. package/src/runs/background/async-execution.d.ts.map +1 -1
  64. package/src/runs/background/async-execution.js +89 -6
  65. package/src/runs/background/async-execution.js.map +1 -1
  66. package/src/runs/background/async-job-tracker.d.ts +4 -2
  67. package/src/runs/background/async-job-tracker.d.ts.map +1 -1
  68. package/src/runs/background/async-job-tracker.js +4 -3
  69. package/src/runs/background/async-job-tracker.js.map +1 -1
  70. package/src/runs/background/await-async-run.d.ts.map +1 -1
  71. package/src/runs/background/await-async-run.js +4 -2
  72. package/src/runs/background/await-async-run.js.map +1 -1
  73. package/src/runs/background/calendar-schedule.d.ts +27 -0
  74. package/src/runs/background/calendar-schedule.d.ts.map +1 -0
  75. package/src/runs/background/calendar-schedule.js +94 -0
  76. package/src/runs/background/calendar-schedule.js.map +1 -0
  77. package/src/runs/background/control-channel.d.ts +10 -0
  78. package/src/runs/background/control-channel.d.ts.map +1 -1
  79. package/src/runs/background/control-channel.js +51 -0
  80. package/src/runs/background/control-channel.js.map +1 -1
  81. package/src/runs/background/notify.d.ts +6 -0
  82. package/src/runs/background/notify.d.ts.map +1 -1
  83. package/src/runs/background/notify.js +24 -4
  84. package/src/runs/background/notify.js.map +1 -1
  85. package/src/runs/background/result-watcher.d.ts +5 -1
  86. package/src/runs/background/result-watcher.d.ts.map +1 -1
  87. package/src/runs/background/result-watcher.js +44 -7
  88. package/src/runs/background/result-watcher.js.map +1 -1
  89. package/src/runs/background/run-child-session.d.ts +2 -0
  90. package/src/runs/background/run-child-session.d.ts.map +1 -1
  91. package/src/runs/background/run-child-session.js +32 -7
  92. package/src/runs/background/run-child-session.js.map +1 -1
  93. package/src/runs/background/runner-aliases.d.ts +1 -0
  94. package/src/runs/background/runner-aliases.d.ts.map +1 -1
  95. package/src/runs/background/runner-aliases.js +6 -2
  96. package/src/runs/background/runner-aliases.js.map +1 -1
  97. package/src/runs/background/scheduled-runs.d.ts +5 -2
  98. package/src/runs/background/scheduled-runs.d.ts.map +1 -1
  99. package/src/runs/background/scheduled-runs.js +210 -56
  100. package/src/runs/background/scheduled-runs.js.map +1 -1
  101. package/src/runs/background/stale-run-reconciler.d.ts +1 -1
  102. package/src/runs/background/stale-run-reconciler.d.ts.map +1 -1
  103. package/src/runs/background/stale-run-reconciler.js +15 -2
  104. package/src/runs/background/stale-run-reconciler.js.map +1 -1
  105. package/src/runs/background/subagent-runner.d.ts +1 -0
  106. package/src/runs/background/subagent-runner.d.ts.map +1 -1
  107. package/src/runs/background/subagent-runner.js +58 -9
  108. package/src/runs/background/subagent-runner.js.map +1 -1
  109. package/src/runs/background/wait-completions.d.ts.map +1 -1
  110. package/src/runs/background/wait-completions.js +1 -0
  111. package/src/runs/background/wait-completions.js.map +1 -1
  112. package/src/runs/background/workflow-terminal-proof.d.ts +14 -1
  113. package/src/runs/background/workflow-terminal-proof.d.ts.map +1 -1
  114. package/src/runs/background/workflow-terminal-proof.js +41 -28
  115. package/src/runs/background/workflow-terminal-proof.js.map +1 -1
  116. package/src/runs/foreground/command-action.d.ts +13 -0
  117. package/src/runs/foreground/command-action.d.ts.map +1 -0
  118. package/src/runs/foreground/command-action.js +38 -0
  119. package/src/runs/foreground/command-action.js.map +1 -0
  120. package/src/runs/foreground/execution.d.ts.map +1 -1
  121. package/src/runs/foreground/execution.js +42 -8
  122. package/src/runs/foreground/execution.js.map +1 -1
  123. package/src/runs/foreground/foreground-history.d.ts.map +1 -1
  124. package/src/runs/foreground/foreground-history.js +1 -0
  125. package/src/runs/foreground/foreground-history.js.map +1 -1
  126. package/src/runs/foreground/subagent-executor.d.ts +2 -1
  127. package/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  128. package/src/runs/foreground/subagent-executor.js +108 -17
  129. package/src/runs/foreground/subagent-executor.js.map +1 -1
  130. package/src/runs/shared/child-commands.d.ts +28 -0
  131. package/src/runs/shared/child-commands.d.ts.map +1 -0
  132. package/src/runs/shared/child-commands.js +250 -0
  133. package/src/runs/shared/child-commands.js.map +1 -0
  134. package/src/runs/shared/child-session.d.ts +4 -0
  135. package/src/runs/shared/child-session.d.ts.map +1 -1
  136. package/src/runs/shared/child-session.js +107 -22
  137. package/src/runs/shared/child-session.js.map +1 -1
  138. package/src/runs/shared/child-tool-plan.js +1 -1
  139. package/src/runs/shared/child-tool-plan.js.map +1 -1
  140. package/src/runs/shared/claude-code-adapter.d.ts +56 -0
  141. package/src/runs/shared/claude-code-adapter.d.ts.map +1 -1
  142. package/src/runs/shared/claude-code-adapter.js +138 -2
  143. package/src/runs/shared/claude-code-adapter.js.map +1 -1
  144. package/src/runs/shared/dynamic-fanout.js +1 -1
  145. package/src/runs/shared/dynamic-fanout.js.map +1 -1
  146. package/src/runs/shared/external-cli-preflight.d.ts +10 -0
  147. package/src/runs/shared/external-cli-preflight.d.ts.map +1 -1
  148. package/src/runs/shared/external-cli-preflight.js +50 -6
  149. package/src/runs/shared/external-cli-preflight.js.map +1 -1
  150. package/src/runs/shared/external-cli-runner.d.ts.map +1 -1
  151. package/src/runs/shared/external-cli-runner.js +4 -2
  152. package/src/runs/shared/external-cli-runner.js.map +1 -1
  153. package/src/runs/shared/mcp-direct-tool-allowlist.d.ts.map +1 -1
  154. package/src/runs/shared/mcp-direct-tool-allowlist.js +8 -5
  155. package/src/runs/shared/mcp-direct-tool-allowlist.js.map +1 -1
  156. package/src/runs/shared/model-resolution.d.ts +6 -2
  157. package/src/runs/shared/model-resolution.d.ts.map +1 -1
  158. package/src/runs/shared/model-resolution.js +9 -2
  159. package/src/runs/shared/model-resolution.js.map +1 -1
  160. package/src/runs/shared/parallel-handoff.d.ts +10 -4
  161. package/src/runs/shared/parallel-handoff.d.ts.map +1 -1
  162. package/src/runs/shared/parallel-handoff.js +53 -6
  163. package/src/runs/shared/parallel-handoff.js.map +1 -1
  164. package/src/runs/shared/parallel-utils.d.ts +2 -0
  165. package/src/runs/shared/parallel-utils.d.ts.map +1 -1
  166. package/src/runs/shared/parallel-utils.js.map +1 -1
  167. package/src/runs/shared/partial-output.d.ts +18 -0
  168. package/src/runs/shared/partial-output.d.ts.map +1 -0
  169. package/src/runs/shared/partial-output.js +29 -0
  170. package/src/runs/shared/partial-output.js.map +1 -0
  171. package/src/runs/shared/run-history.d.ts +47 -0
  172. package/src/runs/shared/run-history.d.ts.map +1 -1
  173. package/src/runs/shared/run-history.js +70 -0
  174. package/src/runs/shared/run-history.js.map +1 -1
  175. package/src/runs/shared/subagent-control.d.ts.map +1 -1
  176. package/src/runs/shared/subagent-control.js +30 -17
  177. package/src/runs/shared/subagent-control.js.map +1 -1
  178. package/src/runs/shared/subagent-prompt-runtime.d.ts +1 -0
  179. package/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
  180. package/src/runs/shared/subagent-prompt-runtime.js +12 -5
  181. package/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
  182. package/src/runs/shared/worktree-cleanup-apply.d.ts +37 -0
  183. package/src/runs/shared/worktree-cleanup-apply.d.ts.map +1 -0
  184. package/src/runs/shared/worktree-cleanup-apply.js +179 -0
  185. package/src/runs/shared/worktree-cleanup-apply.js.map +1 -0
  186. package/src/runs/shared/worktree-cleanup-plan.d.ts +7 -0
  187. package/src/runs/shared/worktree-cleanup-plan.d.ts.map +1 -1
  188. package/src/runs/shared/worktree-cleanup-plan.js +41 -19
  189. package/src/runs/shared/worktree-cleanup-plan.js.map +1 -1
  190. package/src/runs/shared/worktree-lock.d.ts +6 -0
  191. package/src/runs/shared/worktree-lock.d.ts.map +1 -0
  192. package/src/runs/shared/worktree-lock.js +41 -0
  193. package/src/runs/shared/worktree-lock.js.map +1 -0
  194. package/src/runs/shared/worktree.d.ts +3 -0
  195. package/src/runs/shared/worktree.d.ts.map +1 -1
  196. package/src/runs/shared/worktree.js +9 -1
  197. package/src/runs/shared/worktree.js.map +1 -1
  198. package/src/shared/file-lease.d.ts +5 -0
  199. package/src/shared/file-lease.d.ts.map +1 -0
  200. package/src/shared/file-lease.js +101 -0
  201. package/src/shared/file-lease.js.map +1 -0
  202. package/src/shared/required-child-extensions.d.ts +16 -1
  203. package/src/shared/required-child-extensions.d.ts.map +1 -1
  204. package/src/shared/required-child-extensions.js +37 -5
  205. package/src/shared/required-child-extensions.js.map +1 -1
  206. package/src/shared/settings-file-lease.d.ts +5 -0
  207. package/src/shared/settings-file-lease.d.ts.map +1 -0
  208. package/src/shared/settings-file-lease.js +39 -0
  209. package/src/shared/settings-file-lease.js.map +1 -0
  210. package/src/shared/types.d.ts +16 -1
  211. package/src/shared/types.d.ts.map +1 -1
  212. package/src/shared/types.js +1 -1
  213. package/src/shared/types.js.map +1 -1
  214. package/src/slash/subagent-cost.d.ts.map +1 -1
  215. package/src/slash/subagent-cost.js +8 -4
  216. package/src/slash/subagent-cost.js.map +1 -1
  217. package/src/tui/render.d.ts +1 -1
  218. package/src/tui/render.d.ts.map +1 -1
  219. package/src/tui/render.js +11 -6
  220. package/src/tui/render.js.map +1 -1
  221. package/src/watchdog/diff-tool.js +2 -2
  222. package/src/watchdog/diff-tool.js.map +1 -1
  223. package/src/watchdog/register-main.js +1 -2
  224. package/src/watchdog/register-main.js.map +1 -1
  225. package/src/watchdog/runtime.d.ts +3 -3
  226. package/src/watchdog/runtime.d.ts.map +1 -1
  227. package/src/watchdog/runtime.js +7 -3
  228. package/src/watchdog/runtime.js.map +1 -1
  229. package/src/watchdog/settings.d.ts.map +1 -1
  230. package/src/watchdog/settings.js +22 -17
  231. package/src/watchdog/settings.js.map +1 -1
  232. package/src/workflows/workflow-receipt.js +2 -2
  233. package/src/workflows/workflow-receipt.js.map +1 -1
  234. package/src/workflows/workflow-resources.d.ts +4 -0
  235. package/src/workflows/workflow-resources.d.ts.map +1 -1
  236. package/src/workflows/workflow-resources.js +7 -4
  237. package/src/workflows/workflow-resources.js.map +1 -1
@@ -19,6 +19,7 @@ import { isWorkflowScriptPath, normalizePublicSubagentExecution, validateWorkflo
19
19
  import { readReplyWorkflowScript } from "../../extension/reply-workflow-script.js";
20
20
  import { disabledFeatureNotice, disabledFeatureUseError, resolveDisabledFeatureSurface } from "../../shared/disabled-features.js";
21
21
  import { runSync } from "./execution.js";
22
+ import { commandAction } from "./command-action.js";
22
23
  import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.js";
23
24
  import { applyWatchdogLaunchRules } from "../../watchdog/rules.js";
24
25
  import { childWatchdogProgressForModel } from "../../watchdog/child-status.js";
@@ -54,11 +55,11 @@ import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig
54
55
  import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling } from "../shared/capability-ceiling.js";
55
56
  import { isAgentContract } from "../shared/agent-contract.js";
56
57
  import { normalizeExtensionBindings } from "../shared/extension-bindings.js";
57
- import { resolveRequiredChildExtensions } from "../../shared/required-child-extensions.js";
58
+ import { assertRequiredChildExtensionsAdmitted, hasMandatoryRequiredChildExtensions, readRetainedRequiredChildExtensions, resolveRequiredChildExtensions } from "../../shared/required-child-extensions.js";
58
59
  import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.js";
59
60
  import { assertJsonSchemaObject, cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.js";
60
61
  import { compactForegroundDetails, getSingleResultOutput, PROMPT_REDACTED, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.js";
61
- import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.js";
62
+ import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, protectRetainedWorktreeForResume, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.js";
62
63
  import { summarizeContextModes } from "../shared/context-mode.js";
63
64
  import { attachNestedChildrenToResultChildren, buildSubagentResultIntercomPayload, deliverSubagentResultIntercomEvent, formatSubagentResultReceipt, resolveSubagentResultStatus, stripDetailsOutputsForIntercomReceipt, } from "../../intercom/result-intercom.js";
64
65
  import { applySteeringRecoveryAgentConfig, asyncReviveRequiresRecoveryDescriptor, buildRevivedAsyncTask, readAsyncRecoveryDescriptor, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.js";
@@ -106,11 +107,12 @@ import { authorizeWorkflowResourceHost, claimWorkflowChildPermit, consumeWorkflo
106
107
  import { deepFreezeWorkflowArgs, normalizeWorkflowArgs, resolveStructuredWorkflowResource, resolveWorkflowResource } from "../../workflows/workflow-resources.js";
107
108
  import { stableJsonDigest } from "../../shared/launch-contract.js";
108
109
  import { cleanupWorktrees, createWorktrees, preflightWorktreeSource, withWorktreeTransaction, diffWorktrees, formatWorktreeDiffSummary, } from "../shared/worktree.js";
110
+ import { applyReviewedCleanupPlan, formatCleanupReceipt, loadReviewedCleanupPlan } from "../shared/worktree-cleanup-apply.js";
109
111
  import { createWorktreeCleanupPlan, formatWorktreeCleanupPlan } from "../shared/worktree-cleanup-plan.js";
110
112
  import { DIRS, DEFAULT_ARTIFACT_CONFIG, DEFAULT_MAX_OUTPUT, DEFAULT_FORK_PREAMBLE, SUBAGENT_ACTIONS, SUBAGENT_ASYNC_STARTED_EVENT, SUBAGENT_CHILD_STATUS_EVENT, SUBAGENT_CONTROL_EVENT, SUBAGENT_CONTROL_INTERCOM_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, SUBAGENT_LIFECYCLE_ARTIFACT_VERSION, checkSubagentDepth, resolveChildMaxSubagentDepth, resolveCurrentMaxSubagentDepth, resolveMaxSubagentSpawnsPerRun, truncateOutput, wrapForkTask, } from "../../shared/types.js";
111
113
  import { deriveChildSessionName } from "../../shared/child-session-name.js";
112
- const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "worktree.cleanup", "lane.recordMerge", "lane.recordSupersession", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
113
- const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "schedule.delete"]);
114
+ const MUTATING_MANAGEMENT_ACTIONS = new Set(["command.yield", "command.cancel", "create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "worktree.cleanup", "lane.recordMerge", "lane.recordSupersession", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
115
+ const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["command.cancel", "delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "schedule.delete"]);
114
116
  function resolveSteerDeliveryMode(mode) {
115
117
  return mode === "steer" || mode === "follow_up" || mode === "auto" ? mode : undefined;
116
118
  }
@@ -464,6 +466,7 @@ function rememberForegroundRun(state, input) {
464
466
  ...(result.error ? { error: result.error } : {}),
465
467
  ...(result.finalOutput ? { finalOutput: result.finalOutput } : {}),
466
468
  ...(result.outputState ? { outputState: result.outputState } : {}),
469
+ ...(result.outputPartial ? { outputPartial: true } : {}),
467
470
  ...(result.outputMode ? { outputMode: result.outputMode } : {}),
468
471
  ...(result.savedOutputPath ? { savedOutputPath: result.savedOutputPath } : {}),
469
472
  ...(result.outputSaveError ? { outputSaveError: result.outputSaveError } : {}),
@@ -548,6 +551,7 @@ function updateRememberedForegroundChild(state, input) {
548
551
  ...(input.result.error ? { error: input.result.error } : {}),
549
552
  ...(input.result.finalOutput ? { finalOutput: input.result.finalOutput } : {}),
550
553
  outputState: input.result.outputState,
554
+ outputPartial: input.result.outputPartial,
551
555
  outputMode: input.result.outputMode,
552
556
  savedOutputPath: input.result.savedOutputPath,
553
557
  outputSaveError: input.result.outputSaveError,
@@ -960,6 +964,17 @@ function appendStepToAsyncChain(input) {
960
964
  details: { mode: "management", results: [] },
961
965
  };
962
966
  }
967
+ let retainedRequiredExtensions;
968
+ try {
969
+ retainedRequiredExtensions = readRetainedRequiredChildExtensions(resolved.location.asyncDir);
970
+ }
971
+ catch (error) {
972
+ return {
973
+ content: [{ type: "text", text: `Cannot append step to run '${resolved.id}': ${error instanceof Error ? error.message : String(error)}` }],
974
+ isError: true,
975
+ details: { mode: "management", results: [] },
976
+ };
977
+ }
963
978
  const pendingAppendRequests = readPendingChainAppendRequests(resolved.location.asyncDir);
964
979
  const reservedOutputNames = new Set([
965
980
  ...Object.keys(status.outputs ?? {}),
@@ -1031,6 +1046,7 @@ function appendStepToAsyncChain(input) {
1031
1046
  asyncDir: resolved.location.asyncDir,
1032
1047
  validateOutputBindings: false,
1033
1048
  capabilityCeiling: intersectSubagentCapabilityCeilings(status.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(asyncCtx.currentSessionId)),
1049
+ requiredExtensions: retainedRequiredExtensions,
1034
1050
  }));
1035
1051
  if ("error" in built) {
1036
1052
  return {
@@ -1632,6 +1648,19 @@ async function resumeAsyncRun(input) {
1632
1648
  : "External runners do not persist Pi sessions and cannot be resumed.";
1633
1649
  return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
1634
1650
  }
1651
+ if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
1652
+ return { content: [{ type: "text", text: "Cannot resume with baseRef: retained managed-worktree children continue in their existing worktree. Start a new worktree run from that base ref instead." }], isError: true, details: { mode: "management", results: [] } };
1653
+ }
1654
+ if (target.source === "async" && target.managedWorktree && target.asyncDir) {
1655
+ try {
1656
+ const protectedCwd = await protectRetainedWorktreeForResume(parallelHandoffPath(target.asyncDir), target.runId, target.index, input.signal);
1657
+ if (path.resolve(protectedCwd) !== path.resolve(effectiveCwd))
1658
+ throw new Error("Retained worktree cwd changed during resume admission.");
1659
+ }
1660
+ catch (error) {
1661
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
1662
+ }
1663
+ }
1635
1664
  if (target.source === "async" && target.runner?.type === "external-job") {
1636
1665
  if (attachChain)
1637
1666
  return { content: [{ type: "text", text: "External-job follow-up does not support chain attachment. Use action='resume' with message instead." }], isError: true, details: { mode: "management", results: [] } };
@@ -1779,9 +1808,6 @@ async function resumeAsyncRun(input) {
1779
1808
  if (!revivalSessionFile) {
1780
1809
  return { content: [{ type: "text", text: `Async run '${target.runId}' child ${target.index} does not have a persisted session file to resume from.` }], isError: true, details: { mode: "management", results: [] } };
1781
1810
  }
1782
- if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
1783
- return { content: [{ type: "text", text: "Cannot resume with baseRef: retained managed-worktree children continue in their existing worktree. Start a new worktree run from that base ref instead." }], isError: true, details: { mode: "management", results: [] } };
1784
- }
1785
1811
  if (target.source === "async" && asyncReviveRequiresRecoveryDescriptor(target)) {
1786
1812
  return { content: [{ type: "text", text: `Async child '${target.runId}' is missing its required run fan-out recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
1787
1813
  }
@@ -2067,6 +2093,7 @@ async function emitForegroundResultIntercom(input) {
2067
2093
  ...(result.sessionName ? { sessionName: result.sessionName } : {}),
2068
2094
  status: foregroundResultIntercomStatus(result),
2069
2095
  outputState: result.outputState ?? "unknown",
2096
+ ...(result.outputPartial ? { outputPartial: true } : {}),
2070
2097
  summary: resultSummaryForIntercom(result),
2071
2098
  index,
2072
2099
  artifactPath: result.artifactPaths?.outputPath,
@@ -3069,6 +3096,7 @@ async function runAsyncPath(data, deps) {
3069
3096
  goal: params.task ?? "",
3070
3097
  agentConfig: a,
3071
3098
  recoveryAgentConfig: data.recoveryAgents.find((agent) => agent.name === params.agent),
3099
+ requiredExtensions: data.requiredExtensions,
3072
3100
  ctx: asyncCtx,
3073
3101
  availableModels,
3074
3102
  cwd: effectiveCwd,
@@ -3455,8 +3483,10 @@ async function runSinglePath(data, deps) {
3455
3483
  }
3456
3484
  let foregroundMachine;
3457
3485
  const requestedMachine = params.machine ?? agentConfig.machine;
3486
+ const requiredExtensions = data.requiredExtensions ?? deps.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(data.parentPiSessionId);
3458
3487
  if (requestedMachine) {
3459
3488
  try {
3489
+ assertRequiredChildExtensionsAdmitted([requiredExtensions], { agent: agentConfig.name, runnerType: agentConfig.runner?.type, machine: requestedMachine });
3460
3490
  foregroundMachine = resolveHerdrMachinePlacement({ machine: requestedMachine, cwd: ctx.cwd, stepCwd: params.machineCwd }).machine;
3461
3491
  }
3462
3492
  catch (error) {
@@ -3643,7 +3673,6 @@ async function runSinglePath(data, deps) {
3643
3673
  }
3644
3674
  : undefined;
3645
3675
  const deadlineAt = data.deadlineAt ?? (data.timeoutMs !== undefined ? Date.now() + data.timeoutMs : undefined);
3646
- const requiredExtensions = deps.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(data.parentPiSessionId);
3647
3676
  let r;
3648
3677
  let resolveDetachedWorkflowChild;
3649
3678
  const detachedWorkflowChild = params.workflowAwaitDetached === true
@@ -3900,6 +3929,7 @@ function duplicateSubagentCallResult(params) {
3900
3929
  }
3901
3930
  const workflowLaunchObservers = new WeakMap();
3902
3931
  const workflowOwnedUsageBudgets = new WeakMap();
3932
+ const workflowRetainedRequiredExtensions = new WeakMap();
3903
3933
  /**
3904
3934
  * Terminal-mission retention can remove a mission while its children still
3905
3935
  * report. Remember it to prevent later heartbeats from warning repeatedly.
@@ -4087,7 +4117,11 @@ function workflowOutputPathMappingSummary(children) {
4087
4117
  return mappings.length > 0 ? ` Output path mappings: ${mappings.join("; ")}.` : "";
4088
4118
  }
4089
4119
  function workflowDetailsResults(children) {
4090
- return children.flatMap((child) => (child.results ?? []).map((result) => result.workflowKey ? result : { ...result, workflowKey: child.key }));
4120
+ return children.flatMap((child) => (child.results ?? []).map((result) => ({
4121
+ ...result,
4122
+ workflowKey: result.workflowKey ?? child.key,
4123
+ ...(child.runId && !result.runId ? { runId: child.runId } : {}),
4124
+ })));
4091
4125
  }
4092
4126
  function workflowSteerReceipt(key, result) {
4093
4127
  const steering = result.details.steering;
@@ -4625,6 +4659,7 @@ export function createSubagentExecutor(deps) {
4625
4659
  const execute = async (_id, params, signal, onUpdate, ctx, preserveActiveSession = false, parentModelOverride) => {
4626
4660
  const workflowLaunchObserver = workflowLaunchObservers.get(params);
4627
4661
  const inheritedUsageBudget = workflowOwnedUsageBudgets.get(params);
4662
+ const retainedRequiredExtensions = workflowRetainedRequiredExtensions.get(params);
4628
4663
  const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
4629
4664
  const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
4630
4665
  const delegatedExecution = delegatedExecutions.has(params);
@@ -4655,6 +4690,12 @@ export function createSubagentExecutor(deps) {
4655
4690
  ? currentParentModel
4656
4691
  : rememberParentModel(deps.state, resolveCurrentSessionId(ctx.sessionManager), currentParentModel)) ?? null;
4657
4692
  })();
4693
+ // validate checks args with the same normalization launch uses, reported beside script errors.
4694
+ const validateWorkflowRequest = () => {
4695
+ const validation = validateWorkflowScript(requestParams.workflowScript ?? "", workflowValidationOptions(deps, requestParams, ctx.cwd, () => resolveWorkflowParentModel()?.provider));
4696
+ const normalizedArgs = normalizeWorkflowArgs(requestParams.args);
4697
+ return "error" in normalizedArgs ? { ...validation, ok: false, errors: [...validation.errors, { message: normalizedArgs.error }] } : validation;
4698
+ };
4658
4699
  try {
4659
4700
  if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined) {
4660
4701
  throw new Error("preflight requires workflow: true or a workflow script path.");
@@ -4666,7 +4707,7 @@ export function createSubagentExecutor(deps) {
4666
4707
  catch (error) {
4667
4708
  const message = error instanceof Error ? error.message : String(error);
4668
4709
  if (requestParams.action?.trim() === "validate") {
4669
- const validation = validateWorkflowScript(requestParams.workflowScript ?? "", workflowValidationOptions(deps, requestParams, ctx.cwd, () => resolveWorkflowParentModel()?.provider));
4710
+ const validation = validateWorkflowRequest();
4670
4711
  const invalidValidation = { ...validation, ok: false, errors: [...validation.errors, { message }] };
4671
4712
  return {
4672
4713
  content: [{ type: "text", text: JSON.stringify(invalidValidation) }],
@@ -4677,8 +4718,7 @@ export function createSubagentExecutor(deps) {
4677
4718
  return buildRequestedModeError(requestParams, message);
4678
4719
  }
4679
4720
  if (requestParams.action?.trim() === "validate") {
4680
- const validation = validateWorkflowScript(requestParams.workflowScript ?? "", workflowValidationOptions(deps, requestParams, ctx.cwd, () => resolveWorkflowParentModel()?.provider));
4681
- return buildWorkflowValidationResult(validation, "management", workflowPreflight);
4721
+ return buildWorkflowValidationResult(validateWorkflowRequest(), "management", workflowPreflight);
4682
4722
  }
4683
4723
  const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
4684
4724
  if (normalizedAction === "resume" && requestParams.extensionBindings !== undefined)
@@ -4713,6 +4753,9 @@ export function createSubagentExecutor(deps) {
4713
4753
  const acceptanceErrors = validateAcceptanceInput(requestParams.acceptance);
4714
4754
  if (acceptanceErrors.length > 0)
4715
4755
  return buildRequestedModeError(requestParams, acceptanceErrors.join(" "));
4756
+ // Capture mandatory host extensions at admission: script children may launch after the host disposes its registration.
4757
+ const admittedRequiredExtensions = retainedRequiredExtensions ?? deps.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(ctx.sessionManager.getSessionId() ?? undefined);
4758
+ const workflowRequiredExtensions = hasMandatoryRequiredChildExtensions(admittedRequiredExtensions) ? admittedRequiredExtensions : undefined;
4716
4759
  const foregroundWorkflowRunId = encodeIndexSegment(_id);
4717
4760
  if (delegatedWorkflowPermit) {
4718
4761
  const permitError = validateWorkflowChildPermitRoot(delegatedWorkflowPermit, foregroundWorkflowRunId);
@@ -5588,6 +5631,8 @@ export function createSubagentExecutor(deps) {
5588
5631
  preparedChildParams = childRequest;
5589
5632
  if (workflowUsageBudget.budget)
5590
5633
  workflowOwnedUsageBudgets.set(childRequest, workflowUsageBudget.budget);
5634
+ if (workflowRequiredExtensions)
5635
+ workflowRetainedRequiredExtensions.set(childRequest, workflowRequiredExtensions);
5591
5636
  workflowLaunchObservers.set(childRequest, (launch) => {
5592
5637
  if (launch.runId)
5593
5638
  appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: launch.runId });
@@ -5910,6 +5955,8 @@ export function createSubagentExecutor(deps) {
5910
5955
  preparedChildParams = childRequest;
5911
5956
  if (workflowUsageBudget.budget)
5912
5957
  workflowOwnedUsageBudgets.set(childRequest, workflowUsageBudget.budget);
5958
+ if (workflowRequiredExtensions)
5959
+ workflowRetainedRequiredExtensions.set(childRequest, workflowRequiredExtensions);
5913
5960
  if (delegatedWorkflowPermit) {
5914
5961
  if (childRequest.async !== false)
5915
5962
  throw new Error("Workflow child permit supports one foreground child only.");
@@ -6089,11 +6136,35 @@ export function createSubagentExecutor(deps) {
6089
6136
  if (deps.allowMutatingManagementActions === false) {
6090
6137
  return { content: [{ type: "text", text: "Action 'worktree.cleanup' is not available from child-safe subagent fanout mode." }], isError: true, details: { mode: "management", results: [] } };
6091
6138
  }
6139
+ if (paramsWithResolvedCwd.mode === "apply") {
6140
+ try {
6141
+ if (!paramsWithResolvedCwd.repo?.trim() || !paramsWithResolvedCwd.planId?.trim() || paramsWithResolvedCwd.handoffPath !== undefined)
6142
+ throw new Error("worktree.cleanup apply requires explicit repo and planId, without handoffPath.");
6143
+ const repo = path.resolve(requestCwd, paramsWithResolvedCwd.repo);
6144
+ const plan = loadReviewedCleanupPlan(repo, paramsWithResolvedCwd.planId);
6145
+ const decision = resolveAuthorityDecision({ action: "discardWorktree", ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
6146
+ if (decision === "forbid")
6147
+ throw new Error("Authority policy forbids worktree cleanup.");
6148
+ if (decision === "confirm") {
6149
+ if (!ctx.hasUI)
6150
+ throw new Error("Authority policy requires interactive confirmation for worktree cleanup.");
6151
+ if (!await ctx.ui.confirm("Apply reviewed worktree cleanup?", `Repository: ${plan.repoRoot}\nPlan: ${plan.planId}\nRemove up to ${plan.entries.filter((entry) => entry.decision === "remove").length} eligible worktrees. All local branches are retained.`))
6152
+ return { content: [{ type: "text", text: "Worktree cleanup canceled." }], details: { mode: "management", results: [] } };
6153
+ }
6154
+ const applied = await applyReviewedCleanupPlan({ repo: plan.repoRoot, planId: plan.planId, authorized: true, signal,
6155
+ foregroundRunOwnership: (runId) => deps.state.foregroundControls.has(runId) ? "active" : (deps.state.foregroundRuns?.get(runId)?.children.length && deps.state.foregroundRuns.get(runId).children.every((child) => child.status !== "detached")) ? "terminal" : "unknown",
6156
+ });
6157
+ return { content: [{ type: "text", text: formatCleanupReceipt(applied) }], details: { mode: "management", results: [] } };
6158
+ }
6159
+ catch (error) {
6160
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
6161
+ }
6162
+ }
6092
6163
  if (paramsWithResolvedCwd.mode !== "plan") {
6093
- return { content: [{ type: "text", text: "worktree.cleanup currently supports mode='plan' only; apply/removal is not available yet." }], isError: true, details: { mode: "management", results: [] } };
6164
+ return { content: [{ type: "text", text: "worktree.cleanup requires mode='plan' or mode='apply'." }], isError: true, details: { mode: "management", results: [] } };
6094
6165
  }
6095
6166
  if (paramsWithResolvedCwd.planId !== undefined) {
6096
- return { content: [{ type: "text", text: "worktree.cleanup plan mode does not accept planId; apply is not available yet." }], isError: true, details: { mode: "management", results: [] } };
6167
+ return { content: [{ type: "text", text: "worktree.cleanup plan mode does not accept planId." }], isError: true, details: { mode: "management", results: [] } };
6097
6168
  }
6098
6169
  try {
6099
6170
  const created = createWorktreeCleanupPlan({
@@ -6178,7 +6249,7 @@ export function createSubagentExecutor(deps) {
6178
6249
  return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
6179
6250
  }
6180
6251
  }
6181
- const policyAction = action === "stop" ? "stopRun" : action === "steer" ? "steerRun" : action === "schedule.create" ? "scheduleCreate" : action === "inspector.open" ? "inspectorOpen" : action === "project.open" ? "projectOpen" : undefined;
6252
+ const policyAction = action === "stop" || action === "command.cancel" ? "stopRun" : action === "steer" || action === "command.yield" ? "steerRun" : action === "schedule.create" ? "scheduleCreate" : action === "inspector.open" ? "inspectorOpen" : action === "project.open" ? "projectOpen" : undefined;
6182
6253
  if (policyAction) {
6183
6254
  // Child-safe mode is a hard capability boundary; the policy is an operator
6184
6255
  // preference. Refuse first, so the gate never prompts for an action that is
@@ -6402,6 +6473,23 @@ export function createSubagentExecutor(deps) {
6402
6473
  details: { mode: "management", results: [], spawnBudget, activeAsyncCapacity },
6403
6474
  };
6404
6475
  }
6476
+ if (["command.status", "command.yield", "command.cancel"].includes(action)) {
6477
+ try {
6478
+ if (deps.allowMutatingManagementActions === false && action !== "command.status")
6479
+ throw new Error(`Action '${action}' is not available from child-safe subagent fanout mode.`);
6480
+ const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
6481
+ if (!targetRunId || paramsWithResolvedCwd.dir)
6482
+ throw new Error(`${action} requires id or runId; directory targets are not supported.`);
6483
+ deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
6484
+ const target = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
6485
+ if (!target)
6486
+ throw new Error(`No run found for '${targetRunId}'.`);
6487
+ return await commandAction({ state: deps.state, target, operation: action.slice("command.".length), index: paramsWithResolvedCwd.index, toolCallId: paramsWithResolvedCwd.toolCallId, signal });
6488
+ }
6489
+ catch (error) {
6490
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
6491
+ }
6492
+ }
6405
6493
  if (action === "status" || action === "debug.run") {
6406
6494
  if (!preserveActiveSession)
6407
6495
  deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
@@ -7088,6 +7176,7 @@ export function createSubagentExecutor(deps) {
7088
7176
  toolBudget: runToolBudget.toolBudget,
7089
7177
  usageBudget: usageBudget.budget,
7090
7178
  inheritedUsageBudget,
7179
+ requiredExtensions: retainedRequiredExtensions,
7091
7180
  allowZeroToolBudget,
7092
7181
  configToolBudget: configToolBudget.toolBudget,
7093
7182
  configToolTimeoutMs: deps.config.toolTimeoutMs,
@@ -7373,7 +7462,9 @@ export function createSubagentExecutor(deps) {
7373
7462
  }
7374
7463
  else if (workflow !== undefined || publicParams.workflowScript !== undefined) {
7375
7464
  const normalizedArgs = normalizeWorkflowArgs(publicParams.args);
7376
- if ("error" in normalizedArgs)
7465
+ // validate reports args errors with its script errors; launch rejects them here.
7466
+ const validateOnly = publicParams.action === "validate";
7467
+ if ("error" in normalizedArgs && !validateOnly)
7377
7468
  return errorResult(normalizedArgs.error);
7378
7469
  let workflowScript = publicParams.workflowScript;
7379
7470
  if (workflow !== undefined) {
@@ -7383,7 +7474,7 @@ export function createSubagentExecutor(deps) {
7383
7474
  workflowScript = source.script;
7384
7475
  }
7385
7476
  const { workflow: _workflow, ...withoutWorkflowSource } = publicParams;
7386
- publicParams = { ...withoutWorkflowSource, workflowScript, args: deepFreezeWorkflowArgs(normalizedArgs.args) };
7477
+ publicParams = { ...withoutWorkflowSource, workflowScript, args: "error" in normalizedArgs ? publicParams.args : deepFreezeWorkflowArgs(normalizedArgs.args) };
7387
7478
  }
7388
7479
  publicExecutions.add(publicParams);
7389
7480
  return executeWithSingleDispatchGuard(id, publicParams, signal, onUpdate, ctx);