pi-subagents 0.64.0 → 0.65.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 +33 -0
- package/README.md +2 -2
- package/agents/reviewer.md +1 -1
- package/agents/scout.md +1 -1
- package/docs/agents.md +18 -16
- package/docs/configuration.md +7 -15
- package/docs/extension-api.md +15 -6
- package/docs/missions.md +2 -0
- package/docs/observability.md +10 -12
- package/docs/tool-reference.md +7 -6
- package/docs/watchdog.md +1 -1
- package/docs/workflows.md +5 -3
- package/package.json +3 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +2 -2
- package/src/agents/agent-management.ts +47 -15
- package/src/api/capability-ceiling.ts +0 -1
- package/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
- package/src/api/preflight.ts +2 -3
- package/src/extension/doctor.ts +2 -10
- package/src/extension/fanout-child.ts +9 -11
- package/src/extension/index.ts +27 -5
- package/src/extension/public-execution.ts +14 -0
- package/src/extension/rpc.ts +3 -2
- package/src/extension/schemas.ts +2 -1
- package/src/extension/tool-description.ts +9 -8
- package/src/intercom/native-supervisor-channel.ts +138 -60
- package/src/intercom/supervisor-ui.ts +243 -0
- package/src/runs/background/async-execution.ts +43 -24
- package/src/runs/background/async-job-tracker.ts +11 -0
- package/src/runs/background/async-resume.ts +11 -2
- package/src/runs/background/control-channel.ts +2 -204
- package/src/runs/background/process-terminal.ts +1 -1
- package/src/runs/background/run-child-session.ts +613 -0
- package/src/runs/background/run-status.ts +0 -1
- package/src/runs/background/runner-aliases.ts +125 -0
- package/src/runs/background/runner-child-sessions.ts +31 -0
- package/src/runs/background/scheduled-runs.ts +18 -4
- package/src/runs/background/subagent-runner.ts +184 -901
- package/src/runs/foreground/async-steering-action.ts +1 -17
- package/src/runs/foreground/execution.ts +189 -377
- package/src/runs/foreground/foreground-control.ts +4 -0
- package/src/runs/foreground/subagent-executor.ts +100 -57
- package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
- package/src/runs/shared/abort-recovery.ts +3 -3
- package/src/runs/shared/capability-ceiling.ts +1 -2
- package/src/runs/shared/child-hooks.ts +25 -0
- package/src/runs/shared/child-identity.ts +13 -2
- package/src/runs/shared/child-launch.ts +314 -0
- package/src/runs/shared/child-lifecycle.ts +25 -0
- package/src/runs/shared/child-runtime-config.ts +126 -0
- package/src/runs/shared/child-session.ts +342 -0
- package/src/runs/shared/child-tool-plan.ts +530 -0
- package/src/runs/shared/claude-code-adapter.ts +5 -1
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/external-cli-preflight.ts +16 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/src/runs/shared/model-exclusions.ts +82 -14
- package/src/runs/shared/model-fallback.ts +47 -4
- package/src/runs/shared/nested-events.ts +29 -45
- package/src/runs/shared/nested-path.ts +0 -14
- package/src/runs/shared/orca-progress-tabs.ts +12 -7
- package/src/runs/shared/parallel-utils.ts +0 -2
- package/src/runs/shared/permissions.ts +0 -13
- package/src/runs/shared/process-signal.ts +4 -1
- package/src/runs/shared/run-fanout-budget.ts +0 -13
- package/src/runs/shared/runtime-acknowledged-extensions.ts +0 -27
- package/src/runs/shared/structured-output.ts +17 -4
- package/src/runs/shared/subagent-control.ts +2 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +87 -384
- package/src/runs/shared/tool-availability.ts +18 -62
- package/src/runs/shared/tool-budget.ts +0 -14
- package/src/runs/shared/worktree-cleanup-plan.ts +25 -6
- package/src/runs/shared/worktree.ts +117 -30
- package/src/shared/child-session-name.ts +1 -1
- package/src/shared/jsonl-writer.ts +11 -0
- package/src/shared/thinking-ceiling.ts +0 -6
- package/src/shared/types.ts +55 -28
- package/src/shared/utils.ts +3 -4
- package/src/slash/slash-commands.ts +0 -6
- package/src/tui/fleet.ts +0 -1
- package/src/tui/render.ts +233 -31
- package/src/watchdog/child-status.ts +0 -1
- package/src/watchdog/register-child.ts +12 -12
- package/src/workflows/scripted-workflow.ts +48 -1
- package/src/runs/shared/child-protocol.ts +0 -415
- package/src/runs/shared/pi-args.ts +0 -1059
- package/src/runs/shared/subagent-startup-retry.ts +0 -116
- package/src/shared/post-exit-stdio-guard.ts +0 -85
|
@@ -14,6 +14,7 @@ interface BeginForegroundChildInput {
|
|
|
14
14
|
thinking?: string;
|
|
15
15
|
interrupt: () => boolean;
|
|
16
16
|
detach?: () => boolean;
|
|
17
|
+
steer?: ForegroundChildControl["steer"];
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
function copyProgress(target: ForegroundChildControl, progress: AgentProgress | undefined): void {
|
|
@@ -56,6 +57,7 @@ function syncCurrentChild(control: ForegroundRunControl, child: ForegroundChildC
|
|
|
56
57
|
control.toolCount = child.toolCount;
|
|
57
58
|
control.interrupt = child.interrupt;
|
|
58
59
|
control.detach = child.detach;
|
|
60
|
+
control.steer = child.steer;
|
|
59
61
|
control.updatedAt = child.updatedAt;
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -79,6 +81,7 @@ function clearCurrentChild(control: ForegroundRunControl): void {
|
|
|
79
81
|
control.toolCount = undefined;
|
|
80
82
|
control.interrupt = undefined;
|
|
81
83
|
control.detach = undefined;
|
|
84
|
+
control.steer = undefined;
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
export function retainForegroundSchedulingOwner(control: ForegroundRunControl): void {
|
|
@@ -122,6 +125,7 @@ export function beginForegroundChild(control: ForegroundRunControl, input: Begin
|
|
|
122
125
|
return true;
|
|
123
126
|
};
|
|
124
127
|
}
|
|
128
|
+
if (input.steer) child.steer = input.steer;
|
|
125
129
|
control.activeChildren ??= new Map();
|
|
126
130
|
control.activeChildren.set(input.index, child);
|
|
127
131
|
registerLivePromptAudit(control, input.index, input.authoredTask, input.effectivePrompt, {
|
|
@@ -59,7 +59,7 @@ import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
|
59
59
|
import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
|
|
60
60
|
import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
|
|
61
61
|
import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
|
|
62
|
-
import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget,
|
|
62
|
+
import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
|
|
63
63
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
64
64
|
import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
|
|
65
65
|
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
@@ -84,11 +84,8 @@ import { deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, type Ste
|
|
|
84
84
|
import { updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
85
85
|
import { canQueueRetainedAsyncFollowUp, steerAsyncRun } from "./async-steering-action.ts";
|
|
86
86
|
import {
|
|
87
|
-
removeWorkflowForegroundSteeringRoute,
|
|
88
87
|
resolveWorkflowForegroundSteeringTarget,
|
|
89
88
|
steerWorkflowForegroundTarget,
|
|
90
|
-
workflowForegroundSteeringDir,
|
|
91
|
-
workflowForegroundSteeringLaunchOptions,
|
|
92
89
|
} from "./workflow-foreground-steering.ts";
|
|
93
90
|
import { stopAsyncRun } from "./async-stop-action.ts";
|
|
94
91
|
import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
|
|
@@ -96,10 +93,11 @@ import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletio
|
|
|
96
93
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
97
94
|
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
|
|
98
95
|
import { resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
|
|
99
|
-
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult,
|
|
96
|
+
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, resolveNestedAsyncDir, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedParentAddress, type NestedRoute, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
97
|
+
import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
|
|
100
98
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
101
99
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
102
|
-
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/child-identity.ts";
|
|
100
|
+
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild, stopStoppableAsyncStatusChildren } from "../shared/child-identity.ts";
|
|
103
101
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
104
102
|
import { getExternalJobProvider } from "../../api/external-job-provider.ts";
|
|
105
103
|
import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.ts";
|
|
@@ -153,6 +151,9 @@ import {
|
|
|
153
151
|
type Details,
|
|
154
152
|
type ExtensionConfig,
|
|
155
153
|
type ForegroundResumeChild,
|
|
154
|
+
type ForegroundChildSessionControls,
|
|
155
|
+
type ForegroundSteerInput,
|
|
156
|
+
type ForegroundSteerOutcome,
|
|
156
157
|
type ForegroundRunControl,
|
|
157
158
|
type HostStepNodeV1,
|
|
158
159
|
type IntercomBridgeConfig,
|
|
@@ -351,6 +352,8 @@ export interface SubagentParamsLike {
|
|
|
351
352
|
tasks?: TaskParam[];
|
|
352
353
|
concurrency?: number;
|
|
353
354
|
worktree?: boolean;
|
|
355
|
+
/** Git ref used as the managed worktree base. */
|
|
356
|
+
baseRef?: string;
|
|
354
357
|
context?: "fresh" | "fork" | "profile";
|
|
355
358
|
/** Per-run intercom bridge config. It replaces the global config for this launch only. */
|
|
356
359
|
intercomBridge?: IntercomBridgeConfig;
|
|
@@ -436,6 +439,20 @@ interface ExecutorDeps {
|
|
|
436
439
|
activateSupervisorTransport?: () => void;
|
|
437
440
|
refreshResultDelivery?: () => void;
|
|
438
441
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
442
|
+
/** Set when this executor runs inside a child session; carries the runtime settings the host passes instead of environment variables. */
|
|
443
|
+
childRuntime?: ChildRuntimeConfig;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function inheritedNestedRoute(deps: Pick<ExecutorDeps, "childRuntime">): NestedRoute | undefined {
|
|
447
|
+
return inheritedNestedRouteOf(deps.childRuntime);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function inheritedNestedParentAddress(deps: Pick<ExecutorDeps, "childRuntime">): NestedParentAddress | undefined {
|
|
451
|
+
return inheritedNestedParentAddressOf(deps.childRuntime);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function inheritedRunFanoutBudget(deps: Pick<ExecutorDeps, "childRuntime">): RunFanoutBudgetDescriptor | undefined {
|
|
455
|
+
return deps.childRuntime?.runFanoutBudget;
|
|
439
456
|
}
|
|
440
457
|
|
|
441
458
|
type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => string | undefined;
|
|
@@ -512,7 +529,6 @@ function loadWorkflowScriptPath(params: SubagentParamsLike, runtimeCwd: string):
|
|
|
512
529
|
function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
|
|
513
530
|
const control = state.foregroundControls.get(runId);
|
|
514
531
|
if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
|
|
515
|
-
if (control) removeWorkflowForegroundSteeringRoute(control);
|
|
516
532
|
state.foregroundControls.delete(runId);
|
|
517
533
|
if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
|
|
518
534
|
return true;
|
|
@@ -565,8 +581,8 @@ function formatForegroundActivity(control: SubagentState["foregroundControls"] e
|
|
|
565
581
|
|
|
566
582
|
function nestedResolutionScopeForExecutor(deps: ExecutorDeps): NestedRunResolutionScope | undefined {
|
|
567
583
|
if (deps.allowMutatingManagementActions !== false) return undefined;
|
|
568
|
-
const route =
|
|
569
|
-
const address = route ?
|
|
584
|
+
const route = inheritedNestedRoute(deps);
|
|
585
|
+
const address = route ? inheritedNestedParentAddress(deps) : undefined;
|
|
570
586
|
return {
|
|
571
587
|
routes: route ? [route] : [],
|
|
572
588
|
...(address ? { descendantOf: { parentRunId: address.parentRunId, ...(address.parentStepIndex !== undefined ? { parentStepIndex: address.parentStepIndex } : {}) } } : {}),
|
|
@@ -1299,6 +1315,7 @@ function appendStepToAsyncChain(input: {
|
|
|
1299
1315
|
modelScope: discoveredForAppend.modelScope,
|
|
1300
1316
|
interactive: input.ctx.hasUI,
|
|
1301
1317
|
permissions: input.deps.config.permissions,
|
|
1318
|
+
childRuntime: input.deps.childRuntime,
|
|
1302
1319
|
});
|
|
1303
1320
|
const built = buildAsyncRunnerSteps(resolved.id, compactOptional<Parameters<typeof buildAsyncRunnerSteps>[1]>({
|
|
1304
1321
|
chain: wrapChainTasksForFork(chain, contextPolicy),
|
|
@@ -1311,7 +1328,7 @@ function appendStepToAsyncChain(input: {
|
|
|
1311
1328
|
cwd: status.cwd ?? input.requestCwd,
|
|
1312
1329
|
chainSkills,
|
|
1313
1330
|
dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
|
|
1314
|
-
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1331
|
+
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
|
|
1315
1332
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1316
1333
|
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1317
1334
|
contextForAgent: contextPolicy.contextForAgent,
|
|
@@ -1637,11 +1654,11 @@ async function resumeExternalJobFollowUp(input: {
|
|
|
1637
1654
|
return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId, asyncDir, duplicate: true, interactive: input.ctx.hasUI });
|
|
1638
1655
|
}
|
|
1639
1656
|
|
|
1640
|
-
const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth);
|
|
1657
|
+
const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
|
|
1641
1658
|
if (depthState.blocked) {
|
|
1642
1659
|
return { content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depthState.depth}, max=${depthState.maxDepth}). Complete the follow-up directly instead.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1643
1660
|
}
|
|
1644
|
-
const topLevelResume = depthState.depth === 0 && !
|
|
1661
|
+
const topLevelResume = depthState.depth === 0 && !inheritedNestedRoute(input.deps) && !input.deps.state.workflowControllers?.has(input.target.runId);
|
|
1645
1662
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1646
1663
|
try {
|
|
1647
1664
|
activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
|
|
@@ -1679,13 +1696,14 @@ async function resumeExternalJobFollowUp(input: {
|
|
|
1679
1696
|
modelScope: input.modelScope,
|
|
1680
1697
|
interactive: input.ctx.hasUI,
|
|
1681
1698
|
permissions: input.deps.config.permissions,
|
|
1699
|
+
childRuntime: input.deps.childRuntime,
|
|
1682
1700
|
}),
|
|
1683
1701
|
cwd: input.effectiveCwd,
|
|
1684
1702
|
artifactsDir,
|
|
1685
1703
|
artifactConfig,
|
|
1686
1704
|
shareEnabled: false,
|
|
1687
1705
|
...(input.parentSessionFile ? { sessionRoot: input.deps.getSubagentSessionRoot(input.parentSessionFile) } : {}),
|
|
1688
|
-
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1706
|
+
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
|
|
1689
1707
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1690
1708
|
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1691
1709
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
@@ -1796,7 +1814,7 @@ async function resumeAsyncRun(input: {
|
|
|
1796
1814
|
};
|
|
1797
1815
|
}
|
|
1798
1816
|
|
|
1799
|
-
const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth);
|
|
1817
|
+
const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
|
|
1800
1818
|
if (blocked) {
|
|
1801
1819
|
return {
|
|
1802
1820
|
content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depth}, max=${maxDepth}). Complete the follow-up directly instead.` }],
|
|
@@ -1885,7 +1903,7 @@ async function resumeAsyncRun(input: {
|
|
|
1885
1903
|
};
|
|
1886
1904
|
}
|
|
1887
1905
|
const runId = randomUUID();
|
|
1888
|
-
const topLevelResume = depth === 0 && !
|
|
1906
|
+
const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
|
|
1889
1907
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1890
1908
|
try {
|
|
1891
1909
|
activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
|
|
@@ -1931,6 +1949,7 @@ async function resumeAsyncRun(input: {
|
|
|
1931
1949
|
modelScope,
|
|
1932
1950
|
interactive: input.ctx.hasUI,
|
|
1933
1951
|
permissions: input.deps.config.permissions,
|
|
1952
|
+
childRuntime: input.deps.childRuntime,
|
|
1934
1953
|
}),
|
|
1935
1954
|
availableModels,
|
|
1936
1955
|
cwd: effectiveCwd,
|
|
@@ -1943,12 +1962,13 @@ async function resumeAsyncRun(input: {
|
|
|
1943
1962
|
agentContract: input.params.agentContract,
|
|
1944
1963
|
fast: input.params.fast,
|
|
1945
1964
|
dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
|
|
1946
|
-
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1965
|
+
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
|
|
1947
1966
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1948
1967
|
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1949
1968
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
1950
1969
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1951
1970
|
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
1971
|
+
baseRef: input.params.baseRef ?? target.recoveryDescriptor?.baseRef,
|
|
1952
1972
|
worktreeProvider: input.deps.config.worktreeProvider,
|
|
1953
1973
|
worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
|
|
1954
1974
|
controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
|
|
@@ -1985,8 +2005,11 @@ async function resumeAsyncRun(input: {
|
|
|
1985
2005
|
if (target.source === "async" && asyncReviveRequiresRecoveryDescriptor(target)) {
|
|
1986
2006
|
return { content: [{ type: "text", text: `Async child '${target.runId}' is missing its required run fan-out recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1987
2007
|
}
|
|
2008
|
+
if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
|
|
2009
|
+
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: [] } };
|
|
2010
|
+
}
|
|
1988
2011
|
const runId = randomUUID();
|
|
1989
|
-
const topLevelResume = depth === 0 && !
|
|
2012
|
+
const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
|
|
1990
2013
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1991
2014
|
try {
|
|
1992
2015
|
activeAsyncCapacity = !topLevelResume ? undefined : target.source === "async"
|
|
@@ -2034,6 +2057,7 @@ async function resumeAsyncRun(input: {
|
|
|
2034
2057
|
modelScope,
|
|
2035
2058
|
interactive: input.ctx.hasUI,
|
|
2036
2059
|
permissions: input.deps.config.permissions,
|
|
2060
|
+
childRuntime: input.deps.childRuntime,
|
|
2037
2061
|
}),
|
|
2038
2062
|
cwd: effectiveCwd,
|
|
2039
2063
|
maxOutput: input.params.maxOutput ?? recoveryDescriptor?.maxOutput,
|
|
@@ -2058,12 +2082,13 @@ async function resumeAsyncRun(input: {
|
|
|
2058
2082
|
thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
|
|
2059
2083
|
extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
|
|
2060
2084
|
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
2061
|
-
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
2085
|
+
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
|
|
2062
2086
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
2063
2087
|
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
2064
2088
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
2065
2089
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
2066
2090
|
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
2091
|
+
baseRef: input.params.baseRef ?? recoveryDescriptor?.baseRef,
|
|
2067
2092
|
worktreeProvider: input.deps.config.worktreeProvider,
|
|
2068
2093
|
worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
|
|
2069
2094
|
// A retained async child already owns the recorded worktree. Resume it in
|
|
@@ -3187,9 +3212,10 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3187
3212
|
modelScope: data.modelScope,
|
|
3188
3213
|
interactive: ctx.hasUI,
|
|
3189
3214
|
permissions: deps.config.permissions,
|
|
3215
|
+
childRuntime: deps.childRuntime,
|
|
3190
3216
|
});
|
|
3191
3217
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3192
|
-
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
3218
|
+
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
|
|
3193
3219
|
const currentProvider = parentModel?.provider;
|
|
3194
3220
|
const controlIntercomTarget = resolveRunLevelIntercomTarget(intercomBridge, contextPolicy);
|
|
3195
3221
|
const childIntercomTarget = resolveChildIntercomTargetFactory(intercomBridge, contextPolicy, id);
|
|
@@ -3269,6 +3295,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3269
3295
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
3270
3296
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
3271
3297
|
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
3298
|
+
baseRef: params.baseRef,
|
|
3272
3299
|
worktreeProvider: deps.config.worktreeProvider,
|
|
3273
3300
|
worktreeBranchPrefix: deps.config.worktreeBranchPrefix,
|
|
3274
3301
|
controlConfig,
|
|
@@ -3307,6 +3334,7 @@ function createSingleWorktreeSetup(
|
|
|
3307
3334
|
setupHook: ExtensionConfig["worktreeSetupHook"],
|
|
3308
3335
|
setupHookTimeoutMs: ExtensionConfig["worktreeSetupHookTimeoutMs"],
|
|
3309
3336
|
baseDir: ExtensionConfig["worktreeBaseDir"],
|
|
3337
|
+
baseRef: string | undefined,
|
|
3310
3338
|
provider: ExtensionConfig["worktreeProvider"],
|
|
3311
3339
|
branchPrefix: ExtensionConfig["worktreeBranchPrefix"],
|
|
3312
3340
|
label?: string,
|
|
@@ -3322,6 +3350,7 @@ function createSingleWorktreeSetup(
|
|
|
3322
3350
|
? { hookPath: setupHook, ...(setupHookTimeoutMs === undefined ? {} : { timeoutMs: setupHookTimeoutMs }) }
|
|
3323
3351
|
: undefined,
|
|
3324
3352
|
baseDir,
|
|
3353
|
+
baseRef,
|
|
3325
3354
|
provider,
|
|
3326
3355
|
branchPrefix,
|
|
3327
3356
|
labels: [label],
|
|
@@ -3674,7 +3703,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3674
3703
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
3675
3704
|
let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
|
|
3676
3705
|
const effectiveOutputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
|
|
3677
|
-
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
3706
|
+
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
|
|
3678
3707
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
|
|
3679
3708
|
|
|
3680
3709
|
|
|
@@ -3688,6 +3717,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3688
3717
|
deps.config.worktreeSetupHook,
|
|
3689
3718
|
deps.config.worktreeSetupHookTimeoutMs,
|
|
3690
3719
|
deps.config.worktreeBaseDir,
|
|
3720
|
+
params.baseRef,
|
|
3691
3721
|
deps.config.worktreeProvider,
|
|
3692
3722
|
deps.config.worktreeBranchPrefix,
|
|
3693
3723
|
params.lane?.key ?? params.workflowKey,
|
|
@@ -3741,6 +3771,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3741
3771
|
}
|
|
3742
3772
|
const interruptController = new AbortController();
|
|
3743
3773
|
let detachForeground: ((reason?: string) => boolean) | undefined;
|
|
3774
|
+
let childSessionControls: ForegroundChildSessionControls | undefined;
|
|
3744
3775
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3745
3776
|
if (foregroundControl) {
|
|
3746
3777
|
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin));
|
|
@@ -3761,6 +3792,19 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3761
3792
|
return true;
|
|
3762
3793
|
},
|
|
3763
3794
|
detach: () => detachForeground?.("user request") === true,
|
|
3795
|
+
steer: async (input: ForegroundSteerInput): Promise<ForegroundSteerOutcome> => {
|
|
3796
|
+
if (!childSessionControls) return { state: "failed", reason: CHILD_SESSION_NOT_RUNNING_YET };
|
|
3797
|
+
try {
|
|
3798
|
+
if (input.mode === "follow_up") {
|
|
3799
|
+
await childSessionControls.followUp(input.message);
|
|
3800
|
+
return { state: "queued" };
|
|
3801
|
+
}
|
|
3802
|
+
await childSessionControls.steer(input.message);
|
|
3803
|
+
return { state: "delivered" };
|
|
3804
|
+
} catch (error) {
|
|
3805
|
+
return { state: "failed", reason: error instanceof Error ? error.message : String(error) };
|
|
3806
|
+
}
|
|
3807
|
+
},
|
|
3764
3808
|
}));
|
|
3765
3809
|
}
|
|
3766
3810
|
|
|
@@ -3783,7 +3827,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3783
3827
|
runtimeSnapshotHost: deps.pi,
|
|
3784
3828
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3785
3829
|
llmIntentArbiter: createTaskMutationArbiter(ctx),
|
|
3786
|
-
|
|
3830
|
+
childRuntime: deps.childRuntime,
|
|
3831
|
+
onChildSession: (controls) => { childSessionControls = controls; },
|
|
3787
3832
|
context: data.contextPolicy.contextForAgent(params.agent!),
|
|
3788
3833
|
unknownAgentDiagnosticContext: data.unknownAgentDiagnosticContext,
|
|
3789
3834
|
runFanoutBudget: params.runFanoutAdmitted ? data.runFanoutBudget : { ...data.runFanoutBudget, parentPath: `${data.runFanoutBudget.parentPath ? `${data.runFanoutBudget.parentPath}/` : ""}single` },
|
|
@@ -4215,6 +4260,7 @@ function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): Wo
|
|
|
4215
4260
|
};
|
|
4216
4261
|
}
|
|
4217
4262
|
|
|
4263
|
+
const CHILD_SESSION_NOT_RUNNING_YET = "Child session is not running yet.";
|
|
4218
4264
|
const MAX_WORKFLOW_RESUME_HINT_BYTES = 1024;
|
|
4219
4265
|
const MAX_WORKFLOW_CHILD_RUN_ID_BYTES = 256;
|
|
4220
4266
|
const WORKFLOW_RESUME_HINT_PARENT_STATES = new Set(["complete", "failed", "partial"]);
|
|
@@ -4350,7 +4396,6 @@ export async function steerWorkflowChildByKey(input: {
|
|
|
4350
4396
|
while (true) {
|
|
4351
4397
|
const control = [...input.state.foregroundControls.values()].find((candidate) => candidate.parentWorkflowRunId === input.workflowRunId
|
|
4352
4398
|
&& candidate.workflowKey === input.key
|
|
4353
|
-
&& Boolean(candidate.workflowSteeringDir)
|
|
4354
4399
|
&& (candidate.activeChildren?.size ?? 0) > 0);
|
|
4355
4400
|
if (control) {
|
|
4356
4401
|
const result = await steerWorkflowForegroundTarget({
|
|
@@ -4358,10 +4403,9 @@ export async function steerWorkflowChildByKey(input: {
|
|
|
4358
4403
|
message: input.message,
|
|
4359
4404
|
mode: input.options.mode,
|
|
4360
4405
|
index: input.options.index,
|
|
4361
|
-
ackTimeoutMs: Math.max(1, deadline - Date.now()),
|
|
4362
|
-
signal: input.signal,
|
|
4363
4406
|
});
|
|
4364
|
-
|
|
4407
|
+
// The control registers before its child session exists; keep polling until the steer can route.
|
|
4408
|
+
if (!result.details.steering?.targets.some((target) => target.reason === CHILD_SESSION_NOT_RUNNING_YET) || Date.now() >= deadline) return workflowSteerReceipt(input.key, result);
|
|
4365
4409
|
}
|
|
4366
4410
|
|
|
4367
4411
|
const workflowStatus = readStatus(path.join(asyncDirRoot, input.workflowRunId));
|
|
@@ -4434,6 +4478,7 @@ export function prepareWorkflowLaunchParams(
|
|
|
4434
4478
|
const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
|
|
4435
4479
|
const intercomBridge = childParams.intercomBridge ?? workflowDefaults.intercomBridge;
|
|
4436
4480
|
const worktree = childParams.worktree ?? workflowDefaults.worktree;
|
|
4481
|
+
const baseRef = Object.hasOwn(childParams, "baseRef") ? childParams.baseRef : undefined;
|
|
4437
4482
|
const outputSchema = Object.hasOwn(childParams, "outputSchema") ? childParams.outputSchema : workflowDefaults.outputSchema;
|
|
4438
4483
|
if (outputSchema !== undefined) assertJsonSchemaObject(outputSchema, "outputSchema");
|
|
4439
4484
|
const agentContract = Object.hasOwn(childParams, "agentContract") ? childParams.agentContract : workflowDefaults.agentContract;
|
|
@@ -4453,6 +4498,7 @@ export function prepareWorkflowLaunchParams(
|
|
|
4453
4498
|
workflowKey,
|
|
4454
4499
|
...(lane ? { lane } : {}),
|
|
4455
4500
|
...(worktree !== undefined ? { worktree: worktree as boolean } : {}),
|
|
4501
|
+
...(baseRef !== undefined ? { baseRef: baseRef as string } : {}),
|
|
4456
4502
|
...(outputSchema !== undefined ? { outputSchema: outputSchema as JsonSchemaObject } : {}),
|
|
4457
4503
|
...(agentContract !== undefined ? { agentContract: agentContract as AgentContract } : {}),
|
|
4458
4504
|
...(acceptance !== undefined ? { acceptance: acceptance as AcceptanceInput } : {}),
|
|
@@ -4817,11 +4863,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4817
4863
|
const scriptFirstLine = requestParams.workflowScript.split(/\r?\n/).map((line) => line.trim()).find(Boolean) || "Workflow";
|
|
4818
4864
|
const boundedScriptPreview = scriptFirstLine.length > 100 ? `${scriptFirstLine.slice(0, 97)}...` : scriptFirstLine;
|
|
4819
4865
|
const derivedObjective = previewAgent ? `Workflow: ${previewAgent}` : boundedScriptPreview;
|
|
4820
|
-
const workflowDepth = checkSubagentDepth(deps.config.maxSubagentDepth).depth;
|
|
4866
|
+
const workflowDepth = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime).depth;
|
|
4821
4867
|
const asyncWorkflow = requestParams.async !== false;
|
|
4822
4868
|
const topLevelAsyncWorkflow = asyncWorkflow
|
|
4823
4869
|
&& workflowDepth === 0
|
|
4824
|
-
&& !
|
|
4870
|
+
&& !inheritedNestedRoute(deps)
|
|
4825
4871
|
&& !requestParams.workflowParentRunId;
|
|
4826
4872
|
const workflowRunId = asyncWorkflow ? randomUUID() : undefined;
|
|
4827
4873
|
let workflowCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
@@ -4846,7 +4892,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4846
4892
|
let workflowFanoutBudget: RunFanoutBudgetDescriptor;
|
|
4847
4893
|
try {
|
|
4848
4894
|
workflowFanoutBudget = requestParams.runFanoutBudget
|
|
4849
|
-
??
|
|
4895
|
+
?? inheritedRunFanoutBudget(deps)
|
|
4850
4896
|
?? createRunFanoutBudget(_id, requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
4851
4897
|
} catch (error) {
|
|
4852
4898
|
workflowCapacity?.rollback();
|
|
@@ -5205,6 +5251,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5205
5251
|
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5206
5252
|
timeoutMs: timeout,
|
|
5207
5253
|
signal: controller.signal,
|
|
5254
|
+
continueAfterAbortWhenChildrenSettled: (abortError) => {
|
|
5255
|
+
if (abortError.message !== "Workflow stopped because the extension session was replaced or reloaded.") return false;
|
|
5256
|
+
const activeAsyncChild = [...(deps.state.asyncJobs?.values() ?? [])].some((job) => job.parentWorkflowRunId === workflowRunId && (job.status === "queued" || job.status === "running"));
|
|
5257
|
+
const activeForegroundChild = [...deps.state.foregroundControls.values()].some((control) => control.parentWorkflowRunId === workflowRunId && (control.activeChildren?.size ?? 0) > 0);
|
|
5258
|
+
return !activeAsyncChild && !activeForegroundChild;
|
|
5259
|
+
},
|
|
5208
5260
|
registerStopChild: (stop) => {
|
|
5209
5261
|
if (stop) deps.state.workflowChildStops?.set(workflowRunId, stop);
|
|
5210
5262
|
else deps.state.workflowChildStops?.delete(workflowRunId);
|
|
@@ -5992,7 +6044,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5992
6044
|
if (directoryStatus?.mode === "workflow") {
|
|
5993
6045
|
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: directoryStatus.runId || runId, asyncDirRoot: DIRS.async });
|
|
5994
6046
|
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5995
|
-
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index
|
|
6047
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index });
|
|
5996
6048
|
}
|
|
5997
6049
|
if (location.asyncDir) {
|
|
5998
6050
|
const unsupported = externalRunnerControlError(location.asyncDir, "steer");
|
|
@@ -6032,14 +6084,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6032
6084
|
if (resolved?.kind === "foreground") {
|
|
6033
6085
|
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, childRunId: resolved.id, asyncDirRoot: DIRS.async });
|
|
6034
6086
|
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
6035
|
-
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index
|
|
6087
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index });
|
|
6036
6088
|
}
|
|
6037
6089
|
if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6038
6090
|
const resolvedStatus = resolved.location.asyncDir ? readStatus(resolved.location.asyncDir) : null;
|
|
6039
6091
|
if (resolvedStatus?.mode === "workflow") {
|
|
6040
6092
|
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: resolvedStatus.runId || resolved.id, asyncDirRoot: DIRS.async });
|
|
6041
6093
|
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
6042
|
-
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index
|
|
6094
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index });
|
|
6043
6095
|
}
|
|
6044
6096
|
if (resolved.location.asyncDir) {
|
|
6045
6097
|
const unsupported = externalRunnerControlError(resolved.location.asyncDir, "steer");
|
|
@@ -6118,9 +6170,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6118
6170
|
if (action === "stop") {
|
|
6119
6171
|
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
6120
6172
|
const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
|
|
6121
|
-
if (workflowController) {
|
|
6173
|
+
if (workflowController && targetRunId) {
|
|
6174
|
+
const stopChild = deps.state.workflowChildStops?.get(targetRunId);
|
|
6122
6175
|
if (paramsWithResolvedCwd.childId !== undefined) {
|
|
6123
|
-
const workflowRunId = targetRunId
|
|
6176
|
+
const workflowRunId = targetRunId;
|
|
6124
6177
|
const asyncJob = deps.state.asyncJobs.get(workflowRunId);
|
|
6125
6178
|
if (!asyncJob?.asyncDir) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6126
6179
|
const status = readStatus(asyncJob.asyncDir);
|
|
@@ -6128,7 +6181,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6128
6181
|
const resolution = resolveAsyncStatusChild(status, paramsWithResolvedCwd.childId);
|
|
6129
6182
|
if (!resolution.ok) return { content: [{ type: "text", text: resolution.message }], isError: true, details: { mode: "management", results: [] } };
|
|
6130
6183
|
if (!isStoppableAsyncStatusStep(resolution.child.step)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in async run '${targetRunId}' is ${resolution.child.step.status}; stop only supports pending or running children.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6131
|
-
const stopChild = deps.state.workflowChildStops?.get(targetRunId!);
|
|
6132
6184
|
if (!stopChild) return { content: [{ type: "text", text: `Workflow ${targetRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6133
6185
|
if (!stopChild(resolution.child.id, `Workflow child '${resolution.child.id}' stopped.`)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in workflow ${workflowRunId} is not available to stop.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6134
6186
|
try {
|
|
@@ -6153,6 +6205,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6153
6205
|
}
|
|
6154
6206
|
return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
|
|
6155
6207
|
}
|
|
6208
|
+
const asyncJob = deps.state.asyncJobs.get(targetRunId);
|
|
6209
|
+
const status = asyncJob?.asyncDir ? readStatus(asyncJob.asyncDir) : undefined;
|
|
6210
|
+
if (status) stopStoppableAsyncStatusChildren(status, stopChild, "Workflow stopped.");
|
|
6156
6211
|
workflowController.abort(new Error("Workflow stopped."));
|
|
6157
6212
|
return { content: [{ type: "text", text: `Stop requested for async workflow ${targetRunId}.` }], details: { mode: "management", results: [] } };
|
|
6158
6213
|
}
|
|
@@ -6248,7 +6303,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6248
6303
|
});
|
|
6249
6304
|
}
|
|
6250
6305
|
|
|
6251
|
-
const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
6306
|
+
const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
|
|
6252
6307
|
if (blocked) {
|
|
6253
6308
|
return {
|
|
6254
6309
|
content: [
|
|
@@ -6323,8 +6378,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6323
6378
|
});
|
|
6324
6379
|
const agents = applyScopedIntercomBridgeToAgents(discoveredAgents, intercomBridge, contextPolicy);
|
|
6325
6380
|
const runId = randomUUID();
|
|
6326
|
-
const
|
|
6327
|
-
const nestedParentAddress =
|
|
6381
|
+
const inheritedNestedRouteValue = inheritedNestedRoute(deps);
|
|
6382
|
+
const nestedParentAddress = inheritedNestedRouteValue ? inheritedNestedParentAddress(deps) : undefined;
|
|
6328
6383
|
const shareEnabled = effectiveParams.share === true;
|
|
6329
6384
|
const hasChain = (effectiveParams.chain?.length ?? 0) > 0;
|
|
6330
6385
|
const hasTasks = (effectiveParams.tasks?.length ?? 0) > 0;
|
|
@@ -6445,7 +6500,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6445
6500
|
const asyncRunId = requestedWorkflowChildAsyncId && path.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId
|
|
6446
6501
|
? requestedWorkflowChildAsyncId
|
|
6447
6502
|
: randomUUID();
|
|
6448
|
-
const topLevelAsyncCapacityEligible = depth === 0 && !
|
|
6503
|
+
const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRouteValue && !effectiveParams.workflowParentRunId;
|
|
6449
6504
|
const topLevelAsync = effectiveAsync && topLevelAsyncCapacityEligible;
|
|
6450
6505
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
6451
6506
|
if (topLevelAsync) {
|
|
@@ -6470,9 +6525,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6470
6525
|
|
|
6471
6526
|
let runFanoutBudget: RunFanoutBudgetDescriptor;
|
|
6472
6527
|
try {
|
|
6473
|
-
const
|
|
6528
|
+
const inheritedRunFanoutBudgetValue = effectiveParams.runFanoutBudget ? undefined : inheritedRunFanoutBudget(deps);
|
|
6474
6529
|
runFanoutBudget = effectiveParams.runFanoutBudget
|
|
6475
|
-
?? (
|
|
6530
|
+
?? (inheritedRunFanoutBudgetValue ? { ...inheritedRunFanoutBudgetValue, parentPath: `${inheritedRunFanoutBudgetValue.parentPath ? `${inheritedRunFanoutBudgetValue.parentPath}/` : ""}${runId}` } : undefined)
|
|
6476
6531
|
?? createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
6477
6532
|
if (!effectiveParams.runFanoutAdmitted) claimRunFanoutBatch(runFanoutBudget, staticRunFanoutPaths(effectiveParams));
|
|
6478
6533
|
} catch (error) {
|
|
@@ -6480,7 +6535,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6480
6535
|
if (error instanceof RunFanoutLimitError) return runFanoutErrorResult(error, foregroundMode);
|
|
6481
6536
|
return buildRequestedModeError(effectiveParams, error instanceof Error ? error.message : String(error));
|
|
6482
6537
|
}
|
|
6483
|
-
const nestedRoute =
|
|
6538
|
+
const nestedRoute = inheritedNestedRouteValue ?? createNestedRoute(runId);
|
|
6484
6539
|
|
|
6485
6540
|
const artifactConfig: ArtifactConfig = omitUndefinedProperties({
|
|
6486
6541
|
...DEFAULT_ARTIFACT_CONFIG,
|
|
@@ -6652,17 +6707,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6652
6707
|
const foregroundDescription = selectedAgentNames.length === 1
|
|
6653
6708
|
? `${selectedAgentNames[0]} child`
|
|
6654
6709
|
: `${selectedAgentNames.length} live children`;
|
|
6655
|
-
const parentWorkflowStatus = effectiveParams.workflowParentRunId
|
|
6656
|
-
? readStatus(path.join(DIRS.async, effectiveParams.workflowParentRunId))
|
|
6657
|
-
: null;
|
|
6658
|
-
const workflowSteeringDir = effectiveParams.workflowParentRunId
|
|
6659
|
-
&& requestSessionId
|
|
6660
|
-
&& deps.state.workflowControllers?.has(effectiveParams.workflowParentRunId)
|
|
6661
|
-
&& parentWorkflowStatus?.mode === "workflow"
|
|
6662
|
-
&& (parentWorkflowStatus.state === "running" || parentWorkflowStatus.state === "queued")
|
|
6663
|
-
&& parentWorkflowStatus.sessionId === requestSessionId
|
|
6664
|
-
? workflowForegroundSteeringDir(DIRS.async, effectiveParams.workflowParentRunId, runId)
|
|
6665
|
-
: undefined;
|
|
6666
6710
|
const foregroundControl: ForegroundRunControl | undefined = effectiveAsync
|
|
6667
6711
|
? undefined
|
|
6668
6712
|
: compactOptional<ForegroundRunControl>({
|
|
@@ -6671,7 +6715,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6671
6715
|
mode: foregroundMode,
|
|
6672
6716
|
...(effectiveParams.workflowParentRunId ? { parentWorkflowRunId: effectiveParams.workflowParentRunId } : {}),
|
|
6673
6717
|
...(effectiveParams.workflowKey ? { workflowKey: effectiveParams.workflowKey } : {}),
|
|
6674
|
-
workflowSteeringDir,
|
|
6675
6718
|
startedAt: Date.now(),
|
|
6676
6719
|
updatedAt: Date.now(),
|
|
6677
6720
|
cwd: effectiveCwd,
|
|
@@ -6724,7 +6767,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6724
6767
|
}
|
|
6725
6768
|
|
|
6726
6769
|
const writeNestedForegroundEvent = (type: "subagent.nested.started" | "subagent.nested.completed", result?: AgentToolResult<Details>): void => {
|
|
6727
|
-
if (!
|
|
6770
|
+
if (!inheritedNestedRouteValue || !nestedParentAddress) return;
|
|
6728
6771
|
const now = Date.now();
|
|
6729
6772
|
const details = result?.details;
|
|
6730
6773
|
const state = type === "subagent.nested.started"
|
|
@@ -6758,7 +6801,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6758
6801
|
? resolveSubagentIntercomTarget(runId, agentsForSummary[0], 0)
|
|
6759
6802
|
: undefined;
|
|
6760
6803
|
try {
|
|
6761
|
-
writeNestedEvent(
|
|
6804
|
+
writeNestedEvent(inheritedNestedRouteValue, compactOptional<Parameters<typeof writeNestedEvent>[1]>({
|
|
6762
6805
|
type,
|
|
6763
6806
|
ts: now,
|
|
6764
6807
|
parentRunId: nestedParentAddress.parentRunId,
|
|
@@ -6769,7 +6812,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6769
6812
|
parentStepIndex: nestedParentAddress.parentStepIndex,
|
|
6770
6813
|
depth: nestedParentAddress.depth,
|
|
6771
6814
|
path: nestedParentAddress.path,
|
|
6772
|
-
ownerIntercomTarget:
|
|
6815
|
+
ownerIntercomTarget: deps.childRuntime?.intercomSessionName,
|
|
6773
6816
|
leafIntercomTarget,
|
|
6774
6817
|
intercomTarget: leafIntercomTarget,
|
|
6775
6818
|
ownerState: state === "running" ? "live" : "gone",
|
|
@@ -6880,7 +6923,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6880
6923
|
const normalizedAction = typeof params.action === "string" ? params.action.trim() : params.action;
|
|
6881
6924
|
const requestParams = normalizedAction ? { ...params, action: normalizedAction } : params;
|
|
6882
6925
|
if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);
|
|
6883
|
-
const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
6926
|
+
const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
|
|
6884
6927
|
const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
|
|
6885
6928
|
const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
|
|
6886
6929
|
if (!runsForeground) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);
|