pi-subagents 0.64.0 → 0.65.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 (89) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +2 -2
  3. package/agents/reviewer.md +1 -1
  4. package/agents/scout.md +1 -1
  5. package/docs/agents.md +18 -16
  6. package/docs/configuration.md +7 -15
  7. package/docs/extension-api.md +15 -6
  8. package/docs/missions.md +2 -0
  9. package/docs/observability.md +10 -12
  10. package/docs/tool-reference.md +7 -6
  11. package/docs/watchdog.md +1 -1
  12. package/docs/workflows.md +5 -3
  13. package/package.json +3 -4
  14. package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  15. package/skills/pi-subagents/references/execution-controls.md +2 -2
  16. package/src/agents/agent-management.ts +47 -15
  17. package/src/api/capability-ceiling.ts +0 -1
  18. package/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
  19. package/src/api/preflight.ts +2 -3
  20. package/src/extension/doctor.ts +2 -10
  21. package/src/extension/fanout-child.ts +9 -11
  22. package/src/extension/index.ts +27 -5
  23. package/src/extension/public-execution.ts +14 -0
  24. package/src/extension/rpc.ts +3 -2
  25. package/src/extension/schemas.ts +2 -1
  26. package/src/extension/tool-description.ts +9 -8
  27. package/src/intercom/native-supervisor-channel.ts +138 -60
  28. package/src/intercom/supervisor-ui.ts +243 -0
  29. package/src/runs/background/async-execution.ts +43 -24
  30. package/src/runs/background/async-job-tracker.ts +11 -0
  31. package/src/runs/background/async-resume.ts +11 -2
  32. package/src/runs/background/control-channel.ts +2 -204
  33. package/src/runs/background/process-terminal.ts +1 -1
  34. package/src/runs/background/run-child-session.ts +613 -0
  35. package/src/runs/background/run-status.ts +0 -1
  36. package/src/runs/background/runner-aliases.ts +125 -0
  37. package/src/runs/background/runner-child-sessions.ts +31 -0
  38. package/src/runs/background/scheduled-runs.ts +18 -4
  39. package/src/runs/background/subagent-runner.ts +184 -901
  40. package/src/runs/foreground/async-steering-action.ts +1 -17
  41. package/src/runs/foreground/execution.ts +189 -377
  42. package/src/runs/foreground/foreground-control.ts +4 -0
  43. package/src/runs/foreground/subagent-executor.ts +100 -57
  44. package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
  45. package/src/runs/shared/abort-recovery.ts +3 -3
  46. package/src/runs/shared/capability-ceiling.ts +1 -2
  47. package/src/runs/shared/child-hooks.ts +25 -0
  48. package/src/runs/shared/child-identity.ts +13 -2
  49. package/src/runs/shared/child-launch.ts +314 -0
  50. package/src/runs/shared/child-lifecycle.ts +25 -0
  51. package/src/runs/shared/child-runtime-config.ts +126 -0
  52. package/src/runs/shared/child-session.ts +342 -0
  53. package/src/runs/shared/child-tool-plan.ts +530 -0
  54. package/src/runs/shared/claude-code-adapter.ts +5 -1
  55. package/src/runs/shared/completion-guard.ts +1 -1
  56. package/src/runs/shared/external-cli-preflight.ts +16 -0
  57. package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  58. package/src/runs/shared/model-exclusions.ts +82 -14
  59. package/src/runs/shared/model-fallback.ts +47 -4
  60. package/src/runs/shared/nested-events.ts +29 -45
  61. package/src/runs/shared/nested-path.ts +0 -14
  62. package/src/runs/shared/orca-progress-tabs.ts +12 -7
  63. package/src/runs/shared/parallel-utils.ts +0 -2
  64. package/src/runs/shared/permissions.ts +0 -13
  65. package/src/runs/shared/process-signal.ts +4 -1
  66. package/src/runs/shared/run-fanout-budget.ts +0 -13
  67. package/src/runs/shared/runtime-acknowledged-extensions.ts +0 -27
  68. package/src/runs/shared/structured-output.ts +17 -4
  69. package/src/runs/shared/subagent-control.ts +2 -0
  70. package/src/runs/shared/subagent-prompt-runtime.ts +87 -384
  71. package/src/runs/shared/tool-availability.ts +18 -62
  72. package/src/runs/shared/tool-budget.ts +0 -14
  73. package/src/runs/shared/worktree-cleanup-plan.ts +25 -6
  74. package/src/runs/shared/worktree.ts +117 -30
  75. package/src/shared/child-session-name.ts +1 -1
  76. package/src/shared/jsonl-writer.ts +11 -0
  77. package/src/shared/thinking-ceiling.ts +0 -6
  78. package/src/shared/types.ts +55 -28
  79. package/src/shared/utils.ts +3 -4
  80. package/src/slash/slash-commands.ts +0 -6
  81. package/src/tui/fleet.ts +0 -1
  82. package/src/tui/render.ts +233 -31
  83. package/src/watchdog/child-status.ts +0 -1
  84. package/src/watchdog/register-child.ts +12 -12
  85. package/src/workflows/scripted-workflow.ts +48 -1
  86. package/src/runs/shared/child-protocol.ts +0 -415
  87. package/src/runs/shared/pi-args.ts +0 -1059
  88. package/src/runs/shared/subagent-startup-retry.ts +0 -116
  89. package/src/shared/post-exit-stdio-guard.ts +0 -85
@@ -14,6 +14,7 @@ interface BeginForegroundChildInput {
14
14
  thinking?: string;
15
15
  interrupt: () => boolean;
16
16
  detach?: () => boolean;
17
+ steer?: ForegroundChildControl["steer"];
17
18
  }
18
19
 
19
20
  function copyProgress(target: ForegroundChildControl, progress: AgentProgress | undefined): void {
@@ -56,6 +57,7 @@ function syncCurrentChild(control: ForegroundRunControl, child: ForegroundChildC
56
57
  control.toolCount = child.toolCount;
57
58
  control.interrupt = child.interrupt;
58
59
  control.detach = child.detach;
60
+ control.steer = child.steer;
59
61
  control.updatedAt = child.updatedAt;
60
62
  }
61
63
 
@@ -79,6 +81,7 @@ function clearCurrentChild(control: ForegroundRunControl): void {
79
81
  control.toolCount = undefined;
80
82
  control.interrupt = undefined;
81
83
  control.detach = undefined;
84
+ control.steer = undefined;
82
85
  }
83
86
 
84
87
  export function retainForegroundSchedulingOwner(control: ForegroundRunControl): void {
@@ -122,6 +125,7 @@ export function beginForegroundChild(control: ForegroundRunControl, input: Begin
122
125
  return true;
123
126
  };
124
127
  }
128
+ if (input.steer) child.steer = input.steer;
125
129
  control.activeChildren ??= new Map();
126
130
  control.activeChildren.set(input.index, child);
127
131
  registerLivePromptAudit(control, input.index, input.authoredTask, input.effectivePrompt, {
@@ -59,7 +59,7 @@ import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
59
59
  import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
60
60
  import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
61
61
  import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
62
- import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, decodeRunFanoutBudgetDescriptor, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, RUN_FANOUT_BUDGET_ENV, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
62
+ import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
63
63
  import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
64
64
  import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
65
65
  import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
@@ -84,11 +84,8 @@ import { deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, type Ste
84
84
  import { updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
85
85
  import { canQueueRetainedAsyncFollowUp, steerAsyncRun } from "./async-steering-action.ts";
86
86
  import {
87
- removeWorkflowForegroundSteeringRoute,
88
87
  resolveWorkflowForegroundSteeringTarget,
89
88
  steerWorkflowForegroundTarget,
90
- workflowForegroundSteeringDir,
91
- workflowForegroundSteeringLaunchOptions,
92
89
  } from "./workflow-foreground-steering.ts";
93
90
  import { stopAsyncRun } from "./async-stop-action.ts";
94
91
  import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
@@ -96,10 +93,11 @@ import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletio
96
93
  import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
97
94
  import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
98
95
  import { resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
99
- import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
96
+ import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, resolveNestedAsyncDir, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedParentAddress, type NestedRoute, type NestedRunResolutionScope } from "../shared/nested-events.ts";
97
+ import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
100
98
  import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
101
99
  import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
102
- import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/child-identity.ts";
100
+ import { isStoppableAsyncStatusStep, resolveAsyncStatusChild, stopStoppableAsyncStatusChildren } from "../shared/child-identity.ts";
103
101
  import { inspectSubagentStatus } from "../background/run-status.ts";
104
102
  import { getExternalJobProvider } from "../../api/external-job-provider.ts";
105
103
  import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.ts";
@@ -153,6 +151,9 @@ import {
153
151
  type Details,
154
152
  type ExtensionConfig,
155
153
  type ForegroundResumeChild,
154
+ type ForegroundChildSessionControls,
155
+ type ForegroundSteerInput,
156
+ type ForegroundSteerOutcome,
156
157
  type ForegroundRunControl,
157
158
  type HostStepNodeV1,
158
159
  type IntercomBridgeConfig,
@@ -351,6 +352,8 @@ export interface SubagentParamsLike {
351
352
  tasks?: TaskParam[];
352
353
  concurrency?: number;
353
354
  worktree?: boolean;
355
+ /** Git ref used as the managed worktree base. */
356
+ baseRef?: string;
354
357
  context?: "fresh" | "fork" | "profile";
355
358
  /** Per-run intercom bridge config. It replaces the global config for this launch only. */
356
359
  intercomBridge?: IntercomBridgeConfig;
@@ -436,6 +439,20 @@ interface ExecutorDeps {
436
439
  activateSupervisorTransport?: () => void;
437
440
  refreshResultDelivery?: () => void;
438
441
  kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
442
+ /** Set when this executor runs inside a child session; carries the runtime settings the host passes instead of environment variables. */
443
+ childRuntime?: ChildRuntimeConfig;
444
+ }
445
+
446
+ function inheritedNestedRoute(deps: Pick<ExecutorDeps, "childRuntime">): NestedRoute | undefined {
447
+ return inheritedNestedRouteOf(deps.childRuntime);
448
+ }
449
+
450
+ function inheritedNestedParentAddress(deps: Pick<ExecutorDeps, "childRuntime">): NestedParentAddress | undefined {
451
+ return inheritedNestedParentAddressOf(deps.childRuntime);
452
+ }
453
+
454
+ function inheritedRunFanoutBudget(deps: Pick<ExecutorDeps, "childRuntime">): RunFanoutBudgetDescriptor | undefined {
455
+ return deps.childRuntime?.runFanoutBudget;
439
456
  }
440
457
 
441
458
  type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => string | undefined;
@@ -512,7 +529,6 @@ function loadWorkflowScriptPath(params: SubagentParamsLike, runtimeCwd: string):
512
529
  function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
513
530
  const control = state.foregroundControls.get(runId);
514
531
  if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
515
- if (control) removeWorkflowForegroundSteeringRoute(control);
516
532
  state.foregroundControls.delete(runId);
517
533
  if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
518
534
  return true;
@@ -565,8 +581,8 @@ function formatForegroundActivity(control: SubagentState["foregroundControls"] e
565
581
 
566
582
  function nestedResolutionScopeForExecutor(deps: ExecutorDeps): NestedRunResolutionScope | undefined {
567
583
  if (deps.allowMutatingManagementActions !== false) return undefined;
568
- const route = resolveInheritedNestedRouteFromEnv();
569
- const address = route ? resolveNestedParentAddressFromEnv() : undefined;
584
+ const route = inheritedNestedRoute(deps);
585
+ const address = route ? inheritedNestedParentAddress(deps) : undefined;
570
586
  return {
571
587
  routes: route ? [route] : [],
572
588
  ...(address ? { descendantOf: { parentRunId: address.parentRunId, ...(address.parentStepIndex !== undefined ? { parentStepIndex: address.parentStepIndex } : {}) } } : {}),
@@ -1299,6 +1315,7 @@ function appendStepToAsyncChain(input: {
1299
1315
  modelScope: discoveredForAppend.modelScope,
1300
1316
  interactive: input.ctx.hasUI,
1301
1317
  permissions: input.deps.config.permissions,
1318
+ childRuntime: input.deps.childRuntime,
1302
1319
  });
1303
1320
  const built = buildAsyncRunnerSteps(resolved.id, compactOptional<Parameters<typeof buildAsyncRunnerSteps>[1]>({
1304
1321
  chain: wrapChainTasksForFork(chain, contextPolicy),
@@ -1311,7 +1328,7 @@ function appendStepToAsyncChain(input: {
1311
1328
  cwd: status.cwd ?? input.requestCwd,
1312
1329
  chainSkills,
1313
1330
  dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
1314
- maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
1331
+ maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
1315
1332
  waitToolEnabled: input.deps.waitToolEnabled,
1316
1333
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
1317
1334
  contextForAgent: contextPolicy.contextForAgent,
@@ -1637,11 +1654,11 @@ async function resumeExternalJobFollowUp(input: {
1637
1654
  return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId, asyncDir, duplicate: true, interactive: input.ctx.hasUI });
1638
1655
  }
1639
1656
 
1640
- const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth);
1657
+ const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
1641
1658
  if (depthState.blocked) {
1642
1659
  return { content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depthState.depth}, max=${depthState.maxDepth}). Complete the follow-up directly instead.` }], isError: true, details: { mode: "management", results: [] } };
1643
1660
  }
1644
- const topLevelResume = depthState.depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.deps.state.workflowControllers?.has(input.target.runId);
1661
+ const topLevelResume = depthState.depth === 0 && !inheritedNestedRoute(input.deps) && !input.deps.state.workflowControllers?.has(input.target.runId);
1645
1662
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
1646
1663
  try {
1647
1664
  activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
@@ -1679,13 +1696,14 @@ async function resumeExternalJobFollowUp(input: {
1679
1696
  modelScope: input.modelScope,
1680
1697
  interactive: input.ctx.hasUI,
1681
1698
  permissions: input.deps.config.permissions,
1699
+ childRuntime: input.deps.childRuntime,
1682
1700
  }),
1683
1701
  cwd: input.effectiveCwd,
1684
1702
  artifactsDir,
1685
1703
  artifactConfig,
1686
1704
  shareEnabled: false,
1687
1705
  ...(input.parentSessionFile ? { sessionRoot: input.deps.getSubagentSessionRoot(input.parentSessionFile) } : {}),
1688
- maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
1706
+ maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
1689
1707
  waitToolEnabled: input.deps.waitToolEnabled,
1690
1708
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
1691
1709
  worktreeSetupHook: input.deps.config.worktreeSetupHook,
@@ -1796,7 +1814,7 @@ async function resumeAsyncRun(input: {
1796
1814
  };
1797
1815
  }
1798
1816
 
1799
- const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth);
1817
+ const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
1800
1818
  if (blocked) {
1801
1819
  return {
1802
1820
  content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depth}, max=${maxDepth}). Complete the follow-up directly instead.` }],
@@ -1885,7 +1903,7 @@ async function resumeAsyncRun(input: {
1885
1903
  };
1886
1904
  }
1887
1905
  const runId = randomUUID();
1888
- const topLevelResume = depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.params.workflowParentRunId;
1906
+ const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
1889
1907
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
1890
1908
  try {
1891
1909
  activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
@@ -1931,6 +1949,7 @@ async function resumeAsyncRun(input: {
1931
1949
  modelScope,
1932
1950
  interactive: input.ctx.hasUI,
1933
1951
  permissions: input.deps.config.permissions,
1952
+ childRuntime: input.deps.childRuntime,
1934
1953
  }),
1935
1954
  availableModels,
1936
1955
  cwd: effectiveCwd,
@@ -1943,12 +1962,13 @@ async function resumeAsyncRun(input: {
1943
1962
  agentContract: input.params.agentContract,
1944
1963
  fast: input.params.fast,
1945
1964
  dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
1946
- maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
1965
+ maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
1947
1966
  waitToolEnabled: input.deps.waitToolEnabled,
1948
1967
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
1949
1968
  worktreeSetupHook: input.deps.config.worktreeSetupHook,
1950
1969
  worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
1951
1970
  worktreeBaseDir: input.deps.config.worktreeBaseDir,
1971
+ baseRef: input.params.baseRef ?? target.recoveryDescriptor?.baseRef,
1952
1972
  worktreeProvider: input.deps.config.worktreeProvider,
1953
1973
  worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
1954
1974
  controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
@@ -1985,8 +2005,11 @@ async function resumeAsyncRun(input: {
1985
2005
  if (target.source === "async" && asyncReviveRequiresRecoveryDescriptor(target)) {
1986
2006
  return { content: [{ type: "text", text: `Async child '${target.runId}' is missing its required run fan-out recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
1987
2007
  }
2008
+ if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
2009
+ return { content: [{ type: "text", text: "Cannot resume with baseRef: retained managed-worktree children continue in their existing worktree. Start a new worktree run from that base ref instead." }], isError: true, details: { mode: "management", results: [] } };
2010
+ }
1988
2011
  const runId = randomUUID();
1989
- const topLevelResume = depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.params.workflowParentRunId;
2012
+ const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
1990
2013
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
1991
2014
  try {
1992
2015
  activeAsyncCapacity = !topLevelResume ? undefined : target.source === "async"
@@ -2034,6 +2057,7 @@ async function resumeAsyncRun(input: {
2034
2057
  modelScope,
2035
2058
  interactive: input.ctx.hasUI,
2036
2059
  permissions: input.deps.config.permissions,
2060
+ childRuntime: input.deps.childRuntime,
2037
2061
  }),
2038
2062
  cwd: effectiveCwd,
2039
2063
  maxOutput: input.params.maxOutput ?? recoveryDescriptor?.maxOutput,
@@ -2058,12 +2082,13 @@ async function resumeAsyncRun(input: {
2058
2082
  thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
2059
2083
  extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
2060
2084
  outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
2061
- maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
2085
+ maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
2062
2086
  waitToolEnabled: input.deps.waitToolEnabled,
2063
2087
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
2064
2088
  worktreeSetupHook: input.deps.config.worktreeSetupHook,
2065
2089
  worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
2066
2090
  worktreeBaseDir: input.deps.config.worktreeBaseDir,
2091
+ baseRef: input.params.baseRef ?? recoveryDescriptor?.baseRef,
2067
2092
  worktreeProvider: input.deps.config.worktreeProvider,
2068
2093
  worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
2069
2094
  // A retained async child already owns the recorded worktree. Resume it in
@@ -3187,9 +3212,10 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3187
3212
  modelScope: data.modelScope,
3188
3213
  interactive: ctx.hasUI,
3189
3214
  permissions: deps.config.permissions,
3215
+ childRuntime: deps.childRuntime,
3190
3216
  });
3191
3217
  const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
3192
- const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
3218
+ const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
3193
3219
  const currentProvider = parentModel?.provider;
3194
3220
  const controlIntercomTarget = resolveRunLevelIntercomTarget(intercomBridge, contextPolicy);
3195
3221
  const childIntercomTarget = resolveChildIntercomTargetFactory(intercomBridge, contextPolicy, id);
@@ -3269,6 +3295,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3269
3295
  worktreeSetupHook: deps.config.worktreeSetupHook,
3270
3296
  worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
3271
3297
  worktreeBaseDir: deps.config.worktreeBaseDir,
3298
+ baseRef: params.baseRef,
3272
3299
  worktreeProvider: deps.config.worktreeProvider,
3273
3300
  worktreeBranchPrefix: deps.config.worktreeBranchPrefix,
3274
3301
  controlConfig,
@@ -3307,6 +3334,7 @@ function createSingleWorktreeSetup(
3307
3334
  setupHook: ExtensionConfig["worktreeSetupHook"],
3308
3335
  setupHookTimeoutMs: ExtensionConfig["worktreeSetupHookTimeoutMs"],
3309
3336
  baseDir: ExtensionConfig["worktreeBaseDir"],
3337
+ baseRef: string | undefined,
3310
3338
  provider: ExtensionConfig["worktreeProvider"],
3311
3339
  branchPrefix: ExtensionConfig["worktreeBranchPrefix"],
3312
3340
  label?: string,
@@ -3322,6 +3350,7 @@ function createSingleWorktreeSetup(
3322
3350
  ? { hookPath: setupHook, ...(setupHookTimeoutMs === undefined ? {} : { timeoutMs: setupHookTimeoutMs }) }
3323
3351
  : undefined,
3324
3352
  baseDir,
3353
+ baseRef,
3325
3354
  provider,
3326
3355
  branchPrefix,
3327
3356
  labels: [label],
@@ -3674,7 +3703,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3674
3703
  const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
3675
3704
  let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
3676
3705
  const effectiveOutputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
3677
- const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
3706
+ const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
3678
3707
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
3679
3708
 
3680
3709
 
@@ -3688,6 +3717,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3688
3717
  deps.config.worktreeSetupHook,
3689
3718
  deps.config.worktreeSetupHookTimeoutMs,
3690
3719
  deps.config.worktreeBaseDir,
3720
+ params.baseRef,
3691
3721
  deps.config.worktreeProvider,
3692
3722
  deps.config.worktreeBranchPrefix,
3693
3723
  params.lane?.key ?? params.workflowKey,
@@ -3741,6 +3771,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3741
3771
  }
3742
3772
  const interruptController = new AbortController();
3743
3773
  let detachForeground: ((reason?: string) => boolean) | undefined;
3774
+ let childSessionControls: ForegroundChildSessionControls | undefined;
3744
3775
  const foregroundControl = deps.state.foregroundControls.get(runId);
3745
3776
  if (foregroundControl) {
3746
3777
  const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin));
@@ -3761,6 +3792,19 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3761
3792
  return true;
3762
3793
  },
3763
3794
  detach: () => detachForeground?.("user request") === true,
3795
+ steer: async (input: ForegroundSteerInput): Promise<ForegroundSteerOutcome> => {
3796
+ if (!childSessionControls) return { state: "failed", reason: CHILD_SESSION_NOT_RUNNING_YET };
3797
+ try {
3798
+ if (input.mode === "follow_up") {
3799
+ await childSessionControls.followUp(input.message);
3800
+ return { state: "queued" };
3801
+ }
3802
+ await childSessionControls.steer(input.message);
3803
+ return { state: "delivered" };
3804
+ } catch (error) {
3805
+ return { state: "failed", reason: error instanceof Error ? error.message : String(error) };
3806
+ }
3807
+ },
3764
3808
  }));
3765
3809
  }
3766
3810
 
@@ -3783,7 +3827,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3783
3827
  runtimeSnapshotHost: deps.pi,
3784
3828
  parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
3785
3829
  llmIntentArbiter: createTaskMutationArbiter(ctx),
3786
- ...workflowForegroundSteeringLaunchOptions(foregroundControl, 0),
3830
+ childRuntime: deps.childRuntime,
3831
+ onChildSession: (controls) => { childSessionControls = controls; },
3787
3832
  context: data.contextPolicy.contextForAgent(params.agent!),
3788
3833
  unknownAgentDiagnosticContext: data.unknownAgentDiagnosticContext,
3789
3834
  runFanoutBudget: params.runFanoutAdmitted ? data.runFanoutBudget : { ...data.runFanoutBudget, parentPath: `${data.runFanoutBudget.parentPath ? `${data.runFanoutBudget.parentPath}/` : ""}single` },
@@ -4215,6 +4260,7 @@ function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): Wo
4215
4260
  };
4216
4261
  }
4217
4262
 
4263
+ const CHILD_SESSION_NOT_RUNNING_YET = "Child session is not running yet.";
4218
4264
  const MAX_WORKFLOW_RESUME_HINT_BYTES = 1024;
4219
4265
  const MAX_WORKFLOW_CHILD_RUN_ID_BYTES = 256;
4220
4266
  const WORKFLOW_RESUME_HINT_PARENT_STATES = new Set(["complete", "failed", "partial"]);
@@ -4350,7 +4396,6 @@ export async function steerWorkflowChildByKey(input: {
4350
4396
  while (true) {
4351
4397
  const control = [...input.state.foregroundControls.values()].find((candidate) => candidate.parentWorkflowRunId === input.workflowRunId
4352
4398
  && candidate.workflowKey === input.key
4353
- && Boolean(candidate.workflowSteeringDir)
4354
4399
  && (candidate.activeChildren?.size ?? 0) > 0);
4355
4400
  if (control) {
4356
4401
  const result = await steerWorkflowForegroundTarget({
@@ -4358,10 +4403,9 @@ export async function steerWorkflowChildByKey(input: {
4358
4403
  message: input.message,
4359
4404
  mode: input.options.mode,
4360
4405
  index: input.options.index,
4361
- ackTimeoutMs: Math.max(1, deadline - Date.now()),
4362
- signal: input.signal,
4363
4406
  });
4364
- return workflowSteerReceipt(input.key, result);
4407
+ // The control registers before its child session exists; keep polling until the steer can route.
4408
+ if (!result.details.steering?.targets.some((target) => target.reason === CHILD_SESSION_NOT_RUNNING_YET) || Date.now() >= deadline) return workflowSteerReceipt(input.key, result);
4365
4409
  }
4366
4410
 
4367
4411
  const workflowStatus = readStatus(path.join(asyncDirRoot, input.workflowRunId));
@@ -4434,6 +4478,7 @@ export function prepareWorkflowLaunchParams(
4434
4478
  const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
4435
4479
  const intercomBridge = childParams.intercomBridge ?? workflowDefaults.intercomBridge;
4436
4480
  const worktree = childParams.worktree ?? workflowDefaults.worktree;
4481
+ const baseRef = Object.hasOwn(childParams, "baseRef") ? childParams.baseRef : undefined;
4437
4482
  const outputSchema = Object.hasOwn(childParams, "outputSchema") ? childParams.outputSchema : workflowDefaults.outputSchema;
4438
4483
  if (outputSchema !== undefined) assertJsonSchemaObject(outputSchema, "outputSchema");
4439
4484
  const agentContract = Object.hasOwn(childParams, "agentContract") ? childParams.agentContract : workflowDefaults.agentContract;
@@ -4453,6 +4498,7 @@ export function prepareWorkflowLaunchParams(
4453
4498
  workflowKey,
4454
4499
  ...(lane ? { lane } : {}),
4455
4500
  ...(worktree !== undefined ? { worktree: worktree as boolean } : {}),
4501
+ ...(baseRef !== undefined ? { baseRef: baseRef as string } : {}),
4456
4502
  ...(outputSchema !== undefined ? { outputSchema: outputSchema as JsonSchemaObject } : {}),
4457
4503
  ...(agentContract !== undefined ? { agentContract: agentContract as AgentContract } : {}),
4458
4504
  ...(acceptance !== undefined ? { acceptance: acceptance as AcceptanceInput } : {}),
@@ -4817,11 +4863,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4817
4863
  const scriptFirstLine = requestParams.workflowScript.split(/\r?\n/).map((line) => line.trim()).find(Boolean) || "Workflow";
4818
4864
  const boundedScriptPreview = scriptFirstLine.length > 100 ? `${scriptFirstLine.slice(0, 97)}...` : scriptFirstLine;
4819
4865
  const derivedObjective = previewAgent ? `Workflow: ${previewAgent}` : boundedScriptPreview;
4820
- const workflowDepth = checkSubagentDepth(deps.config.maxSubagentDepth).depth;
4866
+ const workflowDepth = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime).depth;
4821
4867
  const asyncWorkflow = requestParams.async !== false;
4822
4868
  const topLevelAsyncWorkflow = asyncWorkflow
4823
4869
  && workflowDepth === 0
4824
- && !resolveInheritedNestedRouteFromEnv()
4870
+ && !inheritedNestedRoute(deps)
4825
4871
  && !requestParams.workflowParentRunId;
4826
4872
  const workflowRunId = asyncWorkflow ? randomUUID() : undefined;
4827
4873
  let workflowCapacity: ActiveAsyncCapacityHandle | undefined;
@@ -4846,7 +4892,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4846
4892
  let workflowFanoutBudget: RunFanoutBudgetDescriptor;
4847
4893
  try {
4848
4894
  workflowFanoutBudget = requestParams.runFanoutBudget
4849
- ?? decodeRunFanoutBudgetDescriptor(process.env[RUN_FANOUT_BUDGET_ENV])
4895
+ ?? inheritedRunFanoutBudget(deps)
4850
4896
  ?? createRunFanoutBudget(_id, requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
4851
4897
  } catch (error) {
4852
4898
  workflowCapacity?.rollback();
@@ -5205,6 +5251,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5205
5251
  globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
5206
5252
  timeoutMs: timeout,
5207
5253
  signal: controller.signal,
5254
+ continueAfterAbortWhenChildrenSettled: (abortError) => {
5255
+ if (abortError.message !== "Workflow stopped because the extension session was replaced or reloaded.") return false;
5256
+ const activeAsyncChild = [...(deps.state.asyncJobs?.values() ?? [])].some((job) => job.parentWorkflowRunId === workflowRunId && (job.status === "queued" || job.status === "running"));
5257
+ const activeForegroundChild = [...deps.state.foregroundControls.values()].some((control) => control.parentWorkflowRunId === workflowRunId && (control.activeChildren?.size ?? 0) > 0);
5258
+ return !activeAsyncChild && !activeForegroundChild;
5259
+ },
5208
5260
  registerStopChild: (stop) => {
5209
5261
  if (stop) deps.state.workflowChildStops?.set(workflowRunId, stop);
5210
5262
  else deps.state.workflowChildStops?.delete(workflowRunId);
@@ -5992,7 +6044,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5992
6044
  if (directoryStatus?.mode === "workflow") {
5993
6045
  const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: directoryStatus.runId || runId, asyncDirRoot: DIRS.async });
5994
6046
  if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
5995
- return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
6047
+ return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index });
5996
6048
  }
5997
6049
  if (location.asyncDir) {
5998
6050
  const unsupported = externalRunnerControlError(location.asyncDir, "steer");
@@ -6032,14 +6084,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6032
6084
  if (resolved?.kind === "foreground") {
6033
6085
  const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, childRunId: resolved.id, asyncDirRoot: DIRS.async });
6034
6086
  if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
6035
- return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
6087
+ return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index });
6036
6088
  }
6037
6089
  if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
6038
6090
  const resolvedStatus = resolved.location.asyncDir ? readStatus(resolved.location.asyncDir) : null;
6039
6091
  if (resolvedStatus?.mode === "workflow") {
6040
6092
  const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: resolvedStatus.runId || resolved.id, asyncDirRoot: DIRS.async });
6041
6093
  if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
6042
- return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
6094
+ return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index });
6043
6095
  }
6044
6096
  if (resolved.location.asyncDir) {
6045
6097
  const unsupported = externalRunnerControlError(resolved.location.asyncDir, "steer");
@@ -6118,9 +6170,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6118
6170
  if (action === "stop") {
6119
6171
  const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
6120
6172
  const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
6121
- if (workflowController) {
6173
+ if (workflowController && targetRunId) {
6174
+ const stopChild = deps.state.workflowChildStops?.get(targetRunId);
6122
6175
  if (paramsWithResolvedCwd.childId !== undefined) {
6123
- const workflowRunId = targetRunId!;
6176
+ const workflowRunId = targetRunId;
6124
6177
  const asyncJob = deps.state.asyncJobs.get(workflowRunId);
6125
6178
  if (!asyncJob?.asyncDir) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
6126
6179
  const status = readStatus(asyncJob.asyncDir);
@@ -6128,7 +6181,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6128
6181
  const resolution = resolveAsyncStatusChild(status, paramsWithResolvedCwd.childId);
6129
6182
  if (!resolution.ok) return { content: [{ type: "text", text: resolution.message }], isError: true, details: { mode: "management", results: [] } };
6130
6183
  if (!isStoppableAsyncStatusStep(resolution.child.step)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in async run '${targetRunId}' is ${resolution.child.step.status}; stop only supports pending or running children.` }], isError: true, details: { mode: "management", results: [] } };
6131
- const stopChild = deps.state.workflowChildStops?.get(targetRunId!);
6132
6184
  if (!stopChild) return { content: [{ type: "text", text: `Workflow ${targetRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
6133
6185
  if (!stopChild(resolution.child.id, `Workflow child '${resolution.child.id}' stopped.`)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in workflow ${workflowRunId} is not available to stop.` }], isError: true, details: { mode: "management", results: [] } };
6134
6186
  try {
@@ -6153,6 +6205,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6153
6205
  }
6154
6206
  return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
6155
6207
  }
6208
+ const asyncJob = deps.state.asyncJobs.get(targetRunId);
6209
+ const status = asyncJob?.asyncDir ? readStatus(asyncJob.asyncDir) : undefined;
6210
+ if (status) stopStoppableAsyncStatusChildren(status, stopChild, "Workflow stopped.");
6156
6211
  workflowController.abort(new Error("Workflow stopped."));
6157
6212
  return { content: [{ type: "text", text: `Stop requested for async workflow ${targetRunId}.` }], details: { mode: "management", results: [] } };
6158
6213
  }
@@ -6248,7 +6303,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6248
6303
  });
6249
6304
  }
6250
6305
 
6251
- const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth);
6306
+ const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
6252
6307
  if (blocked) {
6253
6308
  return {
6254
6309
  content: [
@@ -6323,8 +6378,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6323
6378
  });
6324
6379
  const agents = applyScopedIntercomBridgeToAgents(discoveredAgents, intercomBridge, contextPolicy);
6325
6380
  const runId = randomUUID();
6326
- const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
6327
- const nestedParentAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
6381
+ const inheritedNestedRouteValue = inheritedNestedRoute(deps);
6382
+ const nestedParentAddress = inheritedNestedRouteValue ? inheritedNestedParentAddress(deps) : undefined;
6328
6383
  const shareEnabled = effectiveParams.share === true;
6329
6384
  const hasChain = (effectiveParams.chain?.length ?? 0) > 0;
6330
6385
  const hasTasks = (effectiveParams.tasks?.length ?? 0) > 0;
@@ -6445,7 +6500,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6445
6500
  const asyncRunId = requestedWorkflowChildAsyncId && path.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId
6446
6501
  ? requestedWorkflowChildAsyncId
6447
6502
  : randomUUID();
6448
- const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRoute && !effectiveParams.workflowParentRunId;
6503
+ const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRouteValue && !effectiveParams.workflowParentRunId;
6449
6504
  const topLevelAsync = effectiveAsync && topLevelAsyncCapacityEligible;
6450
6505
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
6451
6506
  if (topLevelAsync) {
@@ -6470,9 +6525,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6470
6525
 
6471
6526
  let runFanoutBudget: RunFanoutBudgetDescriptor;
6472
6527
  try {
6473
- const inheritedRunFanoutBudget = effectiveParams.runFanoutBudget ? undefined : decodeRunFanoutBudgetDescriptor(process.env[RUN_FANOUT_BUDGET_ENV]);
6528
+ const inheritedRunFanoutBudgetValue = effectiveParams.runFanoutBudget ? undefined : inheritedRunFanoutBudget(deps);
6474
6529
  runFanoutBudget = effectiveParams.runFanoutBudget
6475
- ?? (inheritedRunFanoutBudget ? { ...inheritedRunFanoutBudget, parentPath: `${inheritedRunFanoutBudget.parentPath ? `${inheritedRunFanoutBudget.parentPath}/` : ""}${runId}` } : undefined)
6530
+ ?? (inheritedRunFanoutBudgetValue ? { ...inheritedRunFanoutBudgetValue, parentPath: `${inheritedRunFanoutBudgetValue.parentPath ? `${inheritedRunFanoutBudgetValue.parentPath}/` : ""}${runId}` } : undefined)
6476
6531
  ?? createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
6477
6532
  if (!effectiveParams.runFanoutAdmitted) claimRunFanoutBatch(runFanoutBudget, staticRunFanoutPaths(effectiveParams));
6478
6533
  } catch (error) {
@@ -6480,7 +6535,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6480
6535
  if (error instanceof RunFanoutLimitError) return runFanoutErrorResult(error, foregroundMode);
6481
6536
  return buildRequestedModeError(effectiveParams, error instanceof Error ? error.message : String(error));
6482
6537
  }
6483
- const nestedRoute = inheritedNestedRoute ?? createNestedRoute(runId);
6538
+ const nestedRoute = inheritedNestedRouteValue ?? createNestedRoute(runId);
6484
6539
 
6485
6540
  const artifactConfig: ArtifactConfig = omitUndefinedProperties({
6486
6541
  ...DEFAULT_ARTIFACT_CONFIG,
@@ -6652,17 +6707,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6652
6707
  const foregroundDescription = selectedAgentNames.length === 1
6653
6708
  ? `${selectedAgentNames[0]} child`
6654
6709
  : `${selectedAgentNames.length} live children`;
6655
- const parentWorkflowStatus = effectiveParams.workflowParentRunId
6656
- ? readStatus(path.join(DIRS.async, effectiveParams.workflowParentRunId))
6657
- : null;
6658
- const workflowSteeringDir = effectiveParams.workflowParentRunId
6659
- && requestSessionId
6660
- && deps.state.workflowControllers?.has(effectiveParams.workflowParentRunId)
6661
- && parentWorkflowStatus?.mode === "workflow"
6662
- && (parentWorkflowStatus.state === "running" || parentWorkflowStatus.state === "queued")
6663
- && parentWorkflowStatus.sessionId === requestSessionId
6664
- ? workflowForegroundSteeringDir(DIRS.async, effectiveParams.workflowParentRunId, runId)
6665
- : undefined;
6666
6710
  const foregroundControl: ForegroundRunControl | undefined = effectiveAsync
6667
6711
  ? undefined
6668
6712
  : compactOptional<ForegroundRunControl>({
@@ -6671,7 +6715,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6671
6715
  mode: foregroundMode,
6672
6716
  ...(effectiveParams.workflowParentRunId ? { parentWorkflowRunId: effectiveParams.workflowParentRunId } : {}),
6673
6717
  ...(effectiveParams.workflowKey ? { workflowKey: effectiveParams.workflowKey } : {}),
6674
- workflowSteeringDir,
6675
6718
  startedAt: Date.now(),
6676
6719
  updatedAt: Date.now(),
6677
6720
  cwd: effectiveCwd,
@@ -6724,7 +6767,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6724
6767
  }
6725
6768
 
6726
6769
  const writeNestedForegroundEvent = (type: "subagent.nested.started" | "subagent.nested.completed", result?: AgentToolResult<Details>): void => {
6727
- if (!inheritedNestedRoute || !nestedParentAddress) return;
6770
+ if (!inheritedNestedRouteValue || !nestedParentAddress) return;
6728
6771
  const now = Date.now();
6729
6772
  const details = result?.details;
6730
6773
  const state = type === "subagent.nested.started"
@@ -6758,7 +6801,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6758
6801
  ? resolveSubagentIntercomTarget(runId, agentsForSummary[0], 0)
6759
6802
  : undefined;
6760
6803
  try {
6761
- writeNestedEvent(inheritedNestedRoute, compactOptional<Parameters<typeof writeNestedEvent>[1]>({
6804
+ writeNestedEvent(inheritedNestedRouteValue, compactOptional<Parameters<typeof writeNestedEvent>[1]>({
6762
6805
  type,
6763
6806
  ts: now,
6764
6807
  parentRunId: nestedParentAddress.parentRunId,
@@ -6769,7 +6812,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6769
6812
  parentStepIndex: nestedParentAddress.parentStepIndex,
6770
6813
  depth: nestedParentAddress.depth,
6771
6814
  path: nestedParentAddress.path,
6772
- ownerIntercomTarget: process.env.PI_SUBAGENT_INTERCOM_SESSION_NAME,
6815
+ ownerIntercomTarget: deps.childRuntime?.intercomSessionName,
6773
6816
  leafIntercomTarget,
6774
6817
  intercomTarget: leafIntercomTarget,
6775
6818
  ownerState: state === "running" ? "live" : "gone",
@@ -6880,7 +6923,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6880
6923
  const normalizedAction = typeof params.action === "string" ? params.action.trim() : params.action;
6881
6924
  const requestParams = normalizedAction ? { ...params, action: normalizedAction } : params;
6882
6925
  if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);
6883
- const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
6926
+ const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
6884
6927
  const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
6885
6928
  const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
6886
6929
  if (!runsForeground) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);