pi-subagents 0.73.0 → 0.74.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 +65 -0
- package/README.md +1 -1
- package/docs/agents.md +18 -12
- package/docs/configuration.md +66 -9
- package/docs/extension-api.md +5 -4
- package/docs/missions.md +13 -13
- package/docs/models.md +2 -2
- package/docs/observability.md +2 -2
- package/docs/tool-reference.md +63 -86
- package/docs/workflows.md +165 -171
- package/package.json +2 -2
- package/prompts/review-loop.md +1 -1
- package/skills/council-mode/SKILL.md +1 -1
- package/skills/pi-subagents/SKILL.md +12 -5
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -3
- package/skills/pi-subagents/references/execution-controls.md +68 -67
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +45 -48
- package/src/agents/advertised-agent-prompt.d.ts +6 -4
- package/src/agents/advertised-agent-prompt.d.ts.map +1 -1
- package/src/agents/advertised-agent-prompt.js +16 -30
- package/src/agents/advertised-agent-prompt.js.map +1 -1
- package/src/agents/agent-management.js +2 -2
- package/src/agents/agent-management.js.map +1 -1
- package/src/agents/agents.d.ts +1 -0
- package/src/agents/agents.d.ts.map +1 -1
- package/src/agents/agents.js +8 -0
- package/src/agents/agents.js.map +1 -1
- package/src/api/preflight.d.ts +5 -1
- package/src/api/preflight.d.ts.map +1 -1
- package/src/api/preflight.js +2 -1
- package/src/api/preflight.js.map +1 -1
- package/src/extension/config.d.ts.map +1 -1
- package/src/extension/config.js +12 -4
- package/src/extension/config.js.map +1 -1
- package/src/extension/control-notices.d.ts +2 -0
- package/src/extension/control-notices.d.ts.map +1 -1
- package/src/extension/control-notices.js +2 -0
- package/src/extension/control-notices.js.map +1 -1
- package/src/extension/doctor.d.ts.map +1 -1
- package/src/extension/doctor.js +1 -0
- package/src/extension/doctor.js.map +1 -1
- package/src/extension/fanout-child.d.ts.map +1 -1
- package/src/extension/fanout-child.js +13 -3
- package/src/extension/fanout-child.js.map +1 -1
- package/src/extension/herdr-pi-bridge.d.ts.map +1 -1
- package/src/extension/herdr-pi-bridge.js +2 -0
- package/src/extension/herdr-pi-bridge.js.map +1 -1
- package/src/extension/index.d.ts +1 -1
- package/src/extension/index.d.ts.map +1 -1
- package/src/extension/index.js +71 -169
- package/src/extension/index.js.map +1 -1
- package/src/extension/public-execution.d.ts +10 -1
- package/src/extension/public-execution.d.ts.map +1 -1
- package/src/extension/public-execution.js +69 -35
- package/src/extension/public-execution.js.map +1 -1
- package/src/extension/reply-workflow-script.d.ts +12 -0
- package/src/extension/reply-workflow-script.d.ts.map +1 -0
- package/src/extension/reply-workflow-script.js +61 -0
- package/src/extension/reply-workflow-script.js.map +1 -0
- package/src/extension/rpc.d.ts +2 -0
- package/src/extension/rpc.d.ts.map +1 -1
- package/src/extension/rpc.js +33 -10
- package/src/extension/rpc.js.map +1 -1
- package/src/extension/schemas.d.ts +3 -117
- package/src/extension/schemas.d.ts.map +1 -1
- package/src/extension/schemas.js +38 -125
- package/src/extension/schemas.js.map +1 -1
- package/src/extension/tool-activation.d.ts +2 -4
- package/src/extension/tool-activation.d.ts.map +1 -1
- package/src/extension/tool-activation.js +54 -104
- package/src/extension/tool-activation.js.map +1 -1
- package/src/extension/tool-description.d.ts +8 -5
- package/src/extension/tool-description.d.ts.map +1 -1
- package/src/extension/tool-description.js +51 -27
- package/src/extension/tool-description.js.map +1 -1
- package/src/extension/upgrade-notice.d.ts +7 -0
- package/src/extension/upgrade-notice.d.ts.map +1 -0
- package/src/extension/upgrade-notice.js +37 -0
- package/src/extension/upgrade-notice.js.map +1 -0
- package/src/integrations/herdr-status.d.ts +3 -0
- package/src/integrations/herdr-status.d.ts.map +1 -1
- package/src/integrations/herdr-status.js +17 -3
- package/src/integrations/herdr-status.js.map +1 -1
- package/src/intercom/native-supervisor-channel.d.ts.map +1 -1
- package/src/intercom/native-supervisor-channel.js +4 -1
- package/src/intercom/native-supervisor-channel.js.map +1 -1
- package/src/runs/background/async-execution.d.ts +3 -0
- package/src/runs/background/async-execution.d.ts.map +1 -1
- package/src/runs/background/async-execution.js +6 -2
- package/src/runs/background/async-execution.js.map +1 -1
- package/src/runs/background/async-job-tracker.d.ts.map +1 -1
- package/src/runs/background/async-job-tracker.js +65 -22
- package/src/runs/background/async-job-tracker.js.map +1 -1
- package/src/runs/background/async-retention.d.ts +1 -1
- package/src/runs/background/async-retention.d.ts.map +1 -1
- package/src/runs/background/async-retention.js +47 -15
- package/src/runs/background/async-retention.js.map +1 -1
- package/src/runs/background/await-async-run.d.ts +23 -0
- package/src/runs/background/await-async-run.d.ts.map +1 -0
- package/src/runs/background/await-async-run.js +80 -0
- package/src/runs/background/await-async-run.js.map +1 -0
- package/src/runs/background/chain-root-attachment.d.ts +1 -0
- package/src/runs/background/chain-root-attachment.d.ts.map +1 -1
- package/src/runs/background/chain-root-attachment.js +3 -3
- package/src/runs/background/chain-root-attachment.js.map +1 -1
- package/src/runs/background/control-channel.d.ts +4 -0
- package/src/runs/background/control-channel.d.ts.map +1 -1
- package/src/runs/background/control-channel.js +16 -1
- package/src/runs/background/control-channel.js.map +1 -1
- package/src/runs/background/notify.d.ts +3 -0
- package/src/runs/background/notify.d.ts.map +1 -1
- package/src/runs/background/notify.js +3 -0
- package/src/runs/background/notify.js.map +1 -1
- package/src/runs/background/run-child-session.d.ts.map +1 -1
- package/src/runs/background/run-child-session.js +4 -1
- package/src/runs/background/run-child-session.js.map +1 -1
- package/src/runs/background/run-status.d.ts.map +1 -1
- package/src/runs/background/run-status.js +6 -0
- package/src/runs/background/run-status.js.map +1 -1
- package/src/runs/background/runner-child-launch.d.ts +1 -0
- package/src/runs/background/runner-child-launch.d.ts.map +1 -1
- package/src/runs/background/runner-child-launch.js +2 -0
- package/src/runs/background/runner-child-launch.js.map +1 -1
- package/src/runs/background/scheduled-runs.js +5 -5
- package/src/runs/background/scheduled-runs.js.map +1 -1
- package/src/runs/background/subagent-runner.d.ts +3 -0
- package/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/src/runs/background/subagent-runner.js +13 -1
- package/src/runs/background/subagent-runner.js.map +1 -1
- package/src/runs/foreground/async-stop-action.d.ts.map +1 -1
- package/src/runs/foreground/async-stop-action.js +19 -3
- package/src/runs/foreground/async-stop-action.js.map +1 -1
- package/src/runs/foreground/execution.d.ts.map +1 -1
- package/src/runs/foreground/execution.js +8 -1
- package/src/runs/foreground/execution.js.map +1 -1
- package/src/runs/foreground/subagent-executor.d.ts +6 -5
- package/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/src/runs/foreground/subagent-executor.js +293 -182
- package/src/runs/foreground/subagent-executor.js.map +1 -1
- package/src/runs/shared/child-launch.d.ts +5 -1
- package/src/runs/shared/child-launch.d.ts.map +1 -1
- package/src/runs/shared/child-launch.js +15 -1
- package/src/runs/shared/child-launch.js.map +1 -1
- package/src/runs/shared/child-session.d.ts +9 -0
- package/src/runs/shared/child-session.d.ts.map +1 -1
- package/src/runs/shared/child-session.js +90 -4
- package/src/runs/shared/child-session.js.map +1 -1
- package/src/runs/shared/child-tool-plan.d.ts +4 -0
- package/src/runs/shared/child-tool-plan.d.ts.map +1 -1
- package/src/runs/shared/child-tool-plan.js +11 -4
- package/src/runs/shared/child-tool-plan.js.map +1 -1
- package/src/runs/shared/dynamic-fanout.js +1 -1
- package/src/runs/shared/dynamic-fanout.js.map +1 -1
- package/src/runs/shared/mcp-config-sources.d.ts +1 -0
- package/src/runs/shared/mcp-config-sources.d.ts.map +1 -1
- package/src/runs/shared/mcp-config-sources.js +1 -1
- package/src/runs/shared/mcp-config-sources.js.map +1 -1
- package/src/runs/shared/mcp-direct-tool-allowlist.d.ts +28 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.d.ts.map +1 -1
- package/src/runs/shared/mcp-direct-tool-allowlist.js +78 -2
- package/src/runs/shared/mcp-direct-tool-allowlist.js.map +1 -1
- package/src/runs/shared/model-resolution.d.ts +10 -0
- package/src/runs/shared/model-resolution.d.ts.map +1 -1
- package/src/runs/shared/model-resolution.js +30 -9
- package/src/runs/shared/model-resolution.js.map +1 -1
- package/src/runs/shared/model-scope.d.ts +2 -1
- package/src/runs/shared/model-scope.d.ts.map +1 -1
- package/src/runs/shared/model-scope.js +26 -10
- package/src/runs/shared/model-scope.js.map +1 -1
- package/src/runs/shared/parallel-utils.d.ts +2 -0
- package/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/src/runs/shared/parallel-utils.js.map +1 -1
- package/src/runs/shared/pi-spawn.d.ts +1 -10
- package/src/runs/shared/pi-spawn.d.ts.map +1 -1
- package/src/runs/shared/pi-spawn.js +7 -86
- package/src/runs/shared/pi-spawn.js.map +1 -1
- package/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
- package/src/runs/shared/subagent-prompt-runtime.js +2 -0
- package/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
- package/src/runs/shared/worktree.d.ts +1 -1
- package/src/runs/shared/worktree.d.ts.map +1 -1
- package/src/runs/shared/worktree.js +1 -1
- package/src/runs/shared/worktree.js.map +1 -1
- package/src/shared/disabled-features.d.ts +92 -0
- package/src/shared/disabled-features.d.ts.map +1 -0
- package/src/shared/disabled-features.js +113 -0
- package/src/shared/disabled-features.js.map +1 -0
- package/src/shared/extension-context.d.ts +9 -0
- package/src/shared/extension-context.d.ts.map +1 -1
- package/src/shared/extension-context.js +7 -0
- package/src/shared/extension-context.js.map +1 -1
- package/src/shared/settings.d.ts +1 -1
- package/src/shared/types.d.ts +20 -0
- package/src/shared/types.d.ts.map +1 -1
- package/src/shared/types.js.map +1 -1
- package/src/slash/prompt-template-bridge.js +2 -2
- package/src/slash/prompt-template-bridge.js.map +1 -1
- package/src/slash/slash-commands.d.ts +2 -0
- package/src/slash/slash-commands.d.ts.map +1 -1
- package/src/slash/slash-commands.js +2 -2
- package/src/slash/slash-commands.js.map +1 -1
- package/src/tui/render.d.ts.map +1 -1
- package/src/tui/render.js +55 -17
- package/src/tui/render.js.map +1 -1
- package/src/watchdog/diff-tool.d.ts +3 -1
- package/src/watchdog/diff-tool.d.ts.map +1 -1
- package/src/watchdog/diff-tool.js +20 -11
- package/src/watchdog/diff-tool.js.map +1 -1
- package/src/watchdog/register-child.js +2 -2
- package/src/watchdog/register-child.js.map +1 -1
- package/src/watchdog/register-main.js +2 -2
- package/src/watchdog/register-main.js.map +1 -1
- package/src/watchdog/review.d.ts +1 -1
- package/src/watchdog/review.d.ts.map +1 -1
- package/src/watchdog/review.js +1 -1
- package/src/watchdog/review.js.map +1 -1
- package/src/watchdog/turn-delta.js +1 -1
- package/src/watchdog/turn-delta.js.map +1 -1
- package/src/workflows/chat-progress.js +2 -2
- package/src/workflows/chat-progress.js.map +1 -1
- package/src/workflows/scripted-workflow.d.ts +8 -0
- package/src/workflows/scripted-workflow.d.ts.map +1 -1
- package/src/workflows/scripted-workflow.js +10 -2
- package/src/workflows/scripted-workflow.js.map +1 -1
- package/src/workflows/structured-workflow-scripts.d.ts +10 -0
- package/src/workflows/structured-workflow-scripts.d.ts.map +1 -0
- package/src/workflows/structured-workflow-scripts.js +166 -0
- package/src/workflows/structured-workflow-scripts.js.map +1 -0
- package/src/workflows/workflow-receipt.d.ts.map +1 -1
- package/src/workflows/workflow-receipt.js +8 -2
- package/src/workflows/workflow-receipt.js.map +1 -1
- package/src/workflows/workflow-resources.d.ts +9 -0
- package/src/workflows/workflow-resources.d.ts.map +1 -1
- package/src/workflows/workflow-resources.js +39 -12
- package/src/workflows/workflow-resources.js.map +1 -1
- package/src/workflows/workflow-reuse.d.ts +51 -0
- package/src/workflows/workflow-reuse.d.ts.map +1 -0
- package/src/workflows/workflow-reuse.js +103 -0
- package/src/workflows/workflow-reuse.js.map +1 -0
- package/src/workflows/workflow-revival.d.ts +32 -0
- package/src/workflows/workflow-revival.d.ts.map +1 -0
- package/src/workflows/workflow-revival.js +111 -0
- package/src/workflows/workflow-revival.js.map +1 -0
|
@@ -15,12 +15,14 @@ import { handleManagementAction } from "../../agents/agent-management.js";
|
|
|
15
15
|
import { handleRefinementAction } from "../../agents/agent-refinements.js";
|
|
16
16
|
import { buildDoctorReport } from "../../extension/doctor.js";
|
|
17
17
|
import { readSubagentGuide } from "../../extension/subagent-guide.js";
|
|
18
|
-
import { normalizePublicSubagentExecution, validateWorkflowCapacityOverrides } from "../../extension/public-execution.js";
|
|
18
|
+
import { isWorkflowScriptPath, normalizePublicSubagentExecution, validateWorkflowCapacityOverrides } from "../../extension/public-execution.js";
|
|
19
|
+
import { readReplyWorkflowScript } from "../../extension/reply-workflow-script.js";
|
|
20
|
+
import { disabledFeatureNotice, disabledFeatureUseError, resolveDisabledFeatureSurface } from "../../shared/disabled-features.js";
|
|
19
21
|
import { runSync } from "./execution.js";
|
|
20
22
|
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.js";
|
|
21
23
|
import { applyWatchdogLaunchRules } from "../../watchdog/rules.js";
|
|
22
24
|
import { childWatchdogProgressForModel } from "../../watchdog/child-status.js";
|
|
23
|
-
import { normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin } from "../shared/model-resolution.js";
|
|
25
|
+
import { normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, scopedModelIdsFromContext } from "../shared/model-resolution.js";
|
|
24
26
|
import { projectChainOutputSchemas, resolveEffectiveOutputSchema } from "../shared/child-launch-plan.js";
|
|
25
27
|
import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.js";
|
|
26
28
|
import { resolveModelScopesForAgent } from "../shared/model-scope.js";
|
|
@@ -46,6 +48,7 @@ import { formatControlIntercomMessage, formatControlNoticeMessage, resolveContro
|
|
|
46
48
|
import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.js";
|
|
47
49
|
import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.js";
|
|
48
50
|
import { retainLiveForegroundNestedRoute } from "../../integrations/pi-web-session-liveness.js";
|
|
51
|
+
import { HERDR_FOREGROUND_CONTROL_CHANGED_EVENT } from "../../integrations/herdr-status.js";
|
|
49
52
|
import { validateToolBudgetConfig } from "../shared/tool-budget.js";
|
|
50
53
|
import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.js";
|
|
51
54
|
import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling } from "../shared/capability-ceiling.js";
|
|
@@ -68,11 +71,12 @@ import { dismissRecoveredWorkflow } from "./async-dismiss-action.js";
|
|
|
68
71
|
import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletion } from "./workflow-detach-reconcile.js";
|
|
69
72
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.js";
|
|
70
73
|
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.js";
|
|
74
|
+
import { awaitExistingAsyncRun, claimWorkflowAwaitedResult } from "../background/await-async-run.js";
|
|
71
75
|
import { fallbackResultPayloadPathForSessionRun, removeResultIndex, resultFilePath, writeAsyncResultFile } from "../background/result-files.js";
|
|
72
76
|
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, nestedRunScope, resolveNestedAsyncDir, retainNestedLookupRoute, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent } from "../shared/nested-events.js";
|
|
73
77
|
import { resolveSubagentRunId } from "../background/run-id-resolver.js";
|
|
74
78
|
import { formatNestedRunStatusLines } from "../shared/nested-render.js";
|
|
75
|
-
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild
|
|
79
|
+
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/child-identity.js";
|
|
76
80
|
import { inspectSubagentStatus } from "../background/run-status.js";
|
|
77
81
|
import { getExternalJobProvider } from "../../api/external-job-provider.js";
|
|
78
82
|
import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.js";
|
|
@@ -89,15 +93,17 @@ import { getInspectorPlugins } from "../../inspectors/plugins.js";
|
|
|
89
93
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.js";
|
|
90
94
|
import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError } from "../../workflows/scripted-workflow.js";
|
|
91
95
|
import { formatIncrementalChildCompletion, incrementalChildCompletionTriggersTurn } from "../background/notify.js";
|
|
96
|
+
import { appendWorkflowChildJournal, findWorkflowReuseSource, matchWorkflowReuse, workflowChildFingerprint, workflowScriptDigest, workflowStopCause, WORKFLOW_RUNTIME_REPLACED_RELAUNCH_NOTICE } from "../../workflows/workflow-reuse.js";
|
|
92
97
|
import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath } from "../../workflows/host-command.js";
|
|
93
98
|
import { buildWorkflowReceipt, readWorkflowReceipt, workflowReceiptPath, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt } from "../../workflows/workflow-receipt.js";
|
|
99
|
+
import { formatWorkflowKeyRevival, recordWorkflowRevival, withWorkflowRevivals } from "../../workflows/workflow-revival.js";
|
|
94
100
|
import { upsertHostStep, validHostStepNodes } from "../shared/host-step-status.js";
|
|
95
101
|
import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../shared/lane-metadata.js";
|
|
96
102
|
import { parseWorkflowChildSummary, workflowChildProgress, workflowChildSummary } from "../../workflows/workflow-child-summary.js";
|
|
97
103
|
import { resolveWorkflowChatProgress } from "../../workflows/chat-progress.js";
|
|
98
104
|
import { annotateWorkflowPreflightTrace, formatWorkflowPreflight, formatWorkflowPreflightWarnings, normalizeWorkflowPreflight, workflowPreflightWarnings } from "../../workflows/workflow-preflight.js";
|
|
99
105
|
import { authorizeWorkflowResourceHost, claimWorkflowChildPermit, consumeWorkflowResourcePermit, validateWorkflowChildPermitRoot, } from "../../shared/workflow-child-permit.js";
|
|
100
|
-
import { deepFreezeWorkflowArgs, normalizeWorkflowArgs, resolveWorkflowResource } from "../../workflows/workflow-resources.js";
|
|
106
|
+
import { deepFreezeWorkflowArgs, normalizeWorkflowArgs, resolveStructuredWorkflowResource, resolveWorkflowResource } from "../../workflows/workflow-resources.js";
|
|
101
107
|
import { stableJsonDigest } from "../../shared/launch-contract.js";
|
|
102
108
|
import { cleanupWorktrees, createWorktrees, preflightWorktreeSource, withWorktreeTransaction, diffWorktrees, formatWorktreeDiffSummary, } from "../shared/worktree.js";
|
|
103
109
|
import { createWorktreeCleanupPlan, formatWorktreeCleanupPlan } from "../shared/worktree-cleanup-plan.js";
|
|
@@ -132,9 +138,10 @@ function hasSingleAdjacentTransposition(left, right) {
|
|
|
132
138
|
&& left[mismatch + 1] === right[mismatch]
|
|
133
139
|
&& left.slice(mismatch + 2) === right.slice(mismatch + 2);
|
|
134
140
|
}
|
|
135
|
-
export function unknownSubagentActionMessage(action) {
|
|
141
|
+
export function unknownSubagentActionMessage(action, disabled) {
|
|
136
142
|
const requested = action.toLowerCase();
|
|
137
|
-
const
|
|
143
|
+
const enabledActions = SUBAGENT_ACTIONS.filter((candidate) => !disabled?.actions.has(candidate));
|
|
144
|
+
const suggestion = enabledActions.find((candidate) => {
|
|
138
145
|
const distance = editDistance(requested, candidate);
|
|
139
146
|
const closeMatch = distance <= Math.max(1, Math.floor(candidate.length / 4)) || hasSingleAdjacentTransposition(requested, candidate);
|
|
140
147
|
if (DESTRUCTIVE_MANAGEMENT_ACTIONS.has(candidate))
|
|
@@ -142,7 +149,7 @@ export function unknownSubagentActionMessage(action) {
|
|
|
142
149
|
return closeMatch;
|
|
143
150
|
});
|
|
144
151
|
const nextStep = 'Use subagent({ action: "status" }) to inspect runs or subagent({ action: "list" }) to inspect agents.';
|
|
145
|
-
const validActions = `Valid: ${
|
|
152
|
+
const validActions = `Valid: ${enabledActions.join(", ")}.`;
|
|
146
153
|
return suggestion
|
|
147
154
|
? `Unknown action: ${action}. Did you mean ${suggestion}? ${nextStep} ${validActions}`
|
|
148
155
|
: `Unknown action: ${action}. ${nextStep} ${validActions}`;
|
|
@@ -185,21 +192,18 @@ function inheritedRunFanoutBudget(deps) {
|
|
|
185
192
|
function resolveRequestedCwd(runtimeCwd, requestedCwd) {
|
|
186
193
|
return requestedCwd ? path.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
|
|
187
194
|
}
|
|
188
|
-
function
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const scriptPath = path.resolve(resolveRequestedCwd(runtimeCwd, params.cwd), params.workflowScriptPath);
|
|
192
|
-
let workflowScript;
|
|
195
|
+
function readWorkflowScriptFile(requestedPath, requestedCwd, runtimeCwd) {
|
|
196
|
+
const scriptPath = path.resolve(resolveRequestedCwd(runtimeCwd, requestedCwd), requestedPath);
|
|
197
|
+
let script;
|
|
193
198
|
try {
|
|
194
|
-
|
|
199
|
+
script = fs.readFileSync(scriptPath, "utf8");
|
|
195
200
|
}
|
|
196
201
|
catch (error) {
|
|
197
|
-
return { error: `Failed to read
|
|
202
|
+
return { error: `Failed to read workflow script '${scriptPath}': ${error instanceof Error ? error.message : String(error)}` };
|
|
198
203
|
}
|
|
199
|
-
if (!
|
|
200
|
-
return { error: `
|
|
201
|
-
|
|
202
|
-
return { params: { ...rest, workflowScript } };
|
|
204
|
+
if (!script.trim())
|
|
205
|
+
return { error: `Workflow script file '${scriptPath}' is empty.` };
|
|
206
|
+
return { script };
|
|
203
207
|
}
|
|
204
208
|
export function removeForegroundControlIfIdle(state, runId, trackRetainedNestedRoute) {
|
|
205
209
|
const control = state.foregroundControls.get(runId);
|
|
@@ -294,6 +298,10 @@ function trustedSessionRootsForStatus(ctx, deps) {
|
|
|
294
298
|
roots.push(deps.getSubagentSessionRoot(parentSessionFile));
|
|
295
299
|
return [...new Set(roots)];
|
|
296
300
|
}
|
|
301
|
+
/** Children follow the launching session's trust; hosts older than Pi's trust concept keep Pi's default. */
|
|
302
|
+
function sessionProjectTrust(ctx) {
|
|
303
|
+
return typeof ctx.isProjectTrusted === "function" ? ctx.isProjectTrusted() : undefined;
|
|
304
|
+
}
|
|
297
305
|
function spawnBudgetErrorResult(message, mode) {
|
|
298
306
|
return {
|
|
299
307
|
content: [{ type: "text", text: message }],
|
|
@@ -995,6 +1003,7 @@ function appendStepToAsyncChain(input) {
|
|
|
995
1003
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
996
1004
|
currentModelProvider: parentModel?.provider,
|
|
997
1005
|
currentModel: parentModel,
|
|
1006
|
+
scopedModelIds: scopedModelIdsFromContext(input.ctx),
|
|
998
1007
|
modelScope: discoveredForAppend.modelScope,
|
|
999
1008
|
modelResponseAliases: input.deps.config.modelResponseAliases,
|
|
1000
1009
|
interactive: input.ctx.hasUI,
|
|
@@ -1444,11 +1453,13 @@ async function resumeExternalJobFollowUp(input) {
|
|
|
1444
1453
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1445
1454
|
currentModelProvider: parentModel?.provider,
|
|
1446
1455
|
currentModel: parentModel,
|
|
1456
|
+
scopedModelIds: scopedModelIdsFromContext(input.ctx),
|
|
1447
1457
|
modelScope: input.modelScope,
|
|
1448
1458
|
modelResponseAliases: input.deps.config.modelResponseAliases,
|
|
1449
1459
|
interactive: input.ctx.hasUI,
|
|
1450
1460
|
permissions: input.deps.config.permissions,
|
|
1451
1461
|
childRuntime: input.deps.childRuntime,
|
|
1462
|
+
projectTrusted: sessionProjectTrust(input.ctx),
|
|
1452
1463
|
}),
|
|
1453
1464
|
cwd: input.effectiveCwd,
|
|
1454
1465
|
artifactsDir,
|
|
@@ -1708,11 +1719,13 @@ async function resumeAsyncRun(input) {
|
|
|
1708
1719
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1709
1720
|
currentModelProvider: parentModel?.provider,
|
|
1710
1721
|
currentModel: parentModel,
|
|
1722
|
+
scopedModelIds: scopedModelIdsFromContext(input.ctx),
|
|
1711
1723
|
modelScope,
|
|
1712
1724
|
modelResponseAliases: input.deps.config.modelResponseAliases,
|
|
1713
1725
|
interactive: input.ctx.hasUI,
|
|
1714
1726
|
permissions: input.deps.config.permissions,
|
|
1715
1727
|
childRuntime: input.deps.childRuntime,
|
|
1728
|
+
projectTrusted: sessionProjectTrust(input.ctx),
|
|
1716
1729
|
}),
|
|
1717
1730
|
availableModels,
|
|
1718
1731
|
cwd: effectiveCwd,
|
|
@@ -1836,12 +1849,14 @@ async function resumeAsyncRun(input) {
|
|
|
1836
1849
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1837
1850
|
currentModelProvider: parentModel?.provider,
|
|
1838
1851
|
currentModel: parentModel,
|
|
1852
|
+
scopedModelIds: scopedModelIdsFromContext(input.ctx),
|
|
1839
1853
|
modelScope,
|
|
1840
1854
|
// Absence in the retained contract is meaningful; never acquire current aliases.
|
|
1841
1855
|
modelResponseAliases: recoveryDescriptor ? recoveryDescriptor.modelResponseAliases : foregroundContract?.modelResponseAliases,
|
|
1842
1856
|
interactive: input.ctx.hasUI,
|
|
1843
1857
|
permissions: input.deps.config.permissions,
|
|
1844
1858
|
childRuntime: input.deps.childRuntime,
|
|
1859
|
+
projectTrusted: sessionProjectTrust(input.ctx),
|
|
1845
1860
|
}),
|
|
1846
1861
|
cwd: effectiveCwd,
|
|
1847
1862
|
maxOutput: input.params.maxOutput ?? recoveryDescriptor?.maxOutput,
|
|
@@ -1937,67 +1952,34 @@ async function resumeAsyncRun(input) {
|
|
|
1937
1952
|
if (input.params.workflowParentRunId !== undefined && result.details.asyncDir) {
|
|
1938
1953
|
const asyncDir = result.details.asyncDir;
|
|
1939
1954
|
const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
|
|
1940
|
-
const
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
input.signal?.removeEventListener("abort", stopOnAbort);
|
|
1951
|
-
}
|
|
1952
|
-
if (completed.importedPublication)
|
|
1953
|
-
removeWorkflowAwaitedResult(asyncDir, resultPath, revivedId, completed.importedPublication);
|
|
1954
|
-
emitWorkflowAwaitedChildComplete(input.deps.pi, input.deps.state, revivedId, asyncDir, input.params.workflowParentRunId, completed);
|
|
1955
|
-
const usage = importedAsyncRootUsage(completed);
|
|
1956
|
-
const childResult = {
|
|
1957
|
-
index: 0,
|
|
1958
|
-
agent: completed.agent,
|
|
1959
|
-
...(completed.sessionName ? { sessionName: completed.sessionName } : {}),
|
|
1960
|
-
task: effectiveFollowUp,
|
|
1961
|
-
exitCode: completed.exitCode,
|
|
1962
|
-
usage,
|
|
1963
|
-
finalOutput: completed.output,
|
|
1964
|
-
outputState: completed.output.trim() ? "present" : "absent",
|
|
1965
|
-
...(completed.error ? { error: completed.error } : {}),
|
|
1966
|
-
...(completed.timedOut ? { timedOut: true } : {}),
|
|
1967
|
-
...(completed.stopped ? { stopped: true } : {}),
|
|
1968
|
-
...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
|
|
1969
|
-
...(completed.model ? { model: completed.model } : {}),
|
|
1970
|
-
...(completed.requestedModel ? { requestedModel: completed.requestedModel } : {}),
|
|
1971
|
-
...(completed.contextOverflow ? { contextOverflow: true } : {}),
|
|
1972
|
-
...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
|
|
1973
|
-
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
1974
|
-
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
1975
|
-
...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
|
|
1976
|
-
...(completed.artifactPaths ? { artifactPaths: completed.artifactPaths } : {}),
|
|
1977
|
-
...(completed.savedOutputPath ? { savedOutputPath: completed.savedOutputPath } : {}),
|
|
1978
|
-
...(completed.outputSaveError ? { outputSaveError: completed.outputSaveError } : {}),
|
|
1979
|
-
...(completed.transcriptPath ? { transcriptPath: completed.transcriptPath } : {}),
|
|
1980
|
-
...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
|
|
1981
|
-
};
|
|
1982
|
-
return {
|
|
1983
|
-
content: [{ type: "text", text: completed.success ? completed.output || completed.error || `Revived ${target.source} subagent ${revivedId} completed without output.` : completed.error || completed.output || `Revived ${target.source} subagent ${revivedId} completed without output.` }],
|
|
1984
|
-
...(completed.success ? {} : { isError: true }),
|
|
1985
|
-
details: {
|
|
1986
|
-
...result.details,
|
|
1987
|
-
runId: revivedId,
|
|
1988
|
-
results: [childResult],
|
|
1989
|
-
...(target.launchContractDigest ? { sourceLaunchContractDigest: target.launchContractDigest } : {}),
|
|
1990
|
-
},
|
|
1991
|
-
};
|
|
1955
|
+
const stopListener = stopAwaitedAsyncChildOnAbort(input.signal, input.deps.state, revivedId, asyncDir, input.deps.kill);
|
|
1956
|
+
const completed = await waitForImportedAsyncRoot({ runId: revivedId, asyncDir, resultPath, index: 0 }, {
|
|
1957
|
+
shouldAbort: () => input.signal?.aborted === true,
|
|
1958
|
+
timeoutMessage: "Workflow stopped before async child completed.",
|
|
1959
|
+
abortedAsStopped: true,
|
|
1960
|
+
}).finally(stopListener.remove);
|
|
1961
|
+
const details = { ...result.details };
|
|
1962
|
+
if (target.launchContractDigest)
|
|
1963
|
+
details.sourceLaunchContractDigest = target.launchContractDigest;
|
|
1964
|
+
return importWorkflowAwaitedChildResult(completed, { runId: revivedId, asyncDir, resultPath, task: effectiveFollowUp, parentWorkflowRunId: input.params.workflowParentRunId, details, emptyOutputText: `Revived ${target.source} subagent ${revivedId} completed without output.`, state: input.deps.state, pi: input.deps.pi });
|
|
1992
1965
|
}
|
|
1993
1966
|
const revivedTarget = intercomBridge.active ? resolveSubagentIntercomTarget(revivedId, target.agent, 0) : undefined;
|
|
1994
1967
|
const sourceLabel = target.source;
|
|
1968
|
+
let workflowRevivalWarning;
|
|
1969
|
+
try {
|
|
1970
|
+
if (target.source === "async")
|
|
1971
|
+
recordWorkflowRevival(DIRS.async, target.runId, revivedId);
|
|
1972
|
+
}
|
|
1973
|
+
catch (error) {
|
|
1974
|
+
workflowRevivalWarning = `Warning: workflow key link not recorded: ${error instanceof Error ? error.message : String(error)}`;
|
|
1975
|
+
}
|
|
1995
1976
|
const lines = [
|
|
1996
1977
|
`Revived ${sourceLabel} subagent from ${target.runId}.`,
|
|
1997
1978
|
`Revived run: ${revivedId}`,
|
|
1998
1979
|
`Agent: ${target.agent}`,
|
|
1999
1980
|
`Session: ${target.sessionFile}`,
|
|
2000
1981
|
result.details.asyncDir ? `Async dir: ${result.details.asyncDir}` : undefined,
|
|
1982
|
+
workflowRevivalWarning,
|
|
2001
1983
|
revivedTarget ? `Intercom target: ${revivedTarget} (if registered)` : undefined,
|
|
2002
1984
|
`Status if needed: subagent({ action: "status", id: "${revivedId}" })`,
|
|
2003
1985
|
].filter((line) => Boolean(line));
|
|
@@ -2733,7 +2715,7 @@ function toExecutionErrorResult(params, error, contextSummary) {
|
|
|
2733
2715
|
function resolveStaticLaunchSummary(input) {
|
|
2734
2716
|
const agentConfig = input.agents.find((agent) => agent.name === input.agent);
|
|
2735
2717
|
const externalRunner = agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job";
|
|
2736
|
-
const modelScopes = resolveModelScopesForAgent(input.modelScope, input.agent, input.parentModel);
|
|
2718
|
+
const modelScopes = resolveModelScopesForAgent(input.modelScope, input.agent, input.parentModel, input.scopedModelIds);
|
|
2737
2719
|
const model = externalRunner
|
|
2738
2720
|
? undefined
|
|
2739
2721
|
: resolveEffectiveSubagentModel(input.explicitModel, agentConfig?.model, input.parentModel, input.availableModels, agentConfig?.modelProvider ?? input.currentProvider, modelScopes.length === 0 ? {} : { scope: modelScopes });
|
|
@@ -2752,6 +2734,7 @@ function collectStaticLaunchSummaries(input) {
|
|
|
2752
2734
|
explicitModel,
|
|
2753
2735
|
agents: input.agents,
|
|
2754
2736
|
parentModel: input.parentModel,
|
|
2737
|
+
scopedModelIds: input.scopedModelIds,
|
|
2755
2738
|
availableModels: input.availableModels,
|
|
2756
2739
|
currentProvider: input.currentProvider,
|
|
2757
2740
|
modelScope: input.modelScope,
|
|
@@ -2925,29 +2908,11 @@ function emitWorkflowAwaitedChildComplete(pi, state, runId, asyncDir, parentWork
|
|
|
2925
2908
|
parentWorkflowRunId,
|
|
2926
2909
|
});
|
|
2927
2910
|
}
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
return launchResult;
|
|
2931
|
-
const asyncDir = launchResult.details.asyncDir;
|
|
2932
|
-
const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
|
|
2933
|
-
const stopOnAbort = () => { stopAsyncRun(options.state, options.runId, options.kill, { asyncDir, resolvedId: options.runId }); };
|
|
2934
|
-
if (options.signal?.aborted)
|
|
2935
|
-
stopOnAbort();
|
|
2936
|
-
else
|
|
2937
|
-
options.signal?.addEventListener("abort", stopOnAbort, { once: true });
|
|
2938
|
-
let completed;
|
|
2939
|
-
try {
|
|
2940
|
-
completed = await waitForImportedAsyncRoot({ runId: options.runId, asyncDir, resultPath, index: 0 }, omitUndefinedProperties({
|
|
2941
|
-
shouldAbort: () => options.signal?.aborted === true,
|
|
2942
|
-
timeoutMessage: "Workflow stopped before async child completed.",
|
|
2943
|
-
}));
|
|
2944
|
-
}
|
|
2945
|
-
finally {
|
|
2946
|
-
options.signal?.removeEventListener("abort", stopOnAbort);
|
|
2947
|
-
}
|
|
2911
|
+
/** Consumes an awaited workflow child's published result: clears the payload, announces completion, and builds the tool result. */
|
|
2912
|
+
function importWorkflowAwaitedChildResult(completed, options) {
|
|
2948
2913
|
if (completed.importedPublication)
|
|
2949
|
-
removeWorkflowAwaitedResult(asyncDir, resultPath, options.runId, completed.importedPublication);
|
|
2950
|
-
emitWorkflowAwaitedChildComplete(options.pi, options.state, options.runId, asyncDir,
|
|
2914
|
+
removeWorkflowAwaitedResult(options.asyncDir, options.resultPath, options.runId, completed.importedPublication);
|
|
2915
|
+
emitWorkflowAwaitedChildComplete(options.pi, options.state, options.runId, options.asyncDir, options.parentWorkflowRunId, completed);
|
|
2951
2916
|
const usage = importedAsyncRootUsage(completed);
|
|
2952
2917
|
const childResult = omitUndefinedProperties({
|
|
2953
2918
|
index: 0,
|
|
@@ -2976,15 +2941,54 @@ async function waitForWorkflowAsyncSingleResult(params, launchResult, options) {
|
|
|
2976
2941
|
...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
|
|
2977
2942
|
});
|
|
2978
2943
|
return {
|
|
2979
|
-
content: [{ type: "text", text: completed.success ? completed.output || completed.error
|
|
2944
|
+
content: [{ type: "text", text: (completed.success ? completed.output || completed.error : completed.error || completed.output) || (options.emptyOutputText ?? `Async workflow child ${options.runId} completed without output.`) }],
|
|
2980
2945
|
...(completed.success ? {} : { isError: true }),
|
|
2981
2946
|
details: {
|
|
2982
|
-
...
|
|
2947
|
+
...options.details,
|
|
2983
2948
|
runId: options.runId,
|
|
2984
2949
|
results: [childResult],
|
|
2985
2950
|
},
|
|
2986
2951
|
};
|
|
2987
2952
|
}
|
|
2953
|
+
// A replaced runtime leaves the child running so a relaunch of the workflow can re-attach to it.
|
|
2954
|
+
function stopAwaitedAsyncChildOnAbort(signal, state, runId, asyncDir, kill) {
|
|
2955
|
+
const stopOnAbort = () => { if (!workflowStopCause(signal?.reason))
|
|
2956
|
+
stopAsyncRun(state, runId, kill, { asyncDir, resolvedId: runId }); };
|
|
2957
|
+
if (signal?.aborted)
|
|
2958
|
+
stopOnAbort();
|
|
2959
|
+
else
|
|
2960
|
+
signal?.addEventListener("abort", stopOnAbort, { once: true });
|
|
2961
|
+
return { remove: () => signal?.removeEventListener("abort", stopOnAbort) };
|
|
2962
|
+
}
|
|
2963
|
+
// Live workflow controls are keyed by the full run id; a stop id may be a prefix or the launch tool-call id.
|
|
2964
|
+
function resolveLiveWorkflowRunId(deps, id) {
|
|
2965
|
+
const controllers = deps.state.workflowControllers;
|
|
2966
|
+
if (!controllers?.size)
|
|
2967
|
+
return undefined;
|
|
2968
|
+
if (controllers.has(id))
|
|
2969
|
+
return id;
|
|
2970
|
+
try {
|
|
2971
|
+
const resolved = resolveSubagentRunId(id, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }));
|
|
2972
|
+
return resolved?.kind === "async" && controllers.has(resolved.id) ? resolved.id : undefined;
|
|
2973
|
+
}
|
|
2974
|
+
catch {
|
|
2975
|
+
// The generic stop path resolves the id again and reports the error.
|
|
2976
|
+
return undefined;
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
async function waitForWorkflowAsyncSingleResult(params, launchResult, options) {
|
|
2980
|
+
if (params.workflowAwaitAsync !== true || !launchResult.details.asyncDir)
|
|
2981
|
+
return launchResult;
|
|
2982
|
+
const asyncDir = launchResult.details.asyncDir;
|
|
2983
|
+
const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
|
|
2984
|
+
const stopListener = stopAwaitedAsyncChildOnAbort(options.signal, options.state, options.runId, asyncDir, options.kill);
|
|
2985
|
+
const completed = await waitForImportedAsyncRoot({ runId: options.runId, asyncDir, resultPath, index: 0 }, omitUndefinedProperties({
|
|
2986
|
+
shouldAbort: () => options.signal?.aborted === true,
|
|
2987
|
+
timeoutMessage: "Workflow stopped before async child completed.",
|
|
2988
|
+
abortedAsStopped: true,
|
|
2989
|
+
})).finally(stopListener.remove);
|
|
2990
|
+
return importWorkflowAwaitedChildResult(completed, { runId: options.runId, asyncDir, resultPath, task: options.task, parentWorkflowRunId: params.workflowParentRunId, details: launchResult.details, state: options.state, pi: options.pi });
|
|
2991
|
+
}
|
|
2988
2992
|
async function runAsyncPath(data, deps) {
|
|
2989
2993
|
const { params, effectiveCwd, agents, ctx, shareEnabled, sessionRoot, sessionFileForTask, thinkingOverrideForTask, artifactConfig, artifactsDir, effectiveAsync, controlConfig, intercomBridge, nestedRoute, contextPolicy, unknownAgentDiagnosticContext, } = data;
|
|
2990
2994
|
const hasChain = (params.chain?.length ?? 0) > 0;
|
|
@@ -3008,11 +3012,13 @@ async function runAsyncPath(data, deps) {
|
|
|
3008
3012
|
parentSessionId: data.parentPiSessionId,
|
|
3009
3013
|
currentModelProvider: parentModel?.provider,
|
|
3010
3014
|
currentModel: parentModel,
|
|
3015
|
+
scopedModelIds: data.scopedModelIds,
|
|
3011
3016
|
modelScope: data.modelScope,
|
|
3012
3017
|
modelResponseAliases: deps.config.modelResponseAliases,
|
|
3013
3018
|
interactive: ctx.hasUI,
|
|
3014
3019
|
permissions: deps.config.permissions,
|
|
3015
3020
|
childRuntime: deps.childRuntime,
|
|
3021
|
+
projectTrusted: sessionProjectTrust(ctx),
|
|
3016
3022
|
});
|
|
3017
3023
|
const availableModels = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3018
3024
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
|
|
@@ -3040,7 +3046,7 @@ async function runAsyncPath(data, deps) {
|
|
|
3040
3046
|
if ((a.runner?.type === "external-cli" || a.runner?.type === "external-job") && (params.fast ?? a.fast) === true) {
|
|
3041
3047
|
return buildRequestedModeError(params, `Agent '${a.name}' uses runner.type='${a.runner.type}' and does not support fast mode.`);
|
|
3042
3048
|
}
|
|
3043
|
-
const modelScopes = resolveModelScopesForAgent(data.modelScope, a.name, parentModel);
|
|
3049
|
+
const modelScopes = resolveModelScopesForAgent(data.modelScope, a.name, parentModel, data.scopedModelIds);
|
|
3044
3050
|
const modelOrigin = resolveModelOrigin({
|
|
3045
3051
|
storedOrigin: params.modelOrigin,
|
|
3046
3052
|
explicitModel: params.model,
|
|
@@ -3463,7 +3469,7 @@ async function runSinglePath(data, deps) {
|
|
|
3463
3469
|
const parentModel = data.parentModel;
|
|
3464
3470
|
const currentProvider = parentModel?.provider;
|
|
3465
3471
|
const availableModels = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3466
|
-
const modelScopes = resolveModelScopesForAgent(data.modelScope, agentConfig.name, parentModel);
|
|
3472
|
+
const modelScopes = resolveModelScopesForAgent(data.modelScope, agentConfig.name, parentModel, data.scopedModelIds);
|
|
3467
3473
|
let task = typeof params.task === "string" ? params.task : "";
|
|
3468
3474
|
const modelOrigin = resolveModelOrigin({
|
|
3469
3475
|
storedOrigin: params.modelOrigin,
|
|
@@ -3569,6 +3575,24 @@ async function runSinglePath(data, deps) {
|
|
|
3569
3575
|
let detachForeground;
|
|
3570
3576
|
let childSessionControls;
|
|
3571
3577
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3578
|
+
const syncHerdrForegroundChild = () => {
|
|
3579
|
+
if (!foregroundControl?.parentWorkflowRunId)
|
|
3580
|
+
return;
|
|
3581
|
+
try {
|
|
3582
|
+
deps.pi.events.emit(HERDR_FOREGROUND_CONTROL_CHANGED_EVENT, { runId: foregroundControl.parentWorkflowRunId });
|
|
3583
|
+
}
|
|
3584
|
+
catch (error) {
|
|
3585
|
+
console.error("Failed to sync Herdr foreground child:", error);
|
|
3586
|
+
}
|
|
3587
|
+
};
|
|
3588
|
+
const finishTrackedForegroundChild = () => {
|
|
3589
|
+
if (!foregroundControl)
|
|
3590
|
+
return;
|
|
3591
|
+
const wasActive = foregroundControl.activeChildren?.has(0) === true;
|
|
3592
|
+
finishForegroundChild(foregroundControl, 0);
|
|
3593
|
+
if (wasActive)
|
|
3594
|
+
syncHerdrForegroundChild();
|
|
3595
|
+
};
|
|
3572
3596
|
if (foregroundControl) {
|
|
3573
3597
|
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask());
|
|
3574
3598
|
beginForegroundChild(foregroundControl, omitUndefinedProperties({
|
|
@@ -3608,6 +3632,7 @@ async function runSinglePath(data, deps) {
|
|
|
3608
3632
|
// Capture the owned mailbox before a child can start and finish between polling ticks.
|
|
3609
3633
|
if (deps.childRuntime?.fanoutChild)
|
|
3610
3634
|
deps.activateSupervisorTransport?.();
|
|
3635
|
+
syncHerdrForegroundChild();
|
|
3611
3636
|
}
|
|
3612
3637
|
const modelResponseAliases = deps.config.modelResponseAliases === undefined ? undefined : structuredClone(deps.config.modelResponseAliases);
|
|
3613
3638
|
const forwardSingleUpdate = onUpdate
|
|
@@ -3628,6 +3653,7 @@ async function runSinglePath(data, deps) {
|
|
|
3628
3653
|
const launched = await runSync(ctx.cwd, agents, params.agent, task, compactOptional({
|
|
3629
3654
|
machine: foregroundMachine,
|
|
3630
3655
|
parentProviderRegistry: ctx.modelRegistry,
|
|
3656
|
+
projectTrusted: sessionProjectTrust(ctx),
|
|
3631
3657
|
remoteReads: foregroundMachine ? readsOverride : undefined,
|
|
3632
3658
|
permissions: deps.config.permissions,
|
|
3633
3659
|
runtimeSnapshotHost: deps.pi,
|
|
@@ -3641,6 +3667,7 @@ async function runSinglePath(data, deps) {
|
|
|
3641
3667
|
cwd: singleCwd,
|
|
3642
3668
|
requestedCwd: data.requestedCwd,
|
|
3643
3669
|
signal,
|
|
3670
|
+
abortedAsStopped: params.workflowParentRunId !== undefined,
|
|
3644
3671
|
interruptSignal: interruptController.signal,
|
|
3645
3672
|
allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
3646
3673
|
intercomEvents: deps.pi.events,
|
|
@@ -3720,8 +3747,7 @@ async function runSinglePath(data, deps) {
|
|
|
3720
3747
|
}
|
|
3721
3748
|
finally {
|
|
3722
3749
|
try {
|
|
3723
|
-
|
|
3724
|
-
finishForegroundChild(foregroundControl, 0);
|
|
3750
|
+
finishTrackedForegroundChild();
|
|
3725
3751
|
}
|
|
3726
3752
|
finally {
|
|
3727
3753
|
removeForegroundControlIfIdle(deps.state, runId, deps.trackRetainedNestedRoute);
|
|
@@ -3752,8 +3778,7 @@ async function runSinglePath(data, deps) {
|
|
|
3752
3778
|
if (!r?.detached) {
|
|
3753
3779
|
if (!artifactConfig.enabled)
|
|
3754
3780
|
cleanupStructuredOutputRuntime(structuredRuntime);
|
|
3755
|
-
|
|
3756
|
-
finishForegroundChild(foregroundControl, 0);
|
|
3781
|
+
finishTrackedForegroundChild();
|
|
3757
3782
|
}
|
|
3758
3783
|
}
|
|
3759
3784
|
if (!r.detached) {
|
|
@@ -3855,7 +3880,7 @@ async function runSinglePath(data, deps) {
|
|
|
3855
3880
|
};
|
|
3856
3881
|
}
|
|
3857
3882
|
function inferExecutionMode(params) {
|
|
3858
|
-
if (params.workflowScript !== undefined)
|
|
3883
|
+
if (params.workflowScript !== undefined || params.workflow !== undefined)
|
|
3859
3884
|
return "workflow";
|
|
3860
3885
|
if ((params.chain?.length ?? 0) > 0)
|
|
3861
3886
|
return "chain";
|
|
@@ -3936,16 +3961,19 @@ function workflowChildResult(key, result, childParams = {}, resumeState, forcedT
|
|
|
3936
3961
|
? result.details.results[0].finalOutput
|
|
3937
3962
|
: receiptOutput;
|
|
3938
3963
|
const childError = result.details.results.map((child) => child.error).find((error) => Boolean(error));
|
|
3939
|
-
const
|
|
3940
|
-
|
|
3941
|
-
|
|
3964
|
+
const stopped = result.details.results.some((child) => child.stopped);
|
|
3965
|
+
// A stopped child's receipt text carries the run fan-out annotation; report the structured stop reason instead.
|
|
3966
|
+
const failureErrorBase = stopped && childError
|
|
3967
|
+
? childError
|
|
3968
|
+
: childError && receiptOutput
|
|
3969
|
+
? receiptOutput.includes(childError) ? receiptOutput : `${childError}\n\n${receiptOutput}`
|
|
3970
|
+
: childError || receiptOutput || output || "Child run failed.";
|
|
3942
3971
|
const savedOutputEvidence = [...new Set(result.details.results.map((child) => child.savedOutputPath).filter((value) => Boolean(value)))]
|
|
3943
3972
|
.filter((savedOutputPath) => !failureErrorBase.includes(savedOutputPath))
|
|
3944
3973
|
.map((savedOutputPath) => `Saved output: ${savedOutputPath}`);
|
|
3945
3974
|
const failureError = [failureErrorBase, ...savedOutputEvidence].join("\n");
|
|
3946
3975
|
const detached = result.details.results.some((child) => child.detached);
|
|
3947
3976
|
const interrupted = result.details.results.some((child) => child.interrupted);
|
|
3948
|
-
const stopped = result.details.results.some((child) => child.stopped);
|
|
3949
3977
|
const terminalOutcome = forcedTerminalOutcome
|
|
3950
3978
|
?? (result.details.results.some((child) => child.timedOut)
|
|
3951
3979
|
? { state: "partial", reason: "timeout" }
|
|
@@ -4047,9 +4075,9 @@ function workflowRunningChildrenSummary(children) {
|
|
|
4047
4075
|
return running.length ? `${running.length} child ${running.length === 1 ? "run remains" : "runs remain"} running or uncollected: ${running.map((child) => `${child.key}=${child.runId}`).join(", ")}. These launch receipts contain no final child results.` : "";
|
|
4048
4076
|
}
|
|
4049
4077
|
function workflowResultChildren(children, status, includeFailureFields) {
|
|
4050
|
-
return children.map((child) => {
|
|
4078
|
+
return withWorkflowRevivals(DIRS.async, status.runId, children).map((child) => {
|
|
4051
4079
|
const sessionName = status.steps?.find((step) => step.workflowKey === child.key)?.sessionName;
|
|
4052
|
-
return { workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(sessionName ? { sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, ...(child.state === "running" ? { state: "running" } : { success: child.ok }), ...(child.asyncDir ? { asyncDir: child.asyncDir } : {}), ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(includeFailureFields && child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(includeFailureFields && child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.outputArtifactPath || child.outputReference ? { artifactPaths: { outputPath: child.outputArtifactPath ?? child.outputReference } } : {}) };
|
|
4080
|
+
return { workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(child.revival ? { revival: formatWorkflowKeyRevival(child.revival) } : {}), ...(sessionName ? { sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, ...(child.state === "running" ? { state: "running" } : { success: child.ok }), ...(child.asyncDir ? { asyncDir: child.asyncDir } : {}), ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(includeFailureFields && child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(includeFailureFields && child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.outputArtifactPath || child.outputReference ? { artifactPaths: { outputPath: child.outputArtifactPath ?? child.outputReference } } : {}) };
|
|
4053
4081
|
});
|
|
4054
4082
|
}
|
|
4055
4083
|
function workflowOutputPathMappingSummary(children) {
|
|
@@ -4200,7 +4228,7 @@ function resolveWorkflowResume(reference, deps, parentSessionFile, index) {
|
|
|
4200
4228
|
}
|
|
4201
4229
|
}
|
|
4202
4230
|
function terminalWorkflowReceipt(workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps, resource, argsDigest) {
|
|
4203
|
-
return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps, resource, ...(argsDigest ? { argsDigest } : {}) });
|
|
4231
|
+
return buildWorkflowReceipt({ workflowRunId, state, children: withWorkflowRevivals(DIRS.async, workflowRunId, children), workflowChildren, terminalOutcome, hostSteps, resource, ...(argsDigest ? { argsDigest } : {}) });
|
|
4204
4232
|
}
|
|
4205
4233
|
function workflowFailureTerminalOutcome(error, _children, usageBudget) {
|
|
4206
4234
|
if (usageBudget?.exhausted)
|
|
@@ -4580,6 +4608,20 @@ export function createSubagentExecutor(deps) {
|
|
|
4580
4608
|
const workflowPermitContexts = new WeakMap();
|
|
4581
4609
|
const warnedArtifactPackageDirs = new Set();
|
|
4582
4610
|
const scheduledOwnerExecutors = new Map();
|
|
4611
|
+
const disabledFeatures = resolveDisabledFeatureSurface(deps.config);
|
|
4612
|
+
const disabledFeatureResult = (params) => {
|
|
4613
|
+
const error = disabledFeatureUseError(params, disabledFeatures);
|
|
4614
|
+
if (!error)
|
|
4615
|
+
return undefined;
|
|
4616
|
+
return { content: [{ type: "text", text: error }], isError: true, details: { mode: params.action === undefined ? inferExecutionMode(params) : "management", results: [] } };
|
|
4617
|
+
};
|
|
4618
|
+
const admitEnabledWorkflowChildren = (calls) => {
|
|
4619
|
+
for (const call of calls) {
|
|
4620
|
+
const error = disabledFeatureUseError(call.params, disabledFeatures, `workflow child '${call.key}'`);
|
|
4621
|
+
if (error)
|
|
4622
|
+
throw new Error(error);
|
|
4623
|
+
}
|
|
4624
|
+
};
|
|
4583
4625
|
const execute = async (_id, params, signal, onUpdate, ctx, preserveActiveSession = false, parentModelOverride) => {
|
|
4584
4626
|
const workflowLaunchObserver = workflowLaunchObservers.get(params);
|
|
4585
4627
|
const inheritedUsageBudget = workflowOwnedUsageBudgets.get(params);
|
|
@@ -4614,8 +4656,8 @@ export function createSubagentExecutor(deps) {
|
|
|
4614
4656
|
: rememberParentModel(deps.state, resolveCurrentSessionId(ctx.sessionManager), currentParentModel)) ?? null;
|
|
4615
4657
|
})();
|
|
4616
4658
|
try {
|
|
4617
|
-
if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined
|
|
4618
|
-
throw new Error("preflight requires
|
|
4659
|
+
if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined) {
|
|
4660
|
+
throw new Error("preflight requires workflow: true or a workflow script path.");
|
|
4619
4661
|
}
|
|
4620
4662
|
workflowPreflight = normalizeWorkflowPreflight(requestParams.preflight);
|
|
4621
4663
|
if (workflowPreflight)
|
|
@@ -4662,8 +4704,12 @@ export function createSubagentExecutor(deps) {
|
|
|
4662
4704
|
if (launchBlockingErrors.length > 0) {
|
|
4663
4705
|
return buildRequestedModeError(requestParams, `Workflow '${_id}' validation failed before child launch; no children launched. ${launchBlockingErrors.map((error) => error.message).join(" ")}`);
|
|
4664
4706
|
}
|
|
4665
|
-
|
|
4666
|
-
|
|
4707
|
+
// A package chain's per-step stop check defeats static launch counting; runtime fan-out enforcement still applies.
|
|
4708
|
+
const structuredResource = workflowResource?.provenance.name === "chain" || workflowResource?.provenance.name === "tasks";
|
|
4709
|
+
for (const warning of workflowValidation.warnings ?? []) {
|
|
4710
|
+
if (!(structuredResource && warning.kind === "dynamic-spawn-count"))
|
|
4711
|
+
console.warn(`[pi-subagents] ${warning.message}`);
|
|
4712
|
+
}
|
|
4667
4713
|
const acceptanceErrors = validateAcceptanceInput(requestParams.acceptance);
|
|
4668
4714
|
if (acceptanceErrors.length > 0)
|
|
4669
4715
|
return buildRequestedModeError(requestParams, acceptanceErrors.join(" "));
|
|
@@ -4804,6 +4850,12 @@ export function createSubagentExecutor(deps) {
|
|
|
4804
4850
|
const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(currentSessionId));
|
|
4805
4851
|
const completionOwnerId = deps.state.completionOwnerId ?? currentCompletionOwnerId();
|
|
4806
4852
|
deps.state.completionOwnerId = completionOwnerId;
|
|
4853
|
+
const workflowScriptSourceDigest = workflowScriptDigest(requestParams.workflowScript);
|
|
4854
|
+
const workflowReuseSource = findWorkflowReuseSource(DIRS.async, currentSessionId, workflowScriptSourceDigest, workflowArgsDigest);
|
|
4855
|
+
const workflowRunEvidence = { ...workflowArgsEvidence, scriptDigest: workflowScriptSourceDigest };
|
|
4856
|
+
if (workflowReuseSource)
|
|
4857
|
+
workflowRunEvidence.reusedFrom = workflowReuseSource.runId;
|
|
4858
|
+
const workflowJournalFingerprints = new Map();
|
|
4807
4859
|
try {
|
|
4808
4860
|
fs.mkdirSync(asyncDir, { recursive: true });
|
|
4809
4861
|
writeRunFanoutBudgetDescriptor(asyncDir, workflowFanoutBudget);
|
|
@@ -4837,7 +4889,7 @@ export function createSubagentExecutor(deps) {
|
|
|
4837
4889
|
...(workflowCapabilityCeiling ? { admissionCapabilityCeiling: workflowCapabilityCeiling } : {}),
|
|
4838
4890
|
steps: [],
|
|
4839
4891
|
...(workflowPreflight ? { preflight: workflowPreflight } : {}),
|
|
4840
|
-
workflow: { trace: [], emits: [], console: [], ...
|
|
4892
|
+
workflow: { trace: [], emits: [], console: [], ...workflowRunEvidence, ...(workflowResource ? { resource: workflowResource.provenance } : {}) },
|
|
4841
4893
|
workflowChildren: workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "running", inventoryComplete: false }),
|
|
4842
4894
|
runFanoutBudget: getRunFanoutBudgetSnapshot(workflowFanoutBudget),
|
|
4843
4895
|
};
|
|
@@ -5381,6 +5433,8 @@ export function createSubagentExecutor(deps) {
|
|
|
5381
5433
|
workflowSteps.set(entry.key, step);
|
|
5382
5434
|
}
|
|
5383
5435
|
const projectedStep = workflowSteps.get(entry.key);
|
|
5436
|
+
if (entry.reused && projectedStep)
|
|
5437
|
+
projectedStep.reused = true;
|
|
5384
5438
|
if (entry.state === "stopped" && projectedStep) {
|
|
5385
5439
|
appendWorkflowEvent({
|
|
5386
5440
|
type: "subagent.child-status",
|
|
@@ -5414,7 +5468,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5414
5468
|
timeoutMs: timeout,
|
|
5415
5469
|
signal: controller.signal,
|
|
5416
5470
|
continueAfterAbortWhenChildrenSettled: (abortError) => {
|
|
5417
|
-
if (abortError
|
|
5471
|
+
if (!workflowStopCause(abortError))
|
|
5418
5472
|
return false;
|
|
5419
5473
|
const activeAsyncChild = [...(deps.state.asyncJobs?.values() ?? [])].some((job) => job.parentWorkflowRunId === workflowRunId && (job.status === "queued" || job.status === "running"));
|
|
5420
5474
|
const activeForegroundChild = [...deps.state.foregroundControls.values()].some((control) => control.parentWorkflowRunId === workflowRunId && (control.activeChildren?.size ?? 0) > 0);
|
|
@@ -5429,6 +5483,9 @@ export function createSubagentExecutor(deps) {
|
|
|
5429
5483
|
...(workflowState ? { state: workflowState } : {}),
|
|
5430
5484
|
onTrace: updateTrace,
|
|
5431
5485
|
onChildSettled: (notification) => {
|
|
5486
|
+
const journalFingerprint = workflowJournalFingerprints.get(notification.childKey);
|
|
5487
|
+
if (journalFingerprint)
|
|
5488
|
+
appendWorkflowChildJournal(asyncDir, { type: "settle", key: notification.childKey, fingerprint: journalFingerprint, result: notification.result });
|
|
5432
5489
|
appendWorkflowEvent({
|
|
5433
5490
|
type: "subagent.workflow.child_settled",
|
|
5434
5491
|
childKey: notification.childKey,
|
|
@@ -5461,6 +5518,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5461
5518
|
} });
|
|
5462
5519
|
},
|
|
5463
5520
|
admit: async (calls, admissionSignal) => {
|
|
5521
|
+
admitEnabledWorkflowChildren(calls);
|
|
5464
5522
|
await preflightWorkflowWorktrees({ workflowDefaults: workflowChildDefaults, defaultWorktree: deps.config.worktree, calls, ctxCwd: parentCwd, signal: admissionSignal, deadlineAt: workflowDeadlineAt });
|
|
5465
5523
|
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 });
|
|
5466
5524
|
if (outputClaims.error)
|
|
@@ -5483,6 +5541,33 @@ export function createSubagentExecutor(deps) {
|
|
|
5483
5541
|
appendWorkflowEvent({ type: "subagent.workflow.emit", value: emits.at(-1) });
|
|
5484
5542
|
},
|
|
5485
5543
|
launch: async (key, childParams, workflowSignal, admission) => {
|
|
5544
|
+
const journalFingerprint = workflowJournalFingerprints.get(key) ?? workflowChildFingerprint(childParams);
|
|
5545
|
+
workflowJournalFingerprints.set(key, journalFingerprint);
|
|
5546
|
+
const priorChild = workflowReuseSource ? matchWorkflowReuse(workflowReuseSource, key, journalFingerprint) : undefined;
|
|
5547
|
+
if (priorChild?.kind === "settled") {
|
|
5548
|
+
if (priorChild.result.runId)
|
|
5549
|
+
workflowChildRunIds.set(key, priorChild.result.runId);
|
|
5550
|
+
return { ...priorChild.result, reused: true };
|
|
5551
|
+
}
|
|
5552
|
+
if (priorChild?.kind === "started") {
|
|
5553
|
+
// The previous run's awaited async child may still be running: wait for that exact run
|
|
5554
|
+
// instead of launching again. Journal it first so another reload can re-attach too.
|
|
5555
|
+
const priorRunId = priorChild.runId;
|
|
5556
|
+
const priorAsyncDir = path.join(DIRS.async, priorRunId);
|
|
5557
|
+
appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: priorRunId });
|
|
5558
|
+
const stopListener = stopAwaitedAsyncChildOnAbort(workflowSignal, deps.state, priorRunId, priorAsyncDir, deps.kill);
|
|
5559
|
+
const reattached = await awaitExistingAsyncRun(priorAsyncDir, priorRunId, workflowSignal).finally(stopListener.remove);
|
|
5560
|
+
const claimedPath = reattached.status === "settled" ? claimWorkflowAwaitedResult(priorAsyncDir, workflowRunId) : undefined;
|
|
5561
|
+
if (reattached.status === "settled" && claimedPath) {
|
|
5562
|
+
const imported = importWorkflowAwaitedChildResult(reattached.result, { runId: priorRunId, asyncDir: priorAsyncDir, resultPath: claimedPath, task: typeof childParams.task === "string" ? childParams.task : "", parentWorkflowRunId: workflowReuseSource?.runId, details: { mode: "single", asyncId: priorRunId, asyncDir: priorAsyncDir, results: [] }, state: deps.state, pi: deps.pi });
|
|
5563
|
+
const reattachedResult = workflowChildResult(key, imported, childParams, deps.state);
|
|
5564
|
+
// Like a settled journal match: only a successful child is reused; a failed or stopped one launches fresh.
|
|
5565
|
+
if (reattachedResult.ok) {
|
|
5566
|
+
workflowChildRunIds.set(key, priorRunId);
|
|
5567
|
+
return { ...reattachedResult, reused: true };
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5486
5571
|
if (workflowUsageBudget.budget && childParams.async === true)
|
|
5487
5572
|
return workflowChildResult(key, buildRequestedModeError(childParams, "workflow usageBudget does not support async runs.run launches."), childParams, deps.state);
|
|
5488
5573
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
@@ -5504,6 +5589,8 @@ export function createSubagentExecutor(deps) {
|
|
|
5504
5589
|
if (workflowUsageBudget.budget)
|
|
5505
5590
|
workflowOwnedUsageBudgets.set(childRequest, workflowUsageBudget.budget);
|
|
5506
5591
|
workflowLaunchObservers.set(childRequest, (launch) => {
|
|
5592
|
+
if (launch.runId)
|
|
5593
|
+
appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: launch.runId });
|
|
5507
5594
|
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
5508
5595
|
if (step) {
|
|
5509
5596
|
step.agent = launch.agent;
|
|
@@ -5605,7 +5692,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5605
5692
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
5606
5693
|
settleWorkflowSteerInbox("complete");
|
|
5607
5694
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children, steps: status.steps });
|
|
5608
|
-
status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...
|
|
5695
|
+
status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...workflowRunEvidence, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
|
|
5609
5696
|
const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph), workflowResource?.provenance, workflowArgsDigest);
|
|
5610
5697
|
delete status.workflowReceiptPath;
|
|
5611
5698
|
let workflowReceipt;
|
|
@@ -5628,6 +5715,9 @@ export function createSubagentExecutor(deps) {
|
|
|
5628
5715
|
catch (error) {
|
|
5629
5716
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
5630
5717
|
const stopped = controller.signal.aborted;
|
|
5718
|
+
const stopCause = stopped ? workflowStopCause(controller.signal.reason) : undefined;
|
|
5719
|
+
if (stopCause)
|
|
5720
|
+
workflowRunEvidence.stopCause = stopCause;
|
|
5631
5721
|
const detachedChildKeys = new Set(partial.children.filter((child) => child.detached).map((child) => child.key));
|
|
5632
5722
|
const hasRealFailedChild = partial.children.some((child) => !child.ok && child.state !== "running" && !child.detached);
|
|
5633
5723
|
const pauseForDetached = !stopped && error instanceof WorkflowScriptError && error.errorKind === "detached-child" && detachedChildKeys.size > 0 && !hasRealFailedChild;
|
|
@@ -5650,7 +5740,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5650
5740
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: true, trace: partial.trace, children: partial.children, steps: status.steps });
|
|
5651
5741
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
|
|
5652
5742
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
|
|
5653
|
-
status = compactOptional({ ...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, ...(error instanceof WorkflowScriptError && error.errorKind ? { failureKind: error.errorKind } : {}), ...
|
|
5743
|
+
status = compactOptional({ ...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, ...(error instanceof WorkflowScriptError && error.errorKind ? { failureKind: error.errorKind } : {}), ...workflowRunEvidence, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
|
|
5654
5744
|
if (pauseForDetached) {
|
|
5655
5745
|
const promoted = promotePausedWorkflowIfSettled(status);
|
|
5656
5746
|
if (promoted)
|
|
@@ -5660,7 +5750,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5660
5750
|
const errorPreview = status.error && status.error.length > 1_000 ? `${status.error.slice(0, 1_000)}… (truncated; full error: ${statusPath} (error))` : status.error;
|
|
5661
5751
|
const terminalSummary = `${status.state === "complete"
|
|
5662
5752
|
? "Workflow completed after detached child finished."
|
|
5663
|
-
: errorPreview ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.")}${workflowOutputPathMappingSummary(partial.children)}${finalPreflightWarnings.length ? ` ${finalPreflightWarnings.join(" ")}` : ""}`;
|
|
5753
|
+
: errorPreview ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.")}${stopCause ? ` ${WORKFLOW_RUNTIME_REPLACED_RELAUNCH_NOTICE}` : ""}${workflowOutputPathMappingSummary(partial.children)}${finalPreflightWarnings.length ? ` ${finalPreflightWarnings.join(" ")}` : ""}`;
|
|
5664
5754
|
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, terminalSummary, producedChildOutputPaths);
|
|
5665
5755
|
const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
|
|
5666
5756
|
const receiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
|
|
@@ -5774,6 +5864,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5774
5864
|
sendWorkflowProgress();
|
|
5775
5865
|
},
|
|
5776
5866
|
admit: async (calls, admissionSignal) => {
|
|
5867
|
+
admitEnabledWorkflowChildren(calls);
|
|
5777
5868
|
await preflightWorkflowWorktrees({ workflowDefaults: workflowChildDefaults, defaultWorktree: deps.config.worktree, calls, ctxCwd: ctx.cwd, signal: admissionSignal, deadlineAt: workflowDeadlineAt });
|
|
5778
5869
|
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 });
|
|
5779
5870
|
if (outputClaims.error)
|
|
@@ -6245,8 +6336,10 @@ export function createSubagentExecutor(deps) {
|
|
|
6245
6336
|
}
|
|
6246
6337
|
if (action === "guide") {
|
|
6247
6338
|
try {
|
|
6339
|
+
const guide = readSubagentGuide(paramsWithResolvedCwd.topic);
|
|
6340
|
+
const notice = paramsWithResolvedCwd.topic === "tool-reference" ? disabledFeatureNotice(disabledFeatures) : undefined;
|
|
6248
6341
|
return {
|
|
6249
|
-
content: [{ type: "text", text:
|
|
6342
|
+
content: [{ type: "text", text: notice ? `${notice}\n\n${guide}` : guide }],
|
|
6250
6343
|
details: { mode: "management", results: [] },
|
|
6251
6344
|
};
|
|
6252
6345
|
}
|
|
@@ -6476,7 +6569,7 @@ export function createSubagentExecutor(deps) {
|
|
|
6476
6569
|
}
|
|
6477
6570
|
if (action.startsWith("schedule.")) {
|
|
6478
6571
|
if (!isScheduledRunAction(action)) {
|
|
6479
|
-
return { content: [{ type: "text", text: unknownSubagentActionMessage(action) }], isError: true, details: { mode: "management", results: [] } };
|
|
6572
|
+
return { content: [{ type: "text", text: unknownSubagentActionMessage(action, disabledFeatures) }], isError: true, details: { mode: "management", results: [] } };
|
|
6480
6573
|
}
|
|
6481
6574
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
6482
6575
|
return {
|
|
@@ -6520,55 +6613,47 @@ export function createSubagentExecutor(deps) {
|
|
|
6520
6613
|
}
|
|
6521
6614
|
if (action === "stop") {
|
|
6522
6615
|
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
console.error(`Failed to append child status event for workflow ${workflowRunId}:`, error);
|
|
6563
|
-
}
|
|
6564
|
-
return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
|
|
6616
|
+
// Whole-workflow stops reach stopAsyncRun below, which stops a live workflow in-process.
|
|
6617
|
+
const workflowRunId = targetRunId && paramsWithResolvedCwd.childId !== undefined ? resolveLiveWorkflowRunId(deps, targetRunId) : undefined;
|
|
6618
|
+
if (workflowRunId && paramsWithResolvedCwd.childId !== undefined) {
|
|
6619
|
+
const stopChild = deps.state.workflowChildStops?.get(workflowRunId);
|
|
6620
|
+
const asyncJob = deps.state.asyncJobs.get(workflowRunId);
|
|
6621
|
+
if (!asyncJob?.asyncDir)
|
|
6622
|
+
return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6623
|
+
const status = readStatus(asyncJob.asyncDir);
|
|
6624
|
+
if (!status)
|
|
6625
|
+
return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6626
|
+
const resolution = resolveAsyncStatusChild(status, paramsWithResolvedCwd.childId);
|
|
6627
|
+
if (!resolution.ok)
|
|
6628
|
+
return { content: [{ type: "text", text: resolution.message }], isError: true, details: { mode: "management", results: [] } };
|
|
6629
|
+
if (!isStoppableAsyncStatusStep(resolution.child.step))
|
|
6630
|
+
return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in async run '${workflowRunId}' is ${resolution.child.step.status}; stop only supports pending or running children.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6631
|
+
if (!stopChild)
|
|
6632
|
+
return { content: [{ type: "text", text: `Workflow ${workflowRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6633
|
+
if (!stopChild(resolution.child.id, `Workflow child '${resolution.child.id}' stopped.`))
|
|
6634
|
+
return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in workflow ${workflowRunId} is not available to stop.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6635
|
+
try {
|
|
6636
|
+
fs.appendFileSync(path.join(asyncJob.asyncDir, "events.jsonl"), `${JSON.stringify({
|
|
6637
|
+
type: "subagent.child-status",
|
|
6638
|
+
version: 1,
|
|
6639
|
+
runId: workflowRunId,
|
|
6640
|
+
childId: resolution.child.id,
|
|
6641
|
+
status: "stopping",
|
|
6642
|
+
ts: Date.now(),
|
|
6643
|
+
reason: "subagent-action",
|
|
6644
|
+
source: "async",
|
|
6645
|
+
stepIndex: resolution.child.index,
|
|
6646
|
+
agent: resolution.child.step.agent,
|
|
6647
|
+
...(resolution.child.step.runId ? { childRunId: resolution.child.step.runId } : {}),
|
|
6648
|
+
...(resolution.child.step.workflowKey ? { workflowKey: resolution.child.step.workflowKey } : {}),
|
|
6649
|
+
...(resolution.child.step.phase ? { phase: resolution.child.step.phase } : {}),
|
|
6650
|
+
...(resolution.child.step.label ? { label: resolution.child.step.label } : {}),
|
|
6651
|
+
})}\n`, "utf-8");
|
|
6652
|
+
}
|
|
6653
|
+
catch (error) {
|
|
6654
|
+
console.error(`Failed to append child status event for workflow ${workflowRunId}:`, error);
|
|
6565
6655
|
}
|
|
6566
|
-
|
|
6567
|
-
const status = asyncJob?.asyncDir ? readStatus(asyncJob.asyncDir) : undefined;
|
|
6568
|
-
if (status)
|
|
6569
|
-
stopStoppableAsyncStatusChildren(status, stopChild, "Workflow stopped.");
|
|
6570
|
-
workflowController.abort(new Error("Workflow stopped."));
|
|
6571
|
-
return { content: [{ type: "text", text: `Stop requested for async workflow ${targetRunId}.` }], details: { mode: "management", results: [] } };
|
|
6656
|
+
return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
|
|
6572
6657
|
}
|
|
6573
6658
|
let resolved;
|
|
6574
6659
|
if (paramsWithResolvedCwd.dir) {
|
|
@@ -6646,7 +6731,7 @@ export function createSubagentExecutor(deps) {
|
|
|
6646
6731
|
}
|
|
6647
6732
|
if (!SUBAGENT_ACTIONS.includes(action)) {
|
|
6648
6733
|
return {
|
|
6649
|
-
content: [{ type: "text", text: unknownSubagentActionMessage(action) }],
|
|
6734
|
+
content: [{ type: "text", text: unknownSubagentActionMessage(action, disabledFeatures) }],
|
|
6650
6735
|
isError: true,
|
|
6651
6736
|
details: { mode: "management", results: [] },
|
|
6652
6737
|
};
|
|
@@ -7009,6 +7094,7 @@ export function createSubagentExecutor(deps) {
|
|
|
7009
7094
|
contextPolicy,
|
|
7010
7095
|
modelScope,
|
|
7011
7096
|
parentModel: requestParentModel,
|
|
7097
|
+
scopedModelIds: scopedModelIdsFromContext(ctx),
|
|
7012
7098
|
parentSessionId: requestSessionId,
|
|
7013
7099
|
parentPiSessionId: requestPiSessionId,
|
|
7014
7100
|
capabilityCeiling: intersectSubagentCapabilityCeilings(effectiveParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(requestSessionId)),
|
|
@@ -7102,6 +7188,7 @@ export function createSubagentExecutor(deps) {
|
|
|
7102
7188
|
params: effectiveParams,
|
|
7103
7189
|
agents,
|
|
7104
7190
|
parentModel: requestParentModel,
|
|
7191
|
+
scopedModelIds: scopedModelIdsFromContext(ctx),
|
|
7105
7192
|
availableModels: ctx.modelRegistry.getAvailable().map(toModelInfo),
|
|
7106
7193
|
currentProvider: requestParentModel?.provider,
|
|
7107
7194
|
modelScope,
|
|
@@ -7254,33 +7341,57 @@ export function createSubagentExecutor(deps) {
|
|
|
7254
7341
|
}
|
|
7255
7342
|
};
|
|
7256
7343
|
const executePublic = (id, params, signal, onUpdate, ctx) => {
|
|
7257
|
-
const
|
|
7344
|
+
const disabledFeatureError = disabledFeatureResult(params);
|
|
7345
|
+
if (disabledFeatureError)
|
|
7346
|
+
return Promise.resolve(disabledFeatureError);
|
|
7347
|
+
const normalized = normalizePublicSubagentExecution(params, { structuredWorkflows: disabledFeatures.features.has("workflow-scripts") });
|
|
7258
7348
|
if (!normalized.ok) {
|
|
7259
7349
|
return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
|
|
7260
7350
|
}
|
|
7261
7351
|
let publicParams = normalized.params;
|
|
7262
|
-
|
|
7263
|
-
|
|
7352
|
+
const workflow = publicParams.workflow;
|
|
7353
|
+
const errorResult = (text) => Promise.resolve({ content: [{ type: "text", text }], isError: true, details: { mode: publicParams.action ? "management" : "workflow", results: [] } });
|
|
7354
|
+
// Models tend to put script text in the workflow string; say how to pass it instead.
|
|
7355
|
+
const scriptTextHint = " A workflow string is a named workflow resource or a script file path. To run script text, write it in one ```js workflow block in the same reply and call subagent({ workflow: true }).";
|
|
7356
|
+
if (publicParams.tasks !== undefined || publicParams.chain !== undefined) {
|
|
7357
|
+
// Normalization admits chain/tasks only with workflow scripts disabled; the original input was checked above.
|
|
7358
|
+
const kind = publicParams.tasks !== undefined ? "tasks" : "chain";
|
|
7359
|
+
const resolved = resolveStructuredWorkflowResource({ kind, steps: publicParams[kind], task: publicParams.task });
|
|
7264
7360
|
if (!resolved.ok)
|
|
7265
|
-
return
|
|
7361
|
+
return errorResult(resolved.error);
|
|
7362
|
+
const { tasks: _tasks, chain: _chain, task: _task, ...withoutStructuredInput } = publicParams;
|
|
7363
|
+
publicParams = { ...withoutStructuredInput, workflowScript: resolved.resource.script };
|
|
7364
|
+
workflowResourcePermits.set(publicParams, resolved.resource.permit);
|
|
7365
|
+
}
|
|
7366
|
+
else if (typeof workflow === "string" && !isWorkflowScriptPath(workflow)) {
|
|
7367
|
+
const resolved = resolveWorkflowResource(workflow, publicParams.args, ctx.sessionManager.getSessionId() ?? undefined);
|
|
7368
|
+
if (!resolved.ok)
|
|
7369
|
+
return Promise.resolve({ content: [{ type: "text", text: resolved.error + scriptTextHint }], isError: true, details: { mode: "workflow", results: [] } });
|
|
7266
7370
|
const { workflow: _workflow, args: _args, ...withoutResourceInput } = publicParams;
|
|
7267
7371
|
publicParams = { ...withoutResourceInput, workflowScript: resolved.resource.script };
|
|
7268
7372
|
workflowResourcePermits.set(publicParams, resolved.resource.permit);
|
|
7269
7373
|
}
|
|
7270
|
-
else if (
|
|
7374
|
+
else if (workflow !== undefined || publicParams.workflowScript !== undefined) {
|
|
7271
7375
|
const normalizedArgs = normalizeWorkflowArgs(publicParams.args);
|
|
7272
7376
|
if ("error" in normalizedArgs)
|
|
7273
|
-
return
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7377
|
+
return errorResult(normalizedArgs.error);
|
|
7378
|
+
let workflowScript = publicParams.workflowScript;
|
|
7379
|
+
if (workflow !== undefined) {
|
|
7380
|
+
const source = workflow === true ? readReplyWorkflowScript(ctx.sessionManager, id) : readWorkflowScriptFile(workflow, publicParams.cwd, ctx.cwd);
|
|
7381
|
+
if ("error" in source)
|
|
7382
|
+
return errorResult(workflow === true ? source.error : source.error + scriptTextHint);
|
|
7383
|
+
workflowScript = source.script;
|
|
7384
|
+
}
|
|
7385
|
+
const { workflow: _workflow, ...withoutWorkflowSource } = publicParams;
|
|
7386
|
+
publicParams = { ...withoutWorkflowSource, workflowScript, args: deepFreezeWorkflowArgs(normalizedArgs.args) };
|
|
7279
7387
|
}
|
|
7280
|
-
publicExecutions.add(
|
|
7281
|
-
return executeWithSingleDispatchGuard(id,
|
|
7388
|
+
publicExecutions.add(publicParams);
|
|
7389
|
+
return executeWithSingleDispatchGuard(id, publicParams, signal, onUpdate, ctx);
|
|
7282
7390
|
};
|
|
7283
7391
|
const executeDelegated = async (id, params, signal, onUpdate, ctx) => {
|
|
7392
|
+
const disabledFeatureError = disabledFeatureResult(params);
|
|
7393
|
+
if (disabledFeatureError)
|
|
7394
|
+
return disabledFeatureError;
|
|
7284
7395
|
const delegatedParams = { ...params };
|
|
7285
7396
|
const privateParams = delegatedParams;
|
|
7286
7397
|
const thinkingOverride = privateParams.delegatedThinkingOverride;
|