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.
Files changed (53) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +3 -5
  3. package/package.json +1 -1
  4. package/skills/pi-subagents/SKILL.md +2 -2
  5. package/skills/pi-subagents/references/constraints-and-recipes.md +35 -32
  6. package/skills/pi-subagents/references/execution-controls.md +27 -43
  7. package/skills/pi-subagents/references/management-authoring-rpc.md +20 -3
  8. package/skills/pi-subagents/references/prompting-and-roles.md +16 -49
  9. package/src/agents/agent-refinements.ts +624 -0
  10. package/src/agents/agents.ts +0 -2
  11. package/src/agents/proactive-skills.ts +1 -1
  12. package/src/api/delegation.ts +1 -2
  13. package/src/extension/control-notices.ts +2 -2
  14. package/src/extension/fanout-child.ts +46 -22
  15. package/src/extension/index.ts +29 -12
  16. package/src/extension/public-execution.ts +71 -0
  17. package/src/extension/rpc.ts +7 -8
  18. package/src/extension/schemas.ts +14 -14
  19. package/src/extension/tool-description.ts +14 -14
  20. package/src/missions/actions.ts +47 -11
  21. package/src/missions/goal-driver.ts +162 -0
  22. package/src/missions/lifecycle.ts +44 -12
  23. package/src/missions/store.ts +68 -3
  24. package/src/missions/types.ts +25 -3
  25. package/src/missions/workflow-state.ts +77 -0
  26. package/src/profiles/profiles.ts +1 -3
  27. package/src/runs/background/async-execution.ts +3 -0
  28. package/src/runs/background/async-job-tracker.ts +2 -17
  29. package/src/runs/background/control-channel.ts +50 -6
  30. package/src/runs/background/retained-children.ts +68 -0
  31. package/src/runs/background/scheduled-runs.ts +17 -13
  32. package/src/runs/background/steering.ts +7 -5
  33. package/src/runs/background/subagent-runner.ts +10 -4
  34. package/src/runs/foreground/async-steering-action.ts +36 -10
  35. package/src/runs/foreground/execution.ts +4 -0
  36. package/src/runs/foreground/subagent-executor.ts +197 -55
  37. package/src/runs/shared/acceptance.ts +154 -9
  38. package/src/runs/shared/subagent-prompt-runtime.ts +94 -25
  39. package/src/shared/types.ts +16 -2
  40. package/src/slash/delegation-adapters.ts +0 -13
  41. package/src/slash/prompt-template-bridge.ts +14 -12
  42. package/src/slash/prompt-workflows.ts +10 -4
  43. package/src/slash/slash-bridge.ts +8 -6
  44. package/src/slash/slash-commands.ts +24 -9
  45. package/src/slash/slash-live-state.ts +4 -2
  46. package/src/tui/fleet-status.ts +8 -4
  47. package/src/tui/fleet.ts +15 -5
  48. package/src/tui/render.ts +10 -29
  49. package/src/workflows/scripted-workflow.ts +91 -6
  50. package/agents/context-builder.md +0 -46
  51. package/agents/planner.md +0 -56
  52. package/prompts/parallel-context-build.md +0 -55
  53. 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 === "delivered" ? "delivered" : result.state === "failed" ? "failed" : result.state === "partial" ? "partial" : "pending";
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], followUp),
1465
- goal: followUp,
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 requestParams = omitExecutionModeActionAlias(params);
4044
- if (requestParams.workflowScript !== undefined) {
4045
- const invalidMode = requestParams.action !== undefined || requestParams.agent !== undefined || requestParams.step !== undefined || requestParams.tasks !== undefined || requestParams.chain !== undefined;
4046
- if (invalidMode) {
4047
- return { content: [{ type: "text", text: "workflowScript is its own execution mode; do not combine it with action, agent, step, tasks, or chain." }], isError: true, details: { mode: "workflow", results: [] } };
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(ctx.cwd, requestParams.cwd);
4056
- const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd: ctx.cwd, workflowCwd, background: requestParams.async !== false });
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: ctx.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: ctx.cwd, status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], startedAt, updatedAt: startedAt, ...(timeout !== undefined ? { timeoutMs: timeout, deadlineAt: startedAt + timeout } : {}), workflow: status.workflow };
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 = prepareWorkflowChildParams({ ...workflowChildDefaults, ...childParams, workflowParentRunId: workflowRunId, workflowKey: key } as SubagentParamsLike);
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: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
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: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
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 = prepareWorkflowChildParams({ ...workflowChildDefaults, ...childParams, workflowParentRunId: _id, workflowKey: key, suppressRoutineResultIntercom: chatProgress.mode === "live-card" } as SubagentParamsLike);
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 requestParams = omitExecutionModeActionAlias(params);
5208
- if (requestParams.action) return execute(id, requestParams, signal, onUpdate, ctx);
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
  }