pi-subagents 0.42.0 → 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 +35 -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 +17 -17
- 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/chain-clarify.ts +13 -16
- package/src/runs/foreground/execution.ts +4 -0
- package/src/runs/foreground/subagent-executor.ts +200 -59
- package/src/runs/shared/acceptance.ts +154 -9
- package/src/runs/shared/subagent-prompt-runtime.ts +94 -25
- package/src/shared/types.ts +17 -3
- 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 +27 -12
- 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 +13 -31
- package/src/workflows/chat-progress.ts +2 -2
- package/src/workflows/scripted-workflow.ts +97 -10
- 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,10 +213,13 @@ 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
|
-
chatProgress?: "auto" | "off" | "
|
|
222
|
+
chatProgress?: "auto" | "off" | "live-card";
|
|
216
223
|
step?: ChainStep;
|
|
217
224
|
/** Internal workflow ownership metadata; not part of the public schema. */
|
|
218
225
|
workflowParentRunId?: string;
|
|
@@ -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
|
-
|
|
4050
|
-
return { content: [{ type: "text", text: "workflowScript does not support clarify UI." }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4051
|
-
}
|
|
4052
|
-
const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? DEFAULT_FOREGROUND_TIMEOUT_MS;
|
|
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;
|
|
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);
|
|
@@ -4076,9 +4168,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4076
4168
|
state: "running",
|
|
4077
4169
|
startedAt,
|
|
4078
4170
|
lastUpdate: startedAt,
|
|
4079
|
-
deadlineAt: startedAt + timeout,
|
|
4080
|
-
|
|
4081
|
-
cwd: ctx.cwd,
|
|
4171
|
+
...(timeout !== undefined ? { deadlineAt: startedAt + timeout, timeoutMs: timeout } : {}),
|
|
4172
|
+
cwd: parentCwd,
|
|
4082
4173
|
pid: process.pid,
|
|
4083
4174
|
steps: [],
|
|
4084
4175
|
workflow: { trace: [], emits: [], console: [] },
|
|
@@ -4101,7 +4192,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4101
4192
|
job.workflow = status.workflow;
|
|
4102
4193
|
}
|
|
4103
4194
|
};
|
|
4104
|
-
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 };
|
|
4105
4196
|
deps.state.asyncJobs.set(workflowRunId, workflowJob);
|
|
4106
4197
|
deps.state.fleetJobs ??= new Map();
|
|
4107
4198
|
deps.state.fleetJobs.set(workflowRunId, workflowJob);
|
|
@@ -4110,7 +4201,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4110
4201
|
const { workflowScript, async: _workflowAsync, chatProgress: _chatProgress, ...workflowRequest } = requestParams;
|
|
4111
4202
|
void Promise.resolve().then(async () => {
|
|
4112
4203
|
const workflowResults: SingleResult[] = [];
|
|
4113
|
-
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;
|
|
4114
4205
|
const updateTrace = (trace: NonNullable<Details["workflow"]>["trace"]) => {
|
|
4115
4206
|
status.workflow = { ...(status.workflow ?? { emits: [], console: [] }), trace };
|
|
4116
4207
|
for (const entry of trace.filter((candidate) => candidate.operation === "run")) {
|
|
@@ -4135,6 +4226,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4135
4226
|
script: workflowScript,
|
|
4136
4227
|
timeoutMs: timeout,
|
|
4137
4228
|
signal: controller.signal,
|
|
4229
|
+
...(workflowState ? { state: workflowState } : {}),
|
|
4138
4230
|
onTrace: updateTrace,
|
|
4139
4231
|
onEmit: (emits) => {
|
|
4140
4232
|
// Each emit is validated at the host boundary in runWorkflowScript before onEmit fires.
|
|
@@ -4146,7 +4238,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4146
4238
|
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4147
4239
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4148
4240
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4149
|
-
const childRequest =
|
|
4241
|
+
const childRequest = prepareWorkflowLaunchParams(workflowChildDefaults, childParams, workflowRunId, key, { missionDetached: missionBinding !== undefined || requestParams.mission === false });
|
|
4150
4242
|
const result = await execute(randomUUID(), childRequest, workflowSignal, undefined, ctx, preserveActiveSession);
|
|
4151
4243
|
workflowResults.push(...result.details.results);
|
|
4152
4244
|
const child = workflowChildResult(key, result);
|
|
@@ -4165,24 +4257,24 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4165
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) };
|
|
4166
4258
|
persist();
|
|
4167
4259
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: "complete" });
|
|
4168
|
-
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 });
|
|
4169
4261
|
} catch (error) {
|
|
4170
4262
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4171
4263
|
const stopped = controller.signal.aborted;
|
|
4172
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 } });
|
|
4173
4265
|
persist();
|
|
4174
4266
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, error: status.error });
|
|
4175
|
-
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 });
|
|
4176
4268
|
} finally {
|
|
4177
4269
|
deps.state.workflowControllers?.delete(workflowRunId);
|
|
4178
4270
|
}
|
|
4179
4271
|
});
|
|
4180
|
-
return {
|
|
4272
|
+
return attachWorkflowMission({
|
|
4181
4273
|
content: [{ type: "text", text: formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true) }],
|
|
4182
4274
|
details: { mode: "workflow", runId: workflowRunId, asyncId: workflowRunId, asyncDir, results: [], chatProgress },
|
|
4183
|
-
};
|
|
4275
|
+
});
|
|
4184
4276
|
}
|
|
4185
|
-
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;
|
|
4186
4278
|
const workflowResults: SingleResult[] = [];
|
|
4187
4279
|
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
|
|
4188
4280
|
const sendWorkflowProgress = () => {
|
|
@@ -4194,6 +4286,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4194
4286
|
script: requestParams.workflowScript,
|
|
4195
4287
|
timeoutMs: timeout,
|
|
4196
4288
|
signal,
|
|
4289
|
+
...(workflowState ? { state: workflowState } : {}),
|
|
4197
4290
|
onTrace: (trace) => {
|
|
4198
4291
|
liveWorkflow = { ...liveWorkflow, trace };
|
|
4199
4292
|
sendWorkflowProgress();
|
|
@@ -4206,7 +4299,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4206
4299
|
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4207
4300
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4208
4301
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4209
|
-
const childRequest =
|
|
4302
|
+
const childRequest = prepareWorkflowLaunchParams(workflowChildDefaults, childParams, _id, key, { missionDetached: missionBinding !== undefined || requestParams.mission === false, suppressRoutineResultIntercom: chatProgress.mode === "live-card" });
|
|
4210
4303
|
const result = await execute(randomUUID(), childRequest, workflowSignal, undefined, ctx, preserveActiveSession);
|
|
4211
4304
|
workflowResults.push(...result.details.results);
|
|
4212
4305
|
return workflowChildResult(key, result);
|
|
@@ -4218,10 +4311,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4218
4311
|
if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4219
4312
|
if (workflow.console.length > 0) sections.push(`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4220
4313
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
4221
|
-
return {
|
|
4314
|
+
return attachWorkflowMission({
|
|
4222
4315
|
content: [{ type: "text", text: sections.join("\n\n") }],
|
|
4223
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 }),
|
|
4224
|
-
};
|
|
4317
|
+
});
|
|
4225
4318
|
} catch (error) {
|
|
4226
4319
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4227
4320
|
const text = error instanceof Error ? error.message : String(error);
|
|
@@ -4230,11 +4323,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4230
4323
|
if (partial.emits.length > 0) sections.push(`Emitted:\n${partial.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4231
4324
|
if (partial.console.length > 0) sections.push(`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4232
4325
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
4233
|
-
return {
|
|
4326
|
+
return attachWorkflowMission({
|
|
4234
4327
|
content: [{ type: "text", text: sections.join("\n\n") }],
|
|
4235
4328
|
isError: true,
|
|
4236
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 }),
|
|
4237
|
-
};
|
|
4330
|
+
});
|
|
4238
4331
|
}
|
|
4239
4332
|
}
|
|
4240
4333
|
const directParams = prepareWorkflowChildParams(requestParams);
|
|
@@ -4338,6 +4431,29 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4338
4431
|
}
|
|
4339
4432
|
return handleWatchdogToolAction(action, paramsWithResolvedCwd, ctx, deps.watchdog);
|
|
4340
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
|
+
}
|
|
4341
4457
|
if (action === "grant-spawn-budget") {
|
|
4342
4458
|
if (deps.allowMutatingManagementActions === false || !ctx.hasUI) {
|
|
4343
4459
|
return {
|
|
@@ -4410,6 +4526,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4410
4526
|
details: { mode: "management", results: [], spawnBudget: granted.snapshot },
|
|
4411
4527
|
};
|
|
4412
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
|
+
}
|
|
4413
4537
|
if (action === "doctor") {
|
|
4414
4538
|
let currentSessionFile: string | null = null;
|
|
4415
4539
|
let currentSessionId = deps.state.currentSessionId;
|
|
@@ -4566,6 +4690,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4566
4690
|
state: deps.state,
|
|
4567
4691
|
runId,
|
|
4568
4692
|
message,
|
|
4693
|
+
mode: paramsWithResolvedCwd.mode,
|
|
4569
4694
|
index: paramsWithResolvedCwd.index,
|
|
4570
4695
|
kill: deps.kill,
|
|
4571
4696
|
location,
|
|
@@ -4591,7 +4716,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4591
4716
|
const text = error instanceof Error ? error.message : String(error);
|
|
4592
4717
|
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
4593
4718
|
}
|
|
4594
|
-
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 }));
|
|
4595
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: [] } };
|
|
4596
4721
|
if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
4597
4722
|
if (resolved.location.asyncDir) {
|
|
@@ -4602,6 +4727,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4602
4727
|
state: deps.state,
|
|
4603
4728
|
runId: resolved.id,
|
|
4604
4729
|
message,
|
|
4730
|
+
mode: paramsWithResolvedCwd.mode,
|
|
4605
4731
|
index: paramsWithResolvedCwd.index,
|
|
4606
4732
|
kill: deps.kill,
|
|
4607
4733
|
location: resolved.location,
|
|
@@ -5205,8 +5331,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5205
5331
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
5206
5332
|
ctx: ExtensionContext,
|
|
5207
5333
|
): Promise<AgentToolResult<Details>> => {
|
|
5208
|
-
const
|
|
5209
|
-
|
|
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);
|
|
5210
5337
|
const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
5211
5338
|
const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
|
|
5212
5339
|
const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
|
|
@@ -5220,6 +5347,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5220
5347
|
}
|
|
5221
5348
|
};
|
|
5222
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
|
+
|
|
5223
5364
|
const executeDelegated = async (
|
|
5224
5365
|
id: string,
|
|
5225
5366
|
params: SubagentParamsLike,
|
|
@@ -5259,5 +5400,5 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5259
5400
|
return ownerExecutor.execute(id, params, signal, undefined, ctx);
|
|
5260
5401
|
};
|
|
5261
5402
|
|
|
5262
|
-
return { execute: executeWithSingleDispatchGuard, executeDelegated, executeScheduled };
|
|
5403
|
+
return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };
|
|
5263
5404
|
}
|