pi-subagents 0.63.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 (107) hide show
  1. package/CHANGELOG.md +57 -1
  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 +92 -114
  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 +6 -5
  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 +46 -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/notify.ts +41 -2
  34. package/src/runs/background/process-terminal.ts +1 -1
  35. package/src/runs/background/run-child-session.ts +613 -0
  36. package/src/runs/background/run-status.ts +0 -1
  37. package/src/runs/background/runner-aliases.ts +125 -0
  38. package/src/runs/background/runner-child-sessions.ts +31 -0
  39. package/src/runs/background/scheduled-runs.ts +18 -4
  40. package/src/runs/background/subagent-runner.ts +203 -898
  41. package/src/runs/foreground/async-steering-action.ts +1 -17
  42. package/src/runs/foreground/execution.ts +196 -378
  43. package/src/runs/foreground/foreground-control.ts +4 -0
  44. package/src/runs/foreground/subagent-executor.ts +121 -58
  45. package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
  46. package/src/runs/shared/abort-recovery.ts +3 -3
  47. package/src/runs/shared/acceptance.ts +10 -0
  48. package/src/runs/shared/async-status-projection.ts +11 -43
  49. package/src/runs/shared/capability-ceiling.ts +1 -2
  50. package/src/runs/shared/child-hooks.ts +25 -0
  51. package/src/runs/shared/child-identity.ts +13 -2
  52. package/src/runs/shared/child-launch.ts +314 -0
  53. package/src/runs/shared/child-lifecycle.ts +25 -0
  54. package/src/runs/shared/child-runtime-config.ts +126 -0
  55. package/src/runs/shared/child-session.ts +342 -0
  56. package/src/runs/shared/child-tool-plan.ts +530 -0
  57. package/src/runs/shared/claude-code-adapter.ts +5 -1
  58. package/src/runs/shared/completion-guard.ts +1 -1
  59. package/src/runs/shared/external-cli-preflight.ts +16 -0
  60. package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  61. package/src/runs/shared/model-exclusions.ts +82 -14
  62. package/src/runs/shared/model-fallback.ts +47 -4
  63. package/src/runs/shared/nested-events.ts +29 -45
  64. package/src/runs/shared/nested-path.ts +0 -14
  65. package/src/runs/shared/orca-progress-tabs.ts +12 -7
  66. package/src/runs/shared/parallel-utils.ts +0 -2
  67. package/src/runs/shared/permissions.ts +0 -13
  68. package/src/runs/shared/process-signal.ts +4 -1
  69. package/src/runs/shared/run-fanout-budget.ts +0 -13
  70. package/src/runs/shared/runtime-acknowledged-extensions.ts +0 -27
  71. package/src/runs/shared/structured-output.ts +17 -4
  72. package/src/runs/shared/subagent-control.ts +6 -2
  73. package/src/runs/shared/subagent-prompt-runtime.ts +87 -384
  74. package/src/runs/shared/tool-availability.ts +18 -62
  75. package/src/runs/shared/tool-budget.ts +0 -14
  76. package/src/runs/shared/worktree-cleanup-plan.ts +25 -6
  77. package/src/runs/shared/worktree.ts +117 -30
  78. package/src/shared/child-session-name.ts +1 -1
  79. package/src/shared/jsonl-writer.ts +11 -0
  80. package/src/shared/thinking-ceiling.ts +0 -6
  81. package/src/shared/types.ts +64 -30
  82. package/src/shared/utils.ts +3 -4
  83. package/src/slash/slash-commands.ts +0 -6
  84. package/src/tui/fleet.ts +0 -1
  85. package/src/tui/render.ts +238 -31
  86. package/src/watchdog/child-status.ts +54 -34
  87. package/src/watchdog/diff-tool.ts +77 -0
  88. package/src/watchdog/emission-guard.ts +5 -3
  89. package/src/watchdog/guidance.ts +20 -0
  90. package/src/watchdog/register-child.ts +28 -25
  91. package/src/watchdog/register-main.ts +10 -9
  92. package/src/watchdog/render.ts +4 -5
  93. package/src/watchdog/review.ts +15 -4
  94. package/src/watchdog/rules.ts +70 -0
  95. package/src/watchdog/runtime.ts +75 -92
  96. package/src/watchdog/scope.ts +0 -11
  97. package/src/watchdog/settings.ts +48 -104
  98. package/src/watchdog/types.ts +18 -32
  99. package/src/watchdog/warning-format.ts +0 -1
  100. package/src/workflows/chat-progress.ts +3 -2
  101. package/src/workflows/scripted-workflow.ts +48 -1
  102. package/src/workflows/workflow-checklist.ts +10 -12
  103. package/src/workflows/workflow-preflight.ts +28 -1
  104. package/src/runs/shared/child-protocol.ts +0 -415
  105. package/src/runs/shared/pi-args.ts +0 -1062
  106. package/src/runs/shared/subagent-startup-retry.ts +0 -116
  107. package/src/shared/post-exit-stdio-guard.ts +0 -85
@@ -14,12 +14,14 @@ import { appendAgentRefinementOverlay } from "../../agents/agent-refinements.ts"
14
14
  import { createAtomicJsonWriter, writePrivateAtomicJson } from "../../shared/atomic-json.ts";
15
15
  import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
16
16
  import { planChildLaunch, resolveStepBehavior, suppressProgressForReadOnlyTask, type ResolvedStepBehavior } from "../shared/child-launch-plan.ts";
17
- import { applyThinkingSuffix, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/pi-args.ts";
17
+ import { applyThinkingSuffix, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/child-tool-plan.ts";
18
18
  import { injectOutputPathSystemPrompt, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
19
+ import { applyWatchdogLaunchRules, sendRuleViolationWarning } from "../../watchdog/rules.ts";
19
20
  import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveExistingReadInstructionPaths, resolveExistingReadPaths, writeInitialProgressFile, type ChainStep, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
20
21
  import type { RunnerStep } from "../shared/parallel-utils.ts";
21
22
  import type { ContextMode } from "../shared/context-mode.ts";
22
- import { resolveInstalledPiPackageRoot, resolvePiPackageRoot } from "../shared/pi-spawn.ts";
23
+ import { PI_CODING_AGENT_PACKAGE, resolveInstalledPiPackageRoot, resolvePiPackageRoot } from "../shared/pi-spawn.ts";
24
+ import { JITI_ALIAS_ENV, resolveHostPeerAliases } from "./runner-aliases.ts";
23
25
  import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
24
26
  import { resolveNodeExecutable } from "../../shared/node-executable.ts";
25
27
  import { backgroundProcessOptions } from "../shared/background-process-options.ts";
@@ -30,7 +32,7 @@ import { buildModelCandidates, resolveEffectiveSubagentModel, resolveModelOrigin
30
32
  import { resolveToolTimeoutMs, toolTimeoutFromEnv } from "../shared/tool-timeout.ts";
31
33
  import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
32
34
  import { findModelInfo, resolveEffectiveThinking } from "../../shared/model-info.ts";
33
- import { assertThinkingWithinCeiling, decodeThinkingCeiling, intersectThinkingCeilings, SUBAGENT_THINKING_CEILING_ENV, type ThinkingLevel } from "../../shared/thinking-ceiling.ts";
35
+ import { assertThinkingWithinCeiling, intersectThinkingCeilings, type ThinkingLevel } from "../../shared/thinking-ceiling.ts";
34
36
  import { resolveExpectedWorktreeAgentCwd, resolveWorktreeProvider, shouldDeferWorktreeCwd, WORKTREE_AGENT_CWD_PLACEHOLDER } from "../shared/worktree.ts";
35
37
  import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
36
38
  import { ChainOutputValidationError, validateChainOutputBindings } from "../shared/chain-outputs.ts";
@@ -64,7 +66,10 @@ import {
64
66
  getAsyncConfigPath,
65
67
  resolveChildMaxSubagentDepth,
66
68
  } from "../../shared/types.ts";
67
- import { nestedResultsPath, nestedSummaryFromAsyncStatus, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
69
+ import { inheritedNestedParentAddressOf, inheritedNestedRouteOf, nestedResultsPath, nestedSummaryFromAsyncStatus, writeNestedEvent } from "../shared/nested-events.ts";
70
+ import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
71
+ import { childSessionFactoryModule } from "../shared/child-session.ts";
72
+ import { inheritedChildRuntime } from "../shared/child-launch.ts";
68
73
  import { resultFilePath } from "./result-files.ts";
69
74
  import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
70
75
  import { usageBudgetState } from "../shared/usage-budget.ts";
@@ -74,7 +79,7 @@ import { finalizeProcessTerminal, initializeProcessTerminal, readProcessTerminal
74
79
  import type { ActiveAsyncCapacityHandle } from "./active-async-capacity.ts";
75
80
  import { statusStepDescription } from "./chain-append.ts";
76
81
  import { SUBAGENT_PROCESS_TERMINAL_EVENT } from "../../shared/types.ts";
77
- import { assertAgentAllowedByCapabilityCeiling, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
82
+ import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
78
83
  import { agentDefinitionDigest, launchBindingDigest } from "../../shared/launch-contract.ts";
79
84
  import { resolvePermissionRules, type PermissionConfig } from "../shared/permissions.ts";
80
85
  import { normalizeExtensionBindings, omitExtensionBindingsEnv, type ExtensionBindings } from "../shared/extension-bindings.ts";
@@ -143,6 +148,8 @@ interface AsyncExecutionContext {
143
148
  modelScope?: ModelScopeConfig;
144
149
  /** Whether the parent session has an interactive UI. */
145
150
  interactive?: boolean;
151
+ /** The executor's own child runtime when the launch comes from an in-process child. */
152
+ childRuntime?: ChildRuntimeConfig;
146
153
  }
147
154
 
148
155
  export const DEFAULT_ASYNC_TIMEOUT_MS = 30 * 60 * 1000;
@@ -178,6 +185,7 @@ interface AsyncChainParams {
178
185
  worktreeSetupHook?: string;
179
186
  worktreeSetupHookTimeoutMs?: number;
180
187
  worktreeBaseDir?: string;
188
+ baseRef?: string;
181
189
  worktreeProvider?: import("../../shared/types.ts").WorktreeProvider;
182
190
  worktreeBranchPrefix?: string;
183
191
  controlConfig?: ResolvedControlConfig;
@@ -247,6 +255,7 @@ interface AsyncSingleParams {
247
255
  worktreeSetupHook?: string;
248
256
  worktreeSetupHookTimeoutMs?: number;
249
257
  worktreeBaseDir?: string;
258
+ baseRef?: string;
250
259
  worktreeProvider?: import("../../shared/types.ts").WorktreeProvider;
251
260
  worktreeBranchPrefix?: string;
252
261
  worktree?: boolean;
@@ -535,6 +544,13 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
535
544
  if (!jitiCliPath) {
536
545
  return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
537
546
  }
547
+ if (!piPackageRoot) {
548
+ return { error: `Background children require pi installed as the npm package (${PI_CODING_AGENT_PACKAGE}); a standalone pi binary has no package directory, so the async runner cannot create child sessions. Run this child in the foreground (async: false) or install pi from npm.` };
549
+ }
550
+ const hostPeerAliases = resolveHostPeerAliases(piPackageRoot);
551
+ if (hostPeerAliases.missing.length > 0) {
552
+ return { error: `Background children require pi installed as the npm package (${PI_CODING_AGENT_PACKAGE}) with its dependencies; ${piPackageRoot} does not provide ${hostPeerAliases.missing.join(", ")}, so the async runner cannot create child sessions. A standalone pi binary cannot run background children.` };
553
+ }
538
554
 
539
555
  fs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
540
556
  const cfgPath = getAsyncConfigPath(suffix);
@@ -576,7 +592,8 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
576
592
  stdio: ["ignore", stdoutFd ?? "ignore", stderrFd ?? "ignore"],
577
593
  env: {
578
594
  ...omitExtensionBindingsEnv(process.env),
579
- ...(piPackageRoot ? { [PI_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot } : {}),
595
+ [PI_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot,
596
+ [JITI_ALIAS_ENV]: JSON.stringify(hostPeerAliases.aliases),
580
597
  },
581
598
  });
582
599
  closeFd(stdoutFd);
@@ -832,7 +849,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
832
849
  if (unsupported.length > 0) throw new AsyncStartValidationError(`Agent '${a.name}' uses runner.type='${externalRunnerType}' and does not support: ${unsupported.join(", ")}.`);
833
850
  }
834
851
  try {
835
- assertAgentAllowedByCapabilityCeiling(a.name, intersectSubagentCapabilityCeilings(params.capabilityCeiling, decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV])));
852
+ assertAgentAllowedByCapabilityCeiling(a.name, intersectSubagentCapabilityCeilings(params.capabilityCeiling, ctx.childRuntime?.capabilityCeiling));
836
853
  } catch (error) {
837
854
  throw new AsyncStartValidationError(error instanceof Error ? error.message : String(error));
838
855
  }
@@ -912,7 +929,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
912
929
  const thinkingCeiling = externalRunner ? undefined : intersectThinkingCeilings(
913
930
  params.thinkingCeiling,
914
931
  a.maxThinking,
915
- decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]),
932
+ ctx.childRuntime?.thinkingCeiling,
916
933
  );
917
934
  if (!externalRunner) {
918
935
  try {
@@ -939,6 +956,8 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
939
956
  throw new AsyncStartValidationError(error instanceof Error ? error.message : String(error));
940
957
  }
941
958
  }
959
+ const launchRuleError = applyWatchdogLaunchRules({ cwd: stepCwd, agent: a.name, model: modelCandidates[0] ?? model, warn: (violation) => sendRuleViolationWarning(ctx.pi, violation) });
960
+ if (launchRuleError) throw new AsyncStartValidationError(launchRuleError);
942
961
  const fast = s.fast ?? params.fast ?? a.fast;
943
962
  const toolPlan = resolvePiLaunchToolPlan({
944
963
  tools: a.tools,
@@ -954,7 +973,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
954
973
  model,
955
974
  modelCandidates,
956
975
  capabilityCeiling: params.capabilityCeiling,
957
- inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
976
+ inheritedCapabilityCeiling: ctx.childRuntime?.capabilityCeiling,
958
977
  agentName: a.name,
959
978
  permissionRules,
960
979
  runtimeSnapshotHost: ctx.pi,
@@ -999,8 +1018,6 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
999
1018
  thinking: resolveEffectiveThinking(model, effectiveThinking),
1000
1019
  ...(thinkingCeiling ? { thinkingCeiling } : {}),
1001
1020
  launchResolvedExtensions,
1002
- ...(toolPlan.mcpConfig ? { mcpConfig: toolPlan.mcpConfig } : {}),
1003
- ...(toolPlan.runtimeServerNames ? { runtimeServerNames: toolPlan.runtimeServerNames } : {}),
1004
1021
  modelCandidates: externalRunner ? undefined : modelCandidates,
1005
1022
  ...(primaryModelFromParent ? { skipPrimaryModelVerification: true } : {}),
1006
1023
  ...(availableModels && availableModels.length > 0 ? { modelVerificationRegistry: availableModels } : {}),
@@ -1204,6 +1221,7 @@ export function executeAsyncChain(
1204
1221
  worktreeSetupHook,
1205
1222
  worktreeSetupHookTimeoutMs,
1206
1223
  worktreeBaseDir,
1224
+ baseRef,
1207
1225
  worktreeProvider,
1208
1226
  worktreeBranchPrefix,
1209
1227
  controlConfig,
@@ -1221,8 +1239,8 @@ export function executeAsyncChain(
1221
1239
  });
1222
1240
  if (acceptanceErrors.length > 0) return formatAsyncStartError(resultMode, acceptanceErrors.join(" "));
1223
1241
  const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
1224
- const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
1225
- const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
1242
+ const inheritedNestedRoute = inheritedNestedRouteOf(ctx.childRuntime);
1243
+ const nestedAddress = inheritedNestedRoute ? inheritedNestedParentAddressOf(ctx.childRuntime) : undefined;
1226
1244
  const asyncDir = inheritedNestedRoute
1227
1245
  ? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id)
1228
1246
  : path.join(DIRS.async, id);
@@ -1335,10 +1353,12 @@ export function executeAsyncChain(
1335
1353
  completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
1336
1354
  ...(capabilityCeiling ? { capabilityCeiling } : {}),
1337
1355
  piPackageRoot,
1338
- piArgv1: process.argv[1],
1356
+ childSessionFactoryModule: childSessionFactoryModule(),
1357
+ inheritedChildRuntime: inheritedChildRuntime(ctx.childRuntime),
1339
1358
  worktreeSetupHook,
1340
1359
  worktreeSetupHookTimeoutMs,
1341
1360
  worktreeBaseDir,
1361
+ baseRef,
1342
1362
  worktreeProvider,
1343
1363
  worktreeBranchPrefix,
1344
1364
  controlConfig,
@@ -1451,7 +1471,7 @@ export function executeAsyncChain(
1451
1471
  path: nestedAddress.path,
1452
1472
  asyncDir,
1453
1473
  pid: spawnResult.pid,
1454
- ownerIntercomTarget: process.env.PI_SUBAGENT_INTERCOM_SESSION_NAME,
1474
+ ownerIntercomTarget: ctx.childRuntime?.intercomSessionName,
1455
1475
  leafIntercomTarget: childIntercomTargets?.[0],
1456
1476
  intercomTarget: childIntercomTargets?.[0],
1457
1477
  ownerState: "live",
@@ -1538,6 +1558,7 @@ export function executeAsyncSingle(
1538
1558
  worktreeSetupHook,
1539
1559
  worktreeSetupHookTimeoutMs,
1540
1560
  worktreeBaseDir,
1561
+ baseRef,
1541
1562
  worktreeProvider,
1542
1563
  worktreeBranchPrefix,
1543
1564
  controlConfig,
@@ -1578,7 +1599,7 @@ export function executeAsyncSingle(
1578
1599
  if (extensionBindings !== undefined) unsupported.push("extension bindings");
1579
1600
  if (unsupported.length > 0) return formatAsyncStartError("single", `Agent '${agentConfig.name}' uses runner.type='${externalRunnerType}' and does not support: ${unsupported.join(", ")}.`);
1580
1601
  }
1581
- const capabilityCeiling = intersectSubagentCapabilityCeilings(params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId), decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]));
1602
+ const capabilityCeiling = intersectSubagentCapabilityCeilings(params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId), ctx.childRuntime?.capabilityCeiling);
1582
1603
  try {
1583
1604
  assertAgentAllowedByCapabilityCeiling(agentConfig.name, capabilityCeiling);
1584
1605
  } catch (error) {
@@ -1621,8 +1642,8 @@ export function executeAsyncSingle(
1621
1642
  }
1622
1643
  systemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: runnerCwd, agentName: agentConfig.name });
1623
1644
 
1624
- const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
1625
- const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
1645
+ const inheritedNestedRoute = inheritedNestedRouteOf(ctx.childRuntime);
1646
+ const nestedAddress = inheritedNestedRoute ? inheritedNestedParentAddressOf(ctx.childRuntime) : undefined;
1626
1647
  const asyncDir = inheritedNestedRoute
1627
1648
  ? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id)
1628
1649
  : path.join(DIRS.async, id);
@@ -1687,7 +1708,7 @@ export function executeAsyncSingle(
1687
1708
  const thinkingCeiling = externalRunner ? undefined : intersectThinkingCeilings(
1688
1709
  params.thinkingCeiling,
1689
1710
  agentConfig.maxThinking,
1690
- decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]),
1711
+ ctx.childRuntime?.thinkingCeiling,
1691
1712
  );
1692
1713
  if (!externalRunner) {
1693
1714
  try {
@@ -1747,7 +1768,7 @@ export function executeAsyncSingle(
1747
1768
  model,
1748
1769
  modelCandidates,
1749
1770
  capabilityCeiling,
1750
- inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
1771
+ inheritedCapabilityCeiling: ctx.childRuntime?.capabilityCeiling,
1751
1772
  agentName: agentConfig.name,
1752
1773
  permissionRules: resolvePermissionRules(ctx.permissions, agentConfig.permissions),
1753
1774
  runtimeSnapshotHost: ctx.pi,
@@ -1844,6 +1865,7 @@ export function executeAsyncSingle(
1844
1865
  ...(controlConfig ? { controlConfig } : {}),
1845
1866
  ...(params.context ? { context: params.context } : {}),
1846
1867
  ...(params.intercomBridge !== undefined ? { intercomBridge: params.intercomBridge } : {}),
1868
+ ...(params.baseRef !== undefined ? { baseRef: params.baseRef } : {}),
1847
1869
  ...(deadlineAt !== undefined ? { absoluteDeadlineAt: deadlineAt } : {}),
1848
1870
  ...(resolvedToolBudget.budget ? { initialToolBudget: resolvedToolBudget.budget } : {}),
1849
1871
  maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, recoveryAgentConfig.maxSubagentDepth),
@@ -1894,8 +1916,6 @@ export function executeAsyncSingle(
1894
1916
  extensions: agentConfig.extensions,
1895
1917
  subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
1896
1918
  mcpDirectTools: agentConfig.mcpDirectTools,
1897
- ...(toolPlan.mcpConfig ? { mcpConfig: toolPlan.mcpConfig } : {}),
1898
- ...(toolPlan.runtimeServerNames ? { runtimeServerNames: toolPlan.runtimeServerNames } : {}),
1899
1919
  mutationTools: agentConfig.mutationTools,
1900
1920
  completionGuard: agentConfig.completionGuard,
1901
1921
  systemPrompt,
@@ -1941,10 +1961,12 @@ export function executeAsyncSingle(
1941
1961
  completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
1942
1962
  ...(capabilityCeiling ? { capabilityCeiling } : {}),
1943
1963
  piPackageRoot,
1944
- piArgv1: process.argv[1],
1964
+ childSessionFactoryModule: childSessionFactoryModule(),
1965
+ inheritedChildRuntime: inheritedChildRuntime(ctx.childRuntime),
1945
1966
  worktreeSetupHook,
1946
1967
  worktreeSetupHookTimeoutMs,
1947
1968
  worktreeBaseDir,
1969
+ baseRef,
1948
1970
  worktreeProvider,
1949
1971
  worktreeBranchPrefix,
1950
1972
  controlConfig,
@@ -2027,7 +2049,7 @@ export function executeAsyncSingle(
2027
2049
  path: nestedAddress.path,
2028
2050
  asyncDir,
2029
2051
  pid: spawnResult.pid,
2030
- ownerIntercomTarget: process.env.PI_SUBAGENT_INTERCOM_SESSION_NAME,
2052
+ ownerIntercomTarget: ctx.childRuntime?.intercomSessionName,
2031
2053
  leafIntercomTarget: childIntercomTarget?.(agent, 0),
2032
2054
  intercomTarget: childIntercomTarget?.(agent, 0),
2033
2055
  ownerState: "live",
@@ -39,6 +39,8 @@ interface AsyncJobTrackerOptions {
39
39
  watch?: typeof fs.watch;
40
40
  kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
41
41
  now?: () => number;
42
+ /** Resolve native supervisor requests without scanning supervisor mailboxes. */
43
+ supervisorRequestState?: (event: ControlEvent) => "pending" | "resolved" | "unknown";
42
44
  }
43
45
 
44
46
  const CONTROL_EVENT_READ_CHUNK_BYTES = 64 * 1024;
@@ -293,6 +295,15 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
293
295
  if ((parsed as { type?: unknown }).type !== "subagent.control") return;
294
296
  const record = parsed as { event?: ControlEvent; channels?: string[]; childIntercomTarget?: string; noticeText?: string; intercom?: { to?: string; message?: string } };
295
297
  if (!record.event || !Array.isArray(record.channels)) return;
298
+ if (record.event.type === "needs_attention" && record.event.reason === "supervisor_request" && options.supervisorRequestState) {
299
+ let requestState: "pending" | "resolved" | "unknown" = "unknown";
300
+ try {
301
+ requestState = options.supervisorRequestState(record.event);
302
+ } catch (error) {
303
+ console.error(`Failed to resolve supervisor request state for async control event in '${job.asyncDir}':`, error);
304
+ }
305
+ if (requestState === "resolved") return;
306
+ }
296
307
  const payload = {
297
308
  event: record.event,
298
309
  source: "async" as const,
@@ -12,6 +12,7 @@ import { reconcileAsyncRun } from "./stale-run-reconciler.ts";
12
12
  import { resultFilePath, resultPayloadPathForIndexedRun } from "./result-files.ts";
13
13
  import { canScanAsyncRunPrefix, MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH } from "./run-id-query.ts";
14
14
  import { parallelHandoffPath, resolveRetainedWorktreeCwd } from "../shared/parallel-handoff.ts";
15
+ import { normalizeWorktreeBaseRef } from "../shared/worktree.ts";
15
16
  import { intersectThinkingCeilings, parseThinkingLevel, type ThinkingLevel } from "../../shared/thinking-ceiling.ts";
16
17
  import { assertWorkflowGraphHostSteps } from "../shared/host-step-status.ts";
17
18
 
@@ -320,7 +321,7 @@ export function readAsyncRecoveryDescriptor(asyncDir: string | undefined): Steer
320
321
  "subagentOnlyExtensions", "mcpDirectTools", "excludeTools", "mutationTools", "systemPrompt", "systemPromptMode", "inheritProjectContext", "inheritGlobalContext", "inheritSkills", "skills",
321
322
  "skillPath", "agentFilePath", "completionGuard", "memory", "outputPath", "outputMode", "structuredOutputSchema", "acceptance", "sessionDir", "artifactConfig",
322
323
  "artifactsDir", "maxOutput", "controlConfig", "context", "intercomBridge", "absoluteDeadlineAt", "initialTurnBudget", "initialToolBudget", "maxSubagentDepth", "share", "capabilityCeiling",
323
- "launchResolvedExtensions", "runFanoutBudget", "lane",
324
+ "launchResolvedExtensions", "runFanoutBudget", "lane", "baseRef",
324
325
  "extensionBindings",
325
326
  ]);
326
327
  for (const field of Object.keys(parsed)) {
@@ -366,6 +367,14 @@ export function readAsyncRecoveryDescriptor(asyncDir: string | undefined): Steer
366
367
  for (const field of ["launchContractDigest", "sessionFile", "model", "modelProvider", "thinking", "agentFilePath", "outputPath", "sessionDir", "artifactsDir"] as const) {
367
368
  if (parsed[field] !== undefined && (typeof parsed[field] !== "string" || !(parsed[field] as string).trim())) throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must be a non-empty string.`);
368
369
  }
370
+ if (parsed.baseRef !== undefined) {
371
+ if (typeof parsed.baseRef !== "string") throw new Error(`Invalid async recovery descriptor '${descriptorPath}': baseRef must be a string.`);
372
+ try {
373
+ parsed.baseRef = normalizeWorktreeBaseRef(parsed.baseRef);
374
+ } catch (error) {
375
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${error instanceof Error ? error.message : String(error)}`);
376
+ }
377
+ }
369
378
  if (parsed.completionGuard !== undefined && typeof parsed.completionGuard !== "boolean") throw new Error(`Invalid async recovery descriptor '${descriptorPath}': completionGuard must be a boolean.`);
370
379
  if (parsed.structuredOutputSchema !== undefined && (!parsed.structuredOutputSchema || typeof parsed.structuredOutputSchema !== "object" || Array.isArray(parsed.structuredOutputSchema))) throw new Error(`Invalid async recovery descriptor '${descriptorPath}': structuredOutputSchema must be an object.`);
371
380
  if (parsed.memory !== undefined) {
@@ -623,7 +632,7 @@ export function buildRevivedAsyncTask(target: AsyncResumeTarget, message: string
623
632
  `Original agent: ${target.agent}`,
624
633
  target.sessionFile ? `Original session file: ${target.sessionFile}` : undefined,
625
634
  "",
626
- "Use the stored session context as background. Answer the orchestrator's follow-up below. Do not assume the original child process is still alive.",
635
+ "Use the stored session context as background. Answer the orchestrator's follow-up below. Do not assume the original child session is still running.",
627
636
  "",
628
637
  "Follow-up:",
629
638
  message,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Cross-OS control channel for async subagent runs.
3
3
  *
4
- * Background runs use child processes. Unix detaches them from the parent process.
4
+ * Background runs use a detached runner process. Unix detaches it from the parent process.
5
5
  * The original control path delivered an interrupt with
6
6
  * `process.kill(pid, SIGUSR2|SIGBREAK)`, but Windows cannot
7
7
  * deliver those signals cross-process via `process.kill` and throws `ENOSYS`,
@@ -74,33 +74,10 @@ export interface SteerRequest {
74
74
  source?: string;
75
75
  }
76
76
 
77
- export interface SteerCapability {
78
- type: "steer-capability";
79
- protocolVersion: 1;
80
- index: number;
81
- pid: number;
82
- readyAt: number;
83
- supported: boolean;
84
- }
85
-
86
- export interface SteerAck {
87
- type: "steer-ack";
88
- protocolVersion: 1;
89
- requestId: string;
90
- index: number;
91
- ts: number;
92
- state: "delivered" | "queued" | "failed";
93
- deliveryStatus?: SteerDeliveryStatus;
94
- message: string;
95
- }
96
-
97
77
  const STEER_REQUESTS_DIR = "steer-requests";
98
78
  const STOP_REQUESTS_DIR = "stop-requests";
99
79
  const REVIVAL_BRIEFS_DIR = "revival-briefs";
100
80
  export const MAX_STEER_QUEUE_SIZE = 20;
101
- const STEER_TARGETS_DIR = "steer-targets";
102
- const STEER_CAPABILITIES_DIR = "steer-capabilities";
103
- const STEER_ACKS_DIR = "steer-acks";
104
81
  const STEER_INBOX_CLOSED_FILE = "steer-inbox-closed.json";
105
82
  const MAX_STEER_MESSAGE_BYTES = 128 * 1024;
106
83
  const MAX_STEER_REQUEST_ID_LENGTH = 256;
@@ -143,35 +120,6 @@ export function closeSteerInbox(asyncDir: string, state: string, write: (filePat
143
120
  write(steerInboxClosedPath(asyncDir), { version: 1, closedAt: Date.now(), state });
144
121
  }
145
122
 
146
- /** Per-child inbox consumed by the child prompt runtime inside the Pi process. */
147
- export function stepSteerInboxDir(asyncDir: string, index: number): string {
148
- assertChildIndex(index);
149
- return path.join(controlInboxDir(asyncDir), STEER_TARGETS_DIR, String(index));
150
- }
151
-
152
- export function steerCapabilitiesDir(asyncDir: string): string {
153
- return path.join(controlInboxDir(asyncDir), STEER_CAPABILITIES_DIR);
154
- }
155
-
156
- export function steerCapabilityPath(asyncDir: string, index: number): string {
157
- assertChildIndex(index);
158
- return path.join(steerCapabilitiesDir(asyncDir), `${index}.json`);
159
- }
160
-
161
- export function steerAcksDir(asyncDir: string, index: number): string {
162
- assertChildIndex(index);
163
- return path.join(controlInboxDir(asyncDir), STEER_ACKS_DIR, String(index));
164
- }
165
-
166
- function steerAckFileName(requestId: string): string {
167
- return `${Buffer.from(requestId).toString("base64url")}.json`;
168
- }
169
-
170
- export function steerAckPathFromDir(dir: string, requestId: string): string {
171
- if (!/^[^\s]+$/.test(requestId) || requestId.length > 256) throw new Error("steer acknowledgment requestId is invalid.");
172
- return path.join(dir, steerAckFileName(requestId));
173
- }
174
-
175
123
  function assertChildIndex(index: number): void {
176
124
  if (!Number.isInteger(index) || index < 0 || index > 1_000_000) throw new Error("child index must be a non-negative integer.");
177
125
  }
@@ -229,45 +177,6 @@ export function writeSteerRequestToExistingDir(dir: string, request: SteerReques
229
177
  return requestPath;
230
178
  }
231
179
 
232
- export function writeSteerCapabilityAt(filePath: string, capability: Omit<SteerCapability, "type" | "protocolVersion">): string {
233
- assertChildIndex(capability.index);
234
- if (!Number.isInteger(capability.pid) || capability.pid <= 0) throw new Error("steer capability pid must be a positive integer.");
235
- if (!Number.isFinite(capability.readyAt) || capability.readyAt <= 0) throw new Error("steer capability readyAt must be a finite timestamp.");
236
- const record: SteerCapability = { type: "steer-capability", protocolVersion: 1, ...capability };
237
- writeAtomicJson(filePath, record);
238
- return filePath;
239
- }
240
-
241
- export function writeSteerCapability(asyncDir: string, capability: Omit<SteerCapability, "type" | "protocolVersion">): string {
242
- return writeSteerCapabilityAt(steerCapabilityPath(asyncDir, capability.index), capability);
243
- }
244
-
245
- function steerAckWritePath(filePath: string, ack: Omit<SteerAck, "type" | "protocolVersion">): string {
246
- const parsed = path.parse(filePath);
247
- const stateOrder = ack.state === "queued" ? "0" : ack.state === "delivered" ? "1" : "2";
248
- const timestamp = String(Math.trunc(ack.ts)).padStart(13, "0");
249
- for (let suffix = 0; suffix < 1_000; suffix += 1) {
250
- const candidate = path.join(parsed.dir, `${parsed.name}-${timestamp}-${stateOrder}-${ack.state}${suffix === 0 ? "" : `-${suffix}`}${parsed.ext}`);
251
- if (!fs.existsSync(candidate)) return candidate;
252
- }
253
- throw new Error("steer acknowledgment queue is full.");
254
- }
255
-
256
- export function writeSteerAckAt(filePath: string, ack: Omit<SteerAck, "type" | "protocolVersion">): string {
257
- assertChildIndex(ack.index);
258
- if (!/^[^\s]+$/.test(ack.requestId) || ack.requestId.length > 256) throw new Error("steer acknowledgment requestId is invalid.");
259
- if (!Number.isFinite(ack.ts) || ack.ts <= 0) throw new Error("steer acknowledgment ts must be a finite timestamp.");
260
- if (!ack.message.trim() || ack.message.length > 1000) throw new Error("steer acknowledgment message is invalid.");
261
- const record: SteerAck = { type: "steer-ack", protocolVersion: 1, ...ack, message: ack.message.trim() };
262
- const ackPath = steerAckWritePath(filePath, ack);
263
- writeAtomicJson(ackPath, record);
264
- return ackPath;
265
- }
266
-
267
- export function writeSteerAck(asyncDir: string, ack: Omit<SteerAck, "type" | "protocolVersion">): string {
268
- return writeSteerAckAt(path.join(steerAcksDir(asyncDir, ack.index), steerAckFileName(ack.requestId)), ack);
269
- }
270
-
271
180
  /**
272
181
  * Parent side: drop a portable interrupt request the runner's inbox watcher will
273
182
  * pick up regardless of OS. Written atomically (temp + rename), dir auto-created.
@@ -350,97 +259,6 @@ export function requestAsyncSteer(
350
259
  return requestPath;
351
260
  }
352
261
 
353
- export function enqueueStepSteer(asyncDir: string, index: number, request: SteerRequest): string {
354
- assertChildIndex(index);
355
- const { targetIndexes: _targetIndexes, ...singleTargetRequest } = request;
356
- return writeSteerRequestToDir(stepSteerInboxDir(asyncDir, index), { ...singleTargetRequest, targetIndex: index, type: "steer" });
357
- }
358
-
359
- function parseSteerCapability(raw: unknown): SteerCapability | undefined {
360
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
361
- const input = raw as Partial<SteerCapability>;
362
- if (input.type !== "steer-capability" || input.protocolVersion !== 1) return undefined;
363
- const { index, pid, readyAt, supported } = input;
364
- if (typeof index !== "number" || !Number.isInteger(index) || index < 0 || index > 1_000_000) return undefined;
365
- if (typeof pid !== "number" || typeof readyAt !== "number" || !Number.isInteger(pid) || pid <= 0 || !Number.isFinite(readyAt) || readyAt <= 0 || typeof supported !== "boolean") return undefined;
366
- return { type: "steer-capability", protocolVersion: 1, index, pid, readyAt, supported };
367
- }
368
-
369
- function parseSteerAck(raw: unknown): SteerAck | undefined {
370
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
371
- const input = raw as Partial<SteerAck>;
372
- if (input.type !== "steer-ack" || input.protocolVersion !== 1 || typeof input.requestId !== "string" || !/^[^\s]+$/.test(input.requestId) || input.requestId.length > 256) return undefined;
373
- const { index, ts, state, message } = input;
374
- if (typeof index !== "number" || typeof ts !== "number" || !Number.isInteger(index) || index < 0 || index > 1_000_000 || !Number.isFinite(ts) || ts <= 0) return undefined;
375
- if (state !== "delivered" && state !== "queued" && state !== "failed") return undefined;
376
- if (input.deliveryStatus !== undefined && input.deliveryStatus !== "delivered" && input.deliveryStatus !== "queued") return undefined;
377
- if (typeof message !== "string" || !message.trim() || message.length > 1000) return undefined;
378
- return { type: "steer-ack", protocolVersion: 1, requestId: input.requestId, index, ts, state, ...(input.deliveryStatus ? { deliveryStatus: input.deliveryStatus } : {}), message: message.trim() };
379
- }
380
-
381
- export function readSteerCapability(asyncDir: string, index: number): SteerCapability | undefined {
382
- try {
383
- return parseSteerCapability(JSON.parse(fs.readFileSync(steerCapabilityPath(asyncDir, index), "utf-8")));
384
- } catch {
385
- return undefined;
386
- }
387
- }
388
-
389
- export function consumeSteerCapabilities(asyncDir: string, fsImpl: Pick<typeof fs, "existsSync" | "readdirSync" | "readFileSync"> = fs): SteerCapability[] {
390
- const dir = steerCapabilitiesDir(asyncDir);
391
- if (!fsImpl.existsSync(dir)) return [];
392
- const capabilities: SteerCapability[] = [];
393
- for (const entry of fsImpl.readdirSync(dir).filter((name) => /^\d+\.json$/.test(name)).sort()) {
394
- try {
395
- const capability = parseSteerCapability(JSON.parse(fsImpl.readFileSync(path.join(dir, entry), "utf-8")));
396
- if (capability) capabilities.push(capability);
397
- } catch {
398
- // A partially written or malformed capability is ignored until a valid one arrives.
399
- }
400
- }
401
- return capabilities;
402
- }
403
-
404
- export function consumeSteerAckFromDir(
405
- dir: string,
406
- requestId: string,
407
- fsImpl: Pick<typeof fs, "existsSync" | "readdirSync" | "readFileSync" | "rmSync"> = fs,
408
- ): SteerAck | undefined {
409
- if (!fsImpl.existsSync(dir)) return undefined;
410
- let entries: string[];
411
- try { entries = fsImpl.readdirSync(dir).filter((name) => name.endsWith(".json")).sort(); } catch { return undefined; }
412
- for (const entry of entries) {
413
- const target = path.join(dir, entry);
414
- let ack: SteerAck | undefined;
415
- try { ack = parseSteerAck(JSON.parse(fsImpl.readFileSync(target, "utf-8"))); } catch { ack = undefined; }
416
- if (ack?.requestId !== requestId) continue;
417
- try { fsImpl.rmSync(target, { force: true }); } catch { return undefined; }
418
- return ack;
419
- }
420
- return undefined;
421
- }
422
-
423
- export function consumeSteerAcks(asyncDir: string, fsImpl: Pick<typeof fs, "existsSync" | "readdirSync" | "readFileSync" | "rmSync"> = fs): SteerAck[] {
424
- const root = path.join(controlInboxDir(asyncDir), STEER_ACKS_DIR);
425
- if (!fsImpl.existsSync(root)) return [];
426
- const acks: SteerAck[] = [];
427
- let indexNames: string[];
428
- try { indexNames = fsImpl.readdirSync(root).filter((name) => /^\d+$/.test(name)); } catch { return []; }
429
- for (const indexName of indexNames) {
430
- const dir = path.join(root, indexName);
431
- let entries: string[];
432
- try { entries = fsImpl.readdirSync(dir).filter((name) => name.endsWith(".json")).sort(); } catch { continue; }
433
- for (const entry of entries) {
434
- const target = path.join(dir, entry);
435
- let ack: SteerAck | undefined;
436
- try { ack = parseSteerAck(JSON.parse(fsImpl.readFileSync(target, "utf-8"))); } catch { ack = undefined; }
437
- try { fsImpl.rmSync(target, { force: true }); } catch { continue; }
438
- if (ack) acks.push(ack);
439
- }
440
- }
441
- return acks;
442
- }
443
-
444
262
  function parseSteerRequest(raw: unknown): SteerRequest | undefined {
445
263
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
446
264
  const input = raw as Partial<SteerRequest>;
@@ -665,8 +483,6 @@ export function watchAsyncControlInbox(
665
483
  onTimeout?: () => void;
666
484
  onStop?: (request: StopRequest) => void;
667
485
  onSteer?: (request: SteerRequest) => void;
668
- onSteerCapability?: (capability: SteerCapability) => void;
669
- onSteerAck?: (ack: SteerAck) => void;
670
486
  pollIntervalMs?: number;
671
487
  safetyPollIntervalMs?: number;
672
488
  platform?: NodeJS.Platform;
@@ -691,8 +507,6 @@ export function watchAsyncControlInbox(
691
507
  if (consumeTimeoutRequest(asyncDir, fsImpl)) opts.onTimeout?.();
692
508
  if (consumeInterruptRequest(asyncDir, fsImpl)) opts.onInterrupt();
693
509
  for (const request of consumeSteerRequests(asyncDir, fsImpl)) opts.onSteer?.(request);
694
- for (const capability of consumeSteerCapabilities(asyncDir, fsImpl)) opts.onSteerCapability?.(capability);
695
- for (const ack of consumeSteerAcks(asyncDir, fsImpl)) opts.onSteerAck?.(ack);
696
510
  } catch {
697
511
  // Never let inbox errors crash the runner.
698
512
  }
@@ -718,32 +532,16 @@ export function watchAsyncControlInbox(
718
532
  const watchDir = (target: string, create = false): void => {
719
533
  if (disposed || watchedDirs.has(target)) return;
720
534
  if (create) fsImpl.mkdirSync(target, { recursive: true });
721
- const watcher = fsImpl.watch(resolveWatchPath(target, fsImpl.realpathSync.native), () => {
722
- watchExistingSteerAckDirs();
723
- check();
724
- });
535
+ const watcher = fsImpl.watch(resolveWatchPath(target, fsImpl.realpathSync.native), () => check());
725
536
  watcher.on?.("error", startPolling);
726
537
  watchers.push(watcher);
727
538
  watchedDirs.add(target);
728
539
  };
729
- const watchExistingSteerAckDirs = (): void => {
730
- const ackRoot = path.join(dir, STEER_ACKS_DIR);
731
- let entries: string[];
732
- try {
733
- entries = fsImpl.readdirSync(ackRoot).filter((name) => /^\d+$/.test(name));
734
- } catch {
735
- return;
736
- }
737
- for (const entry of entries) watchDir(path.join(ackRoot, entry));
738
- };
739
540
  try {
740
541
  if (shouldUseNativeFsWatch("runner-control-inbox", opts.platform)) {
741
542
  watchDir(dir);
742
543
  watchDir(stopRequestsDir(asyncDir), true);
743
544
  watchDir(steerRequestsDir(asyncDir), true);
744
- watchDir(steerCapabilitiesDir(asyncDir), true);
745
- watchDir(path.join(dir, STEER_ACKS_DIR), true);
746
- watchExistingSteerAckDirs();
747
545
  startSafetyPolling();
748
546
  } else {
749
547
  startPolling();