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.
- package/CHANGELOG.md +138 -0
- package/README.md +5 -4
- package/agents/evidence-auditor.md +34 -0
- package/agents/reviewer.md +3 -2
- package/docs/agents.md +43 -15
- package/docs/configuration.md +67 -23
- package/docs/extension-api.md +38 -19
- package/docs/missions.md +10 -2
- package/docs/models.md +11 -79
- package/docs/observability.md +22 -12
- package/docs/standalone-background.md +59 -0
- package/docs/tool-reference.md +33 -20
- package/docs/watchdog.md +39 -10
- package/docs/workflows.md +37 -13
- package/index.ts +5 -2
- package/inspector-runner.mjs +2 -2
- package/package.json +4 -2
- package/prompts/parallel-review.md +1 -1
- package/runner-peer-loader.mjs +24 -0
- package/runner-peer-preload.mjs +32 -0
- package/skills/pi-subagents/SKILL.md +32 -21
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +11 -9
- package/skills/pi-subagents/references/management-authoring-rpc.md +0 -1
- package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +17 -13
- package/skills/pi-subagents/references/review-and-validation.md +3 -3
- package/src/agents/advertised-agent-prompt.ts +34 -3
- package/src/agents/agent-management.ts +57 -58
- package/src/agents/agent-serializer.ts +4 -3
- package/src/agents/agents.ts +190 -71
- package/src/agents/builtin-names.ts +1 -0
- package/src/agents/chain-serializer.ts +5 -0
- package/src/agents/runtime-agent-registry.ts +7 -6
- package/src/api/delegation.ts +4 -0
- package/src/api/preflight.ts +94 -59
- package/src/api/required-child-extensions.ts +6 -0
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +10 -37
- package/src/extension/fanout-child.ts +66 -4
- package/src/extension/herdr-pi-bridge.ts +160 -0
- package/src/extension/index.ts +62 -39
- package/src/extension/public-execution.ts +7 -5
- package/src/extension/rpc.ts +4 -0
- package/src/extension/schemas.ts +81 -81
- package/src/extension/tool-description.ts +30 -82
- package/src/inspectors/actions.ts +148 -0
- package/src/inspectors/ghostty/actions.ts +74 -0
- package/src/inspectors/ghostty/plugin.ts +17 -0
- package/src/inspectors/herdr/actions.ts +99 -179
- package/src/inspectors/herdr/plugin.ts +20 -0
- package/src/inspectors/herdr/project-panes.ts +1 -1
- package/src/inspectors/{herdr/inspector-runner.ts → inspector-runner.ts} +12 -12
- package/src/inspectors/plugins.ts +8 -0
- package/src/inspectors/{herdr/session-roots-codec.ts → session-roots-codec.ts} +3 -14
- package/src/inspectors/types.ts +51 -0
- package/src/intercom/intercom-bridge.ts +50 -8
- package/src/intercom/native-supervisor-channel.ts +44 -31
- package/src/policy/authority.ts +4 -0
- package/src/profiles/profiles.ts +12 -6
- package/src/runs/background/active-async-capacity.ts +4 -0
- package/src/runs/background/active-run-index.ts +17 -1
- package/src/runs/background/async-execution.ts +348 -176
- package/src/runs/background/async-job-tracker.ts +8 -6
- package/src/runs/background/async-resume.ts +17 -12
- package/src/runs/background/async-status.ts +15 -4
- package/src/runs/background/auto-drain.ts +23 -10
- package/src/runs/background/binary-bootstrap.ts +38 -0
- package/src/runs/background/chain-append.ts +1 -1
- package/src/runs/background/chain-root-attachment.ts +14 -33
- package/src/runs/background/fleet-view.ts +30 -2
- package/src/runs/background/notify.ts +105 -7
- package/src/runs/background/owned-process-tree.ts +29 -2
- package/src/runs/background/result-files.ts +8 -4
- package/src/runs/background/result-watcher.ts +19 -2
- package/src/runs/background/run-child-session.ts +81 -33
- package/src/runs/background/run-status.ts +3 -0
- package/src/runs/background/runner-aliases.ts +12 -33
- package/src/runs/background/runner-child-launch.ts +6 -1
- package/src/runs/background/runner-child-sessions.ts +5 -4
- package/src/runs/background/runner-http-dispatcher.ts +119 -0
- package/src/runs/background/scheduled-runs.ts +51 -18
- package/src/runs/background/stale-run-reconciler.ts +35 -11
- package/src/runs/background/steering.ts +20 -2
- package/src/runs/background/subagent-runner.ts +441 -304
- package/src/runs/background/subagent-wait.ts +176 -28
- package/src/runs/background/wait-completions.ts +75 -27
- package/src/runs/background/wait-subscriptions.ts +9 -3
- package/src/runs/background/wait-tool.ts +5 -3
- package/src/runs/foreground/async-steering-action.ts +18 -7
- package/src/runs/foreground/async-stop-action.ts +93 -3
- package/src/runs/foreground/execution.ts +134 -248
- package/src/runs/foreground/foreground-history.ts +2 -1
- package/src/runs/foreground/prompt-audit.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +374 -178
- package/src/runs/foreground/workflow-detach-reconcile.ts +2 -0
- package/src/runs/foreground/workflow-foreground-steering.ts +2 -1
- package/src/runs/shared/acceptance.ts +38 -11
- package/src/runs/shared/async-status-projection.ts +127 -33
- package/src/runs/shared/capability-ceiling.ts +2 -0
- package/src/runs/shared/child-hooks.ts +25 -10
- package/src/runs/shared/child-launch-plan.ts +15 -3
- package/src/runs/shared/child-launch.ts +28 -5
- package/src/runs/shared/child-lifecycle.ts +6 -3
- package/src/runs/shared/child-runtime-config.ts +8 -1
- package/src/runs/shared/child-session.ts +127 -52
- package/src/runs/shared/child-tool-plan.ts +142 -11
- package/src/runs/shared/completion-guard.ts +5 -3
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/effective-system-prompt.ts +33 -0
- package/src/runs/shared/external-cli-contract.ts +11 -1
- package/src/runs/shared/external-cli-preflight.ts +6 -2
- package/src/runs/shared/external-cli-runner.ts +9 -7
- package/src/runs/shared/herdr-connection.ts +134 -0
- package/src/runs/shared/herdr-external-adapters.ts +169 -0
- package/src/runs/shared/herdr-machine.ts +279 -0
- package/src/runs/shared/herdr-pi-protocol.ts +59 -0
- package/src/runs/shared/herdr-placed-run.ts +263 -0
- package/src/runs/shared/llm-intent-arbiter.ts +12 -3
- package/src/runs/shared/model-resolution-diagnostic.ts +76 -0
- package/src/runs/shared/{model-fallback.ts → model-resolution.ts} +22 -235
- package/src/runs/shared/model-scope.ts +1 -1
- package/src/runs/shared/nested-events.ts +11 -2
- package/src/runs/shared/orca-progress-tabs.ts +1 -1
- package/src/runs/shared/parallel-utils.ts +7 -2
- package/src/runs/shared/pi-spawn.ts +10 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +12 -4
- package/src/runs/shared/task-intent.ts +46 -13
- package/src/runs/shared/workflow-async-child-guidance.ts +18 -0
- package/src/runs/shared/worktree-setup-command.ts +27 -4
- package/src/runs/shared/worktree.ts +45 -15
- package/src/shared/child-cache-retention.ts +43 -0
- package/src/shared/fork-context.ts +15 -72
- package/src/shared/launch-contract.ts +68 -8
- package/src/shared/opencode-session-headers.ts +30 -0
- package/src/shared/pruned-fork.ts +1 -1
- package/src/shared/required-child-extensions.ts +81 -0
- package/src/shared/settings.ts +5 -2
- package/src/shared/shortcuts.ts +0 -4
- package/src/shared/types.ts +74 -30
- package/src/slash/delegation-adapters.ts +3 -1
- package/src/slash/delegation-request.ts +14 -0
- package/src/slash/slash-commands.ts +2 -7
- package/src/slash/subagents-admin.ts +24 -13
- package/src/tui/fleet-status.ts +164 -19
- package/src/tui/fleet.ts +16 -14
- package/src/tui/render.ts +168 -37
- package/src/watchdog/child-status.ts +28 -28
- package/src/watchdog/lsp-diagnostics.ts +1 -1
- package/src/watchdog/model-selection.ts +21 -1
- package/src/watchdog/permission-arbiter.ts +3 -1
- package/src/watchdog/register-child.ts +10 -2
- package/src/watchdog/register-main.ts +39 -35
- package/src/watchdog/render.ts +1 -1
- package/src/watchdog/review.ts +123 -74
- package/src/watchdog/rules.ts +1 -1
- package/src/watchdog/runtime.ts +100 -27
- package/src/watchdog/scope.ts +1 -1
- package/src/watchdog/settings.ts +3 -0
- package/src/watchdog/tool-actions.ts +13 -12
- package/src/watchdog/turn-delta.ts +23 -0
- package/src/watchdog/types.ts +5 -3
- package/src/watchdog/warning-format.ts +1 -1
- package/src/workflows/scripted-workflow.ts +279 -10
- package/src/workflows/workflow-checklist.ts +2 -2
- package/src/workflows/workflow-receipt.ts +21 -3
- package/src/workflows/workflow-resources.ts +13 -2
- package/runner-server-preload.mjs +0 -13
- package/src/runs/shared/model-exclusions.ts +0 -374
- package/src/runs/shared/readonly-model-continuation.ts +0 -69
- package/src/runs/shared/readonly-session-evidence.ts +0 -307
- /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 {
|
|
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,
|
|
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 {
|
|
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
|
|
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:
|
|
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
|
|
1239
|
-
|
|
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: ${
|
|
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
|
|
1801
|
-
if (
|
|
1802
|
-
return {
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
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
|
-
...(
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
2908
|
-
const
|
|
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
|
-
? { ...
|
|
2911
|
-
:
|
|
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:
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
5958
|
-
|
|
5959
|
-
const
|
|
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 ((
|
|
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
|
|
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
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
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(
|
|
6758
|
-
|
|
6759
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
7378
|
+
return attachMission(withRunFanoutBudget(withResolvedContext(result, contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
|
|
7187
7379
|
}
|
|
7188
7380
|
} catch (error) {
|
|
7189
7381
|
asyncLaunchFailed = effectiveAsync;
|
|
7190
|
-
const errorResult =
|
|
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) {
|