pi-subagents 0.45.1 → 0.46.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 -0
- package/docs/agents.md +342 -0
- package/docs/configuration.md +320 -0
- package/docs/extension-api.md +308 -0
- package/docs/missions.md +117 -0
- package/docs/models.md +190 -0
- package/docs/observability.md +174 -0
- package/docs/tool-reference.md +343 -0
- package/docs/watchdog.md +176 -0
- package/docs/workflows.md +163 -0
- package/package.json +4 -2
- package/skills/pi-subagents/references/execution-controls.md +2 -2
- package/src/agents/agents.ts +17 -8
- package/src/agents/frontmatter.ts +7 -3
- package/src/agents/skills.ts +2 -9
- package/src/api/project-panes.ts +30 -0
- package/src/extension/config.ts +15 -1
- package/src/extension/index.ts +36 -16
- package/src/extension/schemas.ts +3 -2
- package/src/extension/subagent-guide.ts +39 -0
- package/src/extension/tool-description.ts +4 -4
- package/src/inspectors/herdr/project-panes.ts +457 -62
- package/src/missions/actions.ts +25 -2
- package/src/missions/lifecycle.ts +21 -2
- package/src/missions/store.ts +77 -1
- package/src/missions/types.ts +33 -0
- package/src/runs/background/async-execution.ts +7 -1
- package/src/runs/background/completion-replay.ts +267 -0
- package/src/runs/background/result-watcher.ts +12 -4
- package/src/runs/background/resume-guidance.ts +33 -0
- package/src/runs/background/subagent-wait.ts +5 -2
- package/src/runs/background/wait-completions.ts +39 -5
- package/src/runs/background/wait-subscriptions.ts +21 -5
- package/src/runs/foreground/execution.ts +4 -0
- package/src/runs/foreground/foreground-history.ts +137 -0
- package/src/runs/foreground/subagent-executor.ts +326 -38
- package/src/runs/shared/subagent-prompt-runtime.ts +71 -5
- package/src/shared/fork-context.ts +13 -0
- package/src/shared/prompt-resources.ts +51 -0
- package/src/shared/types.ts +30 -1
- package/src/shared/utf8.ts +11 -0
- package/src/slash/prompt-workflows.ts +2 -15
- package/src/slash/slash-commands.ts +19 -1
- package/src/tui/fleet-status.ts +8 -2
- package/src/tui/fleet.ts +135 -25
- package/src/tui/render.ts +120 -7
- package/src/workflows/scripted-workflow.ts +187 -11
|
@@ -17,10 +17,12 @@ import {
|
|
|
17
17
|
settleForegroundSchedulingOwner,
|
|
18
18
|
updateForegroundChild,
|
|
19
19
|
} from "./foreground-control.ts";
|
|
20
|
+
import { persistForegroundRunHistory, MAX_REMEMBERED_FOREGROUND_RUNS } from "./foreground-history.ts";
|
|
20
21
|
import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
|
|
21
22
|
import { handleManagementAction } from "../../agents/agent-management.ts";
|
|
22
23
|
import { handleRefinementAction } from "../../agents/agent-refinements.ts";
|
|
23
24
|
import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
25
|
+
import { readSubagentGuide } from "../../extension/subagent-guide.ts";
|
|
24
26
|
import { normalizePublicSubagentExecution } from "../../extension/public-execution.ts";
|
|
25
27
|
import { runSync } from "./execution.ts";
|
|
26
28
|
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
|
|
@@ -49,8 +51,8 @@ import {
|
|
|
49
51
|
type StepOverrides,
|
|
50
52
|
} from "../../shared/settings.ts";
|
|
51
53
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
52
|
-
import { buildAsyncRunnerSteps, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable } from "../background/async-execution.ts";
|
|
53
|
-
import type
|
|
54
|
+
import { buildAsyncRunnerSteps, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable, workflowAwaitedAsyncResultPath } from "../background/async-execution.ts";
|
|
55
|
+
import { isScheduledRunAction, type ScheduledRunAction } from "../background/scheduled-runs.ts";
|
|
54
56
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
55
57
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
56
58
|
import { normalizeGateAcceptance, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
@@ -84,20 +86,22 @@ import { updateSteeringTarget, waitForSteeringAction } from "../background/steer
|
|
|
84
86
|
import { steerAsyncRun } from "./async-steering-action.ts";
|
|
85
87
|
import { stopAsyncRun } from "./async-stop-action.ts";
|
|
86
88
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
87
|
-
import { resolveAsyncRootResultPath } from "../background/chain-root-attachment.ts";
|
|
89
|
+
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
|
|
88
90
|
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
89
91
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
90
92
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
91
93
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
92
94
|
import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
|
|
93
95
|
import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
|
|
94
|
-
import { attachMissionToLaunchResult, prepareMissionLaunch, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
96
|
+
import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
95
97
|
import { updateMission } from "../../missions/store.ts";
|
|
98
|
+
import type { MissionWorkflowChildUpdate } from "../../missions/types.ts";
|
|
96
99
|
import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
|
|
97
100
|
import { resolveAuthorityDecision } from "../../policy/authority.ts";
|
|
98
101
|
import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
|
|
99
102
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
100
103
|
import { previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult } from "../../workflows/scripted-workflow.ts";
|
|
104
|
+
import { renderWorkflowPrompt } from "../../shared/prompt-resources.ts";
|
|
101
105
|
import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
|
|
102
106
|
import {
|
|
103
107
|
cleanupWorktrees,
|
|
@@ -150,7 +154,48 @@ import {
|
|
|
150
154
|
wrapForkTask,
|
|
151
155
|
} from "../../shared/types.ts";
|
|
152
156
|
|
|
153
|
-
const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "refine", "refine.rollback", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
|
|
157
|
+
const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "refine", "refine.rollback", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
|
|
158
|
+
const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "reject-checkpoint", "schedule.delete"]);
|
|
159
|
+
|
|
160
|
+
function editDistance(left: string, right: string): number {
|
|
161
|
+
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
162
|
+
for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
|
|
163
|
+
let diagonal = previous[0]!;
|
|
164
|
+
previous[0] = leftIndex;
|
|
165
|
+
for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
|
|
166
|
+
const above = previous[rightIndex]!;
|
|
167
|
+
previous[rightIndex] = left[leftIndex - 1] === right[rightIndex - 1]
|
|
168
|
+
? diagonal
|
|
169
|
+
: Math.min(diagonal, above, previous[rightIndex - 1]!) + 1;
|
|
170
|
+
diagonal = above;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return previous[right.length]!;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function hasSingleAdjacentTransposition(left: string, right: string): boolean {
|
|
177
|
+
if (left.length !== right.length) return false;
|
|
178
|
+
const mismatch = [...left].findIndex((character, index) => character !== right[index]);
|
|
179
|
+
return mismatch >= 0
|
|
180
|
+
&& left[mismatch] === right[mismatch + 1]
|
|
181
|
+
&& left[mismatch + 1] === right[mismatch]
|
|
182
|
+
&& left.slice(mismatch + 2) === right.slice(mismatch + 2);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function unknownSubagentActionMessage(action: string): string {
|
|
186
|
+
const requested = action.toLowerCase();
|
|
187
|
+
const suggestion = SUBAGENT_ACTIONS.find((candidate) => {
|
|
188
|
+
const distance = editDistance(requested, candidate);
|
|
189
|
+
const closeMatch = distance <= Math.max(1, Math.floor(candidate.length / 4)) || hasSingleAdjacentTransposition(requested, candidate);
|
|
190
|
+
if (DESTRUCTIVE_MANAGEMENT_ACTIONS.has(candidate)) return distance === 1 && requested.length >= candidate.length - 1;
|
|
191
|
+
return closeMatch;
|
|
192
|
+
});
|
|
193
|
+
const nextStep = 'Use subagent({ action: "status" }) to inspect runs or subagent({ action: "list" }) to inspect agents.';
|
|
194
|
+
const validActions = `Valid: ${SUBAGENT_ACTIONS.join(", ")}.`;
|
|
195
|
+
return suggestion
|
|
196
|
+
? `Unknown action: ${action}. Did you mean ${suggestion}? ${nextStep} ${validActions}`
|
|
197
|
+
: `Unknown action: ${action}. ${nextStep} ${validActions}`;
|
|
198
|
+
}
|
|
154
199
|
|
|
155
200
|
type UndefinedOmitted<T extends object> = {
|
|
156
201
|
[K in keyof T as undefined extends T[K] ? never : K]: T[K];
|
|
@@ -209,6 +254,7 @@ export interface SubagentParamsLike {
|
|
|
209
254
|
index?: number;
|
|
210
255
|
view?: "fleet" | "transcript";
|
|
211
256
|
lines?: number;
|
|
257
|
+
topic?: string;
|
|
212
258
|
chainName?: string;
|
|
213
259
|
config?: unknown;
|
|
214
260
|
name?: string;
|
|
@@ -226,6 +272,7 @@ export interface SubagentParamsLike {
|
|
|
226
272
|
/** Internal workflow ownership metadata; not part of the public schema. */
|
|
227
273
|
workflowParentRunId?: string;
|
|
228
274
|
workflowKey?: string;
|
|
275
|
+
workflowChildAsyncId?: string;
|
|
229
276
|
suppressRoutineResultIntercom?: boolean;
|
|
230
277
|
/** Internal durable-run compatibility fields. Public callers must use workflowScript. */
|
|
231
278
|
chain?: ChainStep[];
|
|
@@ -473,7 +520,7 @@ function foregroundStatusResult(control: SubagentState["foregroundControls"] ext
|
|
|
473
520
|
|
|
474
521
|
function trimRememberedForegroundRuns(state: SubagentState): void {
|
|
475
522
|
if (!state.foregroundRuns) return;
|
|
476
|
-
while (state.foregroundRuns.size >
|
|
523
|
+
while (state.foregroundRuns.size > MAX_REMEMBERED_FOREGROUND_RUNS) {
|
|
477
524
|
const oldestTerminal = [...state.foregroundRuns.values()]
|
|
478
525
|
.filter((run) => !run.children.some((child) => child.status === "detached"))
|
|
479
526
|
.sort((left, right) => left.updatedAt - right.updatedAt)[0];
|
|
@@ -482,6 +529,14 @@ function trimRememberedForegroundRuns(state: SubagentState): void {
|
|
|
482
529
|
}
|
|
483
530
|
}
|
|
484
531
|
|
|
532
|
+
function persistRememberedForegroundRuns(state: SubagentState): void {
|
|
533
|
+
try {
|
|
534
|
+
persistForegroundRunHistory(state);
|
|
535
|
+
} catch (error) {
|
|
536
|
+
console.error("Failed to persist foreground run history:", error);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
485
540
|
function foregroundChildActivityFromProgress(progress: SingleResult["progress"] | undefined) {
|
|
486
541
|
return {
|
|
487
542
|
...(progress?.activityState ? { activityState: progress.activityState } : {}),
|
|
@@ -548,6 +603,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
548
603
|
}),
|
|
549
604
|
});
|
|
550
605
|
trimRememberedForegroundRuns(state);
|
|
606
|
+
persistRememberedForegroundRuns(state);
|
|
551
607
|
}
|
|
552
608
|
|
|
553
609
|
function applyControlEventToRememberedForegroundRun(state: SubagentState, event: ControlEvent): void {
|
|
@@ -623,6 +679,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
623
679
|
...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
|
|
624
680
|
});
|
|
625
681
|
trimRememberedForegroundRuns(state);
|
|
682
|
+
persistRememberedForegroundRuns(state);
|
|
626
683
|
const output = getSingleResultOutput(input.result).trim();
|
|
627
684
|
const success = terminalStatus === "completed";
|
|
628
685
|
const summary = !success && input.result.error
|
|
@@ -1277,6 +1334,7 @@ async function resumeAsyncRun(input: {
|
|
|
1277
1334
|
deps: ExecutorDeps;
|
|
1278
1335
|
parentModel?: ParentModel;
|
|
1279
1336
|
absoluteDeadlineAt?: number;
|
|
1337
|
+
signal?: AbortSignal;
|
|
1280
1338
|
}): Promise<AgentToolResult<Details>> {
|
|
1281
1339
|
const followUp = (input.params.message ?? input.params.task ?? "").trim();
|
|
1282
1340
|
const attachChain = (input.params.chain?.length ?? 0) > 0 ? input.params.chain as ChainStep[] : undefined;
|
|
@@ -1563,6 +1621,58 @@ async function resumeAsyncRun(input: {
|
|
|
1563
1621
|
}
|
|
1564
1622
|
|
|
1565
1623
|
const revivedId = result.details.asyncId ?? runId;
|
|
1624
|
+
if (input.params.workflowParentRunId !== undefined && result.details.asyncDir) {
|
|
1625
|
+
const asyncDir = result.details.asyncDir;
|
|
1626
|
+
const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
|
|
1627
|
+
const stopOnAbort = () => { stopAsyncRun(input.deps.state, revivedId, input.deps.kill, { asyncDir, resolvedId: revivedId }); };
|
|
1628
|
+
if (input.signal?.aborted) stopOnAbort();
|
|
1629
|
+
else input.signal?.addEventListener("abort", stopOnAbort, { once: true });
|
|
1630
|
+
let completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>;
|
|
1631
|
+
try {
|
|
1632
|
+
completed = await waitForImportedAsyncRoot({ runId: revivedId, asyncDir, resultPath, index: 0 });
|
|
1633
|
+
} finally {
|
|
1634
|
+
input.signal?.removeEventListener("abort", stopOnAbort);
|
|
1635
|
+
}
|
|
1636
|
+
fs.rmSync(resultPath, { force: true });
|
|
1637
|
+
const totalCost = completed.totalCost;
|
|
1638
|
+
const childResult: SingleResult = {
|
|
1639
|
+
index: 0,
|
|
1640
|
+
agent: completed.agent,
|
|
1641
|
+
task: effectiveFollowUp,
|
|
1642
|
+
exitCode: completed.exitCode,
|
|
1643
|
+
usage: {
|
|
1644
|
+
input: totalCost?.inputTokens ?? 0,
|
|
1645
|
+
output: totalCost?.outputTokens ?? 0,
|
|
1646
|
+
cacheRead: 0,
|
|
1647
|
+
cacheWrite: 0,
|
|
1648
|
+
cost: totalCost?.costUsd ?? 0,
|
|
1649
|
+
turns: 0,
|
|
1650
|
+
},
|
|
1651
|
+
finalOutput: completed.output,
|
|
1652
|
+
outputState: completed.output.trim() ? "present" : "absent",
|
|
1653
|
+
...(completed.error ? { error: completed.error } : {}),
|
|
1654
|
+
...(completed.timedOut ? { timedOut: true } : {}),
|
|
1655
|
+
...(completed.stopped ? { stopped: true } : {}),
|
|
1656
|
+
...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
|
|
1657
|
+
...(completed.model ? { model: completed.model } : {}),
|
|
1658
|
+
...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
|
|
1659
|
+
...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
|
|
1660
|
+
...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
|
|
1661
|
+
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
1662
|
+
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
1663
|
+
...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
|
|
1664
|
+
};
|
|
1665
|
+
return {
|
|
1666
|
+
content: [{ type: "text", text: completed.output || completed.error || `Revived ${target.source} subagent ${revivedId} completed without output.` }],
|
|
1667
|
+
...(completed.success ? {} : { isError: true }),
|
|
1668
|
+
details: {
|
|
1669
|
+
...result.details,
|
|
1670
|
+
runId: revivedId,
|
|
1671
|
+
results: [childResult],
|
|
1672
|
+
...(target.launchContractDigest ? { sourceLaunchContractDigest: target.launchContractDigest } : {}),
|
|
1673
|
+
},
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1566
1676
|
const revivedTarget = intercomBridge.active ? resolveSubagentIntercomTarget(revivedId, target.agent, 0) : undefined;
|
|
1567
1677
|
const sourceLabel = target.source;
|
|
1568
1678
|
const lines = [
|
|
@@ -2414,7 +2524,8 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2414
2524
|
details: { mode: "single" as const, results: [] },
|
|
2415
2525
|
};
|
|
2416
2526
|
}
|
|
2417
|
-
const
|
|
2527
|
+
const requestedWorkflowChildAsyncId = typeof params.workflowChildAsyncId === "string" ? params.workflowChildAsyncId.trim() : "";
|
|
2528
|
+
const id = requestedWorkflowChildAsyncId && path.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId ? requestedWorkflowChildAsyncId : randomUUID();
|
|
2418
2529
|
const parentModel = data.parentModel;
|
|
2419
2530
|
const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
2420
2531
|
pi: deps.pi,
|
|
@@ -3942,6 +4053,63 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
|
|
|
3942
4053
|
};
|
|
3943
4054
|
}
|
|
3944
4055
|
|
|
4056
|
+
const workflowLaunchObservers = new WeakMap<object, (launch: { agent: string; sessionFile?: string }) => void>();
|
|
4057
|
+
|
|
4058
|
+
function recordMissionWorkflowChild(
|
|
4059
|
+
binding: MissionLaunchBinding | undefined,
|
|
4060
|
+
workflowRunId: string,
|
|
4061
|
+
key: string,
|
|
4062
|
+
update: Omit<MissionWorkflowChildUpdate, "workflowRunId" | "key">,
|
|
4063
|
+
): void {
|
|
4064
|
+
if (!binding) return;
|
|
4065
|
+
try {
|
|
4066
|
+
updateMission(binding.location, binding.missionId, { upsertWorkflowChildren: [{ workflowRunId, key, ...update }] });
|
|
4067
|
+
} catch (error) {
|
|
4068
|
+
console.warn(`[pi-subagents] Failed to record mission workflow child '${key}': ${error instanceof Error ? error.message : String(error)}`);
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
|
|
4072
|
+
export function missionWorkflowChildStatus(result: AgentToolResult<Details>): string {
|
|
4073
|
+
const childResults = result.details.results;
|
|
4074
|
+
if (childResults.some((child) => child.detached || child.interrupted)) return "paused";
|
|
4075
|
+
if (result.isError === true || childResults.some((child) => child.exitCode !== 0)) return "failed";
|
|
4076
|
+
if (childResults.length === 0 && (result.details.asyncId || result.details.asyncDir)) return "running";
|
|
4077
|
+
return "completed";
|
|
4078
|
+
}
|
|
4079
|
+
|
|
4080
|
+
export async function runMissionWorkflowChild(
|
|
4081
|
+
binding: MissionLaunchBinding | undefined,
|
|
4082
|
+
workflowRunId: string,
|
|
4083
|
+
key: string,
|
|
4084
|
+
phase: string | undefined,
|
|
4085
|
+
run: () => Promise<AgentToolResult<Details>>,
|
|
4086
|
+
): Promise<AgentToolResult<Details>> {
|
|
4087
|
+
try {
|
|
4088
|
+
return await run();
|
|
4089
|
+
} catch (error) {
|
|
4090
|
+
recordMissionWorkflowChild(binding, workflowRunId, key, {
|
|
4091
|
+
status: "failed",
|
|
4092
|
+
completedAt: new Date().toISOString(),
|
|
4093
|
+
heartbeat: { status: "failed", ...(phase ? { phase } : {}), message: error instanceof Error ? error.message : String(error) },
|
|
4094
|
+
});
|
|
4095
|
+
throw error;
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4099
|
+
export function bindMissionWorkflowChildAsyncLaunch(
|
|
4100
|
+
params: SubagentParamsLike,
|
|
4101
|
+
binding: MissionLaunchBinding | undefined,
|
|
4102
|
+
asyncByDefault: boolean,
|
|
4103
|
+
asyncId: string = randomUUID(),
|
|
4104
|
+
): SubagentParamsLike {
|
|
4105
|
+
const requestedAsync = params.async ?? asyncByDefault;
|
|
4106
|
+
if (!binding || !requestedAsync || params.clarify === true) return params;
|
|
4107
|
+
const id = asyncId.trim();
|
|
4108
|
+
if (!id || path.basename(id) !== id) throw new Error("workflow child async id must be a single path segment");
|
|
4109
|
+
writeMissionAsyncBinding(path.join(DIRS.async, id), binding);
|
|
4110
|
+
return { ...params, workflowChildAsyncId: id };
|
|
4111
|
+
}
|
|
4112
|
+
|
|
3945
4113
|
function workflowChildResult(key: string, result: AgentToolResult<Details>): WorkflowScriptChildResult {
|
|
3946
4114
|
const receiptOutput = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
|
|
3947
4115
|
const output = result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
|
|
@@ -3958,9 +4126,11 @@ function workflowChildResult(key: string, result: AgentToolResult<Details>): Wor
|
|
|
3958
4126
|
if (child.sessionFile) artifactPaths.add(child.sessionFile);
|
|
3959
4127
|
}
|
|
3960
4128
|
const structured = result.details.results.map((child) => child.structuredOutput).filter((value) => value !== undefined);
|
|
4129
|
+
const resolvedAgents = [...new Set(result.details.results.map((child) => child.agent).filter((agent): agent is string => Boolean(agent)))];
|
|
3961
4130
|
return {
|
|
3962
4131
|
key,
|
|
3963
4132
|
ok,
|
|
4133
|
+
...(resolvedAgents.length === 1 ? { agent: resolvedAgents[0] } : {}),
|
|
3964
4134
|
...(result.details.runId || result.details.asyncId ? { runId: result.details.runId ?? result.details.asyncId } : {}),
|
|
3965
4135
|
output,
|
|
3966
4136
|
...(!ok ? { error: receiptOutput || output || "Child run failed." } : {}),
|
|
@@ -3998,6 +4168,7 @@ export function prepareWorkflowLaunchParams(
|
|
|
3998
4168
|
}
|
|
3999
4169
|
const launchParams = {
|
|
4000
4170
|
...workflowDefaults,
|
|
4171
|
+
async: false,
|
|
4001
4172
|
...childParams,
|
|
4002
4173
|
...(options.missionDetached ? { mission: false } : {}),
|
|
4003
4174
|
workflowParentRunId: parentWorkflowRunId,
|
|
@@ -4159,6 +4330,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4159
4330
|
ctx: ExtensionContext,
|
|
4160
4331
|
preserveActiveSession = false,
|
|
4161
4332
|
): Promise<AgentToolResult<Details>> => {
|
|
4333
|
+
const workflowLaunchObserver = workflowLaunchObservers.get(params);
|
|
4162
4334
|
const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
|
|
4163
4335
|
const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
|
|
4164
4336
|
if (!preserveActiveSession) deps.state.baseCwd = ctx.cwd;
|
|
@@ -4175,6 +4347,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4175
4347
|
const workflowUsageBudget = validateUsageBudgetConfig(requestParams.usageBudget ?? deps.config.usageBudget, requestParams.usageBudget ? "usageBudget" : "config.usageBudget");
|
|
4176
4348
|
if (workflowUsageBudget.error) return buildRequestedModeError(requestParams, workflowUsageBudget.error);
|
|
4177
4349
|
const workflowCwd = resolveRequestedCwd(parentCwd, requestParams.cwd);
|
|
4350
|
+
const workflowPrompts = { render: (ref: string, vars?: unknown) => renderWorkflowPrompt(ref, vars, workflowCwd) };
|
|
4178
4351
|
const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd, workflowCwd, background: requestParams.async !== false });
|
|
4179
4352
|
if (chatProgressResult.error) return { content: [{ type: "text", text: chatProgressResult.error }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4180
4353
|
const chatProgress = chatProgressResult.projection!;
|
|
@@ -4318,12 +4491,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4318
4491
|
const mapped = entry.state === "started" || entry.state === "reused" ? "running" : entry.state === "completed" ? "completed" : "failed";
|
|
4319
4492
|
if (existing) {
|
|
4320
4493
|
existing.status = mapped;
|
|
4494
|
+
if (entry.agent) existing.agent = entry.agent;
|
|
4321
4495
|
if (entry.error === undefined) delete existing.error;
|
|
4322
4496
|
else existing.error = entry.error;
|
|
4323
4497
|
if (entry.durationMs === undefined) delete existing.durationMs;
|
|
4324
4498
|
else existing.durationMs = entry.durationMs;
|
|
4325
4499
|
} else {
|
|
4326
|
-
status.steps?.push({ agent: entry.key, label: entry.key, workflowKey: entry.key, parentWorkflowRunId: workflowRunId, status: mapped, startedAt: Date.now() });
|
|
4500
|
+
status.steps?.push({ agent: entry.agent ?? entry.key, label: entry.key, workflowKey: entry.key, parentWorkflowRunId: workflowRunId, status: mapped, startedAt: Date.now() });
|
|
4327
4501
|
}
|
|
4328
4502
|
}
|
|
4329
4503
|
projectWorkflowActivity();
|
|
@@ -4335,6 +4509,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4335
4509
|
script: workflowScript,
|
|
4336
4510
|
timeoutMs: timeout,
|
|
4337
4511
|
signal: controller.signal,
|
|
4512
|
+
prompts: workflowPrompts,
|
|
4338
4513
|
...(workflowState ? { state: workflowState } : {}),
|
|
4339
4514
|
onTrace: updateTrace,
|
|
4340
4515
|
onEmit: (emits) => {
|
|
@@ -4348,30 +4523,70 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4348
4523
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4349
4524
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4350
4525
|
patchMissionObjective(childParams.task);
|
|
4351
|
-
const
|
|
4352
|
-
const
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4526
|
+
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
4527
|
+
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
4528
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
4529
|
+
status: "running",
|
|
4530
|
+
...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
|
|
4531
|
+
...(typeof childParams.task === "string" && childParams.task.trim() ? { task: childParams.task.trim() } : {}),
|
|
4532
|
+
...(childLabel ? { label: childLabel } : {}),
|
|
4533
|
+
...(childPhase ? { phase: childPhase } : {}),
|
|
4534
|
+
heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
|
|
4535
|
+
});
|
|
4536
|
+
const result = await runMissionWorkflowChild(missionBinding, workflowRunId, key, childPhase, () => {
|
|
4537
|
+
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
4538
|
+
prepareWorkflowLaunchParams(workflowChildDefaults, childParams, workflowRunId, key, { missionDetached: detachWorkflowChildMissions }),
|
|
4539
|
+
missionBinding,
|
|
4540
|
+
deps.asyncByDefault,
|
|
4541
|
+
);
|
|
4542
|
+
workflowLaunchObservers.set(childRequest, (launch) => {
|
|
4543
|
+
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
4544
|
+
if (step) {
|
|
4545
|
+
step.agent = launch.agent;
|
|
4546
|
+
step.sessionFile = launch.sessionFile;
|
|
4547
|
+
persist();
|
|
4548
|
+
}
|
|
4549
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, { status: "running", agent: launch.agent, ...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}) });
|
|
4550
|
+
});
|
|
4551
|
+
return execute(randomUUID(), childRequest, workflowSignal, (update) => {
|
|
4552
|
+
const progress = update.details.progress?.[0];
|
|
4553
|
+
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
4554
|
+
if (!progress || !step) return;
|
|
4555
|
+
step.status = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
|
|
4556
|
+
step.activityState = progress.activityState;
|
|
4557
|
+
step.lastActivityAt = progress.lastActivityAt;
|
|
4558
|
+
step.currentTool = progress.currentTool;
|
|
4559
|
+
step.currentToolArgs = progress.currentToolArgs;
|
|
4560
|
+
step.currentToolStartedAt = progress.currentToolStartedAt;
|
|
4561
|
+
step.currentPath = progress.currentPath;
|
|
4562
|
+
step.recentTools = progress.recentTools.map((tool) => ({ ...tool }));
|
|
4563
|
+
step.recentOutput = [...progress.recentOutput];
|
|
4564
|
+
step.turnCount = progress.turnCount;
|
|
4565
|
+
step.toolCount = progress.toolCount;
|
|
4566
|
+
step.model = progress.model;
|
|
4567
|
+
step.thinking = progress.thinking;
|
|
4568
|
+
step.error = progress.error;
|
|
4569
|
+
projectWorkflowActivity();
|
|
4570
|
+
persist();
|
|
4571
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
4572
|
+
status: step.status,
|
|
4573
|
+
heartbeat: { status: step.status, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4574
|
+
});
|
|
4575
|
+
}, ctx, preserveActiveSession);
|
|
4576
|
+
});
|
|
4373
4577
|
workflowResults.push(...result.details.results);
|
|
4578
|
+
if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
|
|
4374
4579
|
const child = workflowChildResult(key, result);
|
|
4580
|
+
const childStatus = missionWorkflowChildStatus(result);
|
|
4581
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
4582
|
+
status: childStatus,
|
|
4583
|
+
...(child.runId ? { runId: child.runId } : {}),
|
|
4584
|
+
...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
|
|
4585
|
+
...(result.details.results[0]?.sessionFile ? { sessionPath: result.details.results[0].sessionFile } : {}),
|
|
4586
|
+
artifactPaths: child.artifactPaths,
|
|
4587
|
+
...(["completed", "failed"].includes(childStatus) ? { completedAt: new Date().toISOString() } : {}),
|
|
4588
|
+
heartbeat: { status: childStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4589
|
+
});
|
|
4375
4590
|
if (result.details.asyncId) {
|
|
4376
4591
|
const childJob = deps.state.asyncJobs.get(result.details.asyncId);
|
|
4377
4592
|
if (childJob) { childJob.parentWorkflowRunId = workflowRunId; childJob.workflowKey = key; }
|
|
@@ -4385,14 +4600,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4385
4600
|
const summary = `Workflow completed with ${workflow.children.length} child run(s). Return: ${returnPreview}${emitPreview} Trace: ${workflow.trace.length} event(s).`;
|
|
4386
4601
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
4387
4602
|
status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
|
|
4388
|
-
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({
|
|
4603
|
+
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4389
4604
|
persist();
|
|
4390
4605
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: "complete" });
|
|
4391
4606
|
} catch (error) {
|
|
4392
4607
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4393
4608
|
const stopped = controller.signal.aborted;
|
|
4394
4609
|
status = compactOptional<AsyncStatus>({ ...status, state: stopped ? "stopped" : "failed", stopped: stopped || undefined, error: error instanceof Error ? error.message : String(error), endedAt: Date.now(), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console } });
|
|
4395
|
-
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({
|
|
4610
|
+
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4396
4611
|
persist();
|
|
4397
4612
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, error: status.error });
|
|
4398
4613
|
} finally {
|
|
@@ -4416,6 +4631,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4416
4631
|
script: requestParams.workflowScript,
|
|
4417
4632
|
timeoutMs: timeout,
|
|
4418
4633
|
signal,
|
|
4634
|
+
prompts: workflowPrompts,
|
|
4419
4635
|
...(workflowState ? { state: workflowState } : {}),
|
|
4420
4636
|
onTrace: (trace) => {
|
|
4421
4637
|
liveWorkflow = { ...liveWorkflow, trace };
|
|
@@ -4430,10 +4646,51 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4430
4646
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4431
4647
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4432
4648
|
patchMissionObjective(childParams.task);
|
|
4433
|
-
const
|
|
4434
|
-
const
|
|
4649
|
+
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
4650
|
+
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
4651
|
+
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
4652
|
+
status: "running",
|
|
4653
|
+
...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
|
|
4654
|
+
...(typeof childParams.task === "string" && childParams.task.trim() ? { task: childParams.task.trim() } : {}),
|
|
4655
|
+
...(childLabel ? { label: childLabel } : {}),
|
|
4656
|
+
...(childPhase ? { phase: childPhase } : {}),
|
|
4657
|
+
heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
|
|
4658
|
+
});
|
|
4659
|
+
const result = await runMissionWorkflowChild(missionBinding, _id, key, childPhase, () => {
|
|
4660
|
+
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
4661
|
+
prepareWorkflowLaunchParams(workflowChildDefaults, childParams, _id, key, { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card" }),
|
|
4662
|
+
missionBinding,
|
|
4663
|
+
deps.asyncByDefault,
|
|
4664
|
+
);
|
|
4665
|
+
workflowLaunchObservers.set(childRequest, (launch) => recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
4666
|
+
status: "running",
|
|
4667
|
+
agent: launch.agent,
|
|
4668
|
+
...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}),
|
|
4669
|
+
}));
|
|
4670
|
+
return execute(randomUUID(), childRequest, workflowSignal, (update) => {
|
|
4671
|
+
const progress = update.details.progress?.[0];
|
|
4672
|
+
if (!progress) return;
|
|
4673
|
+
const progressStatus = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
|
|
4674
|
+
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
4675
|
+
status: progressStatus,
|
|
4676
|
+
heartbeat: { status: progressStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4677
|
+
});
|
|
4678
|
+
}, ctx, preserveActiveSession);
|
|
4679
|
+
});
|
|
4435
4680
|
workflowResults.push(...result.details.results);
|
|
4436
|
-
|
|
4681
|
+
if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
|
|
4682
|
+
const child = workflowChildResult(key, result);
|
|
4683
|
+
const childStatus = missionWorkflowChildStatus(result);
|
|
4684
|
+
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
4685
|
+
status: childStatus,
|
|
4686
|
+
...(child.runId ? { runId: child.runId } : {}),
|
|
4687
|
+
...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
|
|
4688
|
+
...(result.details.results[0]?.sessionFile ? { sessionPath: result.details.results[0].sessionFile } : {}),
|
|
4689
|
+
artifactPaths: child.artifactPaths,
|
|
4690
|
+
...(["completed", "failed"].includes(childStatus) ? { completedAt: new Date().toISOString() } : {}),
|
|
4691
|
+
heartbeat: { status: childStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4692
|
+
});
|
|
4693
|
+
return child;
|
|
4437
4694
|
},
|
|
4438
4695
|
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
|
|
4439
4696
|
});
|
|
@@ -4475,7 +4732,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4475
4732
|
? normalizeParentModel(ctx.model)
|
|
4476
4733
|
: rememberParentModel(deps.state, requestSessionId, ctx.model);
|
|
4477
4734
|
} catch (error) {
|
|
4478
|
-
if (action?.toLowerCase() !== "doctor") throw error;
|
|
4735
|
+
if (action?.toLowerCase() !== "doctor" && action?.toLowerCase() !== "guide") throw error;
|
|
4479
4736
|
requestParentModel = normalizeParentModel(ctx.model);
|
|
4480
4737
|
}
|
|
4481
4738
|
if (action) {
|
|
@@ -4657,6 +4914,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4657
4914
|
details: { mode: "management", results: [], spawnBudget: granted.snapshot },
|
|
4658
4915
|
};
|
|
4659
4916
|
}
|
|
4917
|
+
if (action === "guide") {
|
|
4918
|
+
try {
|
|
4919
|
+
return {
|
|
4920
|
+
content: [{ type: "text", text: readSubagentGuide(paramsWithResolvedCwd.topic) }],
|
|
4921
|
+
details: { mode: "management", results: [] },
|
|
4922
|
+
};
|
|
4923
|
+
} catch (error) {
|
|
4924
|
+
return {
|
|
4925
|
+
content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
|
|
4926
|
+
isError: true,
|
|
4927
|
+
details: { mode: "management", results: [] },
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4660
4931
|
if (action === "children.list") {
|
|
4661
4932
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
4662
4933
|
const children = listRetainedChildren(DIRS.async, deps.state.currentSessionId);
|
|
@@ -4807,7 +5078,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4807
5078
|
}
|
|
4808
5079
|
}
|
|
4809
5080
|
if (action === "resume") {
|
|
4810
|
-
return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
|
|
5081
|
+
return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel, signal }));
|
|
4811
5082
|
}
|
|
4812
5083
|
if (action === "steer") {
|
|
4813
5084
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
@@ -4888,6 +5159,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4888
5159
|
return appendStepToAsyncChain(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
|
|
4889
5160
|
}
|
|
4890
5161
|
if (action.startsWith("schedule.")) {
|
|
5162
|
+
if (!isScheduledRunAction(action)) {
|
|
5163
|
+
return { content: [{ type: "text", text: unknownSubagentActionMessage(action) }], isError: true, details: { mode: "management", results: [] } };
|
|
5164
|
+
}
|
|
4891
5165
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
4892
5166
|
return {
|
|
4893
5167
|
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
@@ -4994,7 +5268,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4994
5268
|
}
|
|
4995
5269
|
if (!(SUBAGENT_ACTIONS as readonly string[]).includes(action)) {
|
|
4996
5270
|
return {
|
|
4997
|
-
content: [{ type: "text", text:
|
|
5271
|
+
content: [{ type: "text", text: unknownSubagentActionMessage(action) }],
|
|
4998
5272
|
isError: true,
|
|
4999
5273
|
details: { mode: "management" as const, results: [] },
|
|
5000
5274
|
};
|
|
@@ -5316,6 +5590,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5316
5590
|
runId,
|
|
5317
5591
|
sessionId: requestSessionId,
|
|
5318
5592
|
mode: foregroundMode,
|
|
5593
|
+
...(effectiveParams.workflowParentRunId ? { parentWorkflowRunId: effectiveParams.workflowParentRunId } : {}),
|
|
5594
|
+
...(effectiveParams.workflowKey ? { workflowKey: effectiveParams.workflowKey } : {}),
|
|
5319
5595
|
startedAt: Date.now(),
|
|
5320
5596
|
updatedAt: Date.now(),
|
|
5321
5597
|
cwd: effectiveCwd,
|
|
@@ -5421,6 +5697,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5421
5697
|
|
|
5422
5698
|
let nestedForegroundStarted = false;
|
|
5423
5699
|
try {
|
|
5700
|
+
if (workflowLaunchObserver) {
|
|
5701
|
+
const singleTask = hasTasks && effectiveParams.tasks?.length === 1 ? effectiveParams.tasks[0] : undefined;
|
|
5702
|
+
const launch = hasSingle
|
|
5703
|
+
? { agent: effectiveParams.agent!, sessionFile: childSessionFileForTask(effectiveParams.agent!, 0, effectiveParams.model) }
|
|
5704
|
+
: singleTask
|
|
5705
|
+
? { agent: singleTask.agent, sessionFile: childSessionFileForTask(singleTask.agent, 0, singleTask.model) }
|
|
5706
|
+
: undefined;
|
|
5707
|
+
if (launch) {
|
|
5708
|
+
workflowLaunchObservers.delete(params);
|
|
5709
|
+
workflowLaunchObserver(launch);
|
|
5710
|
+
}
|
|
5711
|
+
}
|
|
5424
5712
|
const asyncResult = runAsyncPath(execData, deps);
|
|
5425
5713
|
if (asyncResult) return attachMission(withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary));
|
|
5426
5714
|
if (foregroundControl) {
|