pi-subagents 0.42.1 → 0.43.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 +28 -0
- package/README.md +3 -5
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +2 -2
- package/skills/pi-subagents/references/constraints-and-recipes.md +35 -32
- package/skills/pi-subagents/references/execution-controls.md +27 -43
- package/skills/pi-subagents/references/management-authoring-rpc.md +20 -3
- package/skills/pi-subagents/references/prompting-and-roles.md +16 -49
- package/src/agents/agent-refinements.ts +624 -0
- package/src/agents/agents.ts +0 -2
- package/src/agents/proactive-skills.ts +1 -1
- package/src/api/delegation.ts +1 -2
- package/src/extension/control-notices.ts +2 -2
- package/src/extension/fanout-child.ts +46 -22
- package/src/extension/index.ts +29 -12
- package/src/extension/public-execution.ts +71 -0
- package/src/extension/rpc.ts +7 -8
- package/src/extension/schemas.ts +14 -14
- package/src/extension/tool-description.ts +14 -14
- package/src/missions/actions.ts +47 -11
- package/src/missions/goal-driver.ts +162 -0
- package/src/missions/lifecycle.ts +44 -12
- package/src/missions/store.ts +68 -3
- package/src/missions/types.ts +25 -3
- package/src/missions/workflow-state.ts +77 -0
- package/src/profiles/profiles.ts +1 -3
- package/src/runs/background/async-execution.ts +3 -0
- package/src/runs/background/async-job-tracker.ts +2 -17
- package/src/runs/background/control-channel.ts +50 -6
- package/src/runs/background/retained-children.ts +68 -0
- package/src/runs/background/scheduled-runs.ts +17 -13
- package/src/runs/background/steering.ts +7 -5
- package/src/runs/background/subagent-runner.ts +10 -4
- package/src/runs/foreground/async-steering-action.ts +36 -10
- package/src/runs/foreground/execution.ts +4 -0
- package/src/runs/foreground/subagent-executor.ts +197 -55
- package/src/runs/shared/acceptance.ts +154 -9
- package/src/runs/shared/subagent-prompt-runtime.ts +94 -25
- package/src/shared/types.ts +16 -2
- package/src/slash/delegation-adapters.ts +0 -13
- package/src/slash/prompt-template-bridge.ts +14 -12
- package/src/slash/prompt-workflows.ts +10 -4
- package/src/slash/slash-bridge.ts +8 -6
- package/src/slash/slash-commands.ts +24 -9
- package/src/slash/slash-live-state.ts +4 -2
- package/src/tui/fleet-status.ts +8 -4
- package/src/tui/fleet.ts +15 -5
- package/src/tui/render.ts +10 -29
- package/src/workflows/scripted-workflow.ts +91 -6
- package/agents/context-builder.md +0 -46
- package/agents/planner.md +0 -56
- package/prompts/parallel-context-build.md +0 -55
- package/prompts/parallel-handoff-plan.md +0 -61
|
@@ -19,11 +19,14 @@ import {
|
|
|
19
19
|
} from "./foreground-control.ts";
|
|
20
20
|
import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
|
|
21
21
|
import { handleManagementAction } from "../../agents/agent-management.ts";
|
|
22
|
+
import { handleRefinementAction } from "../../agents/agent-refinements.ts";
|
|
22
23
|
import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
24
|
+
import { normalizePublicSubagentExecution } from "../../extension/public-execution.ts";
|
|
23
25
|
import { runSync } from "./execution.ts";
|
|
24
26
|
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
|
|
25
27
|
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
26
28
|
import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
|
|
29
|
+
import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
|
|
27
30
|
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
28
31
|
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
29
32
|
import { recordRun } from "../shared/run-history.ts";
|
|
@@ -49,7 +52,7 @@ import { buildAsyncRunnerSteps, executeAsyncChain, executeAsyncSingle, formatAsy
|
|
|
49
52
|
import type { ScheduledRunAction } from "../background/scheduled-runs.ts";
|
|
50
53
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
51
54
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
52
|
-
import { validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
55
|
+
import { normalizeGateAcceptance, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
53
56
|
import { createForkContextResolver, forkedChildRequiresThinkingOff } from "../../shared/fork-context.ts";
|
|
54
57
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
55
58
|
import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
|
|
@@ -75,8 +78,8 @@ import {
|
|
|
75
78
|
stripDetailsOutputsForIntercomReceipt,
|
|
76
79
|
} from "../../intercom/result-intercom.ts";
|
|
77
80
|
import { applySteeringRecoveryAgentConfig, buildRevivedAsyncTask, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
|
|
78
|
-
import { deliverCheckpointDecisionRequest, deliverInterruptRequest, requestAsyncSteer } from "../background/control-channel.ts";
|
|
79
|
-
import { waitForSteeringAction } from "../background/steering.ts";
|
|
81
|
+
import { deliverCheckpointDecisionRequest, deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, type SteerDeliveryMode } from "../background/control-channel.ts";
|
|
82
|
+
import { updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
80
83
|
import { steerAsyncRun } from "./async-steering-action.ts";
|
|
81
84
|
import { stopAsyncRun } from "./async-stop-action.ts";
|
|
82
85
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
@@ -88,6 +91,7 @@ import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
|
88
91
|
import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
|
|
89
92
|
import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
|
|
90
93
|
import { attachMissionToLaunchResult, prepareMissionLaunch, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
94
|
+
import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
|
|
91
95
|
import { resolveAuthorityDecision } from "../../policy/authority.ts";
|
|
92
96
|
import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
|
|
93
97
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
@@ -144,7 +148,7 @@ import {
|
|
|
144
148
|
wrapForkTask,
|
|
145
149
|
} from "../../shared/types.ts";
|
|
146
150
|
|
|
147
|
-
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", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
|
|
151
|
+
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"]);
|
|
148
152
|
|
|
149
153
|
type UndefinedOmitted<T extends object> = {
|
|
150
154
|
[K in keyof T as undefined extends T[K] ? never : K]: T[K];
|
|
@@ -209,8 +213,11 @@ export interface SubagentParamsLike {
|
|
|
209
213
|
type?: string;
|
|
210
214
|
agent?: string;
|
|
211
215
|
task?: string;
|
|
216
|
+
/** Retained async child run id. Valid only on workflow runs.run items. */
|
|
217
|
+
resume?: string;
|
|
212
218
|
message?: string;
|
|
213
219
|
steeringRecovery?: boolean;
|
|
220
|
+
mode?: SteerDeliveryMode;
|
|
214
221
|
workflowScript?: string;
|
|
215
222
|
chatProgress?: "auto" | "off" | "live-card";
|
|
216
223
|
step?: ChainStep;
|
|
@@ -253,6 +260,7 @@ export interface SubagentParamsLike {
|
|
|
253
260
|
agentScope?: unknown;
|
|
254
261
|
chainDir?: string;
|
|
255
262
|
acceptance?: AcceptanceInput;
|
|
263
|
+
gate?: string;
|
|
256
264
|
agentContract?: AgentContract;
|
|
257
265
|
schedule?: string;
|
|
258
266
|
scheduleName?: string;
|
|
@@ -1160,7 +1168,7 @@ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nes
|
|
|
1160
1168
|
}
|
|
1161
1169
|
}
|
|
1162
1170
|
|
|
1163
|
-
async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details> | undefined> {
|
|
1171
|
+
async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; mode?: SteerDeliveryMode; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details> | undefined> {
|
|
1164
1172
|
const run = input.target.match.run;
|
|
1165
1173
|
const asyncDir = resolveNestedAsyncDir(input.target.match.rootRunId, run);
|
|
1166
1174
|
if (!asyncDir) return undefined;
|
|
@@ -1182,6 +1190,7 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
|
|
|
1182
1190
|
try {
|
|
1183
1191
|
requestAsyncSteer(asyncDir, {
|
|
1184
1192
|
message: input.message,
|
|
1193
|
+
mode: input.mode,
|
|
1185
1194
|
...(effectiveTargetIndex !== undefined ? { targetIndex: effectiveTargetIndex } : { targetIndexes }),
|
|
1186
1195
|
source: "nested-steer",
|
|
1187
1196
|
id: requestId,
|
|
@@ -1191,12 +1200,12 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
|
|
|
1191
1200
|
}
|
|
1192
1201
|
const targets = targetIndexes.map((index) => ({ index, state: steps[index]?.status === "pending" ? "scheduled" as const : "pending" as const }));
|
|
1193
1202
|
if (targets.every((target) => target.state === "scheduled")) {
|
|
1194
|
-
const scheduled = { requestId, state: "scheduled" as const, sourceRunId: run.id, targets };
|
|
1203
|
+
const scheduled = { requestId, state: "scheduled" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
|
|
1195
1204
|
return { content: [{ type: "text", text: `Steering scheduled for nested async run ${run.id} (request ${requestId}).` }], details: { mode: "management", results: [], steering: scheduled } };
|
|
1196
1205
|
}
|
|
1197
1206
|
const waited = await waitForSteeringAction(omitUndefinedProperties({ asyncDir, sourceRunId: run.id, requestId, timeoutMs: 3_000, signal: input.signal }));
|
|
1198
|
-
const result = waited ?? { requestId, state: "pending" as const, sourceRunId: run.id, targets };
|
|
1199
|
-
const stateText = result.state === "
|
|
1207
|
+
const result = waited ?? { requestId, state: "pending" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
|
|
1208
|
+
const stateText = result.state === "failed" ? "failed" : result.state === "partial" ? "partial" : result.deliveryStatus === "queued" ? "queued" : result.state === "delivered" ? "delivered" : "pending";
|
|
1200
1209
|
return { content: [{ type: "text", text: `Steering ${stateText} for nested async run ${run.id} (request ${requestId}).` }], ...(result.state === "failed" || result.state === "partial" ? { isError: true } : {}), details: { mode: "management", results: [], steering: result } };
|
|
1201
1210
|
}
|
|
1202
1211
|
|
|
@@ -1219,7 +1228,7 @@ async function resumeLiveNestedRun(input: { target: ResolvedSubagentRunId & { ki
|
|
|
1219
1228
|
return { content: [{ type: "text", text: `Nested run ${run.id} appears live but its owner route is not reachable. Wait for completion, then retry action='resume'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1220
1229
|
}
|
|
1221
1230
|
|
|
1222
|
-
async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details>> {
|
|
1231
|
+
async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; mode?: SteerDeliveryMode; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details>> {
|
|
1223
1232
|
const run = input.target.match.run;
|
|
1224
1233
|
if (run.state !== "running" && run.state !== "queued") return { content: [{ type: "text", text: `Nested run ${run.id} is ${run.state} and cannot be steered.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1225
1234
|
const direct = await directNestedAsyncSteer(input);
|
|
@@ -1449,6 +1458,9 @@ async function resumeAsyncRun(input: {
|
|
|
1449
1458
|
return { content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), input.ctx.hasUI) }], details: result.details };
|
|
1450
1459
|
}
|
|
1451
1460
|
|
|
1461
|
+
const sourceAsyncDir = target.source === "async" ? target.asyncDir : undefined;
|
|
1462
|
+
const queuedBriefs = sourceAsyncDir ? readRevivalBriefs(sourceAsyncDir) : [];
|
|
1463
|
+
const effectiveFollowUp = [...queuedBriefs.map(({ request }) => request.message), followUp].filter(Boolean).join("\n\n");
|
|
1452
1464
|
const revivalSessionFile = target.sessionFile;
|
|
1453
1465
|
if (!revivalSessionFile) {
|
|
1454
1466
|
return { content: [{ type: "text", text: `Async child '${target.runId}' has no persisted session file to resume.` }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -1461,8 +1473,8 @@ async function resumeAsyncRun(input: {
|
|
|
1461
1473
|
const parentModel = input.parentModel;
|
|
1462
1474
|
const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
1463
1475
|
agent: target.agent,
|
|
1464
|
-
task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0],
|
|
1465
|
-
goal:
|
|
1476
|
+
task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0], effectiveFollowUp),
|
|
1477
|
+
goal: effectiveFollowUp,
|
|
1466
1478
|
agentConfig: recoveryAgentConfig,
|
|
1467
1479
|
ctx: compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
1468
1480
|
pi: input.deps.pi,
|
|
@@ -1512,8 +1524,18 @@ async function resumeAsyncRun(input: {
|
|
|
1512
1524
|
...(input.params.turnBudget !== undefined ? { turnBudget: input.params.turnBudget } : {}),
|
|
1513
1525
|
...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
|
|
1514
1526
|
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, recoveryDescriptor?.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1527
|
+
parentWorkflowRunId: input.params.workflowParentRunId,
|
|
1528
|
+
workflowKey: input.params.workflowKey,
|
|
1515
1529
|
}));
|
|
1516
1530
|
if (result.isError) return result;
|
|
1531
|
+
for (const brief of queuedBriefs) fs.rmSync(brief.path, { force: true });
|
|
1532
|
+
if (queuedBriefs.length > 0 && sourceAsyncDir) {
|
|
1533
|
+
const sourceStatus = readStatus(sourceAsyncDir);
|
|
1534
|
+
if (sourceStatus?.steering) {
|
|
1535
|
+
for (const brief of queuedBriefs) updateSteeringTarget(sourceStatus.steering, brief.request.id, target.index, "delivered", Date.now());
|
|
1536
|
+
writeAtomicJson(path.join(sourceAsyncDir, "status.json"), sourceStatus);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1517
1539
|
|
|
1518
1540
|
const revivedId = result.details.asyncId ?? runId;
|
|
1519
1541
|
const revivedTarget = intercomBridge.active ? resolveSubagentIntercomTarget(revivedId, target.agent, 0) : undefined;
|
|
@@ -3894,6 +3916,60 @@ function workflowChildResult(key: string, result: AgentToolResult<Details>): Wor
|
|
|
3894
3916
|
};
|
|
3895
3917
|
}
|
|
3896
3918
|
|
|
3919
|
+
export function prepareWorkflowLaunchParams(
|
|
3920
|
+
workflowDefaults: SubagentParamsLike,
|
|
3921
|
+
childParams: Record<string, unknown>,
|
|
3922
|
+
parentWorkflowRunId: string,
|
|
3923
|
+
workflowKey: string,
|
|
3924
|
+
options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean } = {},
|
|
3925
|
+
): SubagentParamsLike {
|
|
3926
|
+
if (typeof childParams.resume === "string") {
|
|
3927
|
+
if (childParams.gate !== undefined || workflowDefaults.gate !== undefined) {
|
|
3928
|
+
throw new Error("gate is not supported with retained resume; resume uses the retained child contract.");
|
|
3929
|
+
}
|
|
3930
|
+
const timeoutMs = childParams.timeoutMs ?? childParams.maxRuntimeMs ?? workflowDefaults.timeoutMs ?? workflowDefaults.maxRuntimeMs;
|
|
3931
|
+
const turnBudget = childParams.turnBudget ?? workflowDefaults.turnBudget;
|
|
3932
|
+
const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
|
|
3933
|
+
return {
|
|
3934
|
+
action: "resume",
|
|
3935
|
+
id: childParams.resume.trim(),
|
|
3936
|
+
message: typeof childParams.task === "string" ? childParams.task.trim() : "",
|
|
3937
|
+
workflowParentRunId: parentWorkflowRunId,
|
|
3938
|
+
workflowKey,
|
|
3939
|
+
...(options.missionDetached ? { mission: false } : {}),
|
|
3940
|
+
...(timeoutMs !== undefined ? { timeoutMs: timeoutMs as number } : {}),
|
|
3941
|
+
...(turnBudget !== undefined ? { turnBudget: turnBudget as TurnBudgetConfig } : {}),
|
|
3942
|
+
...(toolBudget !== undefined ? { toolBudget: toolBudget as ToolBudgetConfig } : {}),
|
|
3943
|
+
};
|
|
3944
|
+
}
|
|
3945
|
+
const launchParams = {
|
|
3946
|
+
...workflowDefaults,
|
|
3947
|
+
...childParams,
|
|
3948
|
+
...(options.missionDetached ? { mission: false } : {}),
|
|
3949
|
+
workflowParentRunId: parentWorkflowRunId,
|
|
3950
|
+
workflowKey,
|
|
3951
|
+
...(options.suppressRoutineResultIntercom ? { suppressRoutineResultIntercom: true } : {}),
|
|
3952
|
+
} as SubagentParamsLike;
|
|
3953
|
+
const normalizedGate = normalizeGateParams(launchParams);
|
|
3954
|
+
if (!normalizedGate.ok) throw new Error(normalizedGate.error);
|
|
3955
|
+
return prepareWorkflowChildParams(normalizedGate.params);
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
type GateParamsNormalizationResult =
|
|
3959
|
+
| { ok: true; params: SubagentParamsLike }
|
|
3960
|
+
| { ok: false; error: string };
|
|
3961
|
+
|
|
3962
|
+
function normalizeGateParams(params: SubagentParamsLike): GateParamsNormalizationResult {
|
|
3963
|
+
if (params.gate !== undefined && params.action === "resume") {
|
|
3964
|
+
return { ok: false, error: "gate is not supported with action='resume'; resume uses the retained child contract." };
|
|
3965
|
+
}
|
|
3966
|
+
const normalized = normalizeGateAcceptance(params.gate, params.acceptance);
|
|
3967
|
+
if (!normalized.ok) return { ok: false, error: normalized.error };
|
|
3968
|
+
if (params.gate === undefined) return { ok: true, params };
|
|
3969
|
+
const { gate: _gate, ...rest } = params;
|
|
3970
|
+
return { ok: true, params: { ...rest, ...(normalized.acceptance !== undefined ? { acceptance: normalized.acceptance } : {}) } };
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3897
3973
|
function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsLike {
|
|
3898
3974
|
if (params.worktree !== true || !params.agent) return params;
|
|
3899
3975
|
const {
|
|
@@ -3949,21 +4025,6 @@ function workflowChatProgressUpdate(
|
|
|
3949
4025
|
};
|
|
3950
4026
|
}
|
|
3951
4027
|
|
|
3952
|
-
function omitExecutionModeActionAlias(params: SubagentParamsLike): SubagentParamsLike {
|
|
3953
|
-
const action = params.action?.toLowerCase();
|
|
3954
|
-
if (action === "single" && (params.agent !== undefined || params.task !== undefined)) {
|
|
3955
|
-
const rest = { ...params };
|
|
3956
|
-
delete rest.action;
|
|
3957
|
-
return rest;
|
|
3958
|
-
}
|
|
3959
|
-
if ((action === "parallel" || action === "tasks") && (params.tasks?.length ?? 0) > 0) {
|
|
3960
|
-
const rest = { ...params };
|
|
3961
|
-
delete rest.action;
|
|
3962
|
-
return rest;
|
|
3963
|
-
}
|
|
3964
|
-
return params;
|
|
3965
|
-
}
|
|
3966
|
-
|
|
3967
4028
|
function createScheduledOwnerState(source: SubagentState, ownerSessionId: string, ctx: ExtensionContext): SubagentState {
|
|
3968
4029
|
const ownerSpawns = source.subagentSpawns?.sessionId === ownerSessionId
|
|
3969
4030
|
? {
|
|
@@ -4002,6 +4063,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4002
4063
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
4003
4064
|
ctx: ExtensionContext,
|
|
4004
4065
|
) => Promise<AgentToolResult<Details>>;
|
|
4066
|
+
/** Public/model-facing execution boundary. Internal direct launch primitives use execute or executeDelegated. */
|
|
4067
|
+
executePublic: (
|
|
4068
|
+
id: string,
|
|
4069
|
+
params: SubagentParamsLike,
|
|
4070
|
+
signal: AbortSignal,
|
|
4071
|
+
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
4072
|
+
ctx: ExtensionContext,
|
|
4073
|
+
) => Promise<AgentToolResult<Details>>;
|
|
4005
4074
|
/**
|
|
4006
4075
|
* Correlated extension-to-extension delegation owns its request IDs and
|
|
4007
4076
|
* cancellation controllers, so independent requests may execute concurrently.
|
|
@@ -4040,22 +4109,45 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4040
4109
|
deps.state.foregroundRuns ??= new Map();
|
|
4041
4110
|
deps.state.foregroundControls ??= new Map();
|
|
4042
4111
|
deps.state.lastForegroundControlId ??= null;
|
|
4043
|
-
const
|
|
4044
|
-
if (
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
if (requestParams.clarify === true) {
|
|
4050
|
-
return { content: [{ type: "text", text: "workflowScript does not support clarify UI." }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4051
|
-
}
|
|
4112
|
+
const normalizedGate = normalizeGateParams(params);
|
|
4113
|
+
if (!normalizedGate.ok) return buildRequestedModeError(params, normalizedGate.error);
|
|
4114
|
+
const requestParams = normalizedGate.params;
|
|
4115
|
+
const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
|
|
4116
|
+
if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
|
|
4117
|
+
const parentCwd = ctx.cwd;
|
|
4052
4118
|
const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? (requestParams.async === false ? DEFAULT_FOREGROUND_TIMEOUT_MS : undefined);
|
|
4053
4119
|
const workflowUsageBudget = validateUsageBudgetConfig(requestParams.usageBudget ?? deps.config.usageBudget, requestParams.usageBudget ? "usageBudget" : "config.usageBudget");
|
|
4054
4120
|
if (workflowUsageBudget.error) return buildRequestedModeError(requestParams, workflowUsageBudget.error);
|
|
4055
|
-
const workflowCwd = resolveRequestedCwd(
|
|
4056
|
-
const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd
|
|
4121
|
+
const workflowCwd = resolveRequestedCwd(parentCwd, requestParams.cwd);
|
|
4122
|
+
const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd, workflowCwd, background: requestParams.async !== false });
|
|
4057
4123
|
if (chatProgressResult.error) return { content: [{ type: "text", text: chatProgressResult.error }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4058
4124
|
const chatProgress = chatProgressResult.projection!;
|
|
4125
|
+
const explicitMission = requestParams.missionId !== undefined || requestParams.mission !== undefined;
|
|
4126
|
+
let missionBinding: MissionLaunchBinding | undefined;
|
|
4127
|
+
let missionWarning: string | undefined;
|
|
4128
|
+
try {
|
|
4129
|
+
missionBinding = prepareMissionLaunch({
|
|
4130
|
+
params: requestParams,
|
|
4131
|
+
projectRoot: workflowCwd,
|
|
4132
|
+
...(deps.config.missions ? { config: deps.config.missions } : {}),
|
|
4133
|
+
ownerSessionId: resolveCurrentSessionId(ctx.sessionManager),
|
|
4134
|
+
});
|
|
4135
|
+
} catch (error) {
|
|
4136
|
+
if (explicitMission) return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4137
|
+
missionWarning = `Mission tracking unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
4138
|
+
}
|
|
4139
|
+
const workflowState = missionBinding ? createMissionWorkflowState(missionBinding.location, missionBinding.missionId) : undefined;
|
|
4140
|
+
const attachWorkflowMission = (result: AgentToolResult<Details>): AgentToolResult<Details> => {
|
|
4141
|
+
if (!missionBinding) return missionWarning ? { ...result, details: { ...result.details, missionWarning } } : result;
|
|
4142
|
+
try {
|
|
4143
|
+
return attachMissionToLaunchResult({ binding: missionBinding, result });
|
|
4144
|
+
} catch (error) {
|
|
4145
|
+
const warning = `Mission tracking unavailable after launch: ${error instanceof Error ? error.message : String(error)}`;
|
|
4146
|
+
return explicitMission
|
|
4147
|
+
? { ...result, isError: true, content: [...result.content, { type: "text", text: warning }], details: { ...result.details, missionWarning: warning } }
|
|
4148
|
+
: { ...result, details: { ...result.details, missionWarning: warning } };
|
|
4149
|
+
}
|
|
4150
|
+
};
|
|
4059
4151
|
if (requestParams.async !== false) {
|
|
4060
4152
|
const workflowRunId = _id;
|
|
4061
4153
|
const asyncDir = path.join(DIRS.async, workflowRunId);
|
|
@@ -4077,7 +4169,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4077
4169
|
startedAt,
|
|
4078
4170
|
lastUpdate: startedAt,
|
|
4079
4171
|
...(timeout !== undefined ? { deadlineAt: startedAt + timeout, timeoutMs: timeout } : {}),
|
|
4080
|
-
cwd:
|
|
4172
|
+
cwd: parentCwd,
|
|
4081
4173
|
pid: process.pid,
|
|
4082
4174
|
steps: [],
|
|
4083
4175
|
workflow: { trace: [], emits: [], console: [] },
|
|
@@ -4100,7 +4192,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4100
4192
|
job.workflow = status.workflow;
|
|
4101
4193
|
}
|
|
4102
4194
|
};
|
|
4103
|
-
const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, cwd:
|
|
4195
|
+
const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, cwd: parentCwd, status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], startedAt, updatedAt: startedAt, ...(timeout !== undefined ? { timeoutMs: timeout, deadlineAt: startedAt + timeout } : {}), workflow: status.workflow };
|
|
4104
4196
|
deps.state.asyncJobs.set(workflowRunId, workflowJob);
|
|
4105
4197
|
deps.state.fleetJobs ??= new Map();
|
|
4106
4198
|
deps.state.fleetJobs.set(workflowRunId, workflowJob);
|
|
@@ -4109,7 +4201,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4109
4201
|
const { workflowScript, async: _workflowAsync, chatProgress: _chatProgress, ...workflowRequest } = requestParams;
|
|
4110
4202
|
void Promise.resolve().then(async () => {
|
|
4111
4203
|
const workflowResults: SingleResult[] = [];
|
|
4112
|
-
const { action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, ...workflowChildDefaults } = workflowRequest;
|
|
4204
|
+
const { action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, missionId: _missionId, mission: _mission, ...workflowChildDefaults } = workflowRequest;
|
|
4113
4205
|
const updateTrace = (trace: NonNullable<Details["workflow"]>["trace"]) => {
|
|
4114
4206
|
status.workflow = { ...(status.workflow ?? { emits: [], console: [] }), trace };
|
|
4115
4207
|
for (const entry of trace.filter((candidate) => candidate.operation === "run")) {
|
|
@@ -4134,6 +4226,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4134
4226
|
script: workflowScript,
|
|
4135
4227
|
timeoutMs: timeout,
|
|
4136
4228
|
signal: controller.signal,
|
|
4229
|
+
...(workflowState ? { state: workflowState } : {}),
|
|
4137
4230
|
onTrace: updateTrace,
|
|
4138
4231
|
onEmit: (emits) => {
|
|
4139
4232
|
// Each emit is validated at the host boundary in runWorkflowScript before onEmit fires.
|
|
@@ -4145,7 +4238,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4145
4238
|
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4146
4239
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4147
4240
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4148
|
-
const childRequest =
|
|
4241
|
+
const childRequest = prepareWorkflowLaunchParams(workflowChildDefaults, childParams, workflowRunId, key, { missionDetached: missionBinding !== undefined || requestParams.mission === false });
|
|
4149
4242
|
const result = await execute(randomUUID(), childRequest, workflowSignal, undefined, ctx, preserveActiveSession);
|
|
4150
4243
|
workflowResults.push(...result.details.results);
|
|
4151
4244
|
const child = workflowChildResult(key, result);
|
|
@@ -4164,24 +4257,24 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4164
4257
|
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) };
|
|
4165
4258
|
persist();
|
|
4166
4259
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: "complete" });
|
|
4167
|
-
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ agent: child.key, 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:
|
|
4260
|
+
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ agent: child.key, 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: parentCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4168
4261
|
} catch (error) {
|
|
4169
4262
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4170
4263
|
const stopped = controller.signal.aborted;
|
|
4171
4264
|
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 } });
|
|
4172
4265
|
persist();
|
|
4173
4266
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, error: status.error });
|
|
4174
|
-
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ agent: child.key, 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:
|
|
4267
|
+
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ agent: child.key, 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: parentCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4175
4268
|
} finally {
|
|
4176
4269
|
deps.state.workflowControllers?.delete(workflowRunId);
|
|
4177
4270
|
}
|
|
4178
4271
|
});
|
|
4179
|
-
return {
|
|
4272
|
+
return attachWorkflowMission({
|
|
4180
4273
|
content: [{ type: "text", text: formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true) }],
|
|
4181
4274
|
details: { mode: "workflow", runId: workflowRunId, asyncId: workflowRunId, asyncDir, results: [], chatProgress },
|
|
4182
|
-
};
|
|
4275
|
+
});
|
|
4183
4276
|
}
|
|
4184
|
-
const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, ...workflowChildDefaults } = requestParams;
|
|
4277
|
+
const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, missionId: _missionId, mission: _mission, ...workflowChildDefaults } = requestParams;
|
|
4185
4278
|
const workflowResults: SingleResult[] = [];
|
|
4186
4279
|
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
|
|
4187
4280
|
const sendWorkflowProgress = () => {
|
|
@@ -4193,6 +4286,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4193
4286
|
script: requestParams.workflowScript,
|
|
4194
4287
|
timeoutMs: timeout,
|
|
4195
4288
|
signal,
|
|
4289
|
+
...(workflowState ? { state: workflowState } : {}),
|
|
4196
4290
|
onTrace: (trace) => {
|
|
4197
4291
|
liveWorkflow = { ...liveWorkflow, trace };
|
|
4198
4292
|
sendWorkflowProgress();
|
|
@@ -4205,7 +4299,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4205
4299
|
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4206
4300
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4207
4301
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4208
|
-
const childRequest =
|
|
4302
|
+
const childRequest = prepareWorkflowLaunchParams(workflowChildDefaults, childParams, _id, key, { missionDetached: missionBinding !== undefined || requestParams.mission === false, suppressRoutineResultIntercom: chatProgress.mode === "live-card" });
|
|
4209
4303
|
const result = await execute(randomUUID(), childRequest, workflowSignal, undefined, ctx, preserveActiveSession);
|
|
4210
4304
|
workflowResults.push(...result.details.results);
|
|
4211
4305
|
return workflowChildResult(key, result);
|
|
@@ -4217,10 +4311,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4217
4311
|
if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4218
4312
|
if (workflow.console.length > 0) sections.push(`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4219
4313
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
4220
|
-
return {
|
|
4314
|
+
return attachWorkflowMission({
|
|
4221
4315
|
content: [{ type: "text", text: sections.join("\n\n") }],
|
|
4222
4316
|
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, chatProgress }),
|
|
4223
|
-
};
|
|
4317
|
+
});
|
|
4224
4318
|
} catch (error) {
|
|
4225
4319
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4226
4320
|
const text = error instanceof Error ? error.message : String(error);
|
|
@@ -4229,11 +4323,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4229
4323
|
if (partial.emits.length > 0) sections.push(`Emitted:\n${partial.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4230
4324
|
if (partial.console.length > 0) sections.push(`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4231
4325
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
4232
|
-
return {
|
|
4326
|
+
return attachWorkflowMission({
|
|
4233
4327
|
content: [{ type: "text", text: sections.join("\n\n") }],
|
|
4234
4328
|
isError: true,
|
|
4235
4329
|
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console }, chatProgress }),
|
|
4236
|
-
};
|
|
4330
|
+
});
|
|
4237
4331
|
}
|
|
4238
4332
|
}
|
|
4239
4333
|
const directParams = prepareWorkflowChildParams(requestParams);
|
|
@@ -4337,6 +4431,29 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4337
4431
|
}
|
|
4338
4432
|
return handleWatchdogToolAction(action, paramsWithResolvedCwd, ctx, deps.watchdog);
|
|
4339
4433
|
}
|
|
4434
|
+
if (action === "refine" || action === "refine.show" || action === "refine.rollback") {
|
|
4435
|
+
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
4436
|
+
return {
|
|
4437
|
+
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
4438
|
+
isError: true,
|
|
4439
|
+
details: { mode: "management", results: [] },
|
|
4440
|
+
};
|
|
4441
|
+
}
|
|
4442
|
+
return handleRefinementAction(action, paramsWithResolvedCwd, {
|
|
4443
|
+
cwd: requestCwd,
|
|
4444
|
+
state: deps.state,
|
|
4445
|
+
signal,
|
|
4446
|
+
launchProposalChild: (task, outputSchema, proposalSignal) => execute(randomUUID(), {
|
|
4447
|
+
agent: "reviewer",
|
|
4448
|
+
task,
|
|
4449
|
+
context: "fresh",
|
|
4450
|
+
async: false,
|
|
4451
|
+
artifacts: false,
|
|
4452
|
+
outputSchema,
|
|
4453
|
+
toolBudget: { hard: 1, block: ["write", "edit", "bash"] },
|
|
4454
|
+
}, proposalSignal, undefined, ctx, true),
|
|
4455
|
+
});
|
|
4456
|
+
}
|
|
4340
4457
|
if (action === "grant-spawn-budget") {
|
|
4341
4458
|
if (deps.allowMutatingManagementActions === false || !ctx.hasUI) {
|
|
4342
4459
|
return {
|
|
@@ -4409,6 +4526,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4409
4526
|
details: { mode: "management", results: [], spawnBudget: granted.snapshot },
|
|
4410
4527
|
};
|
|
4411
4528
|
}
|
|
4529
|
+
if (action === "children.list") {
|
|
4530
|
+
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
4531
|
+
const children = listRetainedChildren(DIRS.async, deps.state.currentSessionId);
|
|
4532
|
+
return {
|
|
4533
|
+
content: [{ type: "text", text: formatRetainedChildren(children) }],
|
|
4534
|
+
details: { mode: "management", results: [] },
|
|
4535
|
+
};
|
|
4536
|
+
}
|
|
4412
4537
|
if (action === "doctor") {
|
|
4413
4538
|
let currentSessionFile: string | null = null;
|
|
4414
4539
|
let currentSessionId = deps.state.currentSessionId;
|
|
@@ -4565,6 +4690,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4565
4690
|
state: deps.state,
|
|
4566
4691
|
runId,
|
|
4567
4692
|
message,
|
|
4693
|
+
mode: paramsWithResolvedCwd.mode,
|
|
4568
4694
|
index: paramsWithResolvedCwd.index,
|
|
4569
4695
|
kill: deps.kill,
|
|
4570
4696
|
location,
|
|
@@ -4590,7 +4716,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4590
4716
|
const text = error instanceof Error ? error.message : String(error);
|
|
4591
4717
|
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
4592
4718
|
}
|
|
4593
|
-
if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, index: paramsWithResolvedCwd.index, signal }));
|
|
4719
|
+
if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal }));
|
|
4594
4720
|
if (resolved?.kind === "foreground") return { content: [{ type: "text", text: "action='steer' currently supports live async Pi child sessions only; use action='interrupt' or action='resume' for foreground runs." }], isError: true, details: { mode: "management", results: [] } };
|
|
4595
4721
|
if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
4596
4722
|
if (resolved.location.asyncDir) {
|
|
@@ -4601,6 +4727,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4601
4727
|
state: deps.state,
|
|
4602
4728
|
runId: resolved.id,
|
|
4603
4729
|
message,
|
|
4730
|
+
mode: paramsWithResolvedCwd.mode,
|
|
4604
4731
|
index: paramsWithResolvedCwd.index,
|
|
4605
4732
|
kill: deps.kill,
|
|
4606
4733
|
location: resolved.location,
|
|
@@ -5204,8 +5331,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5204
5331
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
5205
5332
|
ctx: ExtensionContext,
|
|
5206
5333
|
): Promise<AgentToolResult<Details>> => {
|
|
5207
|
-
const
|
|
5208
|
-
|
|
5334
|
+
const normalizedAction = typeof params.action === "string" ? params.action.trim() : params.action;
|
|
5335
|
+
const requestParams = normalizedAction ? { ...params, action: normalizedAction } : params;
|
|
5336
|
+
if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx);
|
|
5209
5337
|
const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
5210
5338
|
const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
|
|
5211
5339
|
const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
|
|
@@ -5219,6 +5347,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5219
5347
|
}
|
|
5220
5348
|
};
|
|
5221
5349
|
|
|
5350
|
+
const executePublic = (
|
|
5351
|
+
id: string,
|
|
5352
|
+
params: SubagentParamsLike,
|
|
5353
|
+
signal: AbortSignal,
|
|
5354
|
+
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
5355
|
+
ctx: ExtensionContext,
|
|
5356
|
+
): Promise<AgentToolResult<Details>> => {
|
|
5357
|
+
const normalized = normalizePublicSubagentExecution(params);
|
|
5358
|
+
if (!normalized.ok) {
|
|
5359
|
+
return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
|
|
5360
|
+
}
|
|
5361
|
+
return executeWithSingleDispatchGuard(id, normalized.params, signal, onUpdate, ctx);
|
|
5362
|
+
};
|
|
5363
|
+
|
|
5222
5364
|
const executeDelegated = async (
|
|
5223
5365
|
id: string,
|
|
5224
5366
|
params: SubagentParamsLike,
|
|
@@ -5258,5 +5400,5 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5258
5400
|
return ownerExecutor.execute(id, params, signal, undefined, ctx);
|
|
5259
5401
|
};
|
|
5260
5402
|
|
|
5261
|
-
return { execute: executeWithSingleDispatchGuard, executeDelegated, executeScheduled };
|
|
5403
|
+
return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };
|
|
5262
5404
|
}
|