pi-subagents 0.66.0 → 0.68.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 (172) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/README.md +5 -4
  3. package/agents/evidence-auditor.md +34 -0
  4. package/agents/reviewer.md +3 -2
  5. package/docs/agents.md +43 -15
  6. package/docs/configuration.md +67 -23
  7. package/docs/extension-api.md +38 -19
  8. package/docs/missions.md +10 -2
  9. package/docs/models.md +11 -79
  10. package/docs/observability.md +22 -12
  11. package/docs/standalone-background.md +59 -0
  12. package/docs/tool-reference.md +33 -20
  13. package/docs/watchdog.md +39 -10
  14. package/docs/workflows.md +37 -13
  15. package/index.ts +5 -2
  16. package/inspector-runner.mjs +2 -2
  17. package/package.json +4 -2
  18. package/prompts/parallel-review.md +1 -1
  19. package/runner-peer-loader.mjs +24 -0
  20. package/runner-peer-preload.mjs +32 -0
  21. package/skills/pi-subagents/SKILL.md +32 -21
  22. package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
  23. package/skills/pi-subagents/references/execution-controls.md +11 -9
  24. package/skills/pi-subagents/references/management-authoring-rpc.md +0 -1
  25. package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
  26. package/skills/pi-subagents/references/prompting-and-roles.md +17 -13
  27. package/skills/pi-subagents/references/review-and-validation.md +3 -3
  28. package/src/agents/advertised-agent-prompt.ts +34 -3
  29. package/src/agents/agent-management.ts +57 -58
  30. package/src/agents/agent-serializer.ts +4 -3
  31. package/src/agents/agents.ts +190 -71
  32. package/src/agents/builtin-names.ts +1 -0
  33. package/src/agents/chain-serializer.ts +5 -0
  34. package/src/agents/runtime-agent-registry.ts +7 -6
  35. package/src/api/delegation.ts +4 -0
  36. package/src/api/preflight.ts +94 -59
  37. package/src/api/required-child-extensions.ts +6 -0
  38. package/src/api/shared-types.ts +2 -0
  39. package/src/extension/config.ts +10 -37
  40. package/src/extension/fanout-child.ts +66 -4
  41. package/src/extension/herdr-pi-bridge.ts +160 -0
  42. package/src/extension/index.ts +62 -39
  43. package/src/extension/public-execution.ts +7 -5
  44. package/src/extension/rpc.ts +4 -0
  45. package/src/extension/schemas.ts +81 -81
  46. package/src/extension/tool-description.ts +30 -82
  47. package/src/inspectors/actions.ts +148 -0
  48. package/src/inspectors/ghostty/actions.ts +74 -0
  49. package/src/inspectors/ghostty/plugin.ts +17 -0
  50. package/src/inspectors/herdr/actions.ts +99 -179
  51. package/src/inspectors/herdr/plugin.ts +20 -0
  52. package/src/inspectors/herdr/project-panes.ts +1 -1
  53. package/src/inspectors/{herdr/inspector-runner.ts → inspector-runner.ts} +12 -12
  54. package/src/inspectors/plugins.ts +8 -0
  55. package/src/inspectors/{herdr/session-roots-codec.ts → session-roots-codec.ts} +3 -14
  56. package/src/inspectors/types.ts +51 -0
  57. package/src/intercom/intercom-bridge.ts +50 -8
  58. package/src/intercom/native-supervisor-channel.ts +44 -31
  59. package/src/policy/authority.ts +4 -0
  60. package/src/profiles/profiles.ts +12 -6
  61. package/src/runs/background/active-async-capacity.ts +4 -0
  62. package/src/runs/background/active-run-index.ts +17 -1
  63. package/src/runs/background/async-execution.ts +348 -176
  64. package/src/runs/background/async-job-tracker.ts +8 -6
  65. package/src/runs/background/async-resume.ts +17 -12
  66. package/src/runs/background/async-status.ts +15 -4
  67. package/src/runs/background/auto-drain.ts +23 -10
  68. package/src/runs/background/binary-bootstrap.ts +38 -0
  69. package/src/runs/background/chain-append.ts +1 -1
  70. package/src/runs/background/chain-root-attachment.ts +14 -33
  71. package/src/runs/background/fleet-view.ts +30 -2
  72. package/src/runs/background/notify.ts +105 -7
  73. package/src/runs/background/owned-process-tree.ts +29 -2
  74. package/src/runs/background/result-files.ts +8 -4
  75. package/src/runs/background/result-watcher.ts +19 -2
  76. package/src/runs/background/run-child-session.ts +81 -33
  77. package/src/runs/background/run-status.ts +3 -0
  78. package/src/runs/background/runner-aliases.ts +12 -33
  79. package/src/runs/background/runner-child-launch.ts +6 -1
  80. package/src/runs/background/runner-child-sessions.ts +5 -4
  81. package/src/runs/background/runner-http-dispatcher.ts +119 -0
  82. package/src/runs/background/scheduled-runs.ts +51 -18
  83. package/src/runs/background/stale-run-reconciler.ts +35 -11
  84. package/src/runs/background/steering.ts +20 -2
  85. package/src/runs/background/subagent-runner.ts +441 -304
  86. package/src/runs/background/subagent-wait.ts +176 -28
  87. package/src/runs/background/wait-completions.ts +75 -27
  88. package/src/runs/background/wait-subscriptions.ts +9 -3
  89. package/src/runs/background/wait-tool.ts +5 -3
  90. package/src/runs/foreground/async-steering-action.ts +18 -7
  91. package/src/runs/foreground/async-stop-action.ts +93 -3
  92. package/src/runs/foreground/execution.ts +134 -248
  93. package/src/runs/foreground/foreground-history.ts +2 -1
  94. package/src/runs/foreground/prompt-audit.ts +3 -1
  95. package/src/runs/foreground/subagent-executor.ts +374 -178
  96. package/src/runs/foreground/workflow-detach-reconcile.ts +2 -0
  97. package/src/runs/foreground/workflow-foreground-steering.ts +2 -1
  98. package/src/runs/shared/acceptance.ts +38 -11
  99. package/src/runs/shared/async-status-projection.ts +127 -33
  100. package/src/runs/shared/capability-ceiling.ts +2 -0
  101. package/src/runs/shared/child-hooks.ts +25 -10
  102. package/src/runs/shared/child-launch-plan.ts +15 -3
  103. package/src/runs/shared/child-launch.ts +28 -5
  104. package/src/runs/shared/child-lifecycle.ts +6 -3
  105. package/src/runs/shared/child-runtime-config.ts +8 -1
  106. package/src/runs/shared/child-session.ts +127 -52
  107. package/src/runs/shared/child-tool-plan.ts +142 -11
  108. package/src/runs/shared/completion-guard.ts +5 -3
  109. package/src/runs/shared/dynamic-fanout.ts +2 -2
  110. package/src/runs/shared/effective-system-prompt.ts +33 -0
  111. package/src/runs/shared/external-cli-contract.ts +11 -1
  112. package/src/runs/shared/external-cli-preflight.ts +6 -2
  113. package/src/runs/shared/external-cli-runner.ts +9 -7
  114. package/src/runs/shared/herdr-connection.ts +134 -0
  115. package/src/runs/shared/herdr-external-adapters.ts +169 -0
  116. package/src/runs/shared/herdr-machine.ts +279 -0
  117. package/src/runs/shared/herdr-pi-protocol.ts +59 -0
  118. package/src/runs/shared/herdr-placed-run.ts +263 -0
  119. package/src/runs/shared/llm-intent-arbiter.ts +12 -3
  120. package/src/runs/shared/model-resolution-diagnostic.ts +76 -0
  121. package/src/runs/shared/{model-fallback.ts → model-resolution.ts} +22 -235
  122. package/src/runs/shared/model-scope.ts +1 -1
  123. package/src/runs/shared/nested-events.ts +11 -2
  124. package/src/runs/shared/orca-progress-tabs.ts +1 -1
  125. package/src/runs/shared/parallel-utils.ts +7 -2
  126. package/src/runs/shared/pi-spawn.ts +10 -0
  127. package/src/runs/shared/subagent-prompt-runtime.ts +12 -4
  128. package/src/runs/shared/task-intent.ts +46 -13
  129. package/src/runs/shared/workflow-async-child-guidance.ts +18 -0
  130. package/src/runs/shared/worktree-setup-command.ts +27 -4
  131. package/src/runs/shared/worktree.ts +45 -15
  132. package/src/shared/child-cache-retention.ts +43 -0
  133. package/src/shared/fork-context.ts +15 -72
  134. package/src/shared/launch-contract.ts +68 -8
  135. package/src/shared/opencode-session-headers.ts +30 -0
  136. package/src/shared/pruned-fork.ts +1 -1
  137. package/src/shared/required-child-extensions.ts +81 -0
  138. package/src/shared/settings.ts +5 -2
  139. package/src/shared/shortcuts.ts +0 -4
  140. package/src/shared/types.ts +74 -30
  141. package/src/slash/delegation-adapters.ts +3 -1
  142. package/src/slash/delegation-request.ts +14 -0
  143. package/src/slash/slash-commands.ts +2 -7
  144. package/src/slash/subagents-admin.ts +24 -13
  145. package/src/tui/fleet-status.ts +164 -19
  146. package/src/tui/fleet.ts +16 -14
  147. package/src/tui/render.ts +168 -37
  148. package/src/watchdog/child-status.ts +28 -28
  149. package/src/watchdog/lsp-diagnostics.ts +1 -1
  150. package/src/watchdog/model-selection.ts +21 -1
  151. package/src/watchdog/permission-arbiter.ts +3 -1
  152. package/src/watchdog/register-child.ts +10 -2
  153. package/src/watchdog/register-main.ts +39 -35
  154. package/src/watchdog/render.ts +1 -1
  155. package/src/watchdog/review.ts +123 -74
  156. package/src/watchdog/rules.ts +1 -1
  157. package/src/watchdog/runtime.ts +100 -27
  158. package/src/watchdog/scope.ts +1 -1
  159. package/src/watchdog/settings.ts +3 -0
  160. package/src/watchdog/tool-actions.ts +13 -12
  161. package/src/watchdog/turn-delta.ts +23 -0
  162. package/src/watchdog/types.ts +5 -3
  163. package/src/watchdog/warning-format.ts +1 -1
  164. package/src/workflows/scripted-workflow.ts +279 -10
  165. package/src/workflows/workflow-checklist.ts +2 -2
  166. package/src/workflows/workflow-receipt.ts +21 -3
  167. package/src/workflows/workflow-resources.ts +13 -2
  168. package/runner-server-preload.mjs +0 -13
  169. package/src/runs/shared/model-exclusions.ts +0 -374
  170. package/src/runs/shared/readonly-model-continuation.ts +0 -69
  171. package/src/runs/shared/readonly-session-evidence.ts +0 -307
  172. /package/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +0 -0
@@ -28,7 +28,10 @@ import { runSync } from "./execution.ts";
28
28
  import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
29
29
  import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
30
30
  import { applyWatchdogLaunchRules } from "../../watchdog/rules.ts";
31
- import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, type ModelOrigin, type ParentModel } from "../shared/model-fallback.ts";
31
+ import { childWatchdogProgressForModel } from "../../watchdog/child-status.ts";
32
+ import { normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, resolveModelSelection, type ModelOrigin, type ParentModel } from "../shared/model-resolution.ts";
33
+ import { getHostBuiltinToolNames } from "../shared/child-tool-plan.ts";
34
+ import { projectChainOutputSchemas, resolveEffectiveOutputSchema } from "../shared/child-launch-plan.ts";
32
35
  import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
33
36
  import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
34
37
  import { recordRun } from "../shared/run-history.ts";
@@ -52,8 +55,8 @@ import { isScheduledRunAction } from "../background/scheduled-runs.ts";
52
55
  import { encodeIndexSegment } from "../background/index-segment.ts";
53
56
  import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
54
57
  import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
55
- import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateAcceptanceInput, validateExecutionAcceptance } from "../shared/acceptance.ts";
56
- import { canPreferFork, createForkContextResolver, forkedChildRequiresThinkingOff, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
58
+ import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateAcceptanceInput, validateExecutionAcceptance, validateExecutionAcceptancePolicy } from "../shared/acceptance.ts";
59
+ import { canPreferFork, createForkContextResolver, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
57
60
  import { createPrunedForkSessionWriter } from "../../shared/pruned-fork.ts";
58
61
  import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
59
62
  import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
@@ -67,6 +70,7 @@ import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig
67
70
  import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
68
71
  import { isAgentContract } from "../shared/agent-contract.ts";
69
72
  import { normalizeExtensionBindings, type ExtensionBindings } from "../shared/extension-bindings.ts";
73
+ import { resolveRequiredChildExtensions } from "../../shared/required-child-extensions.ts";
70
74
  import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
71
75
  import { assertJsonSchemaObject, cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
72
76
  import { compactForegroundDetails, getSingleResultOutput, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.ts";
@@ -95,7 +99,7 @@ import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
95
99
  import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletion } from "./workflow-detach-reconcile.ts";
96
100
  import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
97
101
  import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
98
- import { resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
102
+ import { fallbackResultPayloadPathForSessionRun, removeResultIndex, resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
99
103
  import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, resolveNestedAsyncDir, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedParentAddress, type NestedRoute, type NestedRunResolutionScope } from "../shared/nested-events.ts";
100
104
  import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
101
105
  import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
@@ -105,6 +109,7 @@ import { inspectSubagentStatus } from "../background/run-status.ts";
105
109
  import { getExternalJobProvider } from "../../api/external-job-provider.ts";
106
110
  import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.ts";
107
111
  import { externalCliReceiptMetadata, normalizeExternalCliRunnerStatus } from "../shared/external-cli-contract.ts";
112
+ import { formatHerdrMachineRunnerUnsupported, resolveHerdrMachinePlacement } from "../shared/herdr-machine.ts";
108
113
  import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
109
114
  import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
110
115
  import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
@@ -112,9 +117,11 @@ import { MissionNotFoundError, updateMission } from "../../missions/store.ts";
112
117
  import type { MissionWorkflowChildUpdate } from "../../missions/types.ts";
113
118
  import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
114
119
  import { resolveAuthorityDecision } from "../../policy/authority.ts";
115
- import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
120
+ import { handleInspectorAction, INSPECTOR_ACTIONS } from "../../inspectors/actions.ts";
121
+ import { createBuiltinInspectorPlugins } from "../../inspectors/plugins.ts";
116
122
  import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
117
- import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowLanePlan, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowScriptTraceEntry, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
123
+ import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowChildSettledNotification, type WorkflowLanePlan, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowScriptTraceEntry, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
124
+ import { formatIncrementalChildCompletion, incrementalChildCompletionTriggersTurn } from "../background/notify.ts";
118
125
  import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath, type WorkflowHostCommandParams, type WorkflowHostCommandResult } from "../../workflows/host-command.ts";
119
126
  import { buildWorkflowReceipt, readWorkflowReceipt, workflowReceiptPath, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
120
127
  import { upsertHostStep, validHostStepNodes } from "../shared/host-step-status.ts";
@@ -132,10 +139,12 @@ import {
132
139
  type WorkflowResourceAuthority,
133
140
  type WorkflowResourcePermit,
134
141
  } from "../../shared/workflow-child-permit.ts";
135
- import { resolveWorkflowResource } from "../../workflows/workflow-resources.ts";
142
+ import { deepFreezeWorkflowArgs, normalizeWorkflowArgs, resolveWorkflowResource } from "../../workflows/workflow-resources.ts";
143
+ import { stableJsonDigest } from "../../shared/launch-contract.ts";
136
144
  import {
137
145
  cleanupWorktrees,
138
146
  createWorktrees,
147
+ preflightWorktreeSource,
139
148
  withWorktreeTransaction,
140
149
  type WorktreeSetupProgress,
141
150
  diffWorktrees,
@@ -283,6 +292,7 @@ interface TaskParam {
283
292
  agent: string;
284
293
  task: string;
285
294
  cwd?: string;
295
+ machine?: string;
286
296
  count?: number;
287
297
  output?: string | boolean;
288
298
  outputMode?: "inline" | "file-only";
@@ -291,7 +301,7 @@ interface TaskParam {
291
301
  model?: string;
292
302
  fast?: boolean;
293
303
  skill?: string | string[] | boolean;
294
- outputSchema?: JsonSchemaObject;
304
+ outputSchema?: JsonSchemaObject | false;
295
305
  acceptance?: AcceptanceInput;
296
306
  agentContract?: AgentContract;
297
307
  toolBudget?: ToolBudgetConfig;
@@ -367,6 +377,8 @@ export interface SubagentParamsLike {
367
377
  foregroundOnly?: boolean;
368
378
  timeoutMs?: number;
369
379
  maxRuntimeMs?: number;
380
+ /** Async runs only: steer the child to checkpoint and stop this many ms before the run deadline. */
381
+ checkpointBeforeDeadlineMs?: number;
370
382
  /** Optional hard per-tool-call timeout (ms). Known-fast tools also have a default. */
371
383
  toolTimeoutMs?: number;
372
384
  toolBudget?: ToolBudgetConfig;
@@ -376,6 +388,9 @@ export interface SubagentParamsLike {
376
388
  control?: ControlConfig;
377
389
  sessionDir?: string;
378
390
  cwd?: string;
391
+ machine?: string;
392
+ /** Internal: the typed cwd when a machine is set, kept as a remote path and never resolved locally. */
393
+ machineCwd?: string;
379
394
  maxOutput?: MaxOutputConfig;
380
395
  artifacts?: boolean;
381
396
  includeProgress?: boolean;
@@ -395,7 +410,7 @@ export interface SubagentParamsLike {
395
410
  /** Internal-only; not part of the public tool schema. Wired for single-run reads (chain steps use their own field). */
396
411
  reads?: string[] | false;
397
412
  outputMode?: "inline" | "file-only";
398
- outputSchema?: JsonSchemaObject;
413
+ outputSchema?: JsonSchemaObject | false;
399
414
  agentScope?: unknown;
400
415
  chainDir?: string;
401
416
  acceptance?: AcceptanceInput;
@@ -404,6 +419,7 @@ export interface SubagentParamsLike {
404
419
  at?: string;
405
420
  every?: string;
406
421
  sessionOnly?: boolean;
422
+ quiet?: boolean;
407
423
  on?: string | number;
408
424
  timezone?: string;
409
425
  overlap?: "skip";
@@ -466,7 +482,7 @@ function inheritedRunFanoutBudget(deps: Pick<ExecutorDeps, "childRuntime">): Run
466
482
 
467
483
  type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => string | undefined;
468
484
  type PrepareForkSessionForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => Promise<void>;
469
- type ForkThinkingOverrideForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => AgentConfig["thinking"] | undefined;
485
+ type ThinkingOverrideForTask = () => AgentConfig["thinking"] | undefined;
470
486
 
471
487
  interface ExecutionContextData {
472
488
  params: SubagentParamsLike;
@@ -486,7 +502,7 @@ interface ExecutionContextData {
486
502
  sessionDirForIndex: (idx?: number) => string;
487
503
  sessionFileForIndex: (idx?: number) => string | undefined;
488
504
  sessionFileForTask: ForkSessionFileForTask;
489
- thinkingOverrideForTask: ForkThinkingOverrideForTask;
505
+ thinkingOverrideForTask: ThinkingOverrideForTask;
490
506
  artifactConfig: ArtifactConfig;
491
507
  artifactsDir: string;
492
508
  backgroundRequestedWhileClarifying: boolean;
@@ -741,7 +757,7 @@ function foregroundChildActivityFromProgress(progress: SingleResult["progress"]
741
757
  };
742
758
  }
743
759
 
744
- function rememberForegroundRun(state: SubagentState, input: { modelResponseAliases?: Record<string, string[]>; runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; params: SubagentParamsLike; effectiveOutput?: string | boolean; effectiveOutputMode: OutputMode; extensionBindings?: ExtensionBindings }): void {
760
+ function rememberForegroundRun(state: SubagentState, input: { modelResponseAliases?: Record<string, string[]>; runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; params: SubagentParamsLike; effectiveOutput?: string | boolean; effectiveOutputMode: OutputMode; extensionBindings?: ExtensionBindings; requiredExtensions?: SteeringRecoveryDescriptor["requiredExtensions"] }): void {
745
761
  state.foregroundRuns ??= new Map();
746
762
  const previous = state.foregroundRuns.get(input.runId);
747
763
  const updatedAt = Date.now();
@@ -794,6 +810,7 @@ function rememberForegroundRun(state: SubagentState, input: { modelResponseAlias
794
810
  ...(Object.keys(resumeContract).length ? { resumeContract } : {}),
795
811
  ...(result.launchContractDigest ? { launchContractDigest: result.launchContractDigest } : {}),
796
812
  ...(input.extensionBindings ? { extensionBindings: input.extensionBindings } : {}),
813
+ ...(input.requiredExtensions?.length ? { requiredExtensions: input.requiredExtensions } : {}),
797
814
  ...(result.launchResolvedExtensions ? { launchResolvedExtensions: result.launchResolvedExtensions } : {}),
798
815
  ...(result.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: result.runtimeAcknowledgedExtensions } : {}),
799
816
  ...(result.capabilityCeiling ? { capabilityCeiling: result.capabilityCeiling } : {}),
@@ -945,6 +962,7 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
945
962
  ...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
946
963
  ...(child.resumeContract ? { resumeContract: child.resumeContract } : {}),
947
964
  ...(child.extensionBindings ? { extensionBindings: normalizeExtensionBindings(child.extensionBindings)!.value } : {}),
965
+ ...(child.requiredExtensions ? { requiredExtensions: child.requiredExtensions } : {}),
948
966
  ...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
949
967
  };
950
968
  }
@@ -1235,10 +1253,14 @@ function appendStepToAsyncChain(input: {
1235
1253
  };
1236
1254
  }
1237
1255
  const chain = [input.params.step];
1238
- const acceptanceErrors = validateExecutionAcceptance({ ...input.params, chain } as Parameters<typeof validateExecutionAcceptance>[0]);
1239
- if (acceptanceErrors.length > 0) {
1256
+ const validationParams = { ...input.params, chain } as SubagentParamsLike;
1257
+ const acceptancePolicyErrors = validateExecutionAcceptancePolicy(validationParams);
1258
+ const outputSchemaError = validateLaunchOutputSchemaOverrides(validationParams);
1259
+ const earlyErrors = [...acceptancePolicyErrors];
1260
+ if (outputSchemaError) earlyErrors.push(outputSchemaError);
1261
+ if (earlyErrors.length > 0) {
1240
1262
  return {
1241
- content: [{ type: "text", text: `Cannot append step: ${acceptanceErrors.join(" ")}` }],
1263
+ content: [{ type: "text", text: `Cannot append step: ${earlyErrors.join(" ")}` }],
1242
1264
  isError: true,
1243
1265
  details: { mode: "management", results: [] },
1244
1266
  };
@@ -1320,6 +1342,14 @@ function appendStepToAsyncChain(input: {
1320
1342
  const scope: AgentScope = resolveExecutionAgentScope(input.params.agentScope);
1321
1343
  const discoveredForAppend = input.deps.discoverAgents(input.requestCwd, scope, input.parentModel?.provider);
1322
1344
  const agents = discoveredForAppend.agents;
1345
+ const acceptanceErrors = validateExecutionAcceptance(projectEffectiveAcceptanceSchemas(validationParams, agents));
1346
+ if (acceptanceErrors.length > 0) {
1347
+ return {
1348
+ content: [{ type: "text", text: `Cannot append step: ${acceptanceErrors.join(" ")}` }],
1349
+ isError: true,
1350
+ details: { mode: "management", results: [] },
1351
+ };
1352
+ }
1323
1353
  const contextPolicy = resolveExplicitContextPolicy(input.params);
1324
1354
  const chainSkillInput = normalizeSkillInput(input.params.skill);
1325
1355
  const chainSkills = chainSkillInput === false ? [] : (chainSkillInput ?? []);
@@ -1700,7 +1730,7 @@ async function resumeExternalJobFollowUp(input: {
1700
1730
  ...input.baseAgentConfig,
1701
1731
  runner: { type: "external-job", provider: runner.provider, options: runner.options },
1702
1732
  };
1703
- const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
1733
+ const result = await executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
1704
1734
  agent: input.target.agent,
1705
1735
  task: input.followUp,
1706
1736
  goal: input.followUp,
@@ -1797,13 +1827,13 @@ async function resumeAsyncRun(input: {
1797
1827
  details: { mode: "management", results: [] },
1798
1828
  };
1799
1829
  }
1800
- const acceptanceErrors = validateExecutionAcceptance(input.params as Parameters<typeof validateExecutionAcceptance>[0]);
1801
- if (acceptanceErrors.length > 0) {
1802
- return {
1803
- content: [{ type: "text", text: `Cannot resume: ${acceptanceErrors.join(" ")}` }],
1804
- isError: true,
1805
- details: { mode: "management", results: [] },
1806
- };
1830
+ const acceptancePolicyErrors = validateExecutionAcceptancePolicy(input.params);
1831
+ if (acceptancePolicyErrors.length > 0) {
1832
+ return { content: [{ type: "text", text: `Cannot resume: ${acceptancePolicyErrors.join(" ")}` }], isError: true, details: { mode: "management", results: [] } };
1833
+ }
1834
+ const outputSchemaError = validateLaunchOutputSchemaOverrides(input.params);
1835
+ if (outputSchemaError) {
1836
+ return { content: [{ type: "text", text: `Cannot resume: ${outputSchemaError}` }], isError: true, details: { mode: "management", results: [] } };
1807
1837
  }
1808
1838
  input.deps.state.currentSessionId = resolveCurrentSessionId(input.ctx.sessionManager);
1809
1839
 
@@ -1920,6 +1950,10 @@ async function resumeAsyncRun(input: {
1920
1950
  details: { mode: "chain", results: [] },
1921
1951
  };
1922
1952
  }
1953
+ const acceptanceErrors = validateExecutionAcceptance(projectEffectiveAcceptanceSchemas(input.params, agents));
1954
+ if (acceptanceErrors.length > 0) {
1955
+ return { content: [{ type: "text", text: `Cannot resume: ${acceptanceErrors.join(" ")}` }], isError: true, details: { mode: "chain", results: [] } };
1956
+ }
1923
1957
  const runId = randomUUID();
1924
1958
  const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
1925
1959
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
@@ -1988,6 +2022,7 @@ async function resumeAsyncRun(input: {
1988
2022
  worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
1989
2023
  worktreeBaseDir: input.deps.config.worktreeBaseDir,
1990
2024
  baseRef: input.params.baseRef ?? target.recoveryDescriptor?.baseRef,
2025
+ ...(recoveryDescriptor?.requiredExtensions ? { requiredExtensions: recoveryDescriptor.requiredExtensions } : {}),
1991
2026
  worktreeProvider: input.deps.config.worktreeProvider,
1992
2027
  worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
1993
2028
  controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
@@ -2027,6 +2062,23 @@ async function resumeAsyncRun(input: {
2027
2062
  if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
2028
2063
  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: [] } };
2029
2064
  }
2065
+ const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(baseAgentConfig, recoveryDescriptor) : baseAgentConfig;
2066
+ const agentConfig = intercomBridge.active ? applyIntercomBridgeToAgent(recoveryAgentConfig, intercomBridge) : recoveryAgentConfig;
2067
+ const foregroundContract = target.source === "foreground" ? target.resumeContract : undefined;
2068
+ const outputSchema = Object.hasOwn(input.params, "outputSchema")
2069
+ ? input.params.outputSchema
2070
+ : Object.hasOwn(foregroundContract ?? {}, "outputSchema")
2071
+ ? foregroundContract?.outputSchema
2072
+ : recoveryDescriptor?.structuredOutputSchema;
2073
+ const acceptance = input.params.acceptance !== undefined
2074
+ ? input.params.acceptance
2075
+ : foregroundContract?.acceptance !== undefined
2076
+ ? foregroundContract.acceptance
2077
+ : recoveryDescriptor?.acceptance;
2078
+ const acceptanceErrors = validateExecutionAcceptance({ ...input.params, acceptance, outputSchema });
2079
+ if (acceptanceErrors.length > 0) {
2080
+ return { content: [{ type: "text", text: `Cannot resume: ${acceptanceErrors.join(" ")}` }], isError: true, details: { mode: "management", results: [] } };
2081
+ }
2030
2082
  const runId = randomUUID();
2031
2083
  const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
2032
2084
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
@@ -2050,17 +2102,13 @@ async function resumeAsyncRun(input: {
2050
2102
  if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
2051
2103
  return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "single", results: [] } };
2052
2104
  }
2053
- const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(baseAgentConfig, recoveryDescriptor) : baseAgentConfig;
2054
- const agentConfig = intercomBridge.active ? applyIntercomBridgeToAgent(recoveryAgentConfig, intercomBridge) : recoveryAgentConfig;
2055
- const foregroundContract = target.source === "foreground" ? target.resumeContract : undefined;
2056
- const outputSchema = input.params.outputSchema ?? foregroundContract?.outputSchema ?? recoveryDescriptor?.structuredOutputSchema;
2057
2105
  const agentContract = input.params.agentContract ?? foregroundContract?.agentContract ?? recoveryDescriptor?.agentContract;
2058
2106
  const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? omitUndefinedProperties({ ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir });
2059
2107
  const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
2060
2108
  const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
2061
2109
  const parentModel = input.parentModel;
2062
2110
  const revivalAsyncDir = path.join(DIRS.async, runId);
2063
- const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
2111
+ const result = await executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
2064
2112
  agent: target.agent,
2065
2113
  task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0], effectiveFollowUp),
2066
2114
  goal: effectiveFollowUp,
@@ -2102,6 +2150,7 @@ async function resumeAsyncRun(input: {
2102
2150
  thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
2103
2151
  thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
2104
2152
  extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
2153
+ requiredExtensions: recoveryDescriptor?.requiredExtensions ?? (target as { requiredExtensions?: SteeringRecoveryDescriptor["requiredExtensions"] }).requiredExtensions,
2105
2154
  outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
2106
2155
  maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
2107
2156
  waitToolEnabled: input.deps.waitToolEnabled,
@@ -2127,7 +2176,7 @@ async function resumeAsyncRun(input: {
2127
2176
  ...(agentContract ? { agentContract } : {}),
2128
2177
  ...(outputSchema ? { structuredOutputSchema: outputSchema } : {}),
2129
2178
  ...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
2130
- ...(input.params.acceptance !== undefined ? { acceptance: input.params.acceptance } : foregroundContract?.acceptance !== undefined ? { acceptance: foregroundContract.acceptance } : recoveryDescriptor?.acceptance !== undefined ? { acceptance: recoveryDescriptor.acceptance } : {}),
2179
+ ...(acceptance !== undefined ? { acceptance } : {}),
2131
2180
  ...(input.params.timeoutMs !== undefined ? { timeoutMs: input.params.timeoutMs } : {}),
2132
2181
  ...(input.absoluteDeadlineAt !== undefined ? { absoluteDeadlineAt: input.absoluteDeadlineAt } : {}),
2133
2182
  ...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
@@ -2176,7 +2225,7 @@ async function resumeAsyncRun(input: {
2176
2225
  } finally {
2177
2226
  input.signal?.removeEventListener("abort", stopOnAbort);
2178
2227
  }
2179
- fs.rmSync(resultPath, { force: true });
2228
+ if (completed.importedPublication) removeWorkflowAwaitedResult(asyncDir, resultPath, revivedId, completed.importedPublication);
2180
2229
  const usage = importedAsyncRootUsage(completed);
2181
2230
  const childResult: SingleResult = {
2182
2231
  index: 0,
@@ -2192,8 +2241,7 @@ async function resumeAsyncRun(input: {
2192
2241
  ...(completed.stopped ? { stopped: true } : {}),
2193
2242
  ...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
2194
2243
  ...(completed.model ? { model: completed.model } : {}),
2195
- ...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
2196
- ...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
2244
+ ...(completed.requestedModel ? { requestedModel: completed.requestedModel } : {}),
2197
2245
  ...(completed.contextOverflow ? { contextOverflow: true } : {}),
2198
2246
  ...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
2199
2247
  ...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
@@ -2402,6 +2450,8 @@ function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentCo
2402
2450
  params = omitUndefinedProperties({ ...params, agent: result.name });
2403
2451
  const agent = agents.find((candidate) => candidate.name === result.name);
2404
2452
  if (params.extensionBindings !== undefined && (agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job")) return { error: `extensionBindings is not supported for runner.type='${agent.runner.type}'.` };
2453
+ const machineError = agent ? formatHerdrMachineRunnerUnsupported({ machine: params.machine ?? agent.machine, agentName: agent.name, runnerType: agent.runner?.type, adapter: agent.runner?.type === "external-cli" ? agent.runner.adapter : undefined, worktree: params.worktree }) : undefined;
2454
+ if (machineError) return { error: machineError };
2405
2455
  }
2406
2456
  if (params.extensionBindings !== undefined) {
2407
2457
  try {
@@ -2454,6 +2504,18 @@ function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentCo
2454
2504
  return { params };
2455
2505
  }
2456
2506
 
2507
+ function projectEffectiveAcceptanceSchemas(params: SubagentParamsLike, agents: AgentConfig[]): SubagentParamsLike {
2508
+ const withEffectiveSchema = <T extends { agent: string; outputSchema?: JsonSchemaObject | false }>(step: T): T => {
2509
+ const agent = agents.find((candidate) => candidate.name === step.agent);
2510
+ return agent ? { ...step, outputSchema: resolveEffectiveOutputSchema(agent, step.outputSchema) } : step;
2511
+ };
2512
+ return {
2513
+ ...params,
2514
+ ...(params.tasks ? { tasks: params.tasks.map(withEffectiveSchema) } : {}),
2515
+ ...(params.chain ? { chain: projectChainOutputSchemas(params.chain, agents) as ChainStep[] } : {}),
2516
+ };
2517
+ }
2518
+
2457
2519
  function validateExecutionInput(
2458
2520
  params: SubagentParamsLike,
2459
2521
  agents: AgentConfig[],
@@ -2476,7 +2538,7 @@ function validateExecutionInput(
2476
2538
  };
2477
2539
  }
2478
2540
 
2479
- const acceptanceErrors = validateExecutionAcceptance(params as Parameters<typeof validateExecutionAcceptance>[0]);
2541
+ const acceptanceErrors = validateExecutionAcceptance(projectEffectiveAcceptanceSchemas(params, agents));
2480
2542
  if (acceptanceErrors.length > 0) {
2481
2543
  return {
2482
2544
  content: [{ type: "text", text: acceptanceErrors.join(" ") }],
@@ -2728,6 +2790,7 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
2728
2790
  const parentTimeoutMs = params.timeoutMs === undefined && params.maxRuntimeMs === undefined && agent.defaultTimeoutMs === undefined && params.workflowParentDeadlineAt !== undefined
2729
2791
  ? Math.max(1, params.workflowParentDeadlineAt - Date.now())
2730
2792
  : undefined;
2793
+ const outputSchema = params.outputSchema === false ? false : resolveEffectiveOutputSchema(agent, params.outputSchema);
2731
2794
  return {
2732
2795
  ...params,
2733
2796
  ...(params.async === undefined && agent.defaultAsync !== undefined ? { async: agent.defaultAsync } : {}),
@@ -2738,9 +2801,28 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
2738
2801
  ...(params.acceptance === undefined && agent.defaultAcceptance !== undefined
2739
2802
  ? { acceptance: agent.defaultAcceptance }
2740
2803
  : {}),
2804
+ ...(outputSchema !== undefined ? { outputSchema } : {}),
2741
2805
  };
2742
2806
  }
2743
2807
 
2808
+ function validateLaunchOutputSchemaOverrides(params: SubagentParamsLike): string | undefined {
2809
+ const values: unknown[] = [params.outputSchema, ...(params.tasks ?? []).map((task) => task.outputSchema)];
2810
+ for (const step of params.chain ?? []) {
2811
+ if (isParallelStep(step)) values.push(...step.parallel.map((task) => task.outputSchema));
2812
+ else if (isDynamicParallelStep(step)) values.push(step.parallel.outputSchema);
2813
+ else values.push(step.outputSchema);
2814
+ }
2815
+ for (const value of values) {
2816
+ if (value === undefined || value === false) continue;
2817
+ try {
2818
+ assertJsonSchemaObject(value, "outputSchema");
2819
+ } catch (error) {
2820
+ return error instanceof Error ? error.message : String(error);
2821
+ }
2822
+ }
2823
+ return undefined;
2824
+ }
2825
+
2744
2826
  export const DEFAULT_FOREGROUND_TIMEOUT_MS = 30 * 60 * 1000;
2745
2827
 
2746
2828
  // Async single-agent runs also need a wall-clock backstop: a child whose bash
@@ -2904,24 +2986,20 @@ function withResolvedContext(
2904
2986
  }
2905
2987
 
2906
2988
  function withAggregatedToolUsage(result: AgentToolResult<Details>): AgentToolResult<Details> {
2907
- if (result.details.results.length === 0) return result;
2908
- const usage = sumResultsUsage(result.details.results);
2989
+ const highWarnings = result.details.results.flatMap((child) => child.watchdog?.warnings?.filter((warning) => warning.importance === "high") ?? []);
2990
+ const projected: AgentToolResult<Details> = {
2991
+ ...result,
2992
+ ...(highWarnings.length ? { content: [...result.content, { type: "text", text: ["High-importance watchdog findings:", ...highWarnings.map((warning) => `- ${warning.severity}: ${warning.summary}\n Evidence: ${warning.evidence}\n Recommended action: ${warning.recommendedAction}`)].join("\n") }] } : {}),
2993
+ details: {
2994
+ ...result.details,
2995
+ results: result.details.results.map((child) => ({ ...child, ...(child.watchdog ? { watchdog: childWatchdogProgressForModel(child.watchdog) } : {}) })),
2996
+ },
2997
+ };
2998
+ if (projected.details.results.length === 0) return projected;
2999
+ const usage = sumResultsUsage(projected.details.results);
2909
3000
  return usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0
2910
- ? { ...result, usage: toAgentToolUsage(usage) }
2911
- : result;
2912
- }
2913
-
2914
- function withForkThinkingNotes(
2915
- result: AgentToolResult<Details>,
2916
- downgrades: Map<number, string>,
2917
- ): AgentToolResult<Details> {
2918
- if (downgrades.size === 0) return result;
2919
- const children = [...downgrades.entries()]
2920
- .sort(([a], [b]) => a - b)
2921
- .map(([index, agent]) => `${agent} (child ${index})`)
2922
- .join(", ");
2923
- const note = `Note: fork context forced thinking off for ${children}. The forked transcript contained signed Anthropic thinking blocks that were sanitized, and Anthropic children cannot resume such a transcript with thinking enabled. Use context: "fresh" when an Anthropic child needs thinking.`;
2924
- return { ...result, content: [...result.content, { type: "text", text: note }] };
3001
+ ? { ...projected, usage: toAgentToolUsage(usage) }
3002
+ : projected;
2925
3003
  }
2926
3004
 
2927
3005
  function toExecutionErrorResult(params: SubagentParamsLike, error: unknown, contextSummary?: ContextSummary): AgentToolResult<Details> {
@@ -2947,7 +3025,7 @@ function resolveStaticLaunchSummary(input: {
2947
3025
  availableModels: ModelInfo[];
2948
3026
  currentProvider?: string;
2949
3027
  modelScope?: ModelScopeConfig;
2950
- thinkingOverrideForTask: ForkThinkingOverrideForTask;
3028
+ thinkingOverrideForTask: ThinkingOverrideForTask;
2951
3029
  }): StaticLaunchSummary {
2952
3030
  const agentConfig = input.agents.find((agent) => agent.name === input.agent);
2953
3031
  const externalRunner = agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job";
@@ -2962,7 +3040,7 @@ function resolveStaticLaunchSummary(input: {
2962
3040
  agentConfig?.modelProvider ?? input.currentProvider,
2963
3041
  modelScopes.length === 0 ? {} : { scope: modelScopes },
2964
3042
  );
2965
- const thinkingOverride = externalRunner ? undefined : input.thinkingOverrideForTask(input.agent, input.index, model);
3043
+ const thinkingOverride = externalRunner ? undefined : input.thinkingOverrideForTask();
2966
3044
  const thinking = externalRunner ? undefined : resolveEffectiveThinking(model, thinkingOverride ?? agentConfig?.thinking);
2967
3045
  return {
2968
3046
  agent: input.agent,
@@ -2978,7 +3056,7 @@ function collectStaticLaunchSummaries(input: {
2978
3056
  availableModels: ModelInfo[];
2979
3057
  currentProvider?: string;
2980
3058
  modelScope?: ModelScopeConfig;
2981
- thinkingOverrideForTask: ForkThinkingOverrideForTask;
3059
+ thinkingOverrideForTask: ThinkingOverrideForTask;
2982
3060
  dynamicFanoutMaxItems?: number;
2983
3061
  }): StaticLaunchSummary[] {
2984
3062
  const summary = (agent: string, index: number, explicitModel?: string) => resolveStaticLaunchSummary({
@@ -3132,6 +3210,15 @@ function importedAsyncRootUsage(completed: Awaited<ReturnType<typeof waitForImpo
3132
3210
  };
3133
3211
  }
3134
3212
 
3213
+ function removeWorkflowAwaitedResult(asyncDir: string, resultPath: string, runId: string, publication: NonNullable<Awaited<ReturnType<typeof waitForImportedAsyncRoot>>["importedPublication"]>): void {
3214
+ try {
3215
+ fs.rmSync(resultPath, { force: true });
3216
+ } catch {
3217
+ // Payload cleanup must not replace an already imported delivery.
3218
+ }
3219
+ removeResultIndex(asyncDir, publication.sessionId, runId, publication.toolCallId);
3220
+ }
3221
+
3135
3222
  async function waitForWorkflowAsyncSingleResult(
3136
3223
  params: SubagentParamsLike,
3137
3224
  launchResult: AgentToolResult<Details>,
@@ -3152,7 +3239,7 @@ async function waitForWorkflowAsyncSingleResult(
3152
3239
  } finally {
3153
3240
  options.signal?.removeEventListener("abort", stopOnAbort);
3154
3241
  }
3155
- fs.rmSync(resultPath, { force: true });
3242
+ if (completed.importedPublication) removeWorkflowAwaitedResult(asyncDir, resultPath, options.runId, completed.importedPublication);
3156
3243
  const usage = importedAsyncRootUsage(completed);
3157
3244
  const childResult: SingleResult = omitUndefinedProperties({
3158
3245
  index: 0,
@@ -3168,8 +3255,7 @@ async function waitForWorkflowAsyncSingleResult(
3168
3255
  ...(completed.stopped ? { stopped: true } : {}),
3169
3256
  ...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
3170
3257
  ...(completed.model ? { model: completed.model } : {}),
3171
- ...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
3172
- ...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
3258
+ ...(completed.requestedModel ? { requestedModel: completed.requestedModel } : {}),
3173
3259
  ...(completed.contextOverflow ? { contextOverflow: true } : {}),
3174
3260
  ...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
3175
3261
  ...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
@@ -3283,7 +3369,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3283
3369
  const modelOverrideFromParent = modelOrigin === "inherited";
3284
3370
  const launchRuleError = applyWatchdogLaunchRules({ cwd: effectiveCwd, agent: a.name, model: modelOverride ?? (parentModel && `${parentModel.provider}/${parentModel.id}`), warn: (violation) => deps.watchdog?.displayRuleWarning(violation) });
3285
3371
  if (launchRuleError) return toExecutionErrorResult(params, new Error(launchRuleError), data.contextPolicy.contextSummary);
3286
- const asyncResult = executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
3372
+ const asyncResult = await executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
3287
3373
  agent: params.agent!,
3288
3374
  task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
3289
3375
  goal: params.task ?? "",
@@ -3293,6 +3379,8 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3293
3379
  availableModels,
3294
3380
  cwd: effectiveCwd,
3295
3381
  requestedCwd: data.requestedCwd,
3382
+ machine: params.machine,
3383
+ machineCwd: params.machineCwd,
3296
3384
  maxOutput: params.maxOutput,
3297
3385
  artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
3298
3386
  artifactConfig,
@@ -3311,7 +3399,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3311
3399
  fast: params.fast,
3312
3400
  modelOverrideFromParent,
3313
3401
  modelOrigin,
3314
- thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
3402
+ thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(),
3315
3403
  thinkingCeiling: a.maxThinking,
3316
3404
  maxSubagentDepth,
3317
3405
  waitToolEnabled: deps.waitToolEnabled,
@@ -3329,7 +3417,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3329
3417
  childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(agent, index) : undefined,
3330
3418
  nestedRoute,
3331
3419
  agentContract: params.agentContract,
3332
- structuredOutputSchema: params.outputSchema,
3420
+ structuredOutputSchema: params.outputSchema || undefined,
3333
3421
  extensionBindings: params.extensionBindings,
3334
3422
  acceptance: params.acceptance,
3335
3423
  timeoutMs: data.timeoutMs,
@@ -3337,6 +3425,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3337
3425
  usageBudget: data.usageBudget,
3338
3426
  configToolBudget: data.configToolBudget,
3339
3427
  toolTimeoutMs: data.params?.toolTimeoutMs,
3428
+ checkpointBeforeDeadlineMs: data.params?.checkpointBeforeDeadlineMs ?? deps.config.checkpointBeforeDeadlineMs,
3340
3429
  configToolTimeoutMs: data.configToolTimeoutMs,
3341
3430
  capabilityCeiling: data.capabilityCeiling,
3342
3431
  runFanoutBudget: data.runFanoutBudget,
@@ -3455,6 +3544,23 @@ function appendWorkflowOutputWarning(text: string, warning: string | undefined):
3455
3544
  return warning ? `${text}\n\n${warning}` : text;
3456
3545
  }
3457
3546
 
3547
+ export function resolveWorkflowChildLocalCwd(input: {
3548
+ workflowCwd: string;
3549
+ discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[] };
3550
+ agents: AgentConfig[];
3551
+ workflowAgentScope?: unknown;
3552
+ params: Record<string, unknown>;
3553
+ }): string {
3554
+ if (input.params.machine !== undefined) return input.workflowCwd;
3555
+ if (typeof input.params.agent === "string") {
3556
+ const agentScope = resolveExecutionAgentScope(input.params.agentScope ?? input.workflowAgentScope);
3557
+ const workflowAgents = input.discoverAgents(input.workflowCwd, agentScope).agents;
3558
+ const agent = resolveAgentName(input.params.agent, workflowAgents).agent ?? resolveAgentName(input.params.agent, input.agents).agent;
3559
+ if (agent?.machine !== undefined) return input.workflowCwd;
3560
+ }
3561
+ return typeof input.params.cwd === "string" ? resolveChildCwd(input.workflowCwd, input.params.cwd) : input.workflowCwd;
3562
+ }
3563
+
3458
3564
  function resolveWorkflowChildOutputPath(input: {
3459
3565
  ctxCwd: string;
3460
3566
  workflowCwd: string;
@@ -3480,7 +3586,7 @@ function resolveWorkflowChildOutputPath(input: {
3480
3586
  }, input.state);
3481
3587
  return { path: "recoveryDescriptor" in target ? target.recoveryDescriptor?.outputPath : undefined, inherited: false };
3482
3588
  }
3483
- const childCwd = typeof input.params.cwd === "string" ? resolveChildCwd(input.workflowCwd, input.params.cwd) : input.workflowCwd;
3589
+ const childCwd = resolveWorkflowChildLocalCwd(input);
3484
3590
  let agentOutput: string | undefined;
3485
3591
  if (rawOutput === true || rawOutput === "true" || (!hasExplicitOutput && !input.aggregateOutputPath)) {
3486
3592
  const agentScope = resolveExecutionAgentScope(input.params.agentScope ?? input.workflowAgentScope);
@@ -3582,7 +3688,7 @@ function prepareWorkflowChildLaunchParams(input: {
3582
3688
  const resolvedOutput = resolveWorkflowChildOutputPath({ ctxCwd: input.ctxCwd, workflowCwd: input.workflowCwd, artifactsDir: input.artifactsDir, workflowRunId: input.parentWorkflowRunId, aggregateOutputPath: input.aggregateOutputPath, configuredOutputBaseDir: input.configuredOutputBaseDir, discoverAgents: input.discoverAgents, agents: input.agents, workflowAgentScope: input.workflowAgentScope, key: input.workflowKey, params: input.childParams });
3583
3689
  if (resolvedOutput.path) childParams = { ...input.childParams, output: resolvedOutput.path };
3584
3690
  }
3585
- const childCwd = typeof childParams.cwd === "string" ? resolveChildCwd(input.workflowCwd, childParams.cwd) : input.workflowCwd;
3691
+ const childCwd = resolveWorkflowChildLocalCwd({ ...input, params: childParams });
3586
3692
  const agentScope = resolveExecutionAgentScope(childParams.agentScope ?? input.workflowAgentScope);
3587
3693
  const discoveredAgents = input.discoverAgents(childCwd, agentScope).agents;
3588
3694
  const agent = typeof childParams.agent === "string"
@@ -3714,6 +3820,12 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3714
3820
  details: { mode: "single", results: [] },
3715
3821
  };
3716
3822
  }
3823
+ let foregroundMachine: import("../../shared/types.ts").HerdrMachineReference | undefined;
3824
+ const requestedMachine = params.machine ?? agentConfig.machine;
3825
+ if (requestedMachine) {
3826
+ try { foregroundMachine = resolveHerdrMachinePlacement({ machine: requestedMachine, cwd: ctx.cwd, stepCwd: params.machineCwd }).machine; }
3827
+ catch (error) { return toExecutionErrorResult(params, error instanceof Error ? error : new Error(String(error)), data.contextPolicy.contextSummary); }
3828
+ }
3717
3829
  const effectiveToolBudget = resolveEffectiveToolBudget(omitUndefinedProperties({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget }));
3718
3830
  if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error), data.contextPolicy.contextSummary);
3719
3831
 
@@ -3751,7 +3863,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3751
3863
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
3752
3864
 
3753
3865
 
3754
- const sourceCwd = effectiveCwd;
3866
+ const sourceCwd = foregroundMachine?.cwd ?? effectiveCwd;
3755
3867
  let pendingHandoff: Details["parallelHandoff"];
3756
3868
  const { setup: worktreeSetup, errorResult: worktreeSetupError } = params.worktree ? await createSingleWorktreeSetup(
3757
3869
  sourceCwd,
@@ -3828,7 +3940,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3828
3940
  // Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
3829
3941
  // absolute paths pass through; relative paths resolve against the child cwd.
3830
3942
  const reads = readsOverride !== undefined ? readsOverride : agentConfig.defaultReads ?? false;
3831
- const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
3943
+ const readPaths = !foregroundMachine && Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
3832
3944
  const readsInstruction = readPaths.length > 0
3833
3945
  ? `[Read from: ${readPaths.join(", ")}]\n\n`
3834
3946
  : "";
@@ -3846,7 +3958,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3846
3958
  let childSessionControls: ForegroundChildSessionControls | undefined;
3847
3959
  const foregroundControl = deps.state.foregroundControls.get(runId);
3848
3960
  if (foregroundControl) {
3849
- const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin));
3961
+ const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask());
3850
3962
  beginForegroundChild(foregroundControl, omitUndefinedProperties({
3851
3963
  index: 0,
3852
3964
  agent: params.agent!,
@@ -3878,6 +3990,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3878
3990
  }
3879
3991
  },
3880
3992
  }));
3993
+ // Capture the owned mailbox before a child can start and finish between polling ticks.
3994
+ if (deps.childRuntime?.fanoutChild) deps.activateSupervisorTransport?.();
3881
3995
  }
3882
3996
 
3883
3997
  const modelResponseAliases = deps.config.modelResponseAliases === undefined ? undefined : structuredClone(deps.config.modelResponseAliases);
@@ -3889,6 +4003,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3889
4003
  : undefined;
3890
4004
 
3891
4005
  const deadlineAt = data.deadlineAt ?? (data.timeoutMs !== undefined ? Date.now() + data.timeoutMs : undefined);
4006
+ const requiredExtensions = deps.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(data.parentPiSessionId);
3892
4007
  let r: Awaited<ReturnType<typeof runSync>> | undefined;
3893
4008
  let resolveDetachedWorkflowChild: ((result: Awaited<ReturnType<typeof runSync>>) => void) | undefined;
3894
4009
  const detachedWorkflowChild = params.workflowAwaitDetached === true
@@ -3896,10 +4011,15 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3896
4011
  : undefined;
3897
4012
  try {
3898
4013
  const launched = await runSync(ctx.cwd, agents, params.agent!, task, compactOptional<Parameters<typeof runSync>[4]>({
4014
+ machine: foregroundMachine,
4015
+ parentProviderRegistry: ctx.modelRegistry,
4016
+ remoteReads: foregroundMachine ? readsOverride : undefined,
3899
4017
  permissions: deps.config.permissions,
3900
4018
  runtimeSnapshotHost: deps.pi,
4019
+ hostAvailableBuiltins: getHostBuiltinToolNames(deps.pi),
3901
4020
  parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
3902
- llmIntentArbiter: createTaskMutationArbiter(ctx),
4021
+ requiredExtensions,
4022
+ llmIntentArbiter: createTaskMutationArbiter({ model: ctx.model, modelRegistry: ctx.modelRegistry, sessionId: ctx.sessionManager.getSessionId() }),
3903
4023
  childRuntime: deps.childRuntime,
3904
4024
  onChildSession: (controls) => { childSessionControls = controls; },
3905
4025
  context: data.contextPolicy.contextForAgent(params.agent!),
@@ -3936,7 +4056,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3936
4056
  fast: params.fast,
3937
4057
  modelOverrideFromParent,
3938
4058
  modelOrigin,
3939
- thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
4059
+ thinkingOverride: thinkingOverrideForTask(),
3940
4060
  thinkingCeiling: agentConfig.maxThinking,
3941
4061
  extensionBindings: params.extensionBindings,
3942
4062
  availableModels,
@@ -4059,7 +4179,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
4059
4179
  usageBudget: usageBudgetState(data.usageBudget, totalCost),
4060
4180
  ...(worktreeHandoff?.reference ? { parallelHandoff: worktreeHandoff.reference } : {}),
4061
4181
  }));
4062
- rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings });
4182
+ rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings, requiredExtensions });
4063
4183
 
4064
4184
  const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results: [r] });
4065
4185
  if (!r.detached && !r.interrupted && !suppressRoutineResultIntercom) {
@@ -4461,8 +4581,9 @@ function terminalWorkflowReceipt(
4461
4581
  terminalOutcome?: WorkflowTerminalOutcome,
4462
4582
  hostSteps?: WorkflowReceipt["hostSteps"],
4463
4583
  resource?: WorkflowReceipt["resource"],
4584
+ argsDigest?: string,
4464
4585
  ): WorkflowReceipt {
4465
- return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps, resource });
4586
+ return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps, resource, ...(argsDigest ? { argsDigest } : {}) });
4466
4587
  }
4467
4588
 
4468
4589
  function workflowFailureTerminalOutcome(error: unknown, _children: WorkflowScriptChildResult[], usageBudget: ReturnType<typeof usageBudgetState>): WorkflowTerminalOutcome | undefined {
@@ -4545,6 +4666,35 @@ export async function steerWorkflowChildByKey(input: {
4545
4666
  }
4546
4667
  }
4547
4668
 
4669
+ export async function preflightWorkflowWorktrees(input: {
4670
+ workflowDefaults: SubagentParamsLike;
4671
+ defaultWorktree?: boolean;
4672
+ calls: Array<{ key: string; params: Record<string, unknown> }>;
4673
+ ctxCwd: string;
4674
+ signal: AbortSignal;
4675
+ deadlineAt?: number;
4676
+ }): Promise<void> {
4677
+ const sources = new Map<string, string[]>();
4678
+ for (const { key, params } of input.calls) {
4679
+ // The workflow validates retained IDs/receipt references before admission; resolution follows it.
4680
+ if (params.resume !== undefined) continue;
4681
+ const effective = prepareWorkflowLaunchParams(input.workflowDefaults, params, "preflight", key);
4682
+ if ((effective.worktree ?? input.defaultWorktree) !== true) continue;
4683
+ // Match recursive execute's resolution, including relative default/child cwd.
4684
+ const cwd = resolveRequestedCwd(input.ctxCwd, effective.cwd);
4685
+ const keys = sources.get(cwd) ?? [];
4686
+ keys.push(key);
4687
+ sources.set(cwd, keys);
4688
+ }
4689
+ for (const [cwd, keys] of sources) {
4690
+ try { await preflightWorktreeSource(cwd, { signal: input.signal, deadlineAt: input.deadlineAt }); }
4691
+ catch (error) {
4692
+ throw new Error(`Worktree admission failed for ${keys.map((key) => `'${key}'`).join(", ")} at ${cwd}: ${error instanceof Error ? error.message : String(error)} Select the correct cwd or arrange an operator-approved commit/stash.`, { cause: error });
4693
+ }
4694
+ }
4695
+ input.signal.throwIfAborted();
4696
+ }
4697
+
4548
4698
  export function prepareWorkflowLaunchParams(
4549
4699
  workflowDefaults: SubagentParamsLike,
4550
4700
  childParams: Record<string, unknown>,
@@ -4579,7 +4729,7 @@ export function prepareWorkflowLaunchParams(
4579
4729
  const worktree = childParams.worktree ?? workflowDefaults.worktree;
4580
4730
  const baseRef = Object.hasOwn(childParams, "baseRef") ? childParams.baseRef : undefined;
4581
4731
  const outputSchema = Object.hasOwn(childParams, "outputSchema") ? childParams.outputSchema : workflowDefaults.outputSchema;
4582
- if (outputSchema !== undefined) assertJsonSchemaObject(outputSchema, "outputSchema");
4732
+ if (outputSchema !== undefined && outputSchema !== false) assertJsonSchemaObject(outputSchema, "outputSchema");
4583
4733
  const agentContract = Object.hasOwn(childParams, "agentContract") ? childParams.agentContract : workflowDefaults.agentContract;
4584
4734
  if (agentContract !== undefined && !isAgentContract(agentContract as AgentContract)) throw new Error("agentContract must be { version: 1 }.");
4585
4735
  const acceptance = Object.hasOwn(childParams, "acceptance") ? childParams.acceptance : workflowDefaults.acceptance;
@@ -4599,7 +4749,7 @@ export function prepareWorkflowLaunchParams(
4599
4749
  ...(lane ? { lane } : {}),
4600
4750
  ...(worktree !== undefined ? { worktree: worktree as boolean } : {}),
4601
4751
  ...(baseRef !== undefined ? { baseRef: baseRef as string } : {}),
4602
- ...(outputSchema !== undefined ? { outputSchema: outputSchema as JsonSchemaObject } : {}),
4752
+ ...(outputSchema !== undefined ? { outputSchema } : {}),
4603
4753
  ...(agentContract !== undefined ? { agentContract: agentContract as AgentContract } : {}),
4604
4754
  ...(acceptance !== undefined ? { acceptance: acceptance as AcceptanceInput } : {}),
4605
4755
  ...(output !== undefined ? { output: output as string | boolean } : {}),
@@ -4890,7 +5040,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4890
5040
  } catch (error) {
4891
5041
  const message = error instanceof Error ? error.message : String(error);
4892
5042
  if (requestParams.action?.trim() === "validate") {
4893
- const validation = validateWorkflowScript(requestParams.workflowScript ?? "");
5043
+ const validation = validateWorkflowScript(requestParams.workflowScript ?? "", {
5044
+ maxSubagentSpawnsPerRun: requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
5045
+ });
4894
5046
  const invalidValidation = { ...validation, ok: false, errors: [...validation.errors, { message }] };
4895
5047
  return {
4896
5048
  content: [{ type: "text", text: JSON.stringify(invalidValidation) }],
@@ -4901,7 +5053,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4901
5053
  return buildRequestedModeError(requestParams, message);
4902
5054
  }
4903
5055
  if (requestParams.action?.trim() === "validate") {
4904
- const validation = validateWorkflowScript(requestParams.workflowScript ?? "");
5056
+ const validation = validateWorkflowScript(requestParams.workflowScript ?? "", {
5057
+ maxSubagentSpawnsPerRun: requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
5058
+ });
4905
5059
  const payload = workflowPreflight ? { ...validation, preflight: workflowPreflight } : validation;
4906
5060
  return {
4907
5061
  content: [{ type: "text", text: JSON.stringify(payload) }],
@@ -4919,6 +5073,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4919
5073
  workflowResource = { permit: workflowResourcePermit, ...consumed };
4920
5074
  }
4921
5075
  if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
5076
+ const workflowArgsEvidence = requestParams.args === undefined ? undefined : { args: requestParams.args, argsDigest: stableJsonDigest(requestParams.args) };
5077
+ const workflowArgs = workflowArgsEvidence?.args;
5078
+ const workflowArgsDigest = workflowArgsEvidence?.argsDigest;
5079
+ const spawnBudgetValidation = validateWorkflowScript(requestParams.workflowScript, {
5080
+ maxSubagentSpawnsPerRun: requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
5081
+ });
5082
+ const spawnBudgetErrors = spawnBudgetValidation.errors.filter((error) => error.kind === "spawn-budget");
5083
+ if (spawnBudgetErrors.length > 0) {
5084
+ return buildRequestedModeError(requestParams, `workflowScript validation failed before child launch; no children launched. ${spawnBudgetErrors.map((error) => error.message).join(" ")}`);
5085
+ }
5086
+ for (const warning of spawnBudgetValidation.warnings ?? []) console.warn(`[pi-subagents] ${warning.message}`);
4922
5087
  const acceptanceErrors = validateAcceptanceInput(requestParams.acceptance);
4923
5088
  if (acceptanceErrors.length > 0) return buildRequestedModeError(requestParams, acceptanceErrors.join(" "));
4924
5089
  const foregroundWorkflowRunId = encodeIndexSegment(_id);
@@ -5078,7 +5243,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5078
5243
  pid: process.pid,
5079
5244
  steps: [],
5080
5245
  ...(workflowPreflight ? { preflight: workflowPreflight } : {}),
5081
- workflow: { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) },
5246
+ workflow: { trace: [], emits: [], console: [], ...(workflowArgsEvidence ?? {}), ...(workflowResource ? { resource: workflowResource.provenance } : {}) },
5082
5247
  workflowChildren: workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "running", inventoryComplete: false }),
5083
5248
  runFanoutBudget: getRunFanoutBudgetSnapshot(workflowFanoutBudget),
5084
5249
  };
@@ -5108,10 +5273,37 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5108
5273
  };
5109
5274
  let pendingResultPublication: Promise<boolean> | undefined;
5110
5275
  let settleResultPublication: ((published: boolean) => void) | undefined;
5111
- const reportResultWriteFailure = (error: unknown): void => {
5276
+ let resultWriteFailureWakeDelivered = false;
5277
+ const reportResultWriteFailure = (error: unknown): boolean => {
5112
5278
  const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
5113
5279
  console.error(message, error);
5280
+ try {
5281
+ if (currentSessionId && fallbackResultPayloadPathForSessionRun(path.dirname(resultPath), currentSessionId, workflowRunId)) return true;
5282
+ } catch (pendingError) {
5283
+ console.error(`Failed to verify pending async workflow result '${workflowRunId}':`, pendingError);
5284
+ }
5114
5285
  appendWorkflowEvent({ type: "subagent.workflow.result_write_failed", error: message });
5286
+ // A missing result file means the result watcher will never deliver the
5287
+ // terminal completion wake. Intermediate child notices are context-only
5288
+ // once their turns are suppressed, so surface the failure as its own
5289
+ // actionable wake instead of leaving the parent asleep.
5290
+ if (resultWriteFailureWakeDelivered) return false;
5291
+ if (deps.state.currentSessionId !== currentSessionId || deps.state.completionOwnerId !== completionOwnerId) return false;
5292
+ try {
5293
+ deps.pi.sendMessage(
5294
+ {
5295
+ customType: "subagent-workflow-result-write-failed",
5296
+ content: message,
5297
+ display: true,
5298
+ },
5299
+ { triggerTurn: true },
5300
+ );
5301
+ resultWriteFailureWakeDelivered = true;
5302
+ } catch (sendError) {
5303
+ console.error(`Failed to send workflow result write failure notification for '${workflowRunId}':`, sendError);
5304
+ }
5305
+ deps.refreshResultDelivery?.();
5306
+ return false;
5115
5307
  };
5116
5308
  const runPersistence = createCapacityResilientJsonWriter({
5117
5309
  keepAlive: true,
@@ -5120,7 +5312,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5120
5312
  if (filePath === resultPath) settleResultPublication?.(true);
5121
5313
  },
5122
5314
  onError: (error, filePath) => {
5123
- if (filePath === resultPath) { reportResultWriteFailure(error); settleResultPublication?.(false); }
5315
+ if (filePath === resultPath) settleResultPublication?.(reportResultWriteFailure(error));
5124
5316
  else console.error(`Failed to persist async workflow state '${filePath}':`, error);
5125
5317
  },
5126
5318
  write: (filePath, payload) => filePath === resultPath
@@ -5201,8 +5393,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5201
5393
  if (!resultPublished) pendingResultPublication = new Promise<boolean>((resolve) => { settleResultPublication = resolve; });
5202
5394
  return true;
5203
5395
  } catch (error) {
5204
- reportResultWriteFailure(error);
5205
- return false;
5396
+ return reportResultWriteFailure(error);
5206
5397
  }
5207
5398
  };
5208
5399
  const projectWorkflowActivity = () => {
@@ -5240,7 +5431,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5240
5431
  indexPersistence.dispose();
5241
5432
  return { content: [{ type: "text", text: `Failed to create async workflow storage: ${error instanceof Error ? error.message : String(error)}` }], isError: true, details: { mode: "workflow", results: [] } };
5242
5433
  }
5243
- appendWorkflowEvent({ type: "subagent.workflow.started" });
5434
+ appendWorkflowEvent({ type: "subagent.workflow.started", ...(workflowArgsDigest ? { argsDigest: workflowArgsDigest } : {}) });
5244
5435
  // The runner does not execute workflow scripts: this active owner must consume its own
5245
5436
  // durable steer inbox, deliver through in-process child controls, and persist receipts.
5246
5437
  const emitWorkflowSteerEvent = (type: string, requestId: string, index?: number, extra: Record<string, unknown> = {}): void => {
@@ -5387,7 +5578,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5387
5578
  const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
5388
5579
  const workflowResults: SingleResult[] = [];
5389
5580
  const workflowChildRunIds = new Map<string, string>();
5390
- const { action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, missionId: _missionId, mission: _mission, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = workflowRequest;
5581
+ const { args: _args, action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, missionId: _missionId, mission: _mission, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = workflowRequest;
5391
5582
  const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
5392
5583
  const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
5393
5584
  const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, parentCwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
@@ -5512,6 +5703,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5512
5703
  try {
5513
5704
  const workflow = await runWorkflowScript({
5514
5705
  script: workflowScript,
5706
+ ...(workflowArgs ? { args: workflowArgs } : {}),
5707
+ workflowRunId,
5708
+ processCwd: ctx.cwd,
5515
5709
  globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
5516
5710
  timeoutMs: timeout,
5517
5711
  signal: controller.signal,
@@ -5527,6 +5721,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5527
5721
  },
5528
5722
  ...(workflowState ? { state: workflowState } : {}),
5529
5723
  onTrace: updateTrace,
5724
+ onChildSettled: (notification) => {
5725
+ appendWorkflowEvent({
5726
+ type: "subagent.workflow.child_settled",
5727
+ childKey: notification.childKey,
5728
+ ...(notification.childRunId ? { childRunId: notification.childRunId } : {}),
5729
+ outcome: notification.outcome,
5730
+ workflowRunning: notification.workflowRunning,
5731
+ });
5732
+ try {
5733
+ deps.pi.sendMessage(
5734
+ {
5735
+ customType: "subagent-incremental-child-notify",
5736
+ content: formatIncrementalChildCompletion(notification),
5737
+ display: notification.outcome !== "completed",
5738
+ },
5739
+ { triggerTurn: incrementalChildCompletionTriggersTurn(notification, requestParams.scheduleOrigin) },
5740
+ );
5741
+ } catch (sendError) {
5742
+ console.error(`Failed to send incremental child completion notification for '${notification.childKey}':`, sendError);
5743
+ }
5744
+ deps.refreshResultDelivery?.();
5745
+ },
5530
5746
  onLanePlan: (lanes) => {
5531
5747
  status.workflowGraph = buildWorkflowLaneGraph(workflowRunId, lanes, status.workflowGraph);
5532
5748
  applyWorkflowLaneTrace(status.workflowGraph, status.workflow?.trace ?? []);
@@ -5539,7 +5755,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5539
5755
  persist({ tolerateStatusWriteFailure: true });
5540
5756
  } });
5541
5757
  },
5542
- admit: (calls) => {
5758
+ admit: async (calls, admissionSignal) => {
5759
+ await preflightWorkflowWorktrees({ workflowDefaults: workflowChildDefaults, defaultWorktree: deps.config.worktree, calls, ctxCwd: parentCwd, signal: admissionSignal, deadlineAt: workflowDeadlineAt });
5543
5760
  const outputClaims = workflowChildOutputClaims({ ctxCwd: parentCwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
5544
5761
  if (outputClaims.error) throw new Error(outputClaims.error);
5545
5762
  status.runFanoutBudget = claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
@@ -5659,8 +5876,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5659
5876
  const workflowUsage = sumResultsUsage(workflowResults);
5660
5877
  settleWorkflowSteerInbox("complete");
5661
5878
  const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children, steps: status.steps });
5662
- status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
5663
- const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
5879
+ status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowArgsEvidence ?? {}), ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
5880
+ const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph), workflowResource?.provenance, workflowArgsDigest);
5664
5881
  delete status.workflowReceiptPath;
5665
5882
  let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
5666
5883
  try {
@@ -5674,7 +5891,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5674
5891
  persist();
5675
5892
  deps.refreshResultDelivery?.();
5676
5893
  persistClosed = true;
5677
- appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}) });
5894
+ appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(workflowArgsDigest ? { argsDigest: workflowArgsDigest } : {}), ...(status.error ? { error: status.error } : {}) });
5678
5895
  } catch (error) {
5679
5896
  const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
5680
5897
  const stopped = controller.signal.aborted;
@@ -5698,7 +5915,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5698
5915
  const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: true, trace: partial.trace, children: partial.children, steps: status.steps });
5699
5916
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
5700
5917
  const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
5701
- status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached || status.activityState === "needs_attention" ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
5918
+ status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached || status.activityState === "needs_attention" ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowArgsEvidence ?? {}), ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
5702
5919
  if (pauseForDetached) {
5703
5920
  const promoted = promotePausedWorkflowIfSettled(status);
5704
5921
  if (promoted) status = promoted;
@@ -5710,7 +5927,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5710
5927
  const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
5711
5928
  const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
5712
5929
  const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
5713
- const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children, workflowChildren, terminalOutcome, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
5930
+ const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children, workflowChildren, terminalOutcome, validHostStepNodes(status.workflowGraph), workflowResource?.provenance, workflowArgsDigest);
5714
5931
  delete status.workflowReceiptPath;
5715
5932
  let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
5716
5933
  try {
@@ -5724,7 +5941,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5724
5941
  persist();
5725
5942
  deps.refreshResultDelivery?.();
5726
5943
  persistClosed = true;
5727
- appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(terminalOutcome ? { terminalOutcome } : {}), ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
5944
+ appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(workflowArgsDigest ? { argsDigest: workflowArgsDigest } : {}), ...(terminalOutcome ? { terminalOutcome } : {}), ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
5728
5945
  } finally {
5729
5946
  // Idempotent cleanup only: a failed result/index write must not authorize terminal status.
5730
5947
  try {
@@ -5744,7 +5961,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5744
5961
  details: { mode: "workflow", runId: workflowRunId, toolCallId, asyncId: workflowRunId, asyncDir, results: [], ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflow: status.workflow, workflowChildren: status.workflowChildren, chatProgress, ...(deps.state.activeAsyncCapacity ? { activeAsyncCapacity: deps.state.activeAsyncCapacity } : {}) },
5745
5962
  }, workflowFanoutBudget));
5746
5963
  }
5747
- const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, missionId: _missionId, mission: _mission, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = requestParams;
5964
+ const { workflowScript: _workflowScript, args: _args, action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, missionId: _missionId, mission: _mission, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = requestParams;
5748
5965
  const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
5749
5966
  const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
5750
5967
  const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, foregroundWorkflowRunId));
@@ -5786,6 +6003,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5786
6003
  try {
5787
6004
  const workflow = await runWorkflowScript({
5788
6005
  script: requestParams.workflowScript,
6006
+ ...(workflowArgs ? { args: workflowArgs } : {}),
6007
+ workflowRunId: foregroundWorkflowRunId,
6008
+ processCwd: ctx.cwd,
5789
6009
  ...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, foregroundWorkflowRunId, key) } } : {}),
5790
6010
  globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
5791
6011
  timeoutMs: timeout,
@@ -5804,7 +6024,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5804
6024
  }
5805
6025
  sendWorkflowProgress();
5806
6026
  },
5807
- admit: (calls) => {
6027
+ admit: async (calls, admissionSignal) => {
6028
+ await preflightWorkflowWorktrees({ workflowDefaults: workflowChildDefaults, defaultWorktree: deps.config.worktree, calls, ctxCwd: ctx.cwd, signal: admissionSignal, deadlineAt: workflowDeadlineAt });
5808
6029
  const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: foregroundWorkflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
5809
6030
  if (outputClaims.error) throw new Error(outputClaims.error);
5810
6031
  claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
@@ -5900,7 +6121,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5900
6121
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
5901
6122
  const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
5902
6123
  const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children });
5903
- const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()], workflowResource?.provenance);
6124
+ const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()], workflowResource?.provenance, workflowArgsDigest);
5904
6125
  const traceLines = finalPreflightTrace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.durationMs !== undefined ? ` in ${entry.durationMs}ms` : ""}${entry.warning ? ` — ${entry.warning}` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
5905
6126
  const sections = [
5906
6127
  ...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
@@ -5918,7 +6139,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5918
6139
  const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
5919
6140
  return attachWorkflowMission(withRunFanoutBudget({
5920
6141
  content: [{ type: "text", text: displayText }],
5921
- details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(workflow.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
6142
+ details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(workflow.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowArgsEvidence ?? {}), ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5922
6143
  }, workflowFanoutBudget));
5923
6144
  } catch (error) {
5924
6145
  const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
@@ -5941,11 +6162,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5941
6162
  const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
5942
6163
  const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "failed", inventoryComplete: true, trace: partial.trace, children: partial.children });
5943
6164
  const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
5944
- const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()], workflowResource?.provenance);
6165
+ const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()], workflowResource?.provenance, workflowArgsDigest);
5945
6166
  return attachWorkflowMission(withRunFanoutBudget({
5946
6167
  content: [{ type: "text", text: displayText }],
5947
6168
  isError: true,
5948
- details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(partial.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
6169
+ details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(partial.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowArgsEvidence ?? {}), ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5949
6170
  }, workflowFanoutBudget));
5950
6171
  } finally {
5951
6172
  workflowProgressClosed = true;
@@ -5954,9 +6175,24 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5954
6175
  }
5955
6176
  }
5956
6177
  const directParams = requestParams;
5957
- const requestedCwd = directParams.cwd;
5958
- const requestCwd = resolveRequestedCwd(ctx.cwd, directParams.cwd);
5959
- const paramsWithResolvedCwd = directParams.cwd === undefined ? directParams : { ...directParams, cwd: requestCwd };
6178
+ // With a machine, cwd names a directory on that machine: keep it out of every local path resolution.
6179
+ // Placement can also come from the agent's frontmatter or settings override, so the agent is resolved first (discovery is fingerprint-cached).
6180
+ const placedByAgent = (): boolean => {
6181
+ if (directParams.machine !== undefined || directParams.cwd === undefined || typeof directParams.agent !== "string") return false;
6182
+ try {
6183
+ return resolveAgentName(directParams.agent, deps.discoverAgents(ctx.cwd, resolveExecutionAgentScope(directParams.agentScope)).agents).agent?.machine !== undefined;
6184
+ } catch {
6185
+ return false; // Discovery errors surface from the launch path itself.
6186
+ }
6187
+ };
6188
+ const remotePlacement = directParams.action === undefined && (directParams.machine !== undefined || placedByAgent());
6189
+ const requestedCwd = remotePlacement ? undefined : directParams.cwd;
6190
+ const requestCwd = remotePlacement ? ctx.cwd : resolveRequestedCwd(ctx.cwd, directParams.cwd);
6191
+ const paramsWithResolvedCwd = directParams.cwd === undefined
6192
+ ? directParams
6193
+ : remotePlacement
6194
+ ? omitUndefinedProperties({ ...directParams, cwd: undefined, machineCwd: directParams.cwd })
6195
+ : { ...directParams, cwd: requestCwd };
5960
6196
  const action = paramsWithResolvedCwd.action;
5961
6197
  let requestSessionId = "";
5962
6198
  let requestPiSessionId: string | undefined;
@@ -6055,6 +6291,24 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6055
6291
  return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
6056
6292
  }
6057
6293
  }
6294
+ const policyAction = action === "stop" ? "stopRun" : action === "steer" ? "steerRun" : action === "schedule.create" ? "scheduleCreate" : action === "inspector.open" ? "inspectorOpen" : action === "project.open" ? "projectOpen" : undefined;
6295
+ if (policyAction) {
6296
+ // Child-safe mode is a hard capability boundary; the policy is an operator
6297
+ // preference. Refuse first, so the gate never prompts for an action that is
6298
+ // going to be rejected anyway and never masks the more specific reason.
6299
+ if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
6300
+ return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
6301
+ }
6302
+ const decision = resolveAuthorityDecision({ action: policyAction, ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
6303
+ if (decision === "forbid") {
6304
+ return { content: [{ type: "text", text: `Authority policy forbids action '${action}'.` }], isError: true, details: { mode: "management", results: [] } };
6305
+ }
6306
+ if (decision === "confirm") {
6307
+ if (!ctx.hasUI) return { content: [{ type: "text", text: `Authority policy requires user confirmation for action '${action}', but this session has no interactive UI.` }], isError: true, details: { mode: "management", results: [] } };
6308
+ const confirmed = await ctx.ui.confirm(`Authorize subagent ${action}?`, `Authority policy requires confirmation before '${action}'.`);
6309
+ if (!confirmed) return { content: [{ type: "text", text: `Action '${action}' canceled; authority was not granted.` }], details: { mode: "management", results: [] } };
6310
+ }
6311
+ }
6058
6312
  if ((HERDR_PROJECT_PANE_ACTIONS as readonly string[]).includes(action)) {
6059
6313
  if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
6060
6314
  return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
@@ -6062,16 +6316,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6062
6316
  deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
6063
6317
  return handleHerdrProjectPaneAction(action as (typeof HERDR_PROJECT_PANE_ACTIONS)[number], paramsWithResolvedCwd, { cwd: requestCwd, state: deps.state, signal });
6064
6318
  }
6065
- if ((HERDR_INSPECTOR_ACTIONS as readonly string[]).includes(action)) {
6319
+ if ((INSPECTOR_ACTIONS as readonly string[]).includes(action)) {
6066
6320
  if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
6067
6321
  return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
6068
6322
  }
6069
6323
  deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
6070
- return handleHerdrInspectorAction(action as (typeof HERDR_INSPECTOR_ACTIONS)[number], paramsWithResolvedCwd, {
6324
+ return handleInspectorAction(action as (typeof INSPECTOR_ACTIONS)[number], paramsWithResolvedCwd, {
6071
6325
  state: deps.state,
6072
6326
  cwd: requestCwd,
6073
6327
  ...(deps.config.missions ? { missions: deps.config.missions } : {}),
6074
6328
  ...(deps.config.authorityPolicy ? { authorityPolicy: deps.config.authorityPolicy } : {}),
6329
+ plugins: createBuiltinInspectorPlugins(),
6075
6330
  signal,
6076
6331
  });
6077
6332
  }
@@ -6090,18 +6345,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6090
6345
  ...(currentSessionId ? { currentSessionId } : {}),
6091
6346
  });
6092
6347
  }
6093
- const policyAction = action === "stop" ? "stopRun" : action === "steer" ? "steerRun" : action === "schedule.create" ? "scheduleCreate" : undefined;
6094
- if (policyAction) {
6095
- const decision = resolveAuthorityDecision({ action: policyAction, ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
6096
- if (decision === "forbid") {
6097
- return { content: [{ type: "text", text: `Authority policy forbids action '${action}'.` }], isError: true, details: { mode: "management", results: [] } };
6098
- }
6099
- if (decision === "confirm") {
6100
- if (!ctx.hasUI) return { content: [{ type: "text", text: `Authority policy requires user confirmation for action '${action}', but this session has no interactive UI.` }], isError: true, details: { mode: "management", results: [] } };
6101
- const confirmed = await ctx.ui.confirm(`Authorize subagent ${action}?`, `Authority policy requires confirmation before '${action}'.`);
6102
- if (!confirmed) return { content: [{ type: "text", text: `Action '${action}' canceled; authority was not granted.` }], details: { mode: "management", results: [] } };
6103
- }
6104
- }
6105
6348
  if ((WATCHDOG_TOOL_ACTIONS as readonly string[]).includes(action)) {
6106
6349
  if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
6107
6350
  return {
@@ -6613,6 +6856,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6613
6856
  const normalized = normalizeRepeatedParallelCounts(paramsWithResolvedCwd);
6614
6857
  if (normalized.error) return normalized.error;
6615
6858
  const normalizedParams = normalized.params!;
6859
+ const outputSchemaError = validateLaunchOutputSchemaOverrides(normalizedParams);
6860
+ if (outputSchemaError) return buildRequestedModeError(normalizedParams, outputSchemaError);
6616
6861
 
6617
6862
  let effectiveParams = applyForceTopLevelAsyncOverride(
6618
6863
  normalizedParams,
@@ -6702,65 +6947,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6702
6947
 
6703
6948
  let forkSessionFileForIndex: (idx?: number) => string | undefined = () => undefined;
6704
6949
  let prepareForkSessionForIndex: (idx?: number) => Promise<void> = async () => {};
6705
- let forkThinkingOverrideForIndex: (idx?: number) => AgentConfig["thinking"] | undefined = () => undefined;
6706
- let prepareForkThinking = (_agentName: string, _index: number, _modelOverride?: string, _modelOverrideFromParent?: boolean, _modelOrigin?: ModelOrigin): void => {};
6707
- const forkThinkingRequirements = new Map<number, boolean>();
6708
- const forkThinkingDowngrades = new Map<number, string>();
6950
+ // Forked children keep their requested thinking level. Signed Anthropic thinking
6951
+ // blocks are stripped from the inherited transcript by the resolver (they are bound
6952
+ // to the parent session), which is not a reason to disable the child's own reasoning.
6709
6953
  try {
6710
- const forkAvailableModels = contextPolicy.usesFork ? ctx.modelRegistry.getAvailable().map(toModelInfo) : [];
6711
- const parentModel = requestParentModel;
6712
- prepareForkThinking = (agentName, index, modelOverride, modelOverrideFromParent, storedOrigin) => {
6713
- const agentConfig = agents.find((agent) => agent.name === agentName);
6714
- if (agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job") {
6715
- forkThinkingRequirements.set(index, true);
6716
- return;
6717
- }
6718
- const effectiveStoredOrigin = storedOrigin === "configured" && modelOverride === undefined && agentConfig?.model === undefined
6719
- ? undefined
6720
- : storedOrigin;
6721
- const origin = resolveModelOrigin({
6722
- fromParent: modelOverrideFromParent,
6723
- storedOrigin: effectiveStoredOrigin,
6724
- explicitModel: modelOverrideFromParent || effectiveStoredOrigin === "configured" ? undefined : modelOverride,
6725
- agentModel: agentConfig?.model,
6726
- parentModel,
6727
- });
6728
- const primaryModel = modelOverrideFromParent
6729
- ? modelOverride
6730
- : resolveEffectiveSubagentModel(
6731
- effectiveStoredOrigin === "configured" ? undefined : modelOverride,
6732
- agentConfig?.model,
6733
- parentModel,
6734
- forkAvailableModels,
6735
- agentConfig?.modelProvider ?? parentModel?.provider,
6736
- { source: origin === "explicit" ? "explicit" : "inherited" },
6737
- );
6738
- const candidates = buildModelCandidates(
6739
- primaryModel,
6740
- agentConfig?.fallbackModels,
6741
- forkAvailableModels,
6742
- agentConfig?.modelProvider ?? parentModel?.provider,
6743
- {
6744
- primaryModelFromParent: origin === "inherited",
6745
- origin,
6746
- },
6747
- );
6748
- forkThinkingRequirements.set(
6749
- index,
6750
- candidates.length === 0
6751
- || candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels, parentModel?.provider)),
6752
- );
6753
- };
6754
6954
  const pruneSession = contextPolicy.usesFork && deps.config.forkContext?.mode === "pruned"
6755
6955
  ? await createPrunedForkSessionWriter(ctx, deps.config.forkContext, signal)
6756
6956
  : undefined;
6757
- const forkContextResolver = createForkContextResolver(ctx.sessionManager, contextPolicy.usesFork ? "fork" : undefined, {
6758
- forceThinkingOffForIndex: (index) => forkThinkingRequirements.get(index) ?? true,
6759
- ...(pruneSession ? { pruneSession } : {}),
6760
- });
6957
+ const forkContextResolver = createForkContextResolver(
6958
+ ctx.sessionManager,
6959
+ contextPolicy.usesFork ? "fork" : undefined,
6960
+ pruneSession ? { pruneSession } : {},
6961
+ );
6761
6962
  prepareForkSessionForIndex = forkContextResolver.prepareSessionForIndex;
6762
6963
  forkSessionFileForIndex = forkContextResolver.sessionFileForIndex;
6763
- forkThinkingOverrideForIndex = forkContextResolver.thinkingOverrideForIndex;
6764
6964
  } catch (error) {
6765
6965
  return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
6766
6966
  }
@@ -6863,23 +7063,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6863
7063
  }
6864
7064
  const sessionDirForIndex = (idx?: number) =>
6865
7065
  path.join(sessionRoot, `run-${idx ?? 0}`);
6866
- const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
7066
+ const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0) => {
6867
7067
  if (!shouldForkAgent(contextPolicy, agentName)) return undefined;
6868
- prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
6869
7068
  return forkSessionFileForIndex(idx);
6870
7069
  };
6871
- const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
7070
+ const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0) => {
6872
7071
  if (!shouldForkAgent(contextPolicy, agentName)) return;
6873
- prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
6874
7072
  await prepareForkSessionForIndex(idx);
6875
7073
  };
6876
- const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
6877
- if (!shouldForkAgent(contextPolicy, agentName)) return delegatedThinkingOverride;
6878
- prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
6879
- const override = forkThinkingOverrideForIndex(idx);
6880
- if (override === "off") forkThinkingDowngrades.set(idx, agentName);
6881
- return override ?? delegatedThinkingOverride;
6882
- };
7074
+ const thinkingOverrideForTask: ThinkingOverrideForTask = () => delegatedThinkingOverride;
6883
7075
  const childSessionFileForTask: ForkSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) =>
6884
7076
  forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
6885
7077
  const childSessionFileForIndex = (idx?: number) =>
@@ -6968,7 +7160,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6968
7160
  sessionDirForIndex,
6969
7161
  sessionFileForIndex: childSessionFileForIndex,
6970
7162
  sessionFileForTask: childSessionFileForTask,
6971
- thinkingOverrideForTask: forkThinkingOverrideForTask,
7163
+ thinkingOverrideForTask,
6972
7164
  artifactConfig,
6973
7165
  artifactsDir,
6974
7166
  backgroundRequestedWhileClarifying,
@@ -7082,7 +7274,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7082
7274
  availableModels: ctx.modelRegistry.getAvailable().map(toModelInfo),
7083
7275
  currentProvider: requestParentModel?.provider,
7084
7276
  modelScope,
7085
- thinkingOverrideForTask: forkThinkingOverrideForTask,
7277
+ thinkingOverrideForTask,
7086
7278
  dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
7087
7279
  });
7088
7280
  } catch (error) {
@@ -7173,7 +7365,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7173
7365
  const asyncResult = await runAsyncPath(execData, deps);
7174
7366
  if (asyncResult) {
7175
7367
  asyncLaunchFailed = asyncResult.isError === true;
7176
- return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget));
7368
+ return attachMission(withRunFanoutBudget(withResolvedContext(asyncResult, contextPolicy.contextSummary), runFanoutBudget));
7177
7369
  }
7178
7370
  if (foregroundControl) {
7179
7371
  writeNestedForegroundEvent("subagent.nested.started");
@@ -7183,11 +7375,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7183
7375
  if (hasSingle) {
7184
7376
  const result = await runSinglePath(execData, deps);
7185
7377
  writeNestedForegroundEvent("subagent.nested.completed", result);
7186
- return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
7378
+ return attachMission(withRunFanoutBudget(withResolvedContext(result, contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
7187
7379
  }
7188
7380
  } catch (error) {
7189
7381
  asyncLaunchFailed = effectiveAsync;
7190
- const errorResult = withForkThinkingNotes(toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary), forkThinkingDowngrades);
7382
+ const errorResult = toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
7191
7383
  if (nestedForegroundStarted) writeNestedForegroundEvent("subagent.nested.completed", errorResult);
7192
7384
  return attachMission(errorResult);
7193
7385
  } finally {
@@ -7247,6 +7439,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7247
7439
  const { workflow: _workflow, args: _args, ...withoutResourceInput } = publicParams;
7248
7440
  publicParams = { ...withoutResourceInput, workflowScript: resolved.resource.script };
7249
7441
  workflowResourcePermits.set(publicParams, resolved.resource.permit);
7442
+ } else if (publicParams.workflowScript !== undefined || publicParams.workflowScriptPath !== undefined) {
7443
+ const normalizedArgs = normalizeWorkflowArgs(publicParams.args);
7444
+ if ("error" in normalizedArgs) return Promise.resolve({ content: [{ type: "text", text: normalizedArgs.error }], isError: true, details: { mode: publicParams.action ? "management" : "workflow", results: [] } });
7445
+ publicParams = { ...publicParams, args: deepFreezeWorkflowArgs(normalizedArgs.args) };
7250
7446
  }
7251
7447
  const loaded = loadWorkflowScriptPath(publicParams, ctx.cwd);
7252
7448
  if (loaded.error) {