pi-subagents 0.67.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 +70 -0
- package/README.md +1 -1
- package/docs/agents.md +37 -12
- package/docs/configuration.md +61 -19
- package/docs/extension-api.md +5 -1
- package/docs/missions.md +2 -2
- package/docs/models.md +11 -79
- package/docs/observability.md +18 -8
- package/docs/standalone-background.md +13 -3
- package/docs/tool-reference.md +15 -12
- package/docs/watchdog.md +10 -12
- package/docs/workflows.md +11 -1
- package/index.ts +5 -2
- package/package.json +4 -2
- package/runner-peer-loader.mjs +24 -0
- package/runner-peer-preload.mjs +25 -11
- package/skills/pi-subagents/SKILL.md +18 -21
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +4 -4
- 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 +16 -12
- package/skills/pi-subagents/references/review-and-validation.md +3 -3
- package/src/agents/agent-management.ts +57 -58
- package/src/agents/agent-serializer.ts +4 -3
- package/src/agents/agents.ts +184 -71
- package/src/agents/chain-serializer.ts +5 -0
- package/src/agents/runtime-agent-registry.ts +7 -6
- package/src/api/preflight.ts +20 -16
- package/src/api/required-child-extensions.ts +6 -0
- package/src/extension/config.ts +10 -37
- package/src/extension/fanout-child.ts +3 -0
- package/src/extension/herdr-pi-bridge.ts +160 -0
- package/src/extension/index.ts +42 -31
- package/src/extension/public-execution.ts +3 -3
- package/src/extension/schemas.ts +16 -5
- package/src/extension/tool-description.ts +8 -7
- package/src/intercom/native-supervisor-channel.ts +22 -18
- package/src/policy/authority.ts +4 -0
- package/src/profiles/profiles.ts +12 -6
- package/src/runs/background/active-run-index.ts +17 -1
- package/src/runs/background/async-execution.ts +309 -126
- package/src/runs/background/async-job-tracker.ts +8 -6
- package/src/runs/background/async-resume.ts +13 -4
- package/src/runs/background/async-status.ts +15 -4
- package/src/runs/background/auto-drain.ts +20 -10
- package/src/runs/background/binary-bootstrap.ts +5 -0
- package/src/runs/background/chain-append.ts +1 -1
- package/src/runs/background/chain-root-attachment.ts +14 -33
- package/src/runs/background/notify.ts +74 -6
- 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 +20 -29
- package/src/runs/background/runner-aliases.ts +4 -33
- package/src/runs/background/runner-child-launch.ts +4 -1
- package/src/runs/background/runner-child-sessions.ts +2 -2
- package/src/runs/background/runner-http-dispatcher.ts +119 -0
- package/src/runs/background/scheduled-runs.ts +11 -5
- package/src/runs/background/stale-run-reconciler.ts +35 -11
- package/src/runs/background/subagent-runner.ts +396 -275
- package/src/runs/background/subagent-wait.ts +128 -23
- 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 +4 -2
- package/src/runs/foreground/async-stop-action.ts +93 -3
- package/src/runs/foreground/execution.ts +91 -218
- package/src/runs/foreground/foreground-history.ts +2 -1
- package/src/runs/foreground/subagent-executor.ts +266 -80
- package/src/runs/shared/acceptance.ts +34 -10
- package/src/runs/shared/async-status-projection.ts +123 -33
- package/src/runs/shared/child-launch-plan.ts +15 -3
- package/src/runs/shared/child-launch.ts +19 -6
- package/src/runs/shared/child-runtime-config.ts +5 -0
- package/src/runs/shared/child-session.ts +94 -50
- package/src/runs/shared/child-tool-plan.ts +28 -16
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/external-cli-contract.ts +11 -1
- package/src/runs/shared/external-cli-preflight.ts +6 -2
- 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/model-resolution-diagnostic.ts +76 -0
- package/src/runs/shared/{model-fallback.ts → model-resolution.ts} +22 -237
- package/src/runs/shared/model-scope.ts +1 -1
- package/src/runs/shared/nested-events.ts +11 -2
- package/src/runs/shared/parallel-utils.ts +7 -2
- package/src/runs/shared/pi-spawn.ts +1 -1
- package/src/runs/shared/subagent-prompt-runtime.ts +4 -2
- package/src/runs/shared/worktree-setup-command.ts +27 -4
- package/src/runs/shared/worktree.ts +3 -3
- package/src/shared/child-cache-retention.ts +43 -0
- package/src/shared/launch-contract.ts +6 -9
- 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 +70 -29
- package/src/slash/slash-commands.ts +0 -6
- package/src/slash/subagents-admin.ts +13 -9
- package/src/tui/render.ts +20 -10
- package/src/watchdog/child-status.ts +28 -36
- package/src/watchdog/lsp-diagnostics.ts +1 -1
- package/src/watchdog/model-selection.ts +1 -1
- package/src/watchdog/register-child.ts +10 -3
- package/src/watchdog/register-main.ts +20 -20
- package/src/watchdog/render.ts +1 -1
- package/src/watchdog/review.ts +14 -30
- package/src/watchdog/rules.ts +1 -1
- package/src/watchdog/runtime.ts +23 -12
- package/src/watchdog/settings.ts +3 -6
- package/src/watchdog/types.ts +3 -5
- package/src/watchdog/warning-format.ts +1 -1
- package/src/workflows/scripted-workflow.ts +42 -3
- package/src/workflows/workflow-receipt.ts +21 -3
- package/src/workflows/workflow-resources.ts +13 -2
- 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
|
@@ -28,8 +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";
|
|
32
33
|
import { getHostBuiltinToolNames } from "../shared/child-tool-plan.ts";
|
|
34
|
+
import { projectChainOutputSchemas, resolveEffectiveOutputSchema } from "../shared/child-launch-plan.ts";
|
|
33
35
|
import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
|
|
34
36
|
import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
|
|
35
37
|
import { recordRun } from "../shared/run-history.ts";
|
|
@@ -53,7 +55,7 @@ import { isScheduledRunAction } from "../background/scheduled-runs.ts";
|
|
|
53
55
|
import { encodeIndexSegment } from "../background/index-segment.ts";
|
|
54
56
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
55
57
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
56
|
-
import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateAcceptanceInput, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
58
|
+
import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateAcceptanceInput, validateExecutionAcceptance, validateExecutionAcceptancePolicy } from "../shared/acceptance.ts";
|
|
57
59
|
import { canPreferFork, createForkContextResolver, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
|
|
58
60
|
import { createPrunedForkSessionWriter } from "../../shared/pruned-fork.ts";
|
|
59
61
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
@@ -68,6 +70,7 @@ import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig
|
|
|
68
70
|
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
69
71
|
import { isAgentContract } from "../shared/agent-contract.ts";
|
|
70
72
|
import { normalizeExtensionBindings, type ExtensionBindings } from "../shared/extension-bindings.ts";
|
|
73
|
+
import { resolveRequiredChildExtensions } from "../../shared/required-child-extensions.ts";
|
|
71
74
|
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
72
75
|
import { assertJsonSchemaObject, cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
73
76
|
import { compactForegroundDetails, getSingleResultOutput, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.ts";
|
|
@@ -96,7 +99,7 @@ import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
|
|
|
96
99
|
import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletion } from "./workflow-detach-reconcile.ts";
|
|
97
100
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
98
101
|
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
|
|
99
|
-
import { resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
|
|
102
|
+
import { fallbackResultPayloadPathForSessionRun, removeResultIndex, resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
|
|
100
103
|
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, resolveNestedAsyncDir, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedParentAddress, type NestedRoute, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
101
104
|
import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
|
|
102
105
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
@@ -106,6 +109,7 @@ import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
|
106
109
|
import { getExternalJobProvider } from "../../api/external-job-provider.ts";
|
|
107
110
|
import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.ts";
|
|
108
111
|
import { externalCliReceiptMetadata, normalizeExternalCliRunnerStatus } from "../shared/external-cli-contract.ts";
|
|
112
|
+
import { formatHerdrMachineRunnerUnsupported, resolveHerdrMachinePlacement } from "../shared/herdr-machine.ts";
|
|
109
113
|
import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
|
|
110
114
|
import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
|
|
111
115
|
import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
@@ -117,7 +121,7 @@ import { handleInspectorAction, INSPECTOR_ACTIONS } from "../../inspectors/actio
|
|
|
117
121
|
import { createBuiltinInspectorPlugins } from "../../inspectors/plugins.ts";
|
|
118
122
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
119
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";
|
|
120
|
-
import { formatIncrementalChildCompletion,
|
|
124
|
+
import { formatIncrementalChildCompletion, incrementalChildCompletionTriggersTurn } from "../background/notify.ts";
|
|
121
125
|
import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath, type WorkflowHostCommandParams, type WorkflowHostCommandResult } from "../../workflows/host-command.ts";
|
|
122
126
|
import { buildWorkflowReceipt, readWorkflowReceipt, workflowReceiptPath, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
|
|
123
127
|
import { upsertHostStep, validHostStepNodes } from "../shared/host-step-status.ts";
|
|
@@ -135,7 +139,8 @@ import {
|
|
|
135
139
|
type WorkflowResourceAuthority,
|
|
136
140
|
type WorkflowResourcePermit,
|
|
137
141
|
} from "../../shared/workflow-child-permit.ts";
|
|
138
|
-
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";
|
|
139
144
|
import {
|
|
140
145
|
cleanupWorktrees,
|
|
141
146
|
createWorktrees,
|
|
@@ -287,6 +292,7 @@ interface TaskParam {
|
|
|
287
292
|
agent: string;
|
|
288
293
|
task: string;
|
|
289
294
|
cwd?: string;
|
|
295
|
+
machine?: string;
|
|
290
296
|
count?: number;
|
|
291
297
|
output?: string | boolean;
|
|
292
298
|
outputMode?: "inline" | "file-only";
|
|
@@ -295,7 +301,7 @@ interface TaskParam {
|
|
|
295
301
|
model?: string;
|
|
296
302
|
fast?: boolean;
|
|
297
303
|
skill?: string | string[] | boolean;
|
|
298
|
-
outputSchema?: JsonSchemaObject;
|
|
304
|
+
outputSchema?: JsonSchemaObject | false;
|
|
299
305
|
acceptance?: AcceptanceInput;
|
|
300
306
|
agentContract?: AgentContract;
|
|
301
307
|
toolBudget?: ToolBudgetConfig;
|
|
@@ -371,6 +377,8 @@ export interface SubagentParamsLike {
|
|
|
371
377
|
foregroundOnly?: boolean;
|
|
372
378
|
timeoutMs?: number;
|
|
373
379
|
maxRuntimeMs?: number;
|
|
380
|
+
/** Async runs only: steer the child to checkpoint and stop this many ms before the run deadline. */
|
|
381
|
+
checkpointBeforeDeadlineMs?: number;
|
|
374
382
|
/** Optional hard per-tool-call timeout (ms). Known-fast tools also have a default. */
|
|
375
383
|
toolTimeoutMs?: number;
|
|
376
384
|
toolBudget?: ToolBudgetConfig;
|
|
@@ -380,6 +388,9 @@ export interface SubagentParamsLike {
|
|
|
380
388
|
control?: ControlConfig;
|
|
381
389
|
sessionDir?: string;
|
|
382
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;
|
|
383
394
|
maxOutput?: MaxOutputConfig;
|
|
384
395
|
artifacts?: boolean;
|
|
385
396
|
includeProgress?: boolean;
|
|
@@ -399,7 +410,7 @@ export interface SubagentParamsLike {
|
|
|
399
410
|
/** Internal-only; not part of the public tool schema. Wired for single-run reads (chain steps use their own field). */
|
|
400
411
|
reads?: string[] | false;
|
|
401
412
|
outputMode?: "inline" | "file-only";
|
|
402
|
-
outputSchema?: JsonSchemaObject;
|
|
413
|
+
outputSchema?: JsonSchemaObject | false;
|
|
403
414
|
agentScope?: unknown;
|
|
404
415
|
chainDir?: string;
|
|
405
416
|
acceptance?: AcceptanceInput;
|
|
@@ -746,7 +757,7 @@ function foregroundChildActivityFromProgress(progress: SingleResult["progress"]
|
|
|
746
757
|
};
|
|
747
758
|
}
|
|
748
759
|
|
|
749
|
-
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 {
|
|
750
761
|
state.foregroundRuns ??= new Map();
|
|
751
762
|
const previous = state.foregroundRuns.get(input.runId);
|
|
752
763
|
const updatedAt = Date.now();
|
|
@@ -799,6 +810,7 @@ function rememberForegroundRun(state: SubagentState, input: { modelResponseAlias
|
|
|
799
810
|
...(Object.keys(resumeContract).length ? { resumeContract } : {}),
|
|
800
811
|
...(result.launchContractDigest ? { launchContractDigest: result.launchContractDigest } : {}),
|
|
801
812
|
...(input.extensionBindings ? { extensionBindings: input.extensionBindings } : {}),
|
|
813
|
+
...(input.requiredExtensions?.length ? { requiredExtensions: input.requiredExtensions } : {}),
|
|
802
814
|
...(result.launchResolvedExtensions ? { launchResolvedExtensions: result.launchResolvedExtensions } : {}),
|
|
803
815
|
...(result.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: result.runtimeAcknowledgedExtensions } : {}),
|
|
804
816
|
...(result.capabilityCeiling ? { capabilityCeiling: result.capabilityCeiling } : {}),
|
|
@@ -950,6 +962,7 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
950
962
|
...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
|
|
951
963
|
...(child.resumeContract ? { resumeContract: child.resumeContract } : {}),
|
|
952
964
|
...(child.extensionBindings ? { extensionBindings: normalizeExtensionBindings(child.extensionBindings)!.value } : {}),
|
|
965
|
+
...(child.requiredExtensions ? { requiredExtensions: child.requiredExtensions } : {}),
|
|
953
966
|
...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
|
|
954
967
|
};
|
|
955
968
|
}
|
|
@@ -1240,10 +1253,14 @@ function appendStepToAsyncChain(input: {
|
|
|
1240
1253
|
};
|
|
1241
1254
|
}
|
|
1242
1255
|
const chain = [input.params.step];
|
|
1243
|
-
const
|
|
1244
|
-
|
|
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) {
|
|
1245
1262
|
return {
|
|
1246
|
-
content: [{ type: "text", text: `Cannot append step: ${
|
|
1263
|
+
content: [{ type: "text", text: `Cannot append step: ${earlyErrors.join(" ")}` }],
|
|
1247
1264
|
isError: true,
|
|
1248
1265
|
details: { mode: "management", results: [] },
|
|
1249
1266
|
};
|
|
@@ -1325,6 +1342,14 @@ function appendStepToAsyncChain(input: {
|
|
|
1325
1342
|
const scope: AgentScope = resolveExecutionAgentScope(input.params.agentScope);
|
|
1326
1343
|
const discoveredForAppend = input.deps.discoverAgents(input.requestCwd, scope, input.parentModel?.provider);
|
|
1327
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
|
+
}
|
|
1328
1353
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
1329
1354
|
const chainSkillInput = normalizeSkillInput(input.params.skill);
|
|
1330
1355
|
const chainSkills = chainSkillInput === false ? [] : (chainSkillInput ?? []);
|
|
@@ -1705,7 +1730,7 @@ async function resumeExternalJobFollowUp(input: {
|
|
|
1705
1730
|
...input.baseAgentConfig,
|
|
1706
1731
|
runner: { type: "external-job", provider: runner.provider, options: runner.options },
|
|
1707
1732
|
};
|
|
1708
|
-
const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
1733
|
+
const result = await executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
1709
1734
|
agent: input.target.agent,
|
|
1710
1735
|
task: input.followUp,
|
|
1711
1736
|
goal: input.followUp,
|
|
@@ -1802,13 +1827,13 @@ async function resumeAsyncRun(input: {
|
|
|
1802
1827
|
details: { mode: "management", results: [] },
|
|
1803
1828
|
};
|
|
1804
1829
|
}
|
|
1805
|
-
const
|
|
1806
|
-
if (
|
|
1807
|
-
return {
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
};
|
|
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: [] } };
|
|
1812
1837
|
}
|
|
1813
1838
|
input.deps.state.currentSessionId = resolveCurrentSessionId(input.ctx.sessionManager);
|
|
1814
1839
|
|
|
@@ -1925,6 +1950,10 @@ async function resumeAsyncRun(input: {
|
|
|
1925
1950
|
details: { mode: "chain", results: [] },
|
|
1926
1951
|
};
|
|
1927
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
|
+
}
|
|
1928
1957
|
const runId = randomUUID();
|
|
1929
1958
|
const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
|
|
1930
1959
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
@@ -1993,6 +2022,7 @@ async function resumeAsyncRun(input: {
|
|
|
1993
2022
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1994
2023
|
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
1995
2024
|
baseRef: input.params.baseRef ?? target.recoveryDescriptor?.baseRef,
|
|
2025
|
+
...(recoveryDescriptor?.requiredExtensions ? { requiredExtensions: recoveryDescriptor.requiredExtensions } : {}),
|
|
1996
2026
|
worktreeProvider: input.deps.config.worktreeProvider,
|
|
1997
2027
|
worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
|
|
1998
2028
|
controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
|
|
@@ -2032,6 +2062,23 @@ async function resumeAsyncRun(input: {
|
|
|
2032
2062
|
if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
|
|
2033
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: [] } };
|
|
2034
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
|
+
}
|
|
2035
2082
|
const runId = randomUUID();
|
|
2036
2083
|
const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
|
|
2037
2084
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
@@ -2055,17 +2102,13 @@ async function resumeAsyncRun(input: {
|
|
|
2055
2102
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
|
|
2056
2103
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "single", results: [] } };
|
|
2057
2104
|
}
|
|
2058
|
-
const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(baseAgentConfig, recoveryDescriptor) : baseAgentConfig;
|
|
2059
|
-
const agentConfig = intercomBridge.active ? applyIntercomBridgeToAgent(recoveryAgentConfig, intercomBridge) : recoveryAgentConfig;
|
|
2060
|
-
const foregroundContract = target.source === "foreground" ? target.resumeContract : undefined;
|
|
2061
|
-
const outputSchema = input.params.outputSchema ?? foregroundContract?.outputSchema ?? recoveryDescriptor?.structuredOutputSchema;
|
|
2062
2105
|
const agentContract = input.params.agentContract ?? foregroundContract?.agentContract ?? recoveryDescriptor?.agentContract;
|
|
2063
2106
|
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? omitUndefinedProperties({ ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir });
|
|
2064
2107
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
2065
2108
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
2066
2109
|
const parentModel = input.parentModel;
|
|
2067
2110
|
const revivalAsyncDir = path.join(DIRS.async, runId);
|
|
2068
|
-
const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
2111
|
+
const result = await executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
2069
2112
|
agent: target.agent,
|
|
2070
2113
|
task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0], effectiveFollowUp),
|
|
2071
2114
|
goal: effectiveFollowUp,
|
|
@@ -2107,6 +2150,7 @@ async function resumeAsyncRun(input: {
|
|
|
2107
2150
|
thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
|
|
2108
2151
|
thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
|
|
2109
2152
|
extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
|
|
2153
|
+
requiredExtensions: recoveryDescriptor?.requiredExtensions ?? (target as { requiredExtensions?: SteeringRecoveryDescriptor["requiredExtensions"] }).requiredExtensions,
|
|
2110
2154
|
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
2111
2155
|
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
|
|
2112
2156
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
@@ -2132,7 +2176,7 @@ async function resumeAsyncRun(input: {
|
|
|
2132
2176
|
...(agentContract ? { agentContract } : {}),
|
|
2133
2177
|
...(outputSchema ? { structuredOutputSchema: outputSchema } : {}),
|
|
2134
2178
|
...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
|
|
2135
|
-
...(
|
|
2179
|
+
...(acceptance !== undefined ? { acceptance } : {}),
|
|
2136
2180
|
...(input.params.timeoutMs !== undefined ? { timeoutMs: input.params.timeoutMs } : {}),
|
|
2137
2181
|
...(input.absoluteDeadlineAt !== undefined ? { absoluteDeadlineAt: input.absoluteDeadlineAt } : {}),
|
|
2138
2182
|
...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
|
|
@@ -2181,7 +2225,7 @@ async function resumeAsyncRun(input: {
|
|
|
2181
2225
|
} finally {
|
|
2182
2226
|
input.signal?.removeEventListener("abort", stopOnAbort);
|
|
2183
2227
|
}
|
|
2184
|
-
|
|
2228
|
+
if (completed.importedPublication) removeWorkflowAwaitedResult(asyncDir, resultPath, revivedId, completed.importedPublication);
|
|
2185
2229
|
const usage = importedAsyncRootUsage(completed);
|
|
2186
2230
|
const childResult: SingleResult = {
|
|
2187
2231
|
index: 0,
|
|
@@ -2197,8 +2241,7 @@ async function resumeAsyncRun(input: {
|
|
|
2197
2241
|
...(completed.stopped ? { stopped: true } : {}),
|
|
2198
2242
|
...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
|
|
2199
2243
|
...(completed.model ? { model: completed.model } : {}),
|
|
2200
|
-
...(completed.
|
|
2201
|
-
...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
|
|
2244
|
+
...(completed.requestedModel ? { requestedModel: completed.requestedModel } : {}),
|
|
2202
2245
|
...(completed.contextOverflow ? { contextOverflow: true } : {}),
|
|
2203
2246
|
...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
|
|
2204
2247
|
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
@@ -2407,6 +2450,8 @@ function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentCo
|
|
|
2407
2450
|
params = omitUndefinedProperties({ ...params, agent: result.name });
|
|
2408
2451
|
const agent = agents.find((candidate) => candidate.name === result.name);
|
|
2409
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 };
|
|
2410
2455
|
}
|
|
2411
2456
|
if (params.extensionBindings !== undefined) {
|
|
2412
2457
|
try {
|
|
@@ -2459,6 +2504,18 @@ function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentCo
|
|
|
2459
2504
|
return { params };
|
|
2460
2505
|
}
|
|
2461
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
|
+
|
|
2462
2519
|
function validateExecutionInput(
|
|
2463
2520
|
params: SubagentParamsLike,
|
|
2464
2521
|
agents: AgentConfig[],
|
|
@@ -2481,7 +2538,7 @@ function validateExecutionInput(
|
|
|
2481
2538
|
};
|
|
2482
2539
|
}
|
|
2483
2540
|
|
|
2484
|
-
const acceptanceErrors = validateExecutionAcceptance(params
|
|
2541
|
+
const acceptanceErrors = validateExecutionAcceptance(projectEffectiveAcceptanceSchemas(params, agents));
|
|
2485
2542
|
if (acceptanceErrors.length > 0) {
|
|
2486
2543
|
return {
|
|
2487
2544
|
content: [{ type: "text", text: acceptanceErrors.join(" ") }],
|
|
@@ -2733,6 +2790,7 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
|
|
|
2733
2790
|
const parentTimeoutMs = params.timeoutMs === undefined && params.maxRuntimeMs === undefined && agent.defaultTimeoutMs === undefined && params.workflowParentDeadlineAt !== undefined
|
|
2734
2791
|
? Math.max(1, params.workflowParentDeadlineAt - Date.now())
|
|
2735
2792
|
: undefined;
|
|
2793
|
+
const outputSchema = params.outputSchema === false ? false : resolveEffectiveOutputSchema(agent, params.outputSchema);
|
|
2736
2794
|
return {
|
|
2737
2795
|
...params,
|
|
2738
2796
|
...(params.async === undefined && agent.defaultAsync !== undefined ? { async: agent.defaultAsync } : {}),
|
|
@@ -2743,9 +2801,28 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
|
|
|
2743
2801
|
...(params.acceptance === undefined && agent.defaultAcceptance !== undefined
|
|
2744
2802
|
? { acceptance: agent.defaultAcceptance }
|
|
2745
2803
|
: {}),
|
|
2804
|
+
...(outputSchema !== undefined ? { outputSchema } : {}),
|
|
2746
2805
|
};
|
|
2747
2806
|
}
|
|
2748
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
|
+
|
|
2749
2826
|
export const DEFAULT_FOREGROUND_TIMEOUT_MS = 30 * 60 * 1000;
|
|
2750
2827
|
|
|
2751
2828
|
// Async single-agent runs also need a wall-clock backstop: a child whose bash
|
|
@@ -2909,11 +2986,20 @@ function withResolvedContext(
|
|
|
2909
2986
|
}
|
|
2910
2987
|
|
|
2911
2988
|
function withAggregatedToolUsage(result: AgentToolResult<Details>): AgentToolResult<Details> {
|
|
2912
|
-
|
|
2913
|
-
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);
|
|
2914
3000
|
return usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0
|
|
2915
|
-
? { ...
|
|
2916
|
-
:
|
|
3001
|
+
? { ...projected, usage: toAgentToolUsage(usage) }
|
|
3002
|
+
: projected;
|
|
2917
3003
|
}
|
|
2918
3004
|
|
|
2919
3005
|
function toExecutionErrorResult(params: SubagentParamsLike, error: unknown, contextSummary?: ContextSummary): AgentToolResult<Details> {
|
|
@@ -3124,6 +3210,15 @@ function importedAsyncRootUsage(completed: Awaited<ReturnType<typeof waitForImpo
|
|
|
3124
3210
|
};
|
|
3125
3211
|
}
|
|
3126
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
|
+
|
|
3127
3222
|
async function waitForWorkflowAsyncSingleResult(
|
|
3128
3223
|
params: SubagentParamsLike,
|
|
3129
3224
|
launchResult: AgentToolResult<Details>,
|
|
@@ -3144,7 +3239,7 @@ async function waitForWorkflowAsyncSingleResult(
|
|
|
3144
3239
|
} finally {
|
|
3145
3240
|
options.signal?.removeEventListener("abort", stopOnAbort);
|
|
3146
3241
|
}
|
|
3147
|
-
|
|
3242
|
+
if (completed.importedPublication) removeWorkflowAwaitedResult(asyncDir, resultPath, options.runId, completed.importedPublication);
|
|
3148
3243
|
const usage = importedAsyncRootUsage(completed);
|
|
3149
3244
|
const childResult: SingleResult = omitUndefinedProperties({
|
|
3150
3245
|
index: 0,
|
|
@@ -3160,8 +3255,7 @@ async function waitForWorkflowAsyncSingleResult(
|
|
|
3160
3255
|
...(completed.stopped ? { stopped: true } : {}),
|
|
3161
3256
|
...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
|
|
3162
3257
|
...(completed.model ? { model: completed.model } : {}),
|
|
3163
|
-
...(completed.
|
|
3164
|
-
...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
|
|
3258
|
+
...(completed.requestedModel ? { requestedModel: completed.requestedModel } : {}),
|
|
3165
3259
|
...(completed.contextOverflow ? { contextOverflow: true } : {}),
|
|
3166
3260
|
...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
|
|
3167
3261
|
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
@@ -3275,7 +3369,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3275
3369
|
const modelOverrideFromParent = modelOrigin === "inherited";
|
|
3276
3370
|
const launchRuleError = applyWatchdogLaunchRules({ cwd: effectiveCwd, agent: a.name, model: modelOverride ?? (parentModel && `${parentModel.provider}/${parentModel.id}`), warn: (violation) => deps.watchdog?.displayRuleWarning(violation) });
|
|
3277
3371
|
if (launchRuleError) return toExecutionErrorResult(params, new Error(launchRuleError), data.contextPolicy.contextSummary);
|
|
3278
|
-
const asyncResult = executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
3372
|
+
const asyncResult = await executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
3279
3373
|
agent: params.agent!,
|
|
3280
3374
|
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
3281
3375
|
goal: params.task ?? "",
|
|
@@ -3285,6 +3379,8 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3285
3379
|
availableModels,
|
|
3286
3380
|
cwd: effectiveCwd,
|
|
3287
3381
|
requestedCwd: data.requestedCwd,
|
|
3382
|
+
machine: params.machine,
|
|
3383
|
+
machineCwd: params.machineCwd,
|
|
3288
3384
|
maxOutput: params.maxOutput,
|
|
3289
3385
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3290
3386
|
artifactConfig,
|
|
@@ -3321,7 +3417,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3321
3417
|
childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(agent, index) : undefined,
|
|
3322
3418
|
nestedRoute,
|
|
3323
3419
|
agentContract: params.agentContract,
|
|
3324
|
-
structuredOutputSchema: params.outputSchema,
|
|
3420
|
+
structuredOutputSchema: params.outputSchema || undefined,
|
|
3325
3421
|
extensionBindings: params.extensionBindings,
|
|
3326
3422
|
acceptance: params.acceptance,
|
|
3327
3423
|
timeoutMs: data.timeoutMs,
|
|
@@ -3329,6 +3425,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3329
3425
|
usageBudget: data.usageBudget,
|
|
3330
3426
|
configToolBudget: data.configToolBudget,
|
|
3331
3427
|
toolTimeoutMs: data.params?.toolTimeoutMs,
|
|
3428
|
+
checkpointBeforeDeadlineMs: data.params?.checkpointBeforeDeadlineMs ?? deps.config.checkpointBeforeDeadlineMs,
|
|
3332
3429
|
configToolTimeoutMs: data.configToolTimeoutMs,
|
|
3333
3430
|
capabilityCeiling: data.capabilityCeiling,
|
|
3334
3431
|
runFanoutBudget: data.runFanoutBudget,
|
|
@@ -3447,6 +3544,23 @@ function appendWorkflowOutputWarning(text: string, warning: string | undefined):
|
|
|
3447
3544
|
return warning ? `${text}\n\n${warning}` : text;
|
|
3448
3545
|
}
|
|
3449
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
|
+
|
|
3450
3564
|
function resolveWorkflowChildOutputPath(input: {
|
|
3451
3565
|
ctxCwd: string;
|
|
3452
3566
|
workflowCwd: string;
|
|
@@ -3472,7 +3586,7 @@ function resolveWorkflowChildOutputPath(input: {
|
|
|
3472
3586
|
}, input.state);
|
|
3473
3587
|
return { path: "recoveryDescriptor" in target ? target.recoveryDescriptor?.outputPath : undefined, inherited: false };
|
|
3474
3588
|
}
|
|
3475
|
-
const childCwd =
|
|
3589
|
+
const childCwd = resolveWorkflowChildLocalCwd(input);
|
|
3476
3590
|
let agentOutput: string | undefined;
|
|
3477
3591
|
if (rawOutput === true || rawOutput === "true" || (!hasExplicitOutput && !input.aggregateOutputPath)) {
|
|
3478
3592
|
const agentScope = resolveExecutionAgentScope(input.params.agentScope ?? input.workflowAgentScope);
|
|
@@ -3574,7 +3688,7 @@ function prepareWorkflowChildLaunchParams(input: {
|
|
|
3574
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 });
|
|
3575
3689
|
if (resolvedOutput.path) childParams = { ...input.childParams, output: resolvedOutput.path };
|
|
3576
3690
|
}
|
|
3577
|
-
const childCwd =
|
|
3691
|
+
const childCwd = resolveWorkflowChildLocalCwd({ ...input, params: childParams });
|
|
3578
3692
|
const agentScope = resolveExecutionAgentScope(childParams.agentScope ?? input.workflowAgentScope);
|
|
3579
3693
|
const discoveredAgents = input.discoverAgents(childCwd, agentScope).agents;
|
|
3580
3694
|
const agent = typeof childParams.agent === "string"
|
|
@@ -3706,6 +3820,12 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3706
3820
|
details: { mode: "single", results: [] },
|
|
3707
3821
|
};
|
|
3708
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
|
+
}
|
|
3709
3829
|
const effectiveToolBudget = resolveEffectiveToolBudget(omitUndefinedProperties({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget }));
|
|
3710
3830
|
if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error), data.contextPolicy.contextSummary);
|
|
3711
3831
|
|
|
@@ -3743,7 +3863,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3743
3863
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
|
|
3744
3864
|
|
|
3745
3865
|
|
|
3746
|
-
const sourceCwd = effectiveCwd;
|
|
3866
|
+
const sourceCwd = foregroundMachine?.cwd ?? effectiveCwd;
|
|
3747
3867
|
let pendingHandoff: Details["parallelHandoff"];
|
|
3748
3868
|
const { setup: worktreeSetup, errorResult: worktreeSetupError } = params.worktree ? await createSingleWorktreeSetup(
|
|
3749
3869
|
sourceCwd,
|
|
@@ -3820,7 +3940,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3820
3940
|
// Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
|
|
3821
3941
|
// absolute paths pass through; relative paths resolve against the child cwd.
|
|
3822
3942
|
const reads = readsOverride !== undefined ? readsOverride : agentConfig.defaultReads ?? false;
|
|
3823
|
-
const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
|
|
3943
|
+
const readPaths = !foregroundMachine && Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
|
|
3824
3944
|
const readsInstruction = readPaths.length > 0
|
|
3825
3945
|
? `[Read from: ${readPaths.join(", ")}]\n\n`
|
|
3826
3946
|
: "";
|
|
@@ -3883,6 +4003,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3883
4003
|
: undefined;
|
|
3884
4004
|
|
|
3885
4005
|
const deadlineAt = data.deadlineAt ?? (data.timeoutMs !== undefined ? Date.now() + data.timeoutMs : undefined);
|
|
4006
|
+
const requiredExtensions = deps.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(data.parentPiSessionId);
|
|
3886
4007
|
let r: Awaited<ReturnType<typeof runSync>> | undefined;
|
|
3887
4008
|
let resolveDetachedWorkflowChild: ((result: Awaited<ReturnType<typeof runSync>>) => void) | undefined;
|
|
3888
4009
|
const detachedWorkflowChild = params.workflowAwaitDetached === true
|
|
@@ -3890,10 +4011,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3890
4011
|
: undefined;
|
|
3891
4012
|
try {
|
|
3892
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,
|
|
3893
4017
|
permissions: deps.config.permissions,
|
|
3894
4018
|
runtimeSnapshotHost: deps.pi,
|
|
3895
4019
|
hostAvailableBuiltins: getHostBuiltinToolNames(deps.pi),
|
|
3896
4020
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
4021
|
+
requiredExtensions,
|
|
3897
4022
|
llmIntentArbiter: createTaskMutationArbiter({ model: ctx.model, modelRegistry: ctx.modelRegistry, sessionId: ctx.sessionManager.getSessionId() }),
|
|
3898
4023
|
childRuntime: deps.childRuntime,
|
|
3899
4024
|
onChildSession: (controls) => { childSessionControls = controls; },
|
|
@@ -4054,7 +4179,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4054
4179
|
usageBudget: usageBudgetState(data.usageBudget, totalCost),
|
|
4055
4180
|
...(worktreeHandoff?.reference ? { parallelHandoff: worktreeHandoff.reference } : {}),
|
|
4056
4181
|
}));
|
|
4057
|
-
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 });
|
|
4058
4183
|
|
|
4059
4184
|
const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results: [r] });
|
|
4060
4185
|
if (!r.detached && !r.interrupted && !suppressRoutineResultIntercom) {
|
|
@@ -4456,8 +4581,9 @@ function terminalWorkflowReceipt(
|
|
|
4456
4581
|
terminalOutcome?: WorkflowTerminalOutcome,
|
|
4457
4582
|
hostSteps?: WorkflowReceipt["hostSteps"],
|
|
4458
4583
|
resource?: WorkflowReceipt["resource"],
|
|
4584
|
+
argsDigest?: string,
|
|
4459
4585
|
): WorkflowReceipt {
|
|
4460
|
-
return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps, resource });
|
|
4586
|
+
return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps, resource, ...(argsDigest ? { argsDigest } : {}) });
|
|
4461
4587
|
}
|
|
4462
4588
|
|
|
4463
4589
|
function workflowFailureTerminalOutcome(error: unknown, _children: WorkflowScriptChildResult[], usageBudget: ReturnType<typeof usageBudgetState>): WorkflowTerminalOutcome | undefined {
|
|
@@ -4603,7 +4729,7 @@ export function prepareWorkflowLaunchParams(
|
|
|
4603
4729
|
const worktree = childParams.worktree ?? workflowDefaults.worktree;
|
|
4604
4730
|
const baseRef = Object.hasOwn(childParams, "baseRef") ? childParams.baseRef : undefined;
|
|
4605
4731
|
const outputSchema = Object.hasOwn(childParams, "outputSchema") ? childParams.outputSchema : workflowDefaults.outputSchema;
|
|
4606
|
-
if (outputSchema !== undefined) assertJsonSchemaObject(outputSchema, "outputSchema");
|
|
4732
|
+
if (outputSchema !== undefined && outputSchema !== false) assertJsonSchemaObject(outputSchema, "outputSchema");
|
|
4607
4733
|
const agentContract = Object.hasOwn(childParams, "agentContract") ? childParams.agentContract : workflowDefaults.agentContract;
|
|
4608
4734
|
if (agentContract !== undefined && !isAgentContract(agentContract as AgentContract)) throw new Error("agentContract must be { version: 1 }.");
|
|
4609
4735
|
const acceptance = Object.hasOwn(childParams, "acceptance") ? childParams.acceptance : workflowDefaults.acceptance;
|
|
@@ -4623,7 +4749,7 @@ export function prepareWorkflowLaunchParams(
|
|
|
4623
4749
|
...(lane ? { lane } : {}),
|
|
4624
4750
|
...(worktree !== undefined ? { worktree: worktree as boolean } : {}),
|
|
4625
4751
|
...(baseRef !== undefined ? { baseRef: baseRef as string } : {}),
|
|
4626
|
-
...(outputSchema !== undefined ? { outputSchema
|
|
4752
|
+
...(outputSchema !== undefined ? { outputSchema } : {}),
|
|
4627
4753
|
...(agentContract !== undefined ? { agentContract: agentContract as AgentContract } : {}),
|
|
4628
4754
|
...(acceptance !== undefined ? { acceptance: acceptance as AcceptanceInput } : {}),
|
|
4629
4755
|
...(output !== undefined ? { output: output as string | boolean } : {}),
|
|
@@ -4947,6 +5073,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4947
5073
|
workflowResource = { permit: workflowResourcePermit, ...consumed };
|
|
4948
5074
|
}
|
|
4949
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;
|
|
4950
5079
|
const spawnBudgetValidation = validateWorkflowScript(requestParams.workflowScript, {
|
|
4951
5080
|
maxSubagentSpawnsPerRun: requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
|
|
4952
5081
|
});
|
|
@@ -5114,7 +5243,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5114
5243
|
pid: process.pid,
|
|
5115
5244
|
steps: [],
|
|
5116
5245
|
...(workflowPreflight ? { preflight: workflowPreflight } : {}),
|
|
5117
|
-
workflow: { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) },
|
|
5246
|
+
workflow: { trace: [], emits: [], console: [], ...(workflowArgsEvidence ?? {}), ...(workflowResource ? { resource: workflowResource.provenance } : {}) },
|
|
5118
5247
|
workflowChildren: workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "running", inventoryComplete: false }),
|
|
5119
5248
|
runFanoutBudget: getRunFanoutBudgetSnapshot(workflowFanoutBudget),
|
|
5120
5249
|
};
|
|
@@ -5144,10 +5273,37 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5144
5273
|
};
|
|
5145
5274
|
let pendingResultPublication: Promise<boolean> | undefined;
|
|
5146
5275
|
let settleResultPublication: ((published: boolean) => void) | undefined;
|
|
5147
|
-
|
|
5276
|
+
let resultWriteFailureWakeDelivered = false;
|
|
5277
|
+
const reportResultWriteFailure = (error: unknown): boolean => {
|
|
5148
5278
|
const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
5149
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
|
+
}
|
|
5150
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;
|
|
5151
5307
|
};
|
|
5152
5308
|
const runPersistence = createCapacityResilientJsonWriter({
|
|
5153
5309
|
keepAlive: true,
|
|
@@ -5156,7 +5312,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5156
5312
|
if (filePath === resultPath) settleResultPublication?.(true);
|
|
5157
5313
|
},
|
|
5158
5314
|
onError: (error, filePath) => {
|
|
5159
|
-
if (filePath === resultPath)
|
|
5315
|
+
if (filePath === resultPath) settleResultPublication?.(reportResultWriteFailure(error));
|
|
5160
5316
|
else console.error(`Failed to persist async workflow state '${filePath}':`, error);
|
|
5161
5317
|
},
|
|
5162
5318
|
write: (filePath, payload) => filePath === resultPath
|
|
@@ -5237,8 +5393,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5237
5393
|
if (!resultPublished) pendingResultPublication = new Promise<boolean>((resolve) => { settleResultPublication = resolve; });
|
|
5238
5394
|
return true;
|
|
5239
5395
|
} catch (error) {
|
|
5240
|
-
reportResultWriteFailure(error);
|
|
5241
|
-
return false;
|
|
5396
|
+
return reportResultWriteFailure(error);
|
|
5242
5397
|
}
|
|
5243
5398
|
};
|
|
5244
5399
|
const projectWorkflowActivity = () => {
|
|
@@ -5276,7 +5431,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5276
5431
|
indexPersistence.dispose();
|
|
5277
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: [] } };
|
|
5278
5433
|
}
|
|
5279
|
-
appendWorkflowEvent({ type: "subagent.workflow.started" });
|
|
5434
|
+
appendWorkflowEvent({ type: "subagent.workflow.started", ...(workflowArgsDigest ? { argsDigest: workflowArgsDigest } : {}) });
|
|
5280
5435
|
// The runner does not execute workflow scripts: this active owner must consume its own
|
|
5281
5436
|
// durable steer inbox, deliver through in-process child controls, and persist receipts.
|
|
5282
5437
|
const emitWorkflowSteerEvent = (type: string, requestId: string, index?: number, extra: Record<string, unknown> = {}): void => {
|
|
@@ -5423,7 +5578,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5423
5578
|
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
5424
5579
|
const workflowResults: SingleResult[] = [];
|
|
5425
5580
|
const workflowChildRunIds = new Map<string, string>();
|
|
5426
|
-
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;
|
|
5427
5582
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
5428
5583
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
5429
5584
|
const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, parentCwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
|
|
@@ -5548,7 +5703,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5548
5703
|
try {
|
|
5549
5704
|
const workflow = await runWorkflowScript({
|
|
5550
5705
|
script: workflowScript,
|
|
5706
|
+
...(workflowArgs ? { args: workflowArgs } : {}),
|
|
5551
5707
|
workflowRunId,
|
|
5708
|
+
processCwd: ctx.cwd,
|
|
5552
5709
|
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5553
5710
|
timeoutMs: timeout,
|
|
5554
5711
|
signal: controller.signal,
|
|
@@ -5579,7 +5736,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5579
5736
|
content: formatIncrementalChildCompletion(notification),
|
|
5580
5737
|
display: notification.outcome !== "completed",
|
|
5581
5738
|
},
|
|
5582
|
-
{ triggerTurn:
|
|
5739
|
+
{ triggerTurn: incrementalChildCompletionTriggersTurn(notification, requestParams.scheduleOrigin) },
|
|
5583
5740
|
);
|
|
5584
5741
|
} catch (sendError) {
|
|
5585
5742
|
console.error(`Failed to send incremental child completion notification for '${notification.childKey}':`, sendError);
|
|
@@ -5719,8 +5876,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5719
5876
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
5720
5877
|
settleWorkflowSteerInbox("complete");
|
|
5721
5878
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children, steps: status.steps });
|
|
5722
|
-
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) };
|
|
5723
|
-
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);
|
|
5724
5881
|
delete status.workflowReceiptPath;
|
|
5725
5882
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
5726
5883
|
try {
|
|
@@ -5734,7 +5891,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5734
5891
|
persist();
|
|
5735
5892
|
deps.refreshResultDelivery?.();
|
|
5736
5893
|
persistClosed = true;
|
|
5737
|
-
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 } : {}) });
|
|
5738
5895
|
} catch (error) {
|
|
5739
5896
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
5740
5897
|
const stopped = controller.signal.aborted;
|
|
@@ -5758,7 +5915,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5758
5915
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: true, trace: partial.trace, children: partial.children, steps: status.steps });
|
|
5759
5916
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
|
|
5760
5917
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
|
|
5761
|
-
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 });
|
|
5762
5919
|
if (pauseForDetached) {
|
|
5763
5920
|
const promoted = promotePausedWorkflowIfSettled(status);
|
|
5764
5921
|
if (promoted) status = promoted;
|
|
@@ -5770,7 +5927,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5770
5927
|
const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
|
|
5771
5928
|
const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
|
|
5772
5929
|
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
5773
|
-
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);
|
|
5774
5931
|
delete status.workflowReceiptPath;
|
|
5775
5932
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
5776
5933
|
try {
|
|
@@ -5784,7 +5941,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5784
5941
|
persist();
|
|
5785
5942
|
deps.refreshResultDelivery?.();
|
|
5786
5943
|
persistClosed = true;
|
|
5787
|
-
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 } : {}) });
|
|
5788
5945
|
} finally {
|
|
5789
5946
|
// Idempotent cleanup only: a failed result/index write must not authorize terminal status.
|
|
5790
5947
|
try {
|
|
@@ -5804,7 +5961,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5804
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 } : {}) },
|
|
5805
5962
|
}, workflowFanoutBudget));
|
|
5806
5963
|
}
|
|
5807
|
-
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;
|
|
5808
5965
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
5809
5966
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
5810
5967
|
const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, foregroundWorkflowRunId));
|
|
@@ -5846,7 +6003,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5846
6003
|
try {
|
|
5847
6004
|
const workflow = await runWorkflowScript({
|
|
5848
6005
|
script: requestParams.workflowScript,
|
|
6006
|
+
...(workflowArgs ? { args: workflowArgs } : {}),
|
|
5849
6007
|
workflowRunId: foregroundWorkflowRunId,
|
|
6008
|
+
processCwd: ctx.cwd,
|
|
5850
6009
|
...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, foregroundWorkflowRunId, key) } } : {}),
|
|
5851
6010
|
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5852
6011
|
timeoutMs: timeout,
|
|
@@ -5962,7 +6121,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5962
6121
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
|
|
5963
6122
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
|
|
5964
6123
|
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children });
|
|
5965
|
-
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);
|
|
5966
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}` : ""}`);
|
|
5967
6126
|
const sections = [
|
|
5968
6127
|
...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
|
|
@@ -5980,7 +6139,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5980
6139
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
5981
6140
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5982
6141
|
content: [{ type: "text", text: displayText }],
|
|
5983
|
-
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 }),
|
|
5984
6143
|
}, workflowFanoutBudget));
|
|
5985
6144
|
} catch (error) {
|
|
5986
6145
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
@@ -6003,11 +6162,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6003
6162
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
6004
6163
|
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "failed", inventoryComplete: true, trace: partial.trace, children: partial.children });
|
|
6005
6164
|
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
6006
|
-
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);
|
|
6007
6166
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
6008
6167
|
content: [{ type: "text", text: displayText }],
|
|
6009
6168
|
isError: true,
|
|
6010
|
-
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 }),
|
|
6011
6170
|
}, workflowFanoutBudget));
|
|
6012
6171
|
} finally {
|
|
6013
6172
|
workflowProgressClosed = true;
|
|
@@ -6016,9 +6175,24 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6016
6175
|
}
|
|
6017
6176
|
}
|
|
6018
6177
|
const directParams = requestParams;
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
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 };
|
|
6022
6196
|
const action = paramsWithResolvedCwd.action;
|
|
6023
6197
|
let requestSessionId = "";
|
|
6024
6198
|
let requestPiSessionId: string | undefined;
|
|
@@ -6117,6 +6291,24 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6117
6291
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
|
|
6118
6292
|
}
|
|
6119
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
|
+
}
|
|
6120
6312
|
if ((HERDR_PROJECT_PANE_ACTIONS as readonly string[]).includes(action)) {
|
|
6121
6313
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
6122
6314
|
return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -6153,18 +6345,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6153
6345
|
...(currentSessionId ? { currentSessionId } : {}),
|
|
6154
6346
|
});
|
|
6155
6347
|
}
|
|
6156
|
-
const policyAction = action === "stop" ? "stopRun" : action === "steer" ? "steerRun" : action === "schedule.create" ? "scheduleCreate" : undefined;
|
|
6157
|
-
if (policyAction) {
|
|
6158
|
-
const decision = resolveAuthorityDecision({ action: policyAction, ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
|
|
6159
|
-
if (decision === "forbid") {
|
|
6160
|
-
return { content: [{ type: "text", text: `Authority policy forbids action '${action}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6161
|
-
}
|
|
6162
|
-
if (decision === "confirm") {
|
|
6163
|
-
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: [] } };
|
|
6164
|
-
const confirmed = await ctx.ui.confirm(`Authorize subagent ${action}?`, `Authority policy requires confirmation before '${action}'.`);
|
|
6165
|
-
if (!confirmed) return { content: [{ type: "text", text: `Action '${action}' canceled; authority was not granted.` }], details: { mode: "management", results: [] } };
|
|
6166
|
-
}
|
|
6167
|
-
}
|
|
6168
6348
|
if ((WATCHDOG_TOOL_ACTIONS as readonly string[]).includes(action)) {
|
|
6169
6349
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
6170
6350
|
return {
|
|
@@ -6676,6 +6856,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6676
6856
|
const normalized = normalizeRepeatedParallelCounts(paramsWithResolvedCwd);
|
|
6677
6857
|
if (normalized.error) return normalized.error;
|
|
6678
6858
|
const normalizedParams = normalized.params!;
|
|
6859
|
+
const outputSchemaError = validateLaunchOutputSchemaOverrides(normalizedParams);
|
|
6860
|
+
if (outputSchemaError) return buildRequestedModeError(normalizedParams, outputSchemaError);
|
|
6679
6861
|
|
|
6680
6862
|
let effectiveParams = applyForceTopLevelAsyncOverride(
|
|
6681
6863
|
normalizedParams,
|
|
@@ -7257,6 +7439,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
7257
7439
|
const { workflow: _workflow, args: _args, ...withoutResourceInput } = publicParams;
|
|
7258
7440
|
publicParams = { ...withoutResourceInput, workflowScript: resolved.resource.script };
|
|
7259
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) };
|
|
7260
7446
|
}
|
|
7261
7447
|
const loaded = loadWorkflowScriptPath(publicParams, ctx.cwd);
|
|
7262
7448
|
if (loaded.error) {
|