@ferris1225/pi-subagents 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/spawn.ts CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  emptyUsage,
23
23
  extractToolErrorText,
24
24
  getPiInvocation,
25
+ isRpcCommandTimeoutError,
25
26
  RpcRunControl,
26
27
  runRpcAgentAttempt,
27
28
  sessionExists,
@@ -37,6 +38,7 @@ export {
37
38
  DEPTH_ENV_VAR,
38
39
  extractToolErrorText,
39
40
  getPiInvocation,
41
+ isRpcCommandTimeoutError,
40
42
  RpcRunControl,
41
43
  sessionExists,
42
44
  SUBAGENT_KILL_GRACE_MS,
@@ -201,6 +203,8 @@ export function isModelLevelFailure(result: SingleResult): boolean {
201
203
  if (!isFailedResult(result)) return false;
202
204
  if (result.stopReason === "aborted") return false;
203
205
  if (result.dispatchFailed) return false;
206
+ if (result.rpcStartupFailed) return false;
207
+ if (isRpcCommandTimeoutError(result.errorMessage)) return false;
204
208
  if (result.integrationStatus === "retained") return false;
205
209
  if (result.errorMessage?.includes("idle timeout")) return true;
206
210
  if (result.rpcPromptRejected) return true;
@@ -217,6 +221,9 @@ export function isModelLevelFailure(result: SingleResult): boolean {
217
221
  }
218
222
 
219
223
  if ((result.failedTools?.length ?? 0) > 0) return false;
224
+ // No accepted prompt, no activity, and no assistant turn means the provider
225
+ // was never reached. Stderr or an exit error here is a startup/transport miss.
226
+ if (!result.rpcPromptAccepted && !result.rpcActivity) return false;
220
227
  return Boolean(
221
228
  result.rpcPromptAccepted ||
222
229
  result.rpcActivity ||
@@ -235,6 +242,7 @@ export function isRetryableStartupFailure(result: SingleResult, durationMs: numb
235
242
  if (result.messages.length > 0) return false;
236
243
  const usage = result.usage;
237
244
  if (usage.turns || usage.input || usage.output || usage.cacheRead || usage.cacheWrite || usage.cost) return false;
245
+ if (result.rpcStartupFailed) return true;
238
246
  if (durationMs > MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS) return false;
239
247
  if (result.stderr.trim().length > 0) return false;
240
248
  if (result.errorMessage && result.errorMessage.trim().length > 0) return false;
@@ -242,7 +250,7 @@ export function isRetryableStartupFailure(result: SingleResult, durationMs: numb
242
250
  }
243
251
 
244
252
  export function formatStartupRetryExhaustedError(model: string, attempts: number): string {
245
- return `Subagent failed to start after ${attempts} attempt${attempts === 1 ? "" : "s"} on ${model}: the child exited before any model, tool, output, or usage activity. This is typically a concurrent pi startup race (several sub-agents starting at once). Retry the dispatch, or temporarily lower maxConcurrency in /subagents-setup.`;
253
+ return `Subagent failed to start after ${attempts} attempt${attempts === 1 ? "" : "s"} on ${model}: the child never accepted its initial RPC prompt or produced any model, tool, output, or usage activity. This is typically a concurrent pi startup race (several sub-agents starting at once). Retry the dispatch, or temporarily lower maxConcurrency in /subagents-setup.`;
246
254
  }
247
255
 
248
256
  export async function waitForStartupRetry(delayMs: number, signal?: AbortSignal): Promise<boolean> {
@@ -326,6 +334,8 @@ export interface RunSingleOptions {
326
334
  env?: NodeJS.ProcessEnv;
327
335
  /** Stable logical-generation controller shared across retry attempts. */
328
336
  control?: RpcRunControl;
337
+ rpcReadyTimeoutMs?: number;
338
+ rpcCommandTimeoutMs?: number;
329
339
  }
330
340
 
331
341
  function controlledDisposition(options: RunSingleOptions, base?: SingleResult): SingleResult | undefined {
@@ -391,6 +401,8 @@ export async function runSingleAgent(options: RunSingleOptions): Promise<SingleR
391
401
  onLive: options.onLive,
392
402
  env: options.env,
393
403
  control,
404
+ rpcReadyTimeoutMs: options.rpcReadyTimeoutMs,
405
+ rpcCommandTimeoutMs: options.rpcCommandTimeoutMs,
394
406
  });
395
407
  result.task = control?.getObjective() ?? result.task;
396
408
  return result;