pi-subagents 0.60.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 +32 -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 +12 -3
- package/docs/workflows.md +14 -13
- package/install.mjs +2 -1
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +6 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +42 -11
- package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +4 -4
- package/skills/pi-subagents/references/review-and-validation.md +1 -1
- package/src/agents/agent-management.ts +102 -63
- 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 +1 -0
- package/src/extension/index.ts +7 -4
- package/src/extension/public-execution.ts +47 -4
- package/src/extension/rpc.ts +62 -4
- package/src/extension/schemas.ts +11 -7
- package/src/extension/tool-description.ts +14 -18
- package/src/runs/background/async-execution.ts +51 -35
- package/src/runs/background/async-job-tracker.ts +62 -3
- package/src/runs/background/async-resume.ts +3 -1
- package/src/runs/background/async-status.ts +59 -9
- package/src/runs/background/auto-drain.ts +1 -1
- 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 +2 -2
- package/src/runs/background/subagent-runner.ts +1 -2
- package/src/runs/background/subagent-wait.ts +20 -21
- package/src/runs/background/wait-completions.ts +1 -1
- package/src/runs/background/wait-tool.ts +24 -18
- package/src/runs/foreground/execution.ts +61 -2
- package/src/runs/foreground/subagent-executor.ts +178 -58
- package/src/runs/shared/acceptance.ts +43 -18
- package/src/runs/shared/host-step-status.ts +1 -0
- package/src/runs/shared/model-fallback.ts +61 -17
- package/src/runs/shared/permissions.ts +1 -1
- package/src/runs/shared/tool-timeout.ts +1 -1
- package/src/runs/shared/workflow-graph.ts +3 -2
- package/src/shared/types.ts +40 -3
- 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/render.ts +95 -52
- package/src/workflows/scripted-workflow.ts +153 -4
- 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";
|
|
@@ -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,7 @@ import {
|
|
|
160
170
|
type ToolBudgetConfig,
|
|
161
171
|
type Usage,
|
|
162
172
|
type UsageBudgetConfig,
|
|
173
|
+
type WorkflowResourceProvenanceV1,
|
|
163
174
|
type WorkflowGraphNode,
|
|
164
175
|
type WorkflowGraphSnapshot,
|
|
165
176
|
type WorkflowNodeStatus,
|
|
@@ -309,6 +320,8 @@ export interface SubagentParamsLike {
|
|
|
309
320
|
planId?: string;
|
|
310
321
|
workflowScript?: string;
|
|
311
322
|
workflowScriptPath?: string;
|
|
323
|
+
globalConcurrencyLimit?: number;
|
|
324
|
+
maxSubagentSpawnsPerRun?: number;
|
|
312
325
|
preflight?: import("../../shared/types.ts").WorkflowPreflightV1;
|
|
313
326
|
chatProgress?: "auto" | "off" | "live-card";
|
|
314
327
|
isolation?: "none" | "worktree";
|
|
@@ -357,8 +370,13 @@ export interface SubagentParamsLike {
|
|
|
357
370
|
artifacts?: boolean;
|
|
358
371
|
includeProgress?: boolean;
|
|
359
372
|
model?: string;
|
|
373
|
+
/** Internal recovery provenance for a resolved model override. */
|
|
374
|
+
modelOrigin?: ModelOrigin;
|
|
360
375
|
fast?: boolean;
|
|
361
376
|
thinking?: string | false;
|
|
377
|
+
/** Public named workflow resource. Resolved before entering the workflow sandbox. */
|
|
378
|
+
workflow?: string;
|
|
379
|
+
args?: Record<string, unknown>;
|
|
362
380
|
scope?: string;
|
|
363
381
|
target?: string;
|
|
364
382
|
focus?: boolean;
|
|
@@ -418,9 +436,9 @@ interface ExecutorDeps {
|
|
|
418
436
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
419
437
|
}
|
|
420
438
|
|
|
421
|
-
type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => string | undefined;
|
|
422
|
-
type PrepareForkSessionForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => Promise<void>;
|
|
423
|
-
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;
|
|
424
442
|
|
|
425
443
|
interface ExecutionContextData {
|
|
426
444
|
params: SubagentParamsLike;
|
|
@@ -447,6 +465,8 @@ interface ExecutionContextData {
|
|
|
447
465
|
effectiveAsync: boolean;
|
|
448
466
|
asyncRunId: string;
|
|
449
467
|
controlConfig: ResolvedControlConfig;
|
|
468
|
+
/** Structured delegation consumers do not need duration-only heartbeat snapshots. */
|
|
469
|
+
suppressUnchangedDelegationUpdates?: boolean;
|
|
450
470
|
intercomBridge: IntercomBridgeState;
|
|
451
471
|
nestedRoute?: NestedRouteInfo;
|
|
452
472
|
timeoutMs?: number;
|
|
@@ -865,7 +885,7 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
865
885
|
if (matches.length === 0) return undefined;
|
|
866
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.`);
|
|
867
887
|
const run = matches[0]!;
|
|
868
|
-
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.`);
|
|
869
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.`);
|
|
870
890
|
const index = params.index ?? 0;
|
|
871
891
|
if (!Number.isInteger(index)) throw new Error(`Foreground run '${run.runId}' index must be an integer.`);
|
|
@@ -2024,6 +2044,7 @@ async function resumeAsyncRun(input: {
|
|
|
2024
2044
|
modelOverride: recoveryDescriptor?.model ?? target.model,
|
|
2025
2045
|
fast: recoveryDescriptor?.fast,
|
|
2026
2046
|
modelOverrideFromParent: recoveryDescriptor?.modelOverrideFromParent,
|
|
2047
|
+
modelOrigin: recoveryDescriptor?.modelOrigin ?? (recoveryDescriptor?.modelOverrideFromParent ? "inherited" : undefined),
|
|
2027
2048
|
thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
|
|
2028
2049
|
thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
|
|
2029
2050
|
extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
|
|
@@ -2997,7 +3018,15 @@ async function preflightForkSessionsForStaticTasks(
|
|
|
2997
3018
|
): Promise<void> {
|
|
2998
3019
|
if (!contextPolicy.usesFork) return;
|
|
2999
3020
|
if (params.agent) {
|
|
3000
|
-
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
|
+
}
|
|
3001
3030
|
return;
|
|
3002
3031
|
}
|
|
3003
3032
|
if (params.tasks) {
|
|
@@ -3175,10 +3204,19 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3175
3204
|
return buildRequestedModeError(params, `Agent '${a.name}' uses runner.type='${a.runner.type}' and does not support fast mode.`);
|
|
3176
3205
|
}
|
|
3177
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
|
+
});
|
|
3178
3213
|
const modelOverride = a.runner?.type === "external-cli" || a.runner?.type === "external-job"
|
|
3179
3214
|
? params.model ?? (externalRunnerWithoutExplicitModel ? undefined : a.model)
|
|
3180
|
-
: resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, a.modelProvider ?? currentProvider,
|
|
3181
|
-
|
|
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";
|
|
3182
3220
|
const asyncResult = executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
3183
3221
|
agent: params.agent!,
|
|
3184
3222
|
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
@@ -3195,7 +3233,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3195
3233
|
shareEnabled,
|
|
3196
3234
|
activeAsyncCapacity: data.activeAsyncCapacity,
|
|
3197
3235
|
sessionRoot,
|
|
3198
|
-
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
|
|
3236
|
+
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
|
|
3199
3237
|
context: contextPolicy.contextForAgent(params.agent!),
|
|
3200
3238
|
skills,
|
|
3201
3239
|
output: effectiveOutput,
|
|
@@ -3206,7 +3244,8 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3206
3244
|
modelOverride,
|
|
3207
3245
|
fast: params.fast,
|
|
3208
3246
|
modelOverrideFromParent,
|
|
3209
|
-
|
|
3247
|
+
modelOrigin,
|
|
3248
|
+
thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
|
|
3210
3249
|
thinkingCeiling: a.maxThinking,
|
|
3211
3250
|
maxSubagentDepth,
|
|
3212
3251
|
waitToolEnabled: deps.waitToolEnabled,
|
|
@@ -3311,8 +3350,11 @@ function workflowHostCommandRunner(input: {
|
|
|
3311
3350
|
workflowRunId: string;
|
|
3312
3351
|
claimedOutputPaths: Map<string, string>;
|
|
3313
3352
|
producedOutputPaths: Set<string>;
|
|
3353
|
+
authorize?: (key: string, params: WorkflowHostCommandParams) => string | undefined;
|
|
3314
3354
|
}) {
|
|
3315
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);
|
|
3316
3358
|
const defaultOutputPath = path.join(input.artifactsDir, "outputs", sanitizeRunPathSegment(input.workflowRunId), "host", `${sanitizeRunPathSegment(key)}.log`);
|
|
3317
3359
|
const outputPath = params.output ? path.resolve(input.workflowCwd, params.output) : defaultOutputPath;
|
|
3318
3360
|
const claimPath = resolveWorkflowHostOutputClaimPath(outputPath);
|
|
@@ -3552,6 +3594,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3552
3594
|
onUpdate,
|
|
3553
3595
|
controlConfig,
|
|
3554
3596
|
contextPolicy,
|
|
3597
|
+
suppressUnchangedDelegationUpdates,
|
|
3555
3598
|
} = data;
|
|
3556
3599
|
let lane: import("../../shared/types.ts").WorkflowLaneMetadata | undefined;
|
|
3557
3600
|
try {
|
|
@@ -3581,15 +3624,24 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3581
3624
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3582
3625
|
const modelScopes = resolveModelScopesForAgent(data.modelScope, agentConfig.name, parentModel);
|
|
3583
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
|
+
});
|
|
3584
3633
|
let modelOverride: string | undefined = resolveEffectiveSubagentModel(
|
|
3585
3634
|
params.model as string | undefined,
|
|
3586
3635
|
agentConfig.model,
|
|
3587
3636
|
parentModel,
|
|
3588
3637
|
availableModels,
|
|
3589
3638
|
agentConfig.modelProvider ?? currentProvider,
|
|
3590
|
-
|
|
3639
|
+
{
|
|
3640
|
+
...(modelScopes.length === 0 ? {} : { scope: modelScopes }),
|
|
3641
|
+
source: modelOrigin === "explicit" ? "explicit" : "inherited",
|
|
3642
|
+
},
|
|
3591
3643
|
);
|
|
3592
|
-
const modelOverrideFromParent =
|
|
3644
|
+
const modelOverrideFromParent = modelOrigin === "inherited";
|
|
3593
3645
|
let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
|
|
3594
3646
|
let readsOverride: string[] | false | undefined = params.reads;
|
|
3595
3647
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
@@ -3660,7 +3712,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3660
3712
|
let detachForeground: ((reason?: string) => boolean) | undefined;
|
|
3661
3713
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3662
3714
|
if (foregroundControl) {
|
|
3663
|
-
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent));
|
|
3715
|
+
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin));
|
|
3664
3716
|
beginForegroundChild(foregroundControl, omitUndefinedProperties({
|
|
3665
3717
|
index: 0,
|
|
3666
3718
|
agent: params.agent!,
|
|
@@ -3712,7 +3764,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3712
3764
|
intercomEvents: deps.pi.events,
|
|
3713
3765
|
runId,
|
|
3714
3766
|
sessionDir: sessionDirForIndex(0),
|
|
3715
|
-
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
|
|
3767
|
+
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
|
|
3716
3768
|
share: shareEnabled,
|
|
3717
3769
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3718
3770
|
artifactConfig,
|
|
@@ -3724,6 +3776,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3724
3776
|
waitToolEnabled: deps.waitToolEnabled,
|
|
3725
3777
|
waitToolDefaultTimeoutMs: deps.waitToolDefaultTimeoutMs,
|
|
3726
3778
|
onUpdate: forwardSingleUpdate,
|
|
3779
|
+
suppressUnchangedDelegationUpdates,
|
|
3727
3780
|
controlConfig,
|
|
3728
3781
|
onControlEvent,
|
|
3729
3782
|
intercomSessionName: childIntercomTarget,
|
|
@@ -3733,7 +3786,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3733
3786
|
modelOverride,
|
|
3734
3787
|
fast: params.fast,
|
|
3735
3788
|
modelOverrideFromParent,
|
|
3736
|
-
|
|
3789
|
+
modelOrigin,
|
|
3790
|
+
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
|
|
3737
3791
|
thinkingCeiling: agentConfig.maxThinking,
|
|
3738
3792
|
extensionBindings: params.extensionBindings,
|
|
3739
3793
|
availableModels,
|
|
@@ -3880,11 +3934,11 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3880
3934
|
const worktreeSuffix = worktreeHandoff?.suffix ? `\n\n${worktreeHandoff.suffix}` : "";
|
|
3881
3935
|
if (r.detached) {
|
|
3882
3936
|
const statusRecovery = `subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.`;
|
|
3883
|
-
const blockingRecovery = `
|
|
3937
|
+
const blockingRecovery = `bg_wait({ id: "${runId}" }). Use ${statusRecovery}`;
|
|
3884
3938
|
const message = r.detachedReason === "intercom coordination"
|
|
3885
3939
|
? `Detached for intercom coordination: ${params.agent}. Reply to the supervisor request first, then wait with ${blockingRecovery}`
|
|
3886
3940
|
: r.detachedReason === "user request"
|
|
3887
|
-
? `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}`
|
|
3888
3942
|
: `Detached before task completion: ${params.agent}. Wait with ${blockingRecovery}`;
|
|
3889
3943
|
return {
|
|
3890
3944
|
content: [{ type: "text", text: `${message}${worktreeSuffix}` }],
|
|
@@ -4022,7 +4076,8 @@ function workflowChildResult(
|
|
|
4022
4076
|
: result.details.usageBudget?.exhausted || result.details.results.some((child) => child.turnBudgetExceeded || child.toolBudgetBlocked)
|
|
4023
4077
|
? { state: "partial" as const, reason: "budget_exhausted" as const }
|
|
4024
4078
|
: undefined);
|
|
4025
|
-
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;
|
|
4026
4081
|
const artifactPaths = new Set<string>();
|
|
4027
4082
|
if (result.details.asyncDir) artifactPaths.add(result.details.asyncDir);
|
|
4028
4083
|
if (result.details.parallelHandoff?.path) artifactPaths.add(result.details.parallelHandoff.path);
|
|
@@ -4080,6 +4135,7 @@ function workflowChildResult(
|
|
|
4080
4135
|
...(requestedContext ? { requestedContext } : {}),
|
|
4081
4136
|
...(resolvedContext ? { resolvedContext } : {}),
|
|
4082
4137
|
...(outputReference ? { outputReference } : {}),
|
|
4138
|
+
...(acceptanceRecovery ? { recovery: acceptanceRecovery } : {}),
|
|
4083
4139
|
...(outputPathMapping ? { outputPathMapping } : {}),
|
|
4084
4140
|
...(externalAdapter ? { externalAdapter } : {}),
|
|
4085
4141
|
resumability,
|
|
@@ -4089,13 +4145,14 @@ function workflowChildResult(
|
|
|
4089
4145
|
};
|
|
4090
4146
|
}
|
|
4091
4147
|
|
|
4092
|
-
function workflowChildAccountingFields(child: WorkflowScriptChildResult): { usage?: Usage; sessionFile?: string } {
|
|
4148
|
+
function workflowChildAccountingFields(child: WorkflowScriptChildResult): { usage?: Usage; sessionFile?: string; recovery?: import("../../shared/types.ts").AcceptanceRecoveryMetadata } {
|
|
4093
4149
|
if (!child.results?.length) return {};
|
|
4094
4150
|
const usage = sumResultsUsage(child.results);
|
|
4095
4151
|
const sessionFile = child.results.find((result) => result.sessionFile)?.sessionFile;
|
|
4096
4152
|
return {
|
|
4097
4153
|
...(usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0 ? { usage } : {}),
|
|
4098
4154
|
...(sessionFile ? { sessionFile } : {}),
|
|
4155
|
+
...(child.recovery ? { recovery: child.recovery } : {}),
|
|
4099
4156
|
};
|
|
4100
4157
|
}
|
|
4101
4158
|
|
|
@@ -4223,8 +4280,9 @@ function terminalWorkflowReceipt(
|
|
|
4223
4280
|
workflowChildren?: WorkflowReceipt["workflowChildren"],
|
|
4224
4281
|
terminalOutcome?: WorkflowTerminalOutcome,
|
|
4225
4282
|
hostSteps?: WorkflowReceipt["hostSteps"],
|
|
4283
|
+
resource?: WorkflowReceipt["resource"],
|
|
4226
4284
|
): WorkflowReceipt {
|
|
4227
|
-
return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps });
|
|
4285
|
+
return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps, resource });
|
|
4228
4286
|
}
|
|
4229
4287
|
|
|
4230
4288
|
function workflowFailureTerminalOutcome(error: unknown, _children: WorkflowScriptChildResult[], usageBudget: ReturnType<typeof usageBudgetState>): WorkflowTerminalOutcome | undefined {
|
|
@@ -4316,6 +4374,10 @@ export function prepareWorkflowLaunchParams(
|
|
|
4316
4374
|
workflowKey: string,
|
|
4317
4375
|
options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean; awaitDetachedChild?: boolean; runFanoutBudget?: RunFanoutBudgetDescriptor; parentDeadlineAt?: number; externalAsyncRequired?: boolean; capabilityCeiling?: ResolvedSubagentCapabilityCeiling; outputClaimPath?: string } = {},
|
|
4318
4376
|
): SubagentParamsLike {
|
|
4377
|
+
const { globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowDefaultsWithoutCapacity } = workflowDefaults;
|
|
4378
|
+
const { globalConcurrencyLimit: _childGlobalConcurrencyLimit, maxSubagentSpawnsPerRun: _childMaxSubagentSpawnsPerRun, ...childParamsWithoutCapacity } = childParams;
|
|
4379
|
+
workflowDefaults = workflowDefaultsWithoutCapacity;
|
|
4380
|
+
childParams = childParamsWithoutCapacity;
|
|
4319
4381
|
const capabilityCeiling = intersectSubagentCapabilityCeilings(workflowDefaults.capabilityCeiling, options.capabilityCeiling);
|
|
4320
4382
|
const lane = normalizeWorkflowLaneMetadata(Object.hasOwn(childParams, "lane") ? childParams.lane : workflowDefaults.lane, `workflow child '${workflowKey}'.lane`);
|
|
4321
4383
|
assertWorkflowLaneKey(lane, workflowKey, `workflow child '${workflowKey}'.lane`);
|
|
@@ -4586,6 +4648,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4586
4648
|
const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
|
|
4587
4649
|
const delegatedZeroToolBudgets = new WeakSet<object>();
|
|
4588
4650
|
const delegatedExecutions = new WeakSet<object>();
|
|
4651
|
+
const publicExecutions = new WeakSet<object>();
|
|
4652
|
+
const workflowResourcePermits = new WeakMap<object, WorkflowResourcePermit>();
|
|
4589
4653
|
const workflowPermitContexts = new WeakMap<object, { root: WorkflowChildPermit } | { child: WorkflowChildPermitContext }>();
|
|
4590
4654
|
const warnedArtifactPackageDirs = new Set<string>();
|
|
4591
4655
|
const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
|
|
@@ -4602,6 +4666,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4602
4666
|
const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
|
|
4603
4667
|
const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
|
|
4604
4668
|
const delegatedExecution = delegatedExecutions.has(params);
|
|
4669
|
+
const publicExecution = publicExecutions.has(params);
|
|
4670
|
+
const workflowResourcePermit = workflowResourcePermits.get(params);
|
|
4605
4671
|
const workflowPermitContext = workflowPermitContexts.get(params);
|
|
4606
4672
|
const delegatedWorkflowPermit = workflowPermitContext && "root" in workflowPermitContext ? workflowPermitContext.root : undefined;
|
|
4607
4673
|
const workflowChildPermitLaunch = workflowPermitContext && "child" in workflowPermitContext ? workflowPermitContext.child : undefined;
|
|
@@ -4612,6 +4678,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4612
4678
|
const normalizedGate = normalizeGateParams(params);
|
|
4613
4679
|
if (!normalizedGate.ok) return buildRequestedModeError(params, normalizedGate.error);
|
|
4614
4680
|
let requestParams = normalizedGate.params;
|
|
4681
|
+
const capacityOverrideError = validateWorkflowCapacityOverrides(requestParams);
|
|
4682
|
+
if (capacityOverrideError) return buildRequestedModeError(requestParams, capacityOverrideError);
|
|
4615
4683
|
let workflowPreflight: import("../../shared/types.ts").WorkflowPreflightV1 | undefined;
|
|
4616
4684
|
try {
|
|
4617
4685
|
if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined && requestParams.workflowScriptPath === undefined) {
|
|
@@ -4643,6 +4711,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4643
4711
|
}
|
|
4644
4712
|
const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
|
|
4645
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
|
+
}
|
|
4646
4721
|
if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
|
|
4647
4722
|
if (delegatedWorkflowPermit) {
|
|
4648
4723
|
const permitError = validateWorkflowChildPermitRoot(delegatedWorkflowPermit, _id);
|
|
@@ -4722,7 +4797,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4722
4797
|
try {
|
|
4723
4798
|
workflowFanoutBudget = requestParams.runFanoutBudget
|
|
4724
4799
|
?? decodeRunFanoutBudgetDescriptor(process.env[RUN_FANOUT_BUDGET_ENV])
|
|
4725
|
-
?? createRunFanoutBudget(_id, resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
4800
|
+
?? createRunFanoutBudget(_id, requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
4726
4801
|
} catch (error) {
|
|
4727
4802
|
workflowCapacity?.rollback();
|
|
4728
4803
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
@@ -4800,7 +4875,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4800
4875
|
pid: process.pid,
|
|
4801
4876
|
steps: [],
|
|
4802
4877
|
...(workflowPreflight ? { preflight: workflowPreflight } : {}),
|
|
4803
|
-
workflow: { trace: [], emits: [], console: [] },
|
|
4878
|
+
workflow: { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) },
|
|
4804
4879
|
workflowChildren: workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "running", inventoryComplete: false }),
|
|
4805
4880
|
runFanoutBudget: getRunFanoutBudgetSnapshot(workflowFanoutBudget),
|
|
4806
4881
|
};
|
|
@@ -4952,7 +5027,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4952
5027
|
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
4953
5028
|
const workflowResults: SingleResult[] = [];
|
|
4954
5029
|
const workflowChildRunIds = new Map<string, string>();
|
|
4955
|
-
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;
|
|
4956
5031
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
4957
5032
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
4958
5033
|
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, parentCwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
|
|
@@ -4961,7 +5036,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4961
5036
|
const childOutputClaimPaths = new Map<string, string>();
|
|
4962
5037
|
const producedChildOutputPaths = new Set<string>();
|
|
4963
5038
|
const workflowSteps = new Map<string, NonNullable<AsyncStatus["steps"]>[number]>();
|
|
4964
|
-
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;
|
|
4965
5050
|
let projectedTraceLength = 0;
|
|
4966
5051
|
let projectedTraceTail: NonNullable<Details["workflow"]>["trace"][number] | undefined;
|
|
4967
5052
|
const updateTrace = (trace: NonNullable<Details["workflow"]>["trace"]) => {
|
|
@@ -5067,7 +5152,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5067
5152
|
try {
|
|
5068
5153
|
const workflow = await runWorkflowScript({
|
|
5069
5154
|
script: workflowScript,
|
|
5070
|
-
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
5155
|
+
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5071
5156
|
timeoutMs: timeout,
|
|
5072
5157
|
signal: controller.signal,
|
|
5073
5158
|
registerStopChild: (stop) => {
|
|
@@ -5081,7 +5166,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5081
5166
|
applyWorkflowLaneTrace(status.workflowGraph, status.workflow?.trace ?? []);
|
|
5082
5167
|
persist({ tolerateStatusWriteFailure: true });
|
|
5083
5168
|
},
|
|
5084
|
-
host:
|
|
5169
|
+
...(workflowHost ? { host: workflowHost } : {}),
|
|
5085
5170
|
onHostStep: (hostStep) => {
|
|
5086
5171
|
status = upsertHostStep({ status, hostStep, persist: (nextStatus) => {
|
|
5087
5172
|
status = nextStatus;
|
|
@@ -5206,8 +5291,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5206
5291
|
const resultSummary = appendWorkflowOutputWarning(summary, outputWarning);
|
|
5207
5292
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
5208
5293
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children, steps: status.steps });
|
|
5209
|
-
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) };
|
|
5210
|
-
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);
|
|
5211
5296
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
5212
5297
|
try {
|
|
5213
5298
|
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
@@ -5241,7 +5326,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5241
5326
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: true, trace: partial.trace, children: partial.children, steps: status.steps });
|
|
5242
5327
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
|
|
5243
5328
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
|
|
5244
|
-
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 });
|
|
5245
5330
|
if (pauseForDetached) {
|
|
5246
5331
|
const promoted = promotePausedWorkflowIfSettled(status);
|
|
5247
5332
|
if (promoted) status = promoted;
|
|
@@ -5253,7 +5338,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5253
5338
|
const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
|
|
5254
5339
|
const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
|
|
5255
5340
|
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
5256
|
-
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);
|
|
5257
5342
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
5258
5343
|
try {
|
|
5259
5344
|
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
@@ -5275,10 +5360,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5275
5360
|
});
|
|
5276
5361
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5277
5362
|
content: [{ type: "text", text: `${workflowPreflight ? `${formatWorkflowPreflight(workflowPreflight)}\n\n` : ""}${formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true)}` }],
|
|
5278
|
-
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 } : {}) },
|
|
5279
5364
|
}, workflowFanoutBudget));
|
|
5280
5365
|
}
|
|
5281
|
-
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;
|
|
5282
5367
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
5283
5368
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
5284
5369
|
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, _id));
|
|
@@ -5288,9 +5373,21 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5288
5373
|
const producedChildOutputPaths = new Set<string>();
|
|
5289
5374
|
const workflowResults: SingleResult[] = [];
|
|
5290
5375
|
const workflowChildRunIds = new Map<string, string>();
|
|
5291
|
-
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;
|
|
5292
5389
|
const workflowHostSteps = new Map<string, HostStepNodeV1>();
|
|
5293
|
-
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
|
|
5390
|
+
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) };
|
|
5294
5391
|
let liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "running", inventoryComplete: false });
|
|
5295
5392
|
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
5296
5393
|
const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(resolveCurrentSessionId(ctx.sessionManager)));
|
|
@@ -5302,11 +5399,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5302
5399
|
const workflow = await runWorkflowScript({
|
|
5303
5400
|
script: requestParams.workflowScript,
|
|
5304
5401
|
...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, _id, key) } } : {}),
|
|
5305
|
-
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
5402
|
+
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5306
5403
|
timeoutMs: timeout,
|
|
5307
5404
|
signal,
|
|
5308
5405
|
...(workflowState ? { state: workflowState } : {}),
|
|
5309
|
-
...(
|
|
5406
|
+
...(workflowHost ? { host: workflowHost } : {}),
|
|
5310
5407
|
onHostStep: (hostStep) => {
|
|
5311
5408
|
workflowHostSteps.set(hostStep.id, { ...hostStep, ...(hostStep.freshness ? { freshness: { ...hostStep.freshness } } : {}) });
|
|
5312
5409
|
},
|
|
@@ -5403,7 +5500,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5403
5500
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
|
|
5404
5501
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
|
|
5405
5502
|
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children });
|
|
5406
|
-
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);
|
|
5407
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}` : ""}`);
|
|
5408
5505
|
const sections = [
|
|
5409
5506
|
...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
|
|
@@ -5421,7 +5518,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5421
5518
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
5422
5519
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5423
5520
|
content: [{ type: "text", text: displayText }],
|
|
5424
|
-
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 }),
|
|
5425
5522
|
}, workflowFanoutBudget));
|
|
5426
5523
|
} catch (error) {
|
|
5427
5524
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
@@ -5444,11 +5541,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5444
5541
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
5445
5542
|
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "failed", inventoryComplete: true, trace: partial.trace, children: partial.children });
|
|
5446
5543
|
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
5447
|
-
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);
|
|
5448
5545
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5449
5546
|
content: [{ type: "text", text: displayText }],
|
|
5450
5547
|
isError: true,
|
|
5451
|
-
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 }),
|
|
5452
5549
|
}, workflowFanoutBudget));
|
|
5453
5550
|
}
|
|
5454
5551
|
}
|
|
@@ -6208,34 +6305,47 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6208
6305
|
let forkSessionFileForIndex: (idx?: number) => string | undefined = () => undefined;
|
|
6209
6306
|
let prepareForkSessionForIndex: (idx?: number) => Promise<void> = async () => {};
|
|
6210
6307
|
let forkThinkingOverrideForIndex: (idx?: number) => AgentConfig["thinking"] | undefined = () => undefined;
|
|
6211
|
-
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 => {};
|
|
6212
6309
|
const forkThinkingRequirements = new Map<number, boolean>();
|
|
6213
6310
|
const forkThinkingDowngrades = new Map<number, string>();
|
|
6214
6311
|
try {
|
|
6215
6312
|
const forkAvailableModels = contextPolicy.usesFork ? ctx.modelRegistry.getAvailable().map(toModelInfo) : [];
|
|
6216
6313
|
const parentModel = requestParentModel;
|
|
6217
|
-
prepareForkThinking = (agentName, index, modelOverride, modelOverrideFromParent) => {
|
|
6314
|
+
prepareForkThinking = (agentName, index, modelOverride, modelOverrideFromParent, storedOrigin) => {
|
|
6218
6315
|
const agentConfig = agents.find((agent) => agent.name === agentName);
|
|
6219
6316
|
if (agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job") {
|
|
6220
6317
|
forkThinkingRequirements.set(index, true);
|
|
6221
6318
|
return;
|
|
6222
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
|
+
});
|
|
6223
6330
|
const primaryModel = modelOverrideFromParent
|
|
6224
6331
|
? modelOverride
|
|
6225
6332
|
: resolveEffectiveSubagentModel(
|
|
6226
|
-
modelOverride,
|
|
6333
|
+
effectiveStoredOrigin === "configured" ? undefined : modelOverride,
|
|
6227
6334
|
agentConfig?.model,
|
|
6228
6335
|
parentModel,
|
|
6229
6336
|
forkAvailableModels,
|
|
6230
6337
|
agentConfig?.modelProvider ?? parentModel?.provider,
|
|
6231
|
-
{ source: "inherited" },
|
|
6338
|
+
{ source: origin === "explicit" ? "explicit" : "inherited" },
|
|
6232
6339
|
);
|
|
6233
6340
|
const candidates = buildModelCandidates(
|
|
6234
6341
|
primaryModel,
|
|
6235
6342
|
agentConfig?.fallbackModels,
|
|
6236
6343
|
forkAvailableModels,
|
|
6237
6344
|
agentConfig?.modelProvider ?? parentModel?.provider,
|
|
6238
|
-
{
|
|
6345
|
+
{
|
|
6346
|
+
primaryModelFromParent: origin === "inherited",
|
|
6347
|
+
origin,
|
|
6348
|
+
},
|
|
6239
6349
|
);
|
|
6240
6350
|
forkThinkingRequirements.set(
|
|
6241
6351
|
index,
|
|
@@ -6353,25 +6463,25 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6353
6463
|
}
|
|
6354
6464
|
const sessionDirForIndex = (idx?: number) =>
|
|
6355
6465
|
path.join(sessionRoot, `run-${idx ?? 0}`);
|
|
6356
|
-
const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
6466
|
+
const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
|
|
6357
6467
|
if (!shouldForkAgent(contextPolicy, agentName)) return undefined;
|
|
6358
|
-
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
6468
|
+
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
6359
6469
|
return forkSessionFileForIndex(idx);
|
|
6360
6470
|
};
|
|
6361
|
-
const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
6471
|
+
const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
|
|
6362
6472
|
if (!shouldForkAgent(contextPolicy, agentName)) return;
|
|
6363
|
-
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
6473
|
+
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
6364
6474
|
await prepareForkSessionForIndex(idx);
|
|
6365
6475
|
};
|
|
6366
|
-
const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
6476
|
+
const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
|
|
6367
6477
|
if (!shouldForkAgent(contextPolicy, agentName)) return delegatedThinkingOverride;
|
|
6368
|
-
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
6478
|
+
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
6369
6479
|
const override = forkThinkingOverrideForIndex(idx);
|
|
6370
6480
|
if (override === "off") forkThinkingDowngrades.set(idx, agentName);
|
|
6371
6481
|
return override ?? delegatedThinkingOverride;
|
|
6372
6482
|
};
|
|
6373
|
-
const childSessionFileForTask: ForkSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent) =>
|
|
6374
|
-
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");
|
|
6375
6485
|
const childSessionFileForIndex = (idx?: number) =>
|
|
6376
6486
|
path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
6377
6487
|
try {
|
|
@@ -6465,6 +6575,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6465
6575
|
effectiveAsync,
|
|
6466
6576
|
asyncRunId,
|
|
6467
6577
|
controlConfig,
|
|
6578
|
+
...(delegatedExecution ? { suppressUnchangedDelegationUpdates: true } : {}),
|
|
6468
6579
|
intercomBridge,
|
|
6469
6580
|
nestedRoute,
|
|
6470
6581
|
timeoutMs: foregroundTimeout.timeoutMs,
|
|
@@ -6740,10 +6851,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6740
6851
|
if (!normalized.ok) {
|
|
6741
6852
|
return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
|
|
6742
6853
|
}
|
|
6743
|
-
|
|
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);
|
|
6744
6863
|
if (loaded.error) {
|
|
6745
|
-
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: [] } });
|
|
6746
6865
|
}
|
|
6866
|
+
publicExecutions.add(loaded.params!);
|
|
6747
6867
|
return executeWithSingleDispatchGuard(id, loaded.params!, signal, onUpdate, ctx);
|
|
6748
6868
|
};
|
|
6749
6869
|
|
|
@@ -6788,7 +6908,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6788
6908
|
});
|
|
6789
6909
|
scheduledOwnerExecutors.set(ownerSessionId, ownerExecutor);
|
|
6790
6910
|
}
|
|
6791
|
-
return ownerExecutor.
|
|
6911
|
+
return ownerExecutor.executePublic(id, params, signal, undefined, ctx);
|
|
6792
6912
|
};
|
|
6793
6913
|
|
|
6794
6914
|
return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };
|