{"version":3,"file":"subagent-startup-retry.d.ts","sourceRoot":"","sources":["../../../../src/runs/shared/subagent-startup-retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGnD;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,2BAA4B,CAAC;AAE1E,mFAAmF;AACnF,eAAO,MAAM,wCAAwC,OAAO,CAAC;AAI7D,MAAM,WAAW,8BAA8B;IAC9C,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAaD;;;;GAIG;AACH,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,8BAA8B,GAAG,OAAO,CAuBnG;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CAET;AAED,wBAAgB,wCAAwC,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE3G;AAED,mFAAmF;AACnF,wBAAsB,2BAA2B,CAChD,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,GACrC,OAAO,CAAC,OAAO,CAAC,CAkBlB","sourcesContent":["import type { Usage } from \"../../shared/types.ts\";\nimport { formatProcessSignalError } from \"./process-signal.ts\";\n\n/**\n * Retry delays for child processes that exit before any model or tool activity.\n * The schedule is intentionally short and bounded to avoid amplifying persistent\n * launch failures while allowing concurrent Pi startup locks to clear.\n */\nexport const SUBAGENT_STARTUP_RETRY_DELAYS_MS = [250, 750, 1500] as const;\n\n/** A genuine startup race should fail well before a model request can complete. */\nexport const MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS = 2000;\n\nconst SIGKILL_PROCESS_SIGNAL_ERROR = formatProcessSignalError(\"SIGKILL\");\n\nexport interface SubagentStartupFailureEvidence {\n\texitCode: number | null | undefined;\n\terror?: string;\n\tfinalOutput?: string;\n\tmessageCount: number;\n\ttoolCount: number;\n\tusage: Usage;\n\tdurationMs: number;\n\tprotocolError?: unknown;\n\tprocessSignal?: string | null;\n\tobservedMutationAttempt?: boolean;\n\tdetached?: boolean;\n\tinterrupted?: boolean;\n\ttimedOut?: boolean;\n\tstopped?: boolean;\n\tturnBudgetExceeded?: boolean;\n}\n\nfunction hasUsage(usage: Usage): boolean {\n\treturn (\n\t\tusage.input !== 0 ||\n\t\tusage.output !== 0 ||\n\t\tusage.cacheRead !== 0 ||\n\t\tusage.cacheWrite !== 0 ||\n\t\tusage.cost !== 0 ||\n\t\tusage.turns !== 0\n\t);\n}\n\n/**\n * Classify only unexplained, zero-activity child exits as startup failures.\n * This intentionally fails closed: any model output, tool activity, usage,\n * mutation evidence, diagnostic, or lifecycle interruption prevents a retry.\n */\nexport function isRetryableSubagentStartupFailure(evidence: SubagentStartupFailureEvidence): boolean {\n\treturn (\n\t\tevidence.exitCode !== undefined &&\n\t\tevidence.exitCode !== null &&\n\t\tevidence.exitCode !== 0 &&\n\t\t(!evidence.error?.trim() || evidence.error === SIGKILL_PROCESS_SIGNAL_ERROR) &&\n\t\t!evidence.finalOutput?.trim() &&\n\t\tevidence.messageCount === 0 &&\n\t\tevidence.toolCount === 0 &&\n\t\t!hasUsage(evidence.usage) &&\n\t\tevidence.durationMs >= 0 &&\n\t\tevidence.durationMs <= MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS &&\n\t\tevidence.protocolError === undefined &&\n\t\t(evidence.processSignal === undefined ||\n\t\t\tevidence.processSignal === null ||\n\t\t\tevidence.processSignal === \"SIGKILL\") &&\n\t\tevidence.observedMutationAttempt !== true &&\n\t\tevidence.detached !== true &&\n\t\tevidence.interrupted !== true &&\n\t\tevidence.timedOut !== true &&\n\t\tevidence.stopped !== true &&\n\t\tevidence.turnBudgetExceeded !== true\n\t);\n}\n\nexport function formatSubagentStartupRetryNote(input: {\n\tmodel: string;\n\tattempt: number;\n\tmaxAttempts: number;\n\tdelayMs: number;\n}): string {\n\treturn `[startup-retry] ${input.model} exited before model or tool activity (attempt ${input.attempt}/${input.maxAttempts}). Retrying the same model in ${input.delayMs}ms.`;\n}\n\nexport function formatSubagentStartupRetryExhaustedError(input: { model: string; attempts: number }): string {\n\treturn `Subagent failed to start after ${input.attempts} attempts on ${input.model}; no model, tool, output, usage, or diagnostic activity was observed. This may be a concurrent Pi startup race. Retry the run or temporarily lower subagent concurrency.`;\n}\n\n/** Wait for a retry delay, returning false when any lifecycle signal aborts it. */\nexport async function waitForSubagentStartupRetry(\n\tdelayMs: number,\n\tsignals: Array<AbortSignal | undefined>,\n): Promise<boolean> {\n\tconst activeSignals = signals.filter((signal): signal is AbortSignal => Boolean(signal));\n\tif (activeSignals.some((signal) => signal.aborted)) return false;\n\tif (delayMs <= 0) return true;\n\n\treturn await new Promise<boolean>((resolve) => {\n\t\tlet settled = false;\n\t\tconst finish = (shouldRetry: boolean): void => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tfor (const signal of activeSignals) signal.removeEventListener(\"abort\", abort);\n\t\t\tresolve(shouldRetry);\n\t\t};\n\t\tconst abort = (): void => finish(false);\n\t\tconst timer = setTimeout(() => finish(true), delayMs);\n\t\tfor (const signal of activeSignals) signal.addEventListener(\"abort\", abort, { once: true });\n\t});\n}\n"]}