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.
Files changed (72) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/docs/agents.md +2 -2
  3. package/docs/configuration.md +9 -5
  4. package/docs/extension-api.md +14 -7
  5. package/docs/models.md +1 -1
  6. package/docs/observability.md +1 -1
  7. package/docs/tool-reference.md +15 -3
  8. package/docs/workflows.md +15 -14
  9. package/install.mjs +2 -1
  10. package/package.json +1 -1
  11. package/skills/council-mode/SKILL.md +48 -243
  12. package/skills/council-mode/references/pass-contracts.md +150 -0
  13. package/skills/pi-subagents/SKILL.md +89 -37
  14. package/skills/pi-subagents/references/constraints-and-recipes.md +30 -234
  15. package/skills/pi-subagents/references/execution-controls.md +79 -7
  16. package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  17. package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
  18. package/skills/pi-subagents/references/prompting-and-roles.md +35 -28
  19. package/skills/pi-subagents/references/review-and-validation.md +73 -0
  20. package/src/agents/agent-management.ts +256 -88
  21. package/src/agents/agents.ts +527 -221
  22. package/src/api/background-work.ts +7 -2
  23. package/src/api/external-runs.ts +67 -4
  24. package/src/api/preflight.ts +13 -8
  25. package/src/api/shared-types.ts +3 -0
  26. package/src/extension/index.ts +7 -4
  27. package/src/extension/public-execution.ts +48 -4
  28. package/src/extension/rpc.ts +62 -4
  29. package/src/extension/schemas.ts +12 -7
  30. package/src/extension/tool-description.ts +16 -16
  31. package/src/runs/background/async-execution.ts +53 -37
  32. package/src/runs/background/async-job-tracker.ts +65 -3
  33. package/src/runs/background/async-resume.ts +3 -1
  34. package/src/runs/background/async-status.ts +104 -11
  35. package/src/runs/background/auto-drain.ts +1 -1
  36. package/src/runs/background/control-channel.ts +3 -2
  37. package/src/runs/background/fleet-view.ts +1 -1
  38. package/src/runs/background/result-watcher.ts +1 -1
  39. package/src/runs/background/resume-guidance.ts +1 -1
  40. package/src/runs/background/run-status.ts +15 -4
  41. package/src/runs/background/subagent-runner.ts +6 -3
  42. package/src/runs/background/subagent-wait.ts +30 -23
  43. package/src/runs/background/wait-completions.ts +4 -1
  44. package/src/runs/background/wait-tool.ts +24 -18
  45. package/src/runs/foreground/execution.ts +72 -4
  46. package/src/runs/foreground/subagent-executor.ts +276 -59
  47. package/src/runs/shared/acceptance.ts +43 -18
  48. package/src/runs/shared/async-status-projection.ts +138 -4
  49. package/src/runs/shared/background-process-options.ts +9 -0
  50. package/src/runs/shared/host-step-status.ts +1 -0
  51. package/src/runs/shared/mcp-direct-tool-grant.ts +2 -5
  52. package/src/runs/shared/model-fallback.ts +61 -17
  53. package/src/runs/shared/mutation-evidence.ts +52 -3
  54. package/src/runs/shared/permissions.ts +1 -1
  55. package/src/runs/shared/pi-args.ts +47 -1
  56. package/src/runs/shared/single-output.ts +45 -18
  57. package/src/runs/shared/subagent-prompt-runtime.ts +20 -2
  58. package/src/runs/shared/tool-timeout.ts +1 -1
  59. package/src/runs/shared/workflow-graph.ts +16 -0
  60. package/src/shared/types.ts +74 -4
  61. package/src/shared/workflow-child-permit.ts +91 -0
  62. package/src/slash/prompt-template-bridge.ts +37 -1
  63. package/src/slash/slash-commands.ts +18 -26
  64. package/src/tui/fleet-status.ts +11 -3
  65. package/src/tui/render-helpers.ts +31 -0
  66. package/src/tui/render.ts +673 -145
  67. package/src/watchdog/change-signature.ts +40 -1
  68. package/src/workflows/host-command.ts +6 -1
  69. package/src/workflows/scripted-workflow.ts +206 -6
  70. package/src/workflows/workflow-child-summary.ts +1 -1
  71. package/src/workflows/workflow-receipt.ts +41 -4
  72. 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, inheritsParentModel, normalizeParentModel, resolveEffectiveSubagentModel, type ParentModel } from "../shared/model-fallback.ts";
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 { claimWorkflowChildPermit, validateWorkflowChildPermitRoot, type WorkflowChildPermit, type WorkflowChildPermitContext } from "../../shared/workflow-child-permit.ts";
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 subagent_wait({ id: "${run.runId}" }); use status to recover the result and do not launch a replacement while it remains detached.`);
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)) await prepareSessionForTask(params.agent, 0, params.model);
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, modelScopes.length === 0 ? {} : { scope: modelScopes });
3177
- const modelOverrideFromParent = inheritsParentModel(params.model as string | undefined, a.model, parentModel);
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
- thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
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
- modelScopes.length === 0 ? {} : { scope: modelScopes },
3639
+ {
3640
+ ...(modelScopes.length === 0 ? {} : { scope: modelScopes }),
3641
+ source: modelOrigin === "explicit" ? "explicit" : "inherited",
3642
+ },
3587
3643
  );
3588
- const modelOverrideFromParent = inheritsParentModel(params.model as string | undefined, agentConfig.model, parentModel);
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
- thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
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 = `subagent_wait({ id: "${runId}" }). Use ${statusRecovery}`;
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 subagent_wait({ id: "${runId}", nonBlocking: true }), or use ${statusRecovery}`
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 ok = result.isError !== true && !detached && !interrupted && !stopped;
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({ workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId, claimedOutputPaths, producedOutputPaths: producedChildOutputPaths });
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
- host: runHostCommand,
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({ workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: _id, claimedOutputPaths, producedOutputPaths: producedChildOutputPaths });
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
- ...(!delegatedWorkflowPermit ? { host: runHostCommand } : {}),
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
- { primaryModelFromParent: modelOverrideFromParent ?? inheritsParentModel(modelOverride, agentConfig?.model, parentModel) },
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
- const loaded = loadWorkflowScriptPath(normalized.params, ctx.cwd);
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: normalized.params.action ? "management" : "workflow", results: [] } });
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.execute(id, params, signal, undefined, ctx);
6911
+ return ownerExecutor.executePublic(id, params, signal, undefined, ctx);
6695
6912
  };
6696
6913
 
6697
6914
  return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };