pi-subagents 0.59.0 → 0.61.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 +69 -0
- package/docs/agents.md +2 -2
- package/docs/configuration.md +9 -5
- package/docs/extension-api.md +14 -7
- package/docs/models.md +1 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +15 -3
- package/docs/workflows.md +15 -14
- package/install.mjs +2 -1
- package/package.json +1 -1
- package/skills/council-mode/SKILL.md +48 -243
- package/skills/council-mode/references/pass-contracts.md +150 -0
- package/skills/pi-subagents/SKILL.md +89 -37
- package/skills/pi-subagents/references/constraints-and-recipes.md +30 -234
- package/skills/pi-subagents/references/execution-controls.md +79 -7
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +35 -28
- package/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/src/agents/agent-management.ts +256 -88
- package/src/agents/agents.ts +527 -221
- package/src/api/background-work.ts +7 -2
- package/src/api/external-runs.ts +67 -4
- package/src/api/preflight.ts +13 -8
- package/src/api/shared-types.ts +3 -0
- package/src/extension/index.ts +7 -4
- package/src/extension/public-execution.ts +48 -4
- package/src/extension/rpc.ts +62 -4
- package/src/extension/schemas.ts +12 -7
- package/src/extension/tool-description.ts +16 -16
- package/src/runs/background/async-execution.ts +53 -37
- package/src/runs/background/async-job-tracker.ts +65 -3
- package/src/runs/background/async-resume.ts +3 -1
- package/src/runs/background/async-status.ts +104 -11
- package/src/runs/background/auto-drain.ts +1 -1
- package/src/runs/background/control-channel.ts +3 -2
- package/src/runs/background/fleet-view.ts +1 -1
- package/src/runs/background/result-watcher.ts +1 -1
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/run-status.ts +15 -4
- package/src/runs/background/subagent-runner.ts +6 -3
- package/src/runs/background/subagent-wait.ts +30 -23
- package/src/runs/background/wait-completions.ts +4 -1
- package/src/runs/background/wait-tool.ts +24 -18
- package/src/runs/foreground/execution.ts +72 -4
- package/src/runs/foreground/subagent-executor.ts +276 -59
- package/src/runs/shared/acceptance.ts +43 -18
- package/src/runs/shared/async-status-projection.ts +138 -4
- package/src/runs/shared/background-process-options.ts +9 -0
- package/src/runs/shared/host-step-status.ts +1 -0
- package/src/runs/shared/mcp-direct-tool-grant.ts +2 -5
- package/src/runs/shared/model-fallback.ts +61 -17
- package/src/runs/shared/mutation-evidence.ts +52 -3
- package/src/runs/shared/permissions.ts +1 -1
- package/src/runs/shared/pi-args.ts +47 -1
- package/src/runs/shared/single-output.ts +45 -18
- package/src/runs/shared/subagent-prompt-runtime.ts +20 -2
- package/src/runs/shared/tool-timeout.ts +1 -1
- package/src/runs/shared/workflow-graph.ts +16 -0
- package/src/shared/types.ts +74 -4
- package/src/shared/workflow-child-permit.ts +91 -0
- package/src/slash/prompt-template-bridge.ts +37 -1
- package/src/slash/slash-commands.ts +18 -26
- package/src/tui/fleet-status.ts +11 -3
- package/src/tui/render-helpers.ts +31 -0
- package/src/tui/render.ts +673 -145
- package/src/watchdog/change-signature.ts +40 -1
- package/src/workflows/host-command.ts +6 -1
- package/src/workflows/scripted-workflow.ts +206 -6
- package/src/workflows/workflow-child-summary.ts +1 -1
- package/src/workflows/workflow-receipt.ts +41 -4
- package/src/workflows/workflow-resources.ts +150 -0
|
@@ -23,11 +23,11 @@ import { handleManagementAction } from "../../agents/agent-management.ts";
|
|
|
23
23
|
import { handleRefinementAction } from "../../agents/agent-refinements.ts";
|
|
24
24
|
import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
25
25
|
import { readSubagentGuide } from "../../extension/subagent-guide.ts";
|
|
26
|
-
import { normalizePublicSubagentExecution } from "../../extension/public-execution.ts";
|
|
26
|
+
import { normalizePublicSubagentExecution, validateWorkflowCapacityOverrides } from "../../extension/public-execution.ts";
|
|
27
27
|
import { runSync } from "./execution.ts";
|
|
28
28
|
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
|
|
29
29
|
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
30
|
-
import { buildModelCandidates,
|
|
30
|
+
import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, type ModelOrigin, type ParentModel } from "../shared/model-fallback.ts";
|
|
31
31
|
import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
|
|
32
32
|
import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
|
|
33
33
|
import { recordRun } from "../shared/run-history.ts";
|
|
@@ -112,7 +112,7 @@ import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
|
|
|
112
112
|
import { resolveAuthorityDecision } from "../../policy/authority.ts";
|
|
113
113
|
import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
|
|
114
114
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
115
|
-
import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
|
|
115
|
+
import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowLanePlan, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowScriptTraceEntry, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
|
|
116
116
|
import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath, type WorkflowHostCommandParams, type WorkflowHostCommandResult } from "../../workflows/host-command.ts";
|
|
117
117
|
import { buildWorkflowReceipt, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
|
|
118
118
|
import { upsertHostStep, validHostStepNodes } from "../shared/host-step-status.ts";
|
|
@@ -120,7 +120,17 @@ import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../shared/
|
|
|
120
120
|
import { parseWorkflowChildSummary, workflowChildSummary } from "../../workflows/workflow-child-summary.ts";
|
|
121
121
|
import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
|
|
122
122
|
import { annotateWorkflowPreflightTrace, formatWorkflowPreflight, formatWorkflowPreflightWarnings, normalizeWorkflowPreflight, workflowPreflightWarnings } from "../../workflows/workflow-preflight.ts";
|
|
123
|
-
import {
|
|
123
|
+
import {
|
|
124
|
+
authorizeWorkflowResourceHost,
|
|
125
|
+
claimWorkflowChildPermit,
|
|
126
|
+
consumeWorkflowResourcePermit,
|
|
127
|
+
validateWorkflowChildPermitRoot,
|
|
128
|
+
type WorkflowChildPermit,
|
|
129
|
+
type WorkflowChildPermitContext,
|
|
130
|
+
type WorkflowResourceAuthority,
|
|
131
|
+
type WorkflowResourcePermit,
|
|
132
|
+
} from "../../shared/workflow-child-permit.ts";
|
|
133
|
+
import { resolveWorkflowResource } from "../../workflows/workflow-resources.ts";
|
|
124
134
|
import {
|
|
125
135
|
cleanupWorktrees,
|
|
126
136
|
createWorktrees,
|
|
@@ -160,6 +170,10 @@ import {
|
|
|
160
170
|
type ToolBudgetConfig,
|
|
161
171
|
type Usage,
|
|
162
172
|
type UsageBudgetConfig,
|
|
173
|
+
type WorkflowResourceProvenanceV1,
|
|
174
|
+
type WorkflowGraphNode,
|
|
175
|
+
type WorkflowGraphSnapshot,
|
|
176
|
+
type WorkflowNodeStatus,
|
|
163
177
|
type WorkflowTerminalOutcome,
|
|
164
178
|
type SubagentRunMode,
|
|
165
179
|
type SubagentState,
|
|
@@ -295,6 +309,7 @@ export interface SubagentParamsLike {
|
|
|
295
309
|
type?: string;
|
|
296
310
|
agent?: string;
|
|
297
311
|
task?: string;
|
|
312
|
+
capabilities?: boolean;
|
|
298
313
|
extensionBindings?: ExtensionBindings;
|
|
299
314
|
/** Retained async child run id. Valid only on workflow runs.run items. */
|
|
300
315
|
resume?: string;
|
|
@@ -305,6 +320,8 @@ export interface SubagentParamsLike {
|
|
|
305
320
|
planId?: string;
|
|
306
321
|
workflowScript?: string;
|
|
307
322
|
workflowScriptPath?: string;
|
|
323
|
+
globalConcurrencyLimit?: number;
|
|
324
|
+
maxSubagentSpawnsPerRun?: number;
|
|
308
325
|
preflight?: import("../../shared/types.ts").WorkflowPreflightV1;
|
|
309
326
|
chatProgress?: "auto" | "off" | "live-card";
|
|
310
327
|
isolation?: "none" | "worktree";
|
|
@@ -353,8 +370,13 @@ export interface SubagentParamsLike {
|
|
|
353
370
|
artifacts?: boolean;
|
|
354
371
|
includeProgress?: boolean;
|
|
355
372
|
model?: string;
|
|
373
|
+
/** Internal recovery provenance for a resolved model override. */
|
|
374
|
+
modelOrigin?: ModelOrigin;
|
|
356
375
|
fast?: boolean;
|
|
357
376
|
thinking?: string | false;
|
|
377
|
+
/** Public named workflow resource. Resolved before entering the workflow sandbox. */
|
|
378
|
+
workflow?: string;
|
|
379
|
+
args?: Record<string, unknown>;
|
|
358
380
|
scope?: string;
|
|
359
381
|
target?: string;
|
|
360
382
|
focus?: boolean;
|
|
@@ -414,9 +436,9 @@ interface ExecutorDeps {
|
|
|
414
436
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
415
437
|
}
|
|
416
438
|
|
|
417
|
-
type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => string | undefined;
|
|
418
|
-
type PrepareForkSessionForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => Promise<void>;
|
|
419
|
-
type ForkThinkingOverrideForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => AgentConfig["thinking"] | undefined;
|
|
439
|
+
type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => string | undefined;
|
|
440
|
+
type PrepareForkSessionForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => Promise<void>;
|
|
441
|
+
type ForkThinkingOverrideForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => AgentConfig["thinking"] | undefined;
|
|
420
442
|
|
|
421
443
|
interface ExecutionContextData {
|
|
422
444
|
params: SubagentParamsLike;
|
|
@@ -443,6 +465,8 @@ interface ExecutionContextData {
|
|
|
443
465
|
effectiveAsync: boolean;
|
|
444
466
|
asyncRunId: string;
|
|
445
467
|
controlConfig: ResolvedControlConfig;
|
|
468
|
+
/** Structured delegation consumers do not need duration-only heartbeat snapshots. */
|
|
469
|
+
suppressUnchangedDelegationUpdates?: boolean;
|
|
446
470
|
intercomBridge: IntercomBridgeState;
|
|
447
471
|
nestedRoute?: NestedRouteInfo;
|
|
448
472
|
timeoutMs?: number;
|
|
@@ -861,7 +885,7 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
861
885
|
if (matches.length === 0) return undefined;
|
|
862
886
|
if (matches.length > 1) throw new Error(`Ambiguous foreground run id prefix '${requested}' matched: ${matches.map((run) => run.runId).join(", ")}. Provide a longer id.`);
|
|
863
887
|
const run = matches[0]!;
|
|
864
|
-
if (run.children.some((child) => child.status === "detached")) throw new Error(`Foreground run '${run.runId}' is detached for intercom coordination and cannot be revived safely while any child may still be live. Reply to the supervisor request first, then wait with
|
|
888
|
+
if (run.children.some((child) => child.status === "detached")) throw new Error(`Foreground run '${run.runId}' is detached for intercom coordination and cannot be revived safely while any child may still be live. Reply to the supervisor request first, then wait with bg_wait({ id: "${run.runId}" }); use status to recover the result and do not launch a replacement while it remains detached.`);
|
|
865
889
|
if (run.children.length > 1 && params.index === undefined) throw new Error(`Foreground run '${run.runId}' has ${run.children.length} children. Provide index to choose one.`);
|
|
866
890
|
const index = params.index ?? 0;
|
|
867
891
|
if (!Number.isInteger(index)) throw new Error(`Foreground run '${run.runId}' index must be an integer.`);
|
|
@@ -2020,6 +2044,7 @@ async function resumeAsyncRun(input: {
|
|
|
2020
2044
|
modelOverride: recoveryDescriptor?.model ?? target.model,
|
|
2021
2045
|
fast: recoveryDescriptor?.fast,
|
|
2022
2046
|
modelOverrideFromParent: recoveryDescriptor?.modelOverrideFromParent,
|
|
2047
|
+
modelOrigin: recoveryDescriptor?.modelOrigin ?? (recoveryDescriptor?.modelOverrideFromParent ? "inherited" : undefined),
|
|
2023
2048
|
thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
|
|
2024
2049
|
thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
|
|
2025
2050
|
extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
|
|
@@ -2993,7 +3018,15 @@ async function preflightForkSessionsForStaticTasks(
|
|
|
2993
3018
|
): Promise<void> {
|
|
2994
3019
|
if (!contextPolicy.usesFork) return;
|
|
2995
3020
|
if (params.agent) {
|
|
2996
|
-
if (shouldForkAgent(contextPolicy, params.agent))
|
|
3021
|
+
if (shouldForkAgent(contextPolicy, params.agent)) {
|
|
3022
|
+
await prepareSessionForTask(
|
|
3023
|
+
params.agent,
|
|
3024
|
+
0,
|
|
3025
|
+
params.model,
|
|
3026
|
+
params.modelOrigin === "inherited",
|
|
3027
|
+
params.modelOrigin,
|
|
3028
|
+
);
|
|
3029
|
+
}
|
|
2997
3030
|
return;
|
|
2998
3031
|
}
|
|
2999
3032
|
if (params.tasks) {
|
|
@@ -3171,10 +3204,19 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3171
3204
|
return buildRequestedModeError(params, `Agent '${a.name}' uses runner.type='${a.runner.type}' and does not support fast mode.`);
|
|
3172
3205
|
}
|
|
3173
3206
|
const modelScopes = resolveModelScopesForAgent(data.modelScope, a.name, parentModel);
|
|
3207
|
+
const modelOrigin = resolveModelOrigin({
|
|
3208
|
+
storedOrigin: params.modelOrigin as ModelOrigin | undefined,
|
|
3209
|
+
explicitModel: params.model as string | undefined,
|
|
3210
|
+
agentModel: a.model,
|
|
3211
|
+
parentModel,
|
|
3212
|
+
});
|
|
3174
3213
|
const modelOverride = a.runner?.type === "external-cli" || a.runner?.type === "external-job"
|
|
3175
3214
|
? params.model ?? (externalRunnerWithoutExplicitModel ? undefined : a.model)
|
|
3176
|
-
: resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, a.modelProvider ?? currentProvider,
|
|
3177
|
-
|
|
3215
|
+
: resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, a.modelProvider ?? currentProvider, {
|
|
3216
|
+
...(modelScopes.length === 0 ? {} : { scope: modelScopes }),
|
|
3217
|
+
source: modelOrigin === "explicit" ? "explicit" : "inherited",
|
|
3218
|
+
});
|
|
3219
|
+
const modelOverrideFromParent = modelOrigin === "inherited";
|
|
3178
3220
|
const asyncResult = executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
3179
3221
|
agent: params.agent!,
|
|
3180
3222
|
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
@@ -3191,7 +3233,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3191
3233
|
shareEnabled,
|
|
3192
3234
|
activeAsyncCapacity: data.activeAsyncCapacity,
|
|
3193
3235
|
sessionRoot,
|
|
3194
|
-
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
|
|
3236
|
+
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
|
|
3195
3237
|
context: contextPolicy.contextForAgent(params.agent!),
|
|
3196
3238
|
skills,
|
|
3197
3239
|
output: effectiveOutput,
|
|
@@ -3202,7 +3244,8 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3202
3244
|
modelOverride,
|
|
3203
3245
|
fast: params.fast,
|
|
3204
3246
|
modelOverrideFromParent,
|
|
3205
|
-
|
|
3247
|
+
modelOrigin,
|
|
3248
|
+
thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
|
|
3206
3249
|
thinkingCeiling: a.maxThinking,
|
|
3207
3250
|
maxSubagentDepth,
|
|
3208
3251
|
waitToolEnabled: deps.waitToolEnabled,
|
|
@@ -3307,8 +3350,11 @@ function workflowHostCommandRunner(input: {
|
|
|
3307
3350
|
workflowRunId: string;
|
|
3308
3351
|
claimedOutputPaths: Map<string, string>;
|
|
3309
3352
|
producedOutputPaths: Set<string>;
|
|
3353
|
+
authorize?: (key: string, params: WorkflowHostCommandParams) => string | undefined;
|
|
3310
3354
|
}) {
|
|
3311
3355
|
return async (key: string, params: WorkflowHostCommandParams, signal: AbortSignal): Promise<WorkflowHostCommandResult> => {
|
|
3356
|
+
const authorizationError = input.authorize?.(key, params);
|
|
3357
|
+
if (authorizationError) throw new Error(authorizationError);
|
|
3312
3358
|
const defaultOutputPath = path.join(input.artifactsDir, "outputs", sanitizeRunPathSegment(input.workflowRunId), "host", `${sanitizeRunPathSegment(key)}.log`);
|
|
3313
3359
|
const outputPath = params.output ? path.resolve(input.workflowCwd, params.output) : defaultOutputPath;
|
|
3314
3360
|
const claimPath = resolveWorkflowHostOutputClaimPath(outputPath);
|
|
@@ -3548,6 +3594,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3548
3594
|
onUpdate,
|
|
3549
3595
|
controlConfig,
|
|
3550
3596
|
contextPolicy,
|
|
3597
|
+
suppressUnchangedDelegationUpdates,
|
|
3551
3598
|
} = data;
|
|
3552
3599
|
let lane: import("../../shared/types.ts").WorkflowLaneMetadata | undefined;
|
|
3553
3600
|
try {
|
|
@@ -3577,15 +3624,24 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3577
3624
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3578
3625
|
const modelScopes = resolveModelScopesForAgent(data.modelScope, agentConfig.name, parentModel);
|
|
3579
3626
|
let task = typeof params.task === "string" ? params.task : "";
|
|
3627
|
+
const modelOrigin = resolveModelOrigin({
|
|
3628
|
+
storedOrigin: params.modelOrigin as ModelOrigin | undefined,
|
|
3629
|
+
explicitModel: params.model as string | undefined,
|
|
3630
|
+
agentModel: agentConfig.model,
|
|
3631
|
+
parentModel,
|
|
3632
|
+
});
|
|
3580
3633
|
let modelOverride: string | undefined = resolveEffectiveSubagentModel(
|
|
3581
3634
|
params.model as string | undefined,
|
|
3582
3635
|
agentConfig.model,
|
|
3583
3636
|
parentModel,
|
|
3584
3637
|
availableModels,
|
|
3585
3638
|
agentConfig.modelProvider ?? currentProvider,
|
|
3586
|
-
|
|
3639
|
+
{
|
|
3640
|
+
...(modelScopes.length === 0 ? {} : { scope: modelScopes }),
|
|
3641
|
+
source: modelOrigin === "explicit" ? "explicit" : "inherited",
|
|
3642
|
+
},
|
|
3587
3643
|
);
|
|
3588
|
-
const modelOverrideFromParent =
|
|
3644
|
+
const modelOverrideFromParent = modelOrigin === "inherited";
|
|
3589
3645
|
let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
|
|
3590
3646
|
let readsOverride: string[] | false | undefined = params.reads;
|
|
3591
3647
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
@@ -3656,7 +3712,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3656
3712
|
let detachForeground: ((reason?: string) => boolean) | undefined;
|
|
3657
3713
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3658
3714
|
if (foregroundControl) {
|
|
3659
|
-
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent));
|
|
3715
|
+
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin));
|
|
3660
3716
|
beginForegroundChild(foregroundControl, omitUndefinedProperties({
|
|
3661
3717
|
index: 0,
|
|
3662
3718
|
agent: params.agent!,
|
|
@@ -3708,7 +3764,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3708
3764
|
intercomEvents: deps.pi.events,
|
|
3709
3765
|
runId,
|
|
3710
3766
|
sessionDir: sessionDirForIndex(0),
|
|
3711
|
-
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
|
|
3767
|
+
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
|
|
3712
3768
|
share: shareEnabled,
|
|
3713
3769
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3714
3770
|
artifactConfig,
|
|
@@ -3720,6 +3776,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3720
3776
|
waitToolEnabled: deps.waitToolEnabled,
|
|
3721
3777
|
waitToolDefaultTimeoutMs: deps.waitToolDefaultTimeoutMs,
|
|
3722
3778
|
onUpdate: forwardSingleUpdate,
|
|
3779
|
+
suppressUnchangedDelegationUpdates,
|
|
3723
3780
|
controlConfig,
|
|
3724
3781
|
onControlEvent,
|
|
3725
3782
|
intercomSessionName: childIntercomTarget,
|
|
@@ -3729,7 +3786,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3729
3786
|
modelOverride,
|
|
3730
3787
|
fast: params.fast,
|
|
3731
3788
|
modelOverrideFromParent,
|
|
3732
|
-
|
|
3789
|
+
modelOrigin,
|
|
3790
|
+
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
|
|
3733
3791
|
thinkingCeiling: agentConfig.maxThinking,
|
|
3734
3792
|
extensionBindings: params.extensionBindings,
|
|
3735
3793
|
availableModels,
|
|
@@ -3876,11 +3934,11 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3876
3934
|
const worktreeSuffix = worktreeHandoff?.suffix ? `\n\n${worktreeHandoff.suffix}` : "";
|
|
3877
3935
|
if (r.detached) {
|
|
3878
3936
|
const statusRecovery = `subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.`;
|
|
3879
|
-
const blockingRecovery = `
|
|
3937
|
+
const blockingRecovery = `bg_wait({ id: "${runId}" }). Use ${statusRecovery}`;
|
|
3880
3938
|
const message = r.detachedReason === "intercom coordination"
|
|
3881
3939
|
? `Detached for intercom coordination: ${params.agent}. Reply to the supervisor request first, then wait with ${blockingRecovery}`
|
|
3882
3940
|
: r.detachedReason === "user request"
|
|
3883
|
-
? `Detached at user request: ${params.agent}. The child continues independently. Register a completion wake-up with
|
|
3941
|
+
? `Detached at user request: ${params.agent}. The child continues independently. Register a completion wake-up with bg_wait({ id: "${runId}", nonBlocking: true }), or use ${statusRecovery}`
|
|
3884
3942
|
: `Detached before task completion: ${params.agent}. Wait with ${blockingRecovery}`;
|
|
3885
3943
|
return {
|
|
3886
3944
|
content: [{ type: "text", text: `${message}${worktreeSuffix}` }],
|
|
@@ -4018,7 +4076,8 @@ function workflowChildResult(
|
|
|
4018
4076
|
: result.details.usageBudget?.exhausted || result.details.results.some((child) => child.turnBudgetExceeded || child.toolBudgetBlocked)
|
|
4019
4077
|
? { state: "partial" as const, reason: "budget_exhausted" as const }
|
|
4020
4078
|
: undefined);
|
|
4021
|
-
const
|
|
4079
|
+
const acceptanceRecovery = result.details.results.find((child) => child.acceptance?.recovery)?.acceptance?.recovery;
|
|
4080
|
+
const ok = result.isError !== true && !detached && !interrupted && !stopped && acceptanceRecovery === undefined;
|
|
4022
4081
|
const artifactPaths = new Set<string>();
|
|
4023
4082
|
if (result.details.asyncDir) artifactPaths.add(result.details.asyncDir);
|
|
4024
4083
|
if (result.details.parallelHandoff?.path) artifactPaths.add(result.details.parallelHandoff.path);
|
|
@@ -4076,6 +4135,7 @@ function workflowChildResult(
|
|
|
4076
4135
|
...(requestedContext ? { requestedContext } : {}),
|
|
4077
4136
|
...(resolvedContext ? { resolvedContext } : {}),
|
|
4078
4137
|
...(outputReference ? { outputReference } : {}),
|
|
4138
|
+
...(acceptanceRecovery ? { recovery: acceptanceRecovery } : {}),
|
|
4079
4139
|
...(outputPathMapping ? { outputPathMapping } : {}),
|
|
4080
4140
|
...(externalAdapter ? { externalAdapter } : {}),
|
|
4081
4141
|
resumability,
|
|
@@ -4085,13 +4145,14 @@ function workflowChildResult(
|
|
|
4085
4145
|
};
|
|
4086
4146
|
}
|
|
4087
4147
|
|
|
4088
|
-
function workflowChildAccountingFields(child: WorkflowScriptChildResult): { usage?: Usage; sessionFile?: string } {
|
|
4148
|
+
function workflowChildAccountingFields(child: WorkflowScriptChildResult): { usage?: Usage; sessionFile?: string; recovery?: import("../../shared/types.ts").AcceptanceRecoveryMetadata } {
|
|
4089
4149
|
if (!child.results?.length) return {};
|
|
4090
4150
|
const usage = sumResultsUsage(child.results);
|
|
4091
4151
|
const sessionFile = child.results.find((result) => result.sessionFile)?.sessionFile;
|
|
4092
4152
|
return {
|
|
4093
4153
|
...(usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0 ? { usage } : {}),
|
|
4094
4154
|
...(sessionFile ? { sessionFile } : {}),
|
|
4155
|
+
...(child.recovery ? { recovery: child.recovery } : {}),
|
|
4095
4156
|
};
|
|
4096
4157
|
}
|
|
4097
4158
|
|
|
@@ -4219,8 +4280,9 @@ function terminalWorkflowReceipt(
|
|
|
4219
4280
|
workflowChildren?: WorkflowReceipt["workflowChildren"],
|
|
4220
4281
|
terminalOutcome?: WorkflowTerminalOutcome,
|
|
4221
4282
|
hostSteps?: WorkflowReceipt["hostSteps"],
|
|
4283
|
+
resource?: WorkflowReceipt["resource"],
|
|
4222
4284
|
): WorkflowReceipt {
|
|
4223
|
-
return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps });
|
|
4285
|
+
return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps, resource });
|
|
4224
4286
|
}
|
|
4225
4287
|
|
|
4226
4288
|
function workflowFailureTerminalOutcome(error: unknown, _children: WorkflowScriptChildResult[], usageBudget: ReturnType<typeof usageBudgetState>): WorkflowTerminalOutcome | undefined {
|
|
@@ -4312,6 +4374,10 @@ export function prepareWorkflowLaunchParams(
|
|
|
4312
4374
|
workflowKey: string,
|
|
4313
4375
|
options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean; awaitDetachedChild?: boolean; runFanoutBudget?: RunFanoutBudgetDescriptor; parentDeadlineAt?: number; externalAsyncRequired?: boolean; capabilityCeiling?: ResolvedSubagentCapabilityCeiling; outputClaimPath?: string } = {},
|
|
4314
4376
|
): SubagentParamsLike {
|
|
4377
|
+
const { globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowDefaultsWithoutCapacity } = workflowDefaults;
|
|
4378
|
+
const { globalConcurrencyLimit: _childGlobalConcurrencyLimit, maxSubagentSpawnsPerRun: _childMaxSubagentSpawnsPerRun, ...childParamsWithoutCapacity } = childParams;
|
|
4379
|
+
workflowDefaults = workflowDefaultsWithoutCapacity;
|
|
4380
|
+
childParams = childParamsWithoutCapacity;
|
|
4315
4381
|
const capabilityCeiling = intersectSubagentCapabilityCeilings(workflowDefaults.capabilityCeiling, options.capabilityCeiling);
|
|
4316
4382
|
const lane = normalizeWorkflowLaneMetadata(Object.hasOwn(childParams, "lane") ? childParams.lane : workflowDefaults.lane, `workflow child '${workflowKey}'.lane`);
|
|
4317
4383
|
assertWorkflowLaneKey(lane, workflowKey, `workflow child '${workflowKey}'.lane`);
|
|
@@ -4411,6 +4477,90 @@ function formatWorkflowValue(value: unknown): string {
|
|
|
4411
4477
|
}
|
|
4412
4478
|
}
|
|
4413
4479
|
|
|
4480
|
+
function buildWorkflowLaneGraph(runId: string, lanes: WorkflowLanePlan[], existing?: WorkflowGraphSnapshot): WorkflowGraphSnapshot {
|
|
4481
|
+
const plannedIds = new Set(lanes.flatMap((lane) => lane.stages.map((stage) => stage.generatedKey)));
|
|
4482
|
+
const previousById = new Map((existing?.nodes ?? []).map((node) => [node.id, node]));
|
|
4483
|
+
const nodes: WorkflowGraphNode[] = [];
|
|
4484
|
+
const phases: WorkflowGraphSnapshot["phases"] = [];
|
|
4485
|
+
let flatIndex = 0;
|
|
4486
|
+
for (const lane of lanes) {
|
|
4487
|
+
const nodeIds: string[] = [];
|
|
4488
|
+
for (const [stageIndex, stage] of lane.stages.entries()) {
|
|
4489
|
+
const previous = previousById.get(stage.generatedKey);
|
|
4490
|
+
const outputName = stage.outputName || previous?.outputName;
|
|
4491
|
+
const structured = stage.structured ?? previous?.structured;
|
|
4492
|
+
const node: WorkflowGraphNode = {
|
|
4493
|
+
id: stage.generatedKey,
|
|
4494
|
+
kind: "step",
|
|
4495
|
+
agent: stage.agent ?? previous?.agent,
|
|
4496
|
+
phase: stage.phase ?? previous?.phase,
|
|
4497
|
+
label: stage.label ?? stage.key,
|
|
4498
|
+
status: previous?.status ?? (stageIndex === 0 ? "running" : "pending"),
|
|
4499
|
+
flatIndex,
|
|
4500
|
+
stepIndex: flatIndex,
|
|
4501
|
+
...(outputName ? { outputName } : {}),
|
|
4502
|
+
...(structured !== undefined ? { structured } : {}),
|
|
4503
|
+
...(previous?.acceptanceStatus ? { acceptanceStatus: previous.acceptanceStatus } : {}),
|
|
4504
|
+
...(previous?.error ? { error: previous.error } : {}),
|
|
4505
|
+
};
|
|
4506
|
+
nodes.push(node);
|
|
4507
|
+
nodeIds.push(node.id);
|
|
4508
|
+
flatIndex++;
|
|
4509
|
+
}
|
|
4510
|
+
if (nodeIds.length > 0) phases.push({ title: lane.key, nodeIds });
|
|
4511
|
+
}
|
|
4512
|
+
for (const node of existing?.nodes ?? []) {
|
|
4513
|
+
if (!plannedIds.has(node.id)) nodes.push(node);
|
|
4514
|
+
}
|
|
4515
|
+
for (const phase of existing?.phases ?? []) {
|
|
4516
|
+
const retainedNodeIds = phase.nodeIds.filter((nodeId) => !plannedIds.has(nodeId));
|
|
4517
|
+
if (retainedNodeIds.length === 0) continue;
|
|
4518
|
+
const currentPhase = phases.find((candidate) => candidate.title === phase.title);
|
|
4519
|
+
if (currentPhase) currentPhase.nodeIds.push(...retainedNodeIds);
|
|
4520
|
+
else phases.push({ title: phase.title, nodeIds: retainedNodeIds });
|
|
4521
|
+
}
|
|
4522
|
+
const currentNodeId = nodes.find((node) => node.status === "running")?.id ?? existing?.currentNodeId;
|
|
4523
|
+
return { runId, mode: "workflow", phases, nodes, ...(currentNodeId ? { currentNodeId } : {}) };
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
function workflowLaneTraceStatus(state: WorkflowScriptTraceEntry["state"]): WorkflowNodeStatus | undefined {
|
|
4527
|
+
switch (state) {
|
|
4528
|
+
case "started":
|
|
4529
|
+
return "running";
|
|
4530
|
+
case "reused":
|
|
4531
|
+
return undefined;
|
|
4532
|
+
case "completed":
|
|
4533
|
+
return "completed";
|
|
4534
|
+
case "failed":
|
|
4535
|
+
return "failed";
|
|
4536
|
+
case "detached":
|
|
4537
|
+
return "detached";
|
|
4538
|
+
case "stopped":
|
|
4539
|
+
return "stopped";
|
|
4540
|
+
default:
|
|
4541
|
+
return undefined;
|
|
4542
|
+
}
|
|
4543
|
+
}
|
|
4544
|
+
|
|
4545
|
+
function applyWorkflowLaneTrace(graph: WorkflowGraphSnapshot, trace: WorkflowScriptTraceEntry[]): void {
|
|
4546
|
+
const nodesById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
4547
|
+
for (const entry of trace) {
|
|
4548
|
+
if (entry.operation !== "run") continue;
|
|
4549
|
+
const node = nodesById.get(entry.key);
|
|
4550
|
+
if (!node) continue;
|
|
4551
|
+
const status = workflowLaneTraceStatus(entry.state);
|
|
4552
|
+
if (status) node.status = status;
|
|
4553
|
+
if (entry.agent) node.agent = entry.agent;
|
|
4554
|
+
if (entry.phase && (entry.phase !== "auto-resume" || node.phase === undefined)) node.phase = entry.phase;
|
|
4555
|
+
if (entry.label) node.label = entry.label;
|
|
4556
|
+
if (entry.error) node.error = entry.error;
|
|
4557
|
+
else if (status === "completed" || status === "running") delete node.error;
|
|
4558
|
+
}
|
|
4559
|
+
const currentNodeId = graph.nodes.find((node) => node.status === "running")?.id;
|
|
4560
|
+
if (currentNodeId) graph.currentNodeId = currentNodeId;
|
|
4561
|
+
else delete graph.currentNodeId;
|
|
4562
|
+
}
|
|
4563
|
+
|
|
4414
4564
|
function workflowChatProgressUpdate(
|
|
4415
4565
|
runId: string,
|
|
4416
4566
|
chatProgress: WorkflowChatProgressProjection,
|
|
@@ -4498,6 +4648,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4498
4648
|
const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
|
|
4499
4649
|
const delegatedZeroToolBudgets = new WeakSet<object>();
|
|
4500
4650
|
const delegatedExecutions = new WeakSet<object>();
|
|
4651
|
+
const publicExecutions = new WeakSet<object>();
|
|
4652
|
+
const workflowResourcePermits = new WeakMap<object, WorkflowResourcePermit>();
|
|
4501
4653
|
const workflowPermitContexts = new WeakMap<object, { root: WorkflowChildPermit } | { child: WorkflowChildPermitContext }>();
|
|
4502
4654
|
const warnedArtifactPackageDirs = new Set<string>();
|
|
4503
4655
|
const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
|
|
@@ -4514,6 +4666,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4514
4666
|
const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
|
|
4515
4667
|
const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
|
|
4516
4668
|
const delegatedExecution = delegatedExecutions.has(params);
|
|
4669
|
+
const publicExecution = publicExecutions.has(params);
|
|
4670
|
+
const workflowResourcePermit = workflowResourcePermits.get(params);
|
|
4517
4671
|
const workflowPermitContext = workflowPermitContexts.get(params);
|
|
4518
4672
|
const delegatedWorkflowPermit = workflowPermitContext && "root" in workflowPermitContext ? workflowPermitContext.root : undefined;
|
|
4519
4673
|
const workflowChildPermitLaunch = workflowPermitContext && "child" in workflowPermitContext ? workflowPermitContext.child : undefined;
|
|
@@ -4524,6 +4678,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4524
4678
|
const normalizedGate = normalizeGateParams(params);
|
|
4525
4679
|
if (!normalizedGate.ok) return buildRequestedModeError(params, normalizedGate.error);
|
|
4526
4680
|
let requestParams = normalizedGate.params;
|
|
4681
|
+
const capacityOverrideError = validateWorkflowCapacityOverrides(requestParams);
|
|
4682
|
+
if (capacityOverrideError) return buildRequestedModeError(requestParams, capacityOverrideError);
|
|
4527
4683
|
let workflowPreflight: import("../../shared/types.ts").WorkflowPreflightV1 | undefined;
|
|
4528
4684
|
try {
|
|
4529
4685
|
if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined && requestParams.workflowScriptPath === undefined) {
|
|
@@ -4555,6 +4711,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4555
4711
|
}
|
|
4556
4712
|
const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
|
|
4557
4713
|
if (normalizedAction === "resume" && requestParams.extensionBindings !== undefined) return buildRequestedModeError(requestParams, "extensionBindings is not supported with action='resume'; resume uses the original retained child binding.");
|
|
4714
|
+
let workflowResource: { permit: WorkflowResourcePermit; provenance: WorkflowResourceProvenanceV1; authority: WorkflowResourceAuthority } | undefined;
|
|
4715
|
+
if (workflowResourcePermit) {
|
|
4716
|
+
if (typeof requestParams.workflowScript !== "string") return buildRequestedModeError(requestParams, "Resolved workflow resource is missing its workflow script.");
|
|
4717
|
+
const consumed = consumeWorkflowResourcePermit(workflowResourcePermit, requestParams.workflowScript);
|
|
4718
|
+
if (typeof consumed === "string") return buildRequestedModeError(requestParams, consumed);
|
|
4719
|
+
workflowResource = { permit: workflowResourcePermit, ...consumed };
|
|
4720
|
+
}
|
|
4558
4721
|
if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
|
|
4559
4722
|
if (delegatedWorkflowPermit) {
|
|
4560
4723
|
const permitError = validateWorkflowChildPermitRoot(delegatedWorkflowPermit, _id);
|
|
@@ -4634,7 +4797,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4634
4797
|
try {
|
|
4635
4798
|
workflowFanoutBudget = requestParams.runFanoutBudget
|
|
4636
4799
|
?? decodeRunFanoutBudgetDescriptor(process.env[RUN_FANOUT_BUDGET_ENV])
|
|
4637
|
-
?? createRunFanoutBudget(_id, resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
4800
|
+
?? createRunFanoutBudget(_id, requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
4638
4801
|
} catch (error) {
|
|
4639
4802
|
workflowCapacity?.rollback();
|
|
4640
4803
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
@@ -4712,7 +4875,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4712
4875
|
pid: process.pid,
|
|
4713
4876
|
steps: [],
|
|
4714
4877
|
...(workflowPreflight ? { preflight: workflowPreflight } : {}),
|
|
4715
|
-
workflow: { trace: [], emits: [], console: [] },
|
|
4878
|
+
workflow: { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) },
|
|
4716
4879
|
workflowChildren: workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "running", inventoryComplete: false }),
|
|
4717
4880
|
runFanoutBudget: getRunFanoutBudgetSnapshot(workflowFanoutBudget),
|
|
4718
4881
|
};
|
|
@@ -4800,6 +4963,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4800
4963
|
liveJob.toolCount = status.toolCount;
|
|
4801
4964
|
liveJob.currentStep = status.currentStep;
|
|
4802
4965
|
liveJob.preflight = status.preflight;
|
|
4966
|
+
liveJob.workflowGraph = status.workflowGraph;
|
|
4803
4967
|
if (status.steps) {
|
|
4804
4968
|
liveJob.steps = status.steps.map((step, index) => ({ ...step, index }));
|
|
4805
4969
|
liveJob.agents = status.steps.map((step) => step.agent);
|
|
@@ -4863,7 +5027,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4863
5027
|
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
4864
5028
|
const workflowResults: SingleResult[] = [];
|
|
4865
5029
|
const workflowChildRunIds = new Map<string, string>();
|
|
4866
|
-
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, preflight: _preflight, ...workflowChildDefaults } = workflowRequest;
|
|
5030
|
+
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, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = workflowRequest;
|
|
4867
5031
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
4868
5032
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
4869
5033
|
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, parentCwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
|
|
@@ -4872,7 +5036,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4872
5036
|
const childOutputClaimPaths = new Map<string, string>();
|
|
4873
5037
|
const producedChildOutputPaths = new Set<string>();
|
|
4874
5038
|
const workflowSteps = new Map<string, NonNullable<AsyncStatus["steps"]>[number]>();
|
|
4875
|
-
const runHostCommand = workflowHostCommandRunner({
|
|
5039
|
+
const runHostCommand = workflowHostCommandRunner({
|
|
5040
|
+
workflowCwd,
|
|
5041
|
+
artifactsDir: workflowArtifactsDir,
|
|
5042
|
+
workflowRunId,
|
|
5043
|
+
claimedOutputPaths,
|
|
5044
|
+
producedOutputPaths: producedChildOutputPaths,
|
|
5045
|
+
...(workflowResource ? { authorize: (key, params) => authorizeWorkflowResourceHost(workflowResource!.permit, key, params.command) } : {}),
|
|
5046
|
+
});
|
|
5047
|
+
const workflowHost = workflowResource
|
|
5048
|
+
? workflowResource.authority.host ? runHostCommand : undefined
|
|
5049
|
+
: publicExecution ? undefined : runHostCommand;
|
|
4876
5050
|
let projectedTraceLength = 0;
|
|
4877
5051
|
let projectedTraceTail: NonNullable<Details["workflow"]>["trace"][number] | undefined;
|
|
4878
5052
|
const updateTrace = (trace: NonNullable<Details["workflow"]>["trace"]) => {
|
|
@@ -4883,6 +5057,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4883
5057
|
trace: projectedTrace,
|
|
4884
5058
|
...(preflightWarnings.length ? { preflightWarnings } : {}),
|
|
4885
5059
|
};
|
|
5060
|
+
if (status.workflowGraph) applyWorkflowLaneTrace(status.workflowGraph, trace);
|
|
4886
5061
|
const rebuild = trace.length < projectedTraceLength
|
|
4887
5062
|
|| (projectedTraceLength > 0 && trace[projectedTraceLength - 1] !== projectedTraceTail);
|
|
4888
5063
|
if (rebuild) {
|
|
@@ -4977,7 +5152,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4977
5152
|
try {
|
|
4978
5153
|
const workflow = await runWorkflowScript({
|
|
4979
5154
|
script: workflowScript,
|
|
4980
|
-
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
5155
|
+
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
4981
5156
|
timeoutMs: timeout,
|
|
4982
5157
|
signal: controller.signal,
|
|
4983
5158
|
registerStopChild: (stop) => {
|
|
@@ -4986,7 +5161,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4986
5161
|
},
|
|
4987
5162
|
...(workflowState ? { state: workflowState } : {}),
|
|
4988
5163
|
onTrace: updateTrace,
|
|
4989
|
-
|
|
5164
|
+
onLanePlan: (lanes) => {
|
|
5165
|
+
status.workflowGraph = buildWorkflowLaneGraph(workflowRunId, lanes, status.workflowGraph);
|
|
5166
|
+
applyWorkflowLaneTrace(status.workflowGraph, status.workflow?.trace ?? []);
|
|
5167
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5168
|
+
},
|
|
5169
|
+
...(workflowHost ? { host: workflowHost } : {}),
|
|
4990
5170
|
onHostStep: (hostStep) => {
|
|
4991
5171
|
status = upsertHostStep({ status, hostStep, persist: (nextStatus) => {
|
|
4992
5172
|
status = nextStatus;
|
|
@@ -5111,8 +5291,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5111
5291
|
const resultSummary = appendWorkflowOutputWarning(summary, outputWarning);
|
|
5112
5292
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
5113
5293
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children, steps: status.steps });
|
|
5114
|
-
status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
|
|
5115
|
-
const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph));
|
|
5294
|
+
status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
|
|
5295
|
+
const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
|
|
5116
5296
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
5117
5297
|
try {
|
|
5118
5298
|
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
@@ -5121,6 +5301,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5121
5301
|
}
|
|
5122
5302
|
if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary: resultSummary, output: resultSummary, workflowChildren, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(status.steps?.find((step) => step.workflowKey === child.key)?.sessionName ? { sessionName: status.steps?.find((step) => step.workflowKey === child.key)?.sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
|
|
5123
5303
|
persist();
|
|
5304
|
+
deps.refreshResultDelivery?.();
|
|
5124
5305
|
persistClosed = true;
|
|
5125
5306
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}) });
|
|
5126
5307
|
} catch (error) {
|
|
@@ -5145,7 +5326,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5145
5326
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: true, trace: partial.trace, children: partial.children, steps: status.steps });
|
|
5146
5327
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
|
|
5147
5328
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
|
|
5148
|
-
status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
|
|
5329
|
+
status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
|
|
5149
5330
|
if (pauseForDetached) {
|
|
5150
5331
|
const promoted = promotePausedWorkflowIfSettled(status);
|
|
5151
5332
|
if (promoted) status = promoted;
|
|
@@ -5157,7 +5338,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5157
5338
|
const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
|
|
5158
5339
|
const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
|
|
5159
5340
|
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
5160
|
-
const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children, workflowChildren, terminalOutcome, validHostStepNodes(status.workflowGraph));
|
|
5341
|
+
const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children, workflowChildren, terminalOutcome, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
|
|
5161
5342
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
5162
5343
|
try {
|
|
5163
5344
|
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
@@ -5166,6 +5347,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5166
5347
|
}
|
|
5167
5348
|
if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: status.state === "complete", state: status.state, summary: resultSummary, error: status.state === "complete" ? undefined : status.error, stopped: status.stopped, activityState: status.activityState, workflowChildren, ...(terminalOutcome ? { terminalOutcome } : {}), results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(status.steps?.find((step) => step.workflowKey === child.key)?.sessionName ? { sessionName: status.steps?.find((step) => step.workflowKey === child.key)?.sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
|
|
5168
5349
|
persist();
|
|
5350
|
+
deps.refreshResultDelivery?.();
|
|
5169
5351
|
persistClosed = true;
|
|
5170
5352
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(terminalOutcome ? { terminalOutcome } : {}), ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
|
|
5171
5353
|
} finally {
|
|
@@ -5178,10 +5360,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5178
5360
|
});
|
|
5179
5361
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5180
5362
|
content: [{ type: "text", text: `${workflowPreflight ? `${formatWorkflowPreflight(workflowPreflight)}\n\n` : ""}${formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true)}` }],
|
|
5181
|
-
details: { mode: "workflow", runId: workflowRunId, toolCallId, asyncId: workflowRunId, asyncDir, results: [], ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren: status.workflowChildren, chatProgress, ...(deps.state.activeAsyncCapacity ? { activeAsyncCapacity: deps.state.activeAsyncCapacity } : {}) },
|
|
5363
|
+
details: { mode: "workflow", runId: workflowRunId, toolCallId, asyncId: workflowRunId, asyncDir, results: [], ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflow: status.workflow, workflowChildren: status.workflowChildren, chatProgress, ...(deps.state.activeAsyncCapacity ? { activeAsyncCapacity: deps.state.activeAsyncCapacity } : {}) },
|
|
5182
5364
|
}, workflowFanoutBudget));
|
|
5183
5365
|
}
|
|
5184
|
-
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, preflight: _preflight, ...workflowChildDefaults } = requestParams;
|
|
5366
|
+
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, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = requestParams;
|
|
5185
5367
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
5186
5368
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
5187
5369
|
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, _id));
|
|
@@ -5191,9 +5373,21 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5191
5373
|
const producedChildOutputPaths = new Set<string>();
|
|
5192
5374
|
const workflowResults: SingleResult[] = [];
|
|
5193
5375
|
const workflowChildRunIds = new Map<string, string>();
|
|
5194
|
-
const runHostCommand = workflowHostCommandRunner({
|
|
5376
|
+
const runHostCommand = workflowHostCommandRunner({
|
|
5377
|
+
workflowCwd,
|
|
5378
|
+
artifactsDir: workflowArtifactsDir,
|
|
5379
|
+
workflowRunId: _id,
|
|
5380
|
+
claimedOutputPaths,
|
|
5381
|
+
producedOutputPaths: producedChildOutputPaths,
|
|
5382
|
+
...(workflowResource ? { authorize: (key, params) => authorizeWorkflowResourceHost(workflowResource!.permit, key, params.command) } : {}),
|
|
5383
|
+
});
|
|
5384
|
+
const workflowHost = delegatedWorkflowPermit
|
|
5385
|
+
? undefined
|
|
5386
|
+
: workflowResource
|
|
5387
|
+
? workflowResource.authority.host ? runHostCommand : undefined
|
|
5388
|
+
: publicExecution ? undefined : runHostCommand;
|
|
5195
5389
|
const workflowHostSteps = new Map<string, HostStepNodeV1>();
|
|
5196
|
-
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
|
|
5390
|
+
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) };
|
|
5197
5391
|
let liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "running", inventoryComplete: false });
|
|
5198
5392
|
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
5199
5393
|
const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(resolveCurrentSessionId(ctx.sessionManager)));
|
|
@@ -5205,11 +5399,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5205
5399
|
const workflow = await runWorkflowScript({
|
|
5206
5400
|
script: requestParams.workflowScript,
|
|
5207
5401
|
...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, _id, key) } } : {}),
|
|
5208
|
-
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
5402
|
+
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5209
5403
|
timeoutMs: timeout,
|
|
5210
5404
|
signal,
|
|
5211
5405
|
...(workflowState ? { state: workflowState } : {}),
|
|
5212
|
-
...(
|
|
5406
|
+
...(workflowHost ? { host: workflowHost } : {}),
|
|
5213
5407
|
onHostStep: (hostStep) => {
|
|
5214
5408
|
workflowHostSteps.set(hostStep.id, { ...hostStep, ...(hostStep.freshness ? { freshness: { ...hostStep.freshness } } : {}) });
|
|
5215
5409
|
},
|
|
@@ -5306,7 +5500,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5306
5500
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
|
|
5307
5501
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
|
|
5308
5502
|
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children });
|
|
5309
|
-
const receipt = terminalWorkflowReceipt(_id, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()]);
|
|
5503
|
+
const receipt = terminalWorkflowReceipt(_id, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()], workflowResource?.provenance);
|
|
5310
5504
|
const traceLines = finalPreflightTrace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.durationMs !== undefined ? ` in ${entry.durationMs}ms` : ""}${entry.warning ? ` — ${entry.warning}` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
|
|
5311
5505
|
const sections = [
|
|
5312
5506
|
...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
|
|
@@ -5324,7 +5518,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5324
5518
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
5325
5519
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5326
5520
|
content: [{ type: "text", text: displayText }],
|
|
5327
|
-
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
|
|
5521
|
+
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
|
|
5328
5522
|
}, workflowFanoutBudget));
|
|
5329
5523
|
} catch (error) {
|
|
5330
5524
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
@@ -5347,11 +5541,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5347
5541
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
5348
5542
|
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "failed", inventoryComplete: true, trace: partial.trace, children: partial.children });
|
|
5349
5543
|
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
5350
|
-
const receipt = terminalWorkflowReceipt(_id, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()]);
|
|
5544
|
+
const receipt = terminalWorkflowReceipt(_id, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()], workflowResource?.provenance);
|
|
5351
5545
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5352
5546
|
content: [{ type: "text", text: displayText }],
|
|
5353
5547
|
isError: true,
|
|
5354
|
-
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
|
|
5548
|
+
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
|
|
5355
5549
|
}, workflowFanoutBudget));
|
|
5356
5550
|
}
|
|
5357
5551
|
}
|
|
@@ -6111,34 +6305,47 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6111
6305
|
let forkSessionFileForIndex: (idx?: number) => string | undefined = () => undefined;
|
|
6112
6306
|
let prepareForkSessionForIndex: (idx?: number) => Promise<void> = async () => {};
|
|
6113
6307
|
let forkThinkingOverrideForIndex: (idx?: number) => AgentConfig["thinking"] | undefined = () => undefined;
|
|
6114
|
-
let prepareForkThinking = (_agentName: string, _index: number, _modelOverride?: string, _modelOverrideFromParent?: boolean): void => {};
|
|
6308
|
+
let prepareForkThinking = (_agentName: string, _index: number, _modelOverride?: string, _modelOverrideFromParent?: boolean, _modelOrigin?: ModelOrigin): void => {};
|
|
6115
6309
|
const forkThinkingRequirements = new Map<number, boolean>();
|
|
6116
6310
|
const forkThinkingDowngrades = new Map<number, string>();
|
|
6117
6311
|
try {
|
|
6118
6312
|
const forkAvailableModels = contextPolicy.usesFork ? ctx.modelRegistry.getAvailable().map(toModelInfo) : [];
|
|
6119
6313
|
const parentModel = requestParentModel;
|
|
6120
|
-
prepareForkThinking = (agentName, index, modelOverride, modelOverrideFromParent) => {
|
|
6314
|
+
prepareForkThinking = (agentName, index, modelOverride, modelOverrideFromParent, storedOrigin) => {
|
|
6121
6315
|
const agentConfig = agents.find((agent) => agent.name === agentName);
|
|
6122
6316
|
if (agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job") {
|
|
6123
6317
|
forkThinkingRequirements.set(index, true);
|
|
6124
6318
|
return;
|
|
6125
6319
|
}
|
|
6320
|
+
const effectiveStoredOrigin = storedOrigin === "configured" && modelOverride === undefined && agentConfig?.model === undefined
|
|
6321
|
+
? undefined
|
|
6322
|
+
: storedOrigin;
|
|
6323
|
+
const origin = resolveModelOrigin({
|
|
6324
|
+
fromParent: modelOverrideFromParent,
|
|
6325
|
+
storedOrigin: effectiveStoredOrigin,
|
|
6326
|
+
explicitModel: modelOverrideFromParent || effectiveStoredOrigin === "configured" ? undefined : modelOverride,
|
|
6327
|
+
agentModel: agentConfig?.model,
|
|
6328
|
+
parentModel,
|
|
6329
|
+
});
|
|
6126
6330
|
const primaryModel = modelOverrideFromParent
|
|
6127
6331
|
? modelOverride
|
|
6128
6332
|
: resolveEffectiveSubagentModel(
|
|
6129
|
-
modelOverride,
|
|
6333
|
+
effectiveStoredOrigin === "configured" ? undefined : modelOverride,
|
|
6130
6334
|
agentConfig?.model,
|
|
6131
6335
|
parentModel,
|
|
6132
6336
|
forkAvailableModels,
|
|
6133
6337
|
agentConfig?.modelProvider ?? parentModel?.provider,
|
|
6134
|
-
{ source: "inherited" },
|
|
6338
|
+
{ source: origin === "explicit" ? "explicit" : "inherited" },
|
|
6135
6339
|
);
|
|
6136
6340
|
const candidates = buildModelCandidates(
|
|
6137
6341
|
primaryModel,
|
|
6138
6342
|
agentConfig?.fallbackModels,
|
|
6139
6343
|
forkAvailableModels,
|
|
6140
6344
|
agentConfig?.modelProvider ?? parentModel?.provider,
|
|
6141
|
-
{
|
|
6345
|
+
{
|
|
6346
|
+
primaryModelFromParent: origin === "inherited",
|
|
6347
|
+
origin,
|
|
6348
|
+
},
|
|
6142
6349
|
);
|
|
6143
6350
|
forkThinkingRequirements.set(
|
|
6144
6351
|
index,
|
|
@@ -6256,25 +6463,25 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6256
6463
|
}
|
|
6257
6464
|
const sessionDirForIndex = (idx?: number) =>
|
|
6258
6465
|
path.join(sessionRoot, `run-${idx ?? 0}`);
|
|
6259
|
-
const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
6466
|
+
const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
|
|
6260
6467
|
if (!shouldForkAgent(contextPolicy, agentName)) return undefined;
|
|
6261
|
-
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
6468
|
+
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
6262
6469
|
return forkSessionFileForIndex(idx);
|
|
6263
6470
|
};
|
|
6264
|
-
const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
6471
|
+
const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
|
|
6265
6472
|
if (!shouldForkAgent(contextPolicy, agentName)) return;
|
|
6266
|
-
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
6473
|
+
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
6267
6474
|
await prepareForkSessionForIndex(idx);
|
|
6268
6475
|
};
|
|
6269
|
-
const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
6476
|
+
const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
|
|
6270
6477
|
if (!shouldForkAgent(contextPolicy, agentName)) return delegatedThinkingOverride;
|
|
6271
|
-
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
6478
|
+
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
6272
6479
|
const override = forkThinkingOverrideForIndex(idx);
|
|
6273
6480
|
if (override === "off") forkThinkingDowngrades.set(idx, agentName);
|
|
6274
6481
|
return override ?? delegatedThinkingOverride;
|
|
6275
6482
|
};
|
|
6276
|
-
const childSessionFileForTask: ForkSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent) =>
|
|
6277
|
-
forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
6483
|
+
const childSessionFileForTask: ForkSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) =>
|
|
6484
|
+
forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
6278
6485
|
const childSessionFileForIndex = (idx?: number) =>
|
|
6279
6486
|
path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
6280
6487
|
try {
|
|
@@ -6368,6 +6575,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6368
6575
|
effectiveAsync,
|
|
6369
6576
|
asyncRunId,
|
|
6370
6577
|
controlConfig,
|
|
6578
|
+
...(delegatedExecution ? { suppressUnchangedDelegationUpdates: true } : {}),
|
|
6371
6579
|
intercomBridge,
|
|
6372
6580
|
nestedRoute,
|
|
6373
6581
|
timeoutMs: foregroundTimeout.timeoutMs,
|
|
@@ -6643,10 +6851,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6643
6851
|
if (!normalized.ok) {
|
|
6644
6852
|
return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
|
|
6645
6853
|
}
|
|
6646
|
-
|
|
6854
|
+
let publicParams = normalized.params as SubagentParamsLike;
|
|
6855
|
+
if (publicParams.workflow !== undefined) {
|
|
6856
|
+
const resolved = resolveWorkflowResource(publicParams.workflow, publicParams.args);
|
|
6857
|
+
if (!resolved.ok) return Promise.resolve({ content: [{ type: "text", text: resolved.error }], isError: true, details: { mode: "workflow", results: [] } });
|
|
6858
|
+
const { workflow: _workflow, args: _args, ...withoutResourceInput } = publicParams;
|
|
6859
|
+
publicParams = { ...withoutResourceInput, workflowScript: resolved.resource.script };
|
|
6860
|
+
workflowResourcePermits.set(publicParams, resolved.resource.permit);
|
|
6861
|
+
}
|
|
6862
|
+
const loaded = loadWorkflowScriptPath(publicParams, ctx.cwd);
|
|
6647
6863
|
if (loaded.error) {
|
|
6648
|
-
return Promise.resolve({ content: [{ type: "text", text: loaded.error }], isError: true, details: { mode:
|
|
6864
|
+
return Promise.resolve({ content: [{ type: "text", text: loaded.error }], isError: true, details: { mode: publicParams.action ? "management" : "workflow", results: [] } });
|
|
6649
6865
|
}
|
|
6866
|
+
publicExecutions.add(loaded.params!);
|
|
6650
6867
|
return executeWithSingleDispatchGuard(id, loaded.params!, signal, onUpdate, ctx);
|
|
6651
6868
|
};
|
|
6652
6869
|
|
|
@@ -6691,7 +6908,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6691
6908
|
});
|
|
6692
6909
|
scheduledOwnerExecutors.set(ownerSessionId, ownerExecutor);
|
|
6693
6910
|
}
|
|
6694
|
-
return ownerExecutor.
|
|
6911
|
+
return ownerExecutor.executePublic(id, params, signal, undefined, ctx);
|
|
6695
6912
|
};
|
|
6696
6913
|
|
|
6697
6914
|
return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };
|