pi-subagents 0.64.0 → 0.65.1

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 (106) hide show
  1. package/CHANGELOG.md +65 -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 +19 -17
  6. package/docs/configuration.md +23 -15
  7. package/docs/extension-api.md +18 -6
  8. package/docs/missions.md +2 -0
  9. package/docs/observability.md +10 -12
  10. package/docs/tool-reference.md +15 -8
  11. package/docs/watchdog.md +1 -1
  12. package/docs/workflows.md +13 -3
  13. package/package.json +5 -4
  14. package/runner-server-preload.mjs +13 -0
  15. package/skills/pi-subagents/SKILL.md +2 -1
  16. package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  17. package/skills/pi-subagents/references/execution-controls.md +7 -3
  18. package/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
  19. package/src/agents/agent-management.ts +47 -15
  20. package/src/api/capability-ceiling.ts +0 -1
  21. package/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
  22. package/src/api/preflight.ts +7 -4
  23. package/src/extension/config.ts +4 -2
  24. package/src/extension/doctor.ts +2 -10
  25. package/src/extension/fanout-child.ts +9 -11
  26. package/src/extension/index.ts +58 -7
  27. package/src/extension/public-execution.ts +14 -0
  28. package/src/extension/rpc.ts +3 -2
  29. package/src/extension/schemas.ts +3 -2
  30. package/src/extension/tool-description.ts +13 -8
  31. package/src/integrations/pi-web-session-liveness.ts +73 -0
  32. package/src/intercom/native-supervisor-channel.ts +159 -95
  33. package/src/intercom/supervisor-ui.ts +244 -0
  34. package/src/missions/workflow-state.ts +37 -16
  35. package/src/runs/background/async-execution.ts +51 -25
  36. package/src/runs/background/async-job-tracker.ts +11 -0
  37. package/src/runs/background/async-resume.ts +14 -3
  38. package/src/runs/background/async-retention.ts +9 -0
  39. package/src/runs/background/control-channel.ts +2 -204
  40. package/src/runs/background/notify.ts +2 -0
  41. package/src/runs/background/process-terminal.ts +1 -1
  42. package/src/runs/background/retained-nested-route-tracker.ts +96 -0
  43. package/src/runs/background/run-child-session.ts +614 -0
  44. package/src/runs/background/run-status.ts +0 -1
  45. package/src/runs/background/runner-aliases.ts +152 -0
  46. package/src/runs/background/runner-child-sessions.ts +31 -0
  47. package/src/runs/background/scheduled-runs.ts +18 -4
  48. package/src/runs/background/subagent-runner.ts +203 -901
  49. package/src/runs/foreground/async-steering-action.ts +1 -17
  50. package/src/runs/foreground/execution.ts +204 -378
  51. package/src/runs/foreground/foreground-control.ts +4 -0
  52. package/src/runs/foreground/foreground-history.ts +3 -1
  53. package/src/runs/foreground/prompt-audit.ts +9 -5
  54. package/src/runs/foreground/subagent-executor.ts +161 -91
  55. package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
  56. package/src/runs/shared/abort-recovery.ts +3 -3
  57. package/src/runs/shared/acceptance.ts +14 -1
  58. package/src/runs/shared/capability-ceiling.ts +1 -2
  59. package/src/runs/shared/child-hooks.ts +25 -0
  60. package/src/runs/shared/child-identity.ts +13 -2
  61. package/src/runs/shared/child-launch.ts +314 -0
  62. package/src/runs/shared/child-lifecycle.ts +25 -0
  63. package/src/runs/shared/child-runtime-config.ts +126 -0
  64. package/src/runs/shared/child-session.ts +337 -0
  65. package/src/runs/shared/child-tool-plan.ts +530 -0
  66. package/src/runs/shared/claude-code-adapter.ts +5 -1
  67. package/src/runs/shared/completion-guard.ts +1 -1
  68. package/src/runs/shared/external-cli-preflight.ts +16 -0
  69. package/src/runs/shared/llm-intent-arbiter.ts +20 -11
  70. package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  71. package/src/runs/shared/model-exclusions.ts +84 -15
  72. package/src/runs/shared/model-fallback.ts +78 -6
  73. package/src/runs/shared/nested-events.ts +32 -48
  74. package/src/runs/shared/nested-path.ts +0 -14
  75. package/src/runs/shared/orca-progress-tabs.ts +12 -7
  76. package/src/runs/shared/parallel-utils.ts +1 -2
  77. package/src/runs/shared/permissions.ts +0 -13
  78. package/src/runs/shared/process-signal.ts +4 -1
  79. package/src/runs/shared/run-fanout-budget.ts +0 -13
  80. package/src/runs/shared/runtime-acknowledged-extensions.ts +0 -27
  81. package/src/runs/shared/structured-output.ts +17 -4
  82. package/src/runs/shared/subagent-control.ts +2 -0
  83. package/src/runs/shared/subagent-prompt-runtime.ts +87 -384
  84. package/src/runs/shared/tool-availability.ts +18 -62
  85. package/src/runs/shared/tool-budget.ts +0 -14
  86. package/src/runs/shared/worktree-cleanup-plan.ts +31 -9
  87. package/src/runs/shared/worktree.ts +192 -40
  88. package/src/shared/child-session-name.ts +1 -1
  89. package/src/shared/jsonl-writer.ts +11 -0
  90. package/src/shared/model-response-aliases.ts +13 -0
  91. package/src/shared/thinking-ceiling.ts +0 -6
  92. package/src/shared/types.ts +63 -28
  93. package/src/shared/utils.ts +6 -4
  94. package/src/shared/watch-strategy.ts +2 -0
  95. package/src/slash/slash-commands.ts +0 -6
  96. package/src/tui/fleet-status.ts +1 -1
  97. package/src/tui/fleet.ts +0 -1
  98. package/src/tui/render.ts +254 -41
  99. package/src/watchdog/child-status.ts +0 -1
  100. package/src/watchdog/register-child.ts +12 -12
  101. package/src/workflows/scripted-workflow.ts +80 -7
  102. package/src/workflows/workflow-checklist.ts +4 -3
  103. package/src/runs/shared/child-protocol.ts +0 -415
  104. package/src/runs/shared/pi-args.ts +0 -1059
  105. package/src/runs/shared/subagent-startup-retry.ts +0 -116
  106. package/src/shared/post-exit-stdio-guard.ts +0 -85
@@ -8,6 +8,7 @@ export interface DrainableSource {
8
8
  export interface JsonlWriteStream {
9
9
  write(chunk: string): boolean;
10
10
  once(event: "drain", listener: () => void): JsonlWriteStream;
11
+ on?(event: "error", listener: (error: Error) => void): JsonlWriteStream;
11
12
  end(callback?: () => void): void;
12
13
  }
13
14
 
@@ -50,6 +51,16 @@ export function createJsonlWriter(
50
51
  let closed = false;
51
52
  let bytesWritten = 0;
52
53
  const maxBytes = deps.maxBytes ?? DEFAULT_MAX_JSONL_BYTES;
54
+ // The mirror is best effort: a stream that cannot open or write (for example
55
+ // because its directory was removed) must not surface as an uncaught error.
56
+ stream.on?.("error", () => {
57
+ closed = true;
58
+ stream = undefined;
59
+ if (backpressured) {
60
+ backpressured = false;
61
+ source.resume();
62
+ }
63
+ });
53
64
 
54
65
  return {
55
66
  writeLine(line: string) {
@@ -0,0 +1,13 @@
1
+ export function validateModelResponseAliases(value: unknown, label = "config.modelResponseAliases"): void {
2
+ if (value === undefined) return;
3
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be a JSON object`);
4
+ for (const [candidate, aliases] of Object.entries(value)) {
5
+ const slash = candidate.indexOf("/");
6
+ if (slash <= 0 || !candidate.slice(0, slash).trim() || !candidate.slice(slash + 1).trim()) {
7
+ throw new Error(`${label} key ${JSON.stringify(candidate)} must be a non-empty provider/model ID`);
8
+ }
9
+ if (!Array.isArray(aliases) || aliases.some((alias) => typeof alias !== "string" || !alias.trim())) {
10
+ throw new Error(`${label}[${JSON.stringify(candidate)}] must be an array of non-empty response ID strings`);
11
+ }
12
+ }
13
+ }
@@ -1,8 +1,6 @@
1
1
  import { resolveEffectiveThinking, THINKING_LEVELS, type ThinkingLevel } from "./model-info.ts";
2
2
  export type { ThinkingLevel } from "./model-info.ts";
3
3
 
4
- export const SUBAGENT_THINKING_CEILING_ENV = "PI_SUBAGENT_THINKING_CEILING";
5
-
6
4
  const thinkingLevelRanks = new Map<ThinkingLevel, number>(THINKING_LEVELS.map((level, index) => [level, index]));
7
5
 
8
6
  export function parseThinkingLevel(value: unknown, field = "thinking level"): ThinkingLevel {
@@ -26,10 +24,6 @@ export function intersectThinkingCeilings(...ceilings: Array<ThinkingLevel | und
26
24
  return active.reduce((lowest, ceiling) => compareThinkingLevels(ceiling, lowest) < 0 ? ceiling : lowest);
27
25
  }
28
26
 
29
- export function decodeThinkingCeiling(value: string | undefined): ThinkingLevel | undefined {
30
- if (value === undefined || value === "") return undefined;
31
- return parseThinkingLevel(value, "inherited thinking ceiling");
32
- }
33
27
 
34
28
  export interface ThinkingCeilingCheck {
35
29
  model?: string;
@@ -369,6 +369,7 @@ export interface ControlEvent {
369
369
  tokens?: number;
370
370
  toolCount?: number;
371
371
  currentTool?: string;
372
+ toolCallId?: string;
372
373
  currentToolDurationMs?: number;
373
374
  currentPath?: string;
374
375
  elapsedMs?: number;
@@ -778,6 +779,8 @@ export interface RunFanoutRejection extends RunFanoutBudgetSnapshot {
778
779
  }
779
780
 
780
781
  export interface SteeringRecoveryDescriptor {
782
+ /** Captured response identity authority; absence means no declared aliases on revival. */
783
+ modelResponseAliases?: Record<string, string[]>;
781
784
  version: 1;
782
785
  launchContractDigest?: string;
783
786
  extensionBindings?: ExtensionBindings;
@@ -786,6 +789,8 @@ export interface SteeringRecoveryDescriptor {
786
789
  agentContract?: AgentContract;
787
790
  agent: string;
788
791
  sessionFile?: string;
792
+ /** Git ref used to allocate managed worktrees for this run. */
793
+ baseRef?: string;
789
794
  cwd: string;
790
795
  model?: string;
791
796
  modelProvider?: string;
@@ -1159,15 +1164,6 @@ export interface AcceptanceLedger {
1159
1164
  };
1160
1165
  }
1161
1166
 
1162
- export interface ProtocolOutputLimit {
1163
- code: "protocol_output_limit";
1164
- stream: "stdout" | "stderr";
1165
- limitBytes: number;
1166
- observedBytes: number;
1167
- diagnosticPrefix: string;
1168
- diagnosticTail: string;
1169
- }
1170
-
1171
1167
  export interface LaunchResolvedChildExtensionsV1 {
1172
1168
  version: 1;
1173
1169
  /** This is parent-resolved launch intent, not child-runtime acknowledgement that extensions loaded. */
@@ -1262,7 +1258,6 @@ export interface SingleResult {
1262
1258
  * a signal to reduce input size or re-decompose the task.
1263
1259
  */
1264
1260
  contextOverflow?: boolean;
1265
- protocolError?: ProtocolOutputLimit;
1266
1261
  sessionFile?: string;
1267
1262
  skills?: string[];
1268
1263
  skillsWarning?: string;
@@ -1369,7 +1364,7 @@ export interface AgentCapabilityRow {
1369
1364
  executable: boolean;
1370
1365
  restrictionSources?: string[];
1371
1366
  aliases?: string[];
1372
- runner: { type: "pi" } | { type: "external-cli"; adapter?: string; capabilities: ExternalCliCapabilities } | { type: "external-job"; provider: string; available?: boolean; capabilities: ExternalJobRunnerStatus["capabilities"] };
1367
+ runner: { type: "pi" } | { type: "external-cli"; adapter?: string; command: string; available: boolean; unavailableReason?: string; capabilities: ExternalCliCapabilities } | { type: "external-job"; provider: string; available?: boolean; capabilities: ExternalJobRunnerStatus["capabilities"] };
1373
1368
  tools: { ambient: boolean; names: string[]; excludeTools?: string[]; mcpDirectTools: string[]; mutationTools?: string[] };
1374
1369
  model?: { value?: string; fallbackModels?: string[]; thinking?: string | false };
1375
1370
  execution?: { defaultAsync?: boolean; timeoutMs?: number };
@@ -2054,6 +2049,7 @@ export interface ForegroundResumeChild {
2054
2049
  agentContract?: AgentContract;
2055
2050
  /** Private bounded launch fields needed to preserve the child contract on resume. */
2056
2051
  resumeContract?: {
2052
+ modelResponseAliases?: Record<string, string[]>;
2057
2053
  outputSchema?: JsonSchemaObject;
2058
2054
  agentContract?: AgentContract;
2059
2055
  acceptance?: AcceptanceInput;
@@ -2107,6 +2103,18 @@ export interface ForegroundChildControl {
2107
2103
  toolCount?: number;
2108
2104
  interrupt?: () => boolean;
2109
2105
  detach?: () => boolean;
2106
+ /** Steer the live in-process child session; undefined until the session exists. */
2107
+ steer?: (input: ForegroundSteerInput) => Promise<ForegroundSteerOutcome>;
2108
+ }
2109
+
2110
+ export interface ForegroundSteerInput {
2111
+ message: string;
2112
+ mode?: "steer" | "follow_up" | "auto";
2113
+ }
2114
+
2115
+ export interface ForegroundSteerOutcome {
2116
+ state: "delivered" | "queued" | "failed";
2117
+ reason?: string;
2110
2118
  }
2111
2119
 
2112
2120
  export interface ForegroundRunControl {
@@ -2115,8 +2123,6 @@ export interface ForegroundRunControl {
2115
2123
  parentWorkflowRunId?: string;
2116
2124
  /** Stable workflow lane key for this live foreground child. */
2117
2125
  workflowKey?: string;
2118
- /** Private control root used to steer a live workflow-owned child. */
2119
- workflowSteeringDir?: string;
2120
2126
  /** Originating parent session; required for public fleet projection. */
2121
2127
  sessionId?: string;
2122
2128
  mode: SubagentRunMode;
@@ -2158,6 +2164,7 @@ export interface ForegroundRunControl {
2158
2164
  nestedChildren?: NestedRunSummary[];
2159
2165
  interrupt?: () => boolean;
2160
2166
  detach?: () => boolean;
2167
+ steer?: ForegroundChildControl["steer"];
2161
2168
  }
2162
2169
 
2163
2170
  export interface WaitSubscriptionRecord {
@@ -2197,6 +2204,8 @@ export interface SubagentState {
2197
2204
  trustedSessionFileRoot?: string;
2198
2205
  /** Live async session roots created by this parent executor, keyed by run id. */
2199
2206
  liveAsyncSessionRoots?: Map<string, string>;
2207
+ /** Foreground nested routes retained after their direct parent settles, keyed by root run id. */
2208
+ retainedForegroundNestedRoutes?: Map<string, NestedRouteInfo>;
2200
2209
  /** Last valid parent session model observed for this session; used when continuation contexts omit ctx.model. */
2201
2210
  lastParentModel?: { provider: string; id: string };
2202
2211
  subagentInProgress?: boolean;
@@ -2318,17 +2327,25 @@ export interface SubagentChildStatusEvent {
2318
2327
  // Execution Options
2319
2328
  // ============================================================================
2320
2329
 
2330
+ /** Live controls for one in-process foreground child session. */
2331
+ export interface ForegroundChildSessionControls {
2332
+ steer: (text: string) => Promise<void>;
2333
+ followUp: (text: string) => Promise<void>;
2334
+ }
2335
+
2321
2336
  export interface RunSyncOptions {
2322
2337
  /** Exact discovery provenance for an unknown-agent error; omission uses defensive fallback discovery. */
2323
2338
  unknownAgentDiagnosticContext?: import("../agents/agents.ts").UnknownAgentDiagnosticContext;
2339
+ /** Session factory for the in-process child; defaults to the process-wide factory. */
2340
+ childSessionFactory?: import("../runs/shared/child-session.ts").ChildSessionFactory;
2341
+ /** The launching executor's own child runtime when it is itself an in-process child. */
2342
+ childRuntime?: import("../runs/shared/child-runtime-config.ts").ChildRuntimeConfig;
2343
+ /** Fires once the child session exists and can be steered. */
2344
+ onChildSession?: (controls: ForegroundChildSessionControls) => void;
2324
2345
  /** Opt-in global permission rules; missing tools remain allowed. */
2325
2346
  permissions?: import("../runs/shared/permissions.ts").PermissionConfig;
2326
2347
  /** Session id of the direct parent session for permission-system ask forwarding. */
2327
2348
  parentSessionId?: string;
2328
- /** Private prompt-runtime steering transport for workflow-owned foreground children. */
2329
- steerInboxDir?: string;
2330
- steerCapabilityPath?: string;
2331
- steerAckDir?: string;
2332
2349
  /** Resolved launch context for this child. */
2333
2350
  context?: "fresh" | "fork";
2334
2351
  cwd?: string;
@@ -2397,6 +2414,7 @@ export interface RunSyncOptions {
2397
2414
  workflowChildPermitLaunch?: WorkflowChildPermitContext;
2398
2415
  /** Registry models available for heuristic bare-model resolution */
2399
2416
  availableModels?: Array<{ provider: string; id: string; fullId: string; contextWindow?: number }>;
2417
+ modelResponseAliases?: Record<string, string[]>;
2400
2418
  /** Current parent-session provider to prefer for ambiguous bare model ids */
2401
2419
  preferredModelProvider?: string;
2402
2420
  /** Parent Pi event host used to snapshot runtime-registered MCP servers before child launch. */
@@ -2410,6 +2428,7 @@ export interface RunSyncOptions {
2410
2428
  schemaPath: string;
2411
2429
  outputPath: string;
2412
2430
  acceptanceReportPath?: string;
2431
+ acceptanceReportRequired?: boolean;
2413
2432
  };
2414
2433
  agentContract?: AgentContract;
2415
2434
  acceptance?: AcceptanceInput;
@@ -2531,6 +2550,8 @@ export interface ExtensionConfig {
2531
2550
  asyncWidget?: boolean;
2532
2551
  /** Configure the process-wide TTL policy for persisted model exclusions. */
2533
2552
  modelExclusions?: ModelExclusionsConfig;
2553
+ /** Exact provider/model candidates mapped to operator-declared equivalent response IDs. Empty arrays add no accepted IDs. */
2554
+ modelResponseAliases?: Record<string, string[]>;
2534
2555
  /** Tool description variant registered for the parent-facing subagent tool. Defaults to split metadata. */
2535
2556
  toolDescriptionMode?: ToolDescriptionMode;
2536
2557
  /** Inline chat rendering for the subagent tool. Defaults to rich. */
@@ -2765,8 +2786,17 @@ export function normalizeMaxSubagentDepth(value: unknown): number | undefined {
2765
2786
  return normalizeNonNegativeInteger(value);
2766
2787
  }
2767
2788
 
2768
- export function resolveCurrentMaxSubagentDepth(configMaxDepth?: number): number {
2769
- return normalizeMaxSubagentDepth(process.env.PI_SUBAGENT_MAX_DEPTH)
2789
+ /** Depth context of the executor's own child runtime, when it runs as an in-process child. */
2790
+ export interface SubagentDepthContext {
2791
+ depth: number;
2792
+ maxDepth?: number;
2793
+ }
2794
+
2795
+ /** Operator override for the top-level parent; children inherit their limit through their runtime config. */
2796
+ export const SUBAGENT_MAX_DEPTH_ENV = "PI_SUBAGENT_MAX_DEPTH";
2797
+
2798
+ export function resolveCurrentMaxSubagentDepth(configMaxDepth?: number, runtime?: SubagentDepthContext): number {
2799
+ return normalizeMaxSubagentDepth(runtime ? runtime.maxDepth : process.env[SUBAGENT_MAX_DEPTH_ENV])
2770
2800
  ?? normalizeMaxSubagentDepth(configMaxDepth)
2771
2801
  ?? DEFAULT_SUBAGENT_MAX_DEPTH;
2772
2802
  }
@@ -2777,19 +2807,24 @@ export function resolveChildMaxSubagentDepth(parentMaxDepth: number, agentMaxDep
2777
2807
  return normalizedAgent === undefined ? normalizedParent : Math.min(normalizedParent, normalizedAgent);
2778
2808
  }
2779
2809
 
2780
- export function checkSubagentDepth(configMaxDepth?: number): { blocked: boolean; depth: number; maxDepth: number } {
2781
- const depth = Number(process.env.PI_SUBAGENT_DEPTH ?? "0");
2782
- const maxDepth = resolveCurrentMaxSubagentDepth(configMaxDepth);
2783
- const blocked = Number.isFinite(depth) && depth >= maxDepth;
2810
+ /** Depth of the executor itself: 0 for a top-level parent, its own child depth otherwise. */
2811
+ export function resolveCurrentSubagentDepth(runtime?: SubagentDepthContext): number {
2812
+ const depth = runtime ? runtime.depth : 0;
2813
+ return Number.isFinite(depth) ? depth : 0;
2814
+ }
2815
+
2816
+ export function checkSubagentDepth(configMaxDepth?: number, runtime?: SubagentDepthContext): { blocked: boolean; depth: number; maxDepth: number } {
2817
+ const depth = resolveCurrentSubagentDepth(runtime);
2818
+ const maxDepth = resolveCurrentMaxSubagentDepth(configMaxDepth, runtime);
2819
+ const blocked = depth >= maxDepth;
2784
2820
  return { blocked, depth, maxDepth };
2785
2821
  }
2786
2822
 
2787
- export function getSubagentDepthEnv(maxDepth?: number): Record<string, string> {
2788
- const parentDepth = Number(process.env.PI_SUBAGENT_DEPTH ?? "0");
2789
- const nextDepth = Number.isFinite(parentDepth) ? parentDepth + 1 : 1;
2823
+ /** Depth context handed to a child launched by an executor at `runtime` (undefined for a top-level parent). */
2824
+ export function resolveChildDepth(maxDepth?: number, runtime?: SubagentDepthContext): Required<SubagentDepthContext> {
2790
2825
  return {
2791
- PI_SUBAGENT_DEPTH: String(nextDepth),
2792
- PI_SUBAGENT_MAX_DEPTH: String(normalizeMaxSubagentDepth(maxDepth) ?? resolveCurrentMaxSubagentDepth()),
2826
+ depth: resolveCurrentSubagentDepth(runtime) + 1,
2827
+ maxDepth: normalizeMaxSubagentDepth(maxDepth) ?? resolveCurrentMaxSubagentDepth(undefined, runtime),
2793
2828
  };
2794
2829
  }
2795
2830
 
@@ -135,6 +135,9 @@ function isNotFoundError(error: unknown): boolean {
135
135
  * Read async job status from disk (with mtime-based caching)
136
136
  */
137
137
  export function readStatus(asyncDir: string): AsyncStatus | null {
138
+ if (Buffer.byteLength(path.basename(asyncDir), "utf-8") > 255) {
139
+ return null;
140
+ }
138
141
  const statusPath = path.join(asyncDir, "status.json");
139
142
 
140
143
  let stat: fs.Stats;
@@ -413,10 +416,9 @@ export function compactForegroundDetails(details: Details): Details {
413
416
  *
414
417
  * The completed-compaction helpers above bail out while a child is still
415
418
  * `running`, so a long or deeply nested fan-out streams full, unbounded progress on
416
- * every tick. Pi serializes each streamed `tool_execution_update` as a single
417
- * child-stdout line, which the parent reads under `MAX_CHILD_PENDING_LINE_BYTES`;
418
- * an unbounded running snapshot can cross that cap and kill the child with
419
- * `protocol_output_limit`.
419
+ * every tick. The parent records every streamed `tool_execution_update` in its
420
+ * transcript and `events.jsonl`, so an unbounded running snapshot grows those
421
+ * artifacts and the live display state without bound.
420
422
  *
421
423
  * These bound the STREAMED snapshot only. The final returned result keeps the full
422
424
  * live progress and message transcript, and every live-display consumer already
@@ -2,9 +2,11 @@ export type FileWatchPurpose =
2
2
  | "result-delivery"
3
3
  | "supervisor-channel"
4
4
  | "async-job-tracker"
5
+ | "retained-nested-route-tracker"
5
6
  | "runner-control-inbox"
6
7
  | "child-steering-inbox";
7
8
 
8
9
  export function shouldUseNativeFsWatch(_purpose: FileWatchPurpose, platform: NodeJS.Platform = process.platform): boolean {
10
+ if (_purpose === "retained-nested-route-tracker" && platform === "win32") return false;
9
11
  return platform !== "darwin";
10
12
  }
@@ -21,7 +21,6 @@ import { formatTokens, shortenPath } from "../shared/formatters.ts";
21
21
  import { listAsyncRuns, formatAsyncRunProgressLabel, type AsyncRunSummary } from "../runs/background/async-status.ts";
22
22
  import { encodeInspectReply, handleInspectRpcArgs, INSPECT_WIDGET_KEY } from "../runs/background/inspect-rpc.ts";
23
23
  import { listScheduledRunSummaries } from "../runs/background/scheduled-runs.ts";
24
- import { SUBAGENT_FANOUT_CHILD_ENV } from "../runs/shared/pi-args.ts";
25
24
  import { resolveAsyncStatusChild } from "../runs/shared/child-identity.ts";
26
25
  import { readStatus } from "../shared/utils.ts";
27
26
  import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.ts";
@@ -1030,11 +1029,6 @@ export function registerSlashCommands(
1030
1029
  return;
1031
1030
  }
1032
1031
 
1033
- if (process.env[SUBAGENT_FANOUT_CHILD_ENV] === "1") {
1034
- sendSlashText(pi, "Selector unavailable in child-safe fanout mode. Pass an explicit current-session top-level async run id, for example `/subagents-stop <run-id>` or `subagent({ action: \"stop\", id: \"<run-id>\" })`.");
1035
- return;
1036
- }
1037
-
1038
1032
  let targets: StopSelectorTarget[];
1039
1033
  try {
1040
1034
  targets = discoverStopTargets(ctx, state);
@@ -666,7 +666,7 @@ export class SubagentFleetStatus {
666
666
  const capacity = this.state.activeAsyncCapacity;
667
667
  const hasNativeRows = workEntries.some((entry) => !entry.external);
668
668
  const showNativeSummary = hasNativeRows || Boolean(capacity?.used);
669
- const asyncRuns = capacity && showNativeSummary ? `Async runs ${capacity.used}/${capacity.limit || "∞"}` : "";
669
+ const asyncRuns = capacity && showNativeSummary && (capacity.used > 0 || capacity.limit > 0) ? `Async runs ${capacity.used}/${capacity.limit || "∞"}` : "";
670
670
  const activeEntries = activeLeafAgentCount(workEntries);
671
671
  const noun = workEntries.some((entry) => entry.external) ? "job" : "agent";
672
672
  const agents = activeEntries > 0 ? `${activeEntries} active ${noun}${activeEntries === 1 ? "" : "s"}` : "";
package/src/tui/fleet.ts CHANGED
@@ -204,7 +204,6 @@ export function collectFleetSnapshot(
204
204
  if (!control.parentWorkflowRunId
205
205
  || !workflowParentIds.has(control.parentWorkflowRunId)
206
206
  || !belongsToCurrentSession(control.sessionId, state.currentSessionId)
207
- || !control.workflowSteeringDir
208
207
  || activeChildCount === 0) continue;
209
208
  liveWorkflowForegroundControls.add(control);
210
209
  workflowForegroundChildCounts.set(control.parentWorkflowRunId, (workflowForegroundChildCounts.get(control.parentWorkflowRunId) ?? 0) + activeChildCount);