( url: string, body: Record<string, unknown>, leg: StreamingContext, execContext: ExecutionContext, options: CopilotLifecycleOptions )
| 352 | // failure cancelling this leg, see driveSubagentChains) is non-retryable and |
| 353 | // propagates immediately. |
| 354 | async function runResumeLegWithRetry( |
| 355 | url: string, |
| 356 | body: Record<string, unknown>, |
| 357 | leg: StreamingContext, |
| 358 | execContext: ExecutionContext, |
| 359 | options: CopilotLifecycleOptions |
| 360 | ): Promise<void> { |
| 361 | let attempt = 0 |
| 362 | for (;;) { |
| 363 | const errorsBeforeAttempt = leg.errors.length |
| 364 | const willRetryOnStreamError = attempt < MAX_RESUME_ATTEMPTS - 1 |
| 365 | const legBody = willRetryOnStreamError ? { ...body, willRetryOnStreamError: true } : body |
| 366 | try { |
| 367 | await runStreamLoop( |
| 368 | url, |
| 369 | { method: 'POST', headers: mothershipRequestHeaders(), body: JSON.stringify(legBody) }, |
| 370 | leg, |
| 371 | execContext, |
| 372 | options |
| 373 | ) |
| 374 | return |
| 375 | } catch (error) { |
| 376 | if (isRetryableStreamError(error) && attempt < MAX_RESUME_ATTEMPTS - 1) { |
| 377 | leg.errors.length = errorsBeforeAttempt |
| 378 | attempt++ |
| 379 | const backoff = RESUME_BACKOFF_MS[attempt - 1] ?? 1000 |
| 380 | logger.warn('Child resume leg failed, retrying', { |
| 381 | attempt: attempt + 1, |
| 382 | maxAttempts: MAX_RESUME_ATTEMPTS, |
| 383 | backoffMs: backoff, |
| 384 | error: toError(error).message, |
| 385 | }) |
| 386 | await sleepWithAbort(backoff, options.abortSignal) |
| 387 | continue |
| 388 | } |
| 389 | throw error |
| 390 | } |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | // driveOneChildChain resumes a single subagent's checkpoint chain to its end: |
| 395 | // resume -> (re-pause -> resume)* -> fold into join. Returns the orchestrator's |
no test coverage detected