{"version":3,"file":"stale-run-reconciler.d.ts","sourceRoot":"","sources":["../../../../src/runs/background/stale-run-reconciler.ts"],"names":[],"mappings":"AAIA,OAAO,EACN,KAAK,wBAAwB,EAC7B,KAAK,WAAW,EAGhB,KAAK,eAAe,EACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,KAAK,WAAW,EAKhB,MAAM,4BAA4B,CAAC;AAGpC,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAEvD,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,OAAO,CAAC;AAEpE,UAAU,kBAAkB;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,wBAAwB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,uBAAuB;IAChC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAsUD,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,GAAE,wBAA6B,GAAG,IAAI,CA8BhH;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,MAAqB,GAAG,WAAW,CAatF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,wBAA6B,GAAG,uBAAuB,CAwDnH","sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { writeAtomicJson } from \"../../shared/atomic-json.ts\";\nimport { resolveEffectiveThinking } from \"../../shared/model-info.ts\";\nimport {\n\ttype AsyncParallelGroupStatus,\n\ttype AsyncStatus,\n\tDIRS,\n\ttype NestedRunSummary,\n\ttype SubagentRunMode,\n} from \"../../shared/types.ts\";\nimport {\n\ttype NestedRoute,\n\tnestedSummaryFromAsyncStatus,\n\tprojectNestedEvents,\n\tresolveNestedAsyncDir,\n\twriteNestedEvent,\n} from \"../shared/nested-events.ts\";\nimport { normalizeParallelGroups } from \"./parallel-groups.ts\";\n\nexport type PidLiveness = \"alive\" | \"dead\" | \"unknown\";\n\ntype KillFn = (pid: number, signal?: NodeJS.Signals | 0) => boolean;\n\ninterface StartedRunMetadata {\n\trunId: string;\n\tpid?: number;\n\tsessionId?: string;\n\tmode?: SubagentRunMode;\n\tagents?: string[];\n\tchainStepCount?: number;\n\tparallelGroups?: AsyncParallelGroupStatus[];\n\tstartedAt?: number;\n\tsessionFile?: string;\n}\n\ninterface ReconcileAsyncRunOptions {\n\tresultsDir?: string;\n\tkill?: KillFn;\n\tnow?: () => number;\n\tstartedRun?: StartedRunMetadata;\n\tmissingStatusGraceMs?: number;\n\tstaleAlivePidMs?: number;\n}\n\ninterface ReconcileAsyncRunResult {\n\tstatus: AsyncStatus | null;\n\trepaired: boolean;\n\tresultPath?: string;\n\tmessage?: string;\n}\n\nfunction getErrorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nfunction readRunnerStartupDiagnostics(asyncDir: string): string | undefined {\n\tconst stderrPath = path.join(asyncDir, \"runner.stderr.log\");\n\tconst maxBytes = 64 * 1024;\n\tlet content: string;\n\ttry {\n\t\tconst stat = fs.statSync(stderrPath);\n\t\tif (stat.size <= 0) return undefined;\n\t\tconst fd = fs.openSync(stderrPath, \"r\");\n\t\ttry {\n\t\t\tconst bytesToRead = Math.min(stat.size, maxBytes);\n\t\t\tconst start = Math.max(0, stat.size - bytesToRead);\n\t\t\tconst buffer = Buffer.alloc(bytesToRead);\n\t\t\tfs.readSync(fd, buffer, 0, bytesToRead, start);\n\t\t\tcontent = buffer.toString(\"utf-8\").trim();\n\t\t} finally {\n\t\t\tfs.closeSync(fd);\n\t\t}\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!content) return undefined;\n\tconst lines = content.split(/\\r?\\n/).slice(-30).join(\"\\n\");\n\treturn lines.length > 4000 ? `${lines.slice(-4000)}\\n[stderr tail truncated]` : lines;\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error as NodeJS.ErrnoException).code === \"ENOENT\"\n\t);\n}\n\nfunction appendJsonlBestEffort(filePath: string, payload: object): void {\n\ttry {\n\t\tfs.mkdirSync(path.dirname(filePath), { recursive: true });\n\t\tfs.appendFileSync(filePath, `${JSON.stringify(payload)}\\n`, \"utf-8\");\n\t} catch {\n\t\t// Repair status/result writes are the important path. A broken or full\n\t\t// diagnostic event log must not make stale-run reconciliation fail.\n\t}\n}\n\nfunction readStatusFile(asyncDir: string): AsyncStatus | null {\n\tconst statusPath = path.join(asyncDir, \"status.json\");\n\tlet content: string;\n\ttry {\n\t\tcontent = fs.readFileSync(statusPath, \"utf-8\");\n\t} catch (error) {\n\t\tif (isNotFoundError(error)) return null;\n\t\tthrow new Error(`Failed to read async status file '${statusPath}': ${getErrorMessage(error)}`, {\n\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t});\n\t}\n\ttry {\n\t\treturn JSON.parse(content) as AsyncStatus;\n\t} catch (error) {\n\t\tthrow new Error(`Failed to parse async status file '${statusPath}': ${getErrorMessage(error)}`, {\n\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t});\n\t}\n}\n\ninterface ResultChildOutcome {\n\tagent?: string;\n\tsuccess?: boolean;\n\terror?: string;\n\tsessionFile?: string;\n\tmodel?: string;\n\tthinking?: string;\n\tattemptedModels?: string[];\n\tmodelAttempts?: NonNullable<AsyncStatus[\"steps\"]>[number][\"modelAttempts\"];\n}\n\ninterface ResultRepairData {\n\tstate: \"complete\" | \"failed\" | \"paused\" | \"stopped\";\n\tresults?: ResultChildOutcome[];\n}\n\nfunction readResultRepairData(resultPath: string): ResultRepairData | undefined {\n\ttry {\n\t\tconst data = JSON.parse(fs.readFileSync(resultPath, \"utf-8\")) as {\n\t\t\tsuccess?: boolean;\n\t\t\tstate?: string;\n\t\t\texitCode?: number;\n\t\t\tresults?: unknown;\n\t\t};\n\t\tconst state = data.success\n\t\t\t? \"complete\"\n\t\t\t: data.state === \"stopped\"\n\t\t\t\t? \"stopped\"\n\t\t\t\t: data.state === \"paused\" || data.exitCode === 0\n\t\t\t\t\t? \"paused\"\n\t\t\t\t\t: \"failed\";\n\t\tconst results = Array.isArray(data.results)\n\t\t\t? data.results.map((entry, index) => {\n\t\t\t\t\tif (!entry || typeof entry !== \"object\" || Array.isArray(entry)) return {};\n\t\t\t\t\tconst child = entry as ResultChildOutcome;\n\t\t\t\t\tif (child.model !== undefined && typeof child.model !== \"string\")\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Invalid async result file '${resultPath}': results[${index}].model must be a string.`,\n\t\t\t\t\t\t);\n\t\t\t\t\tif (child.thinking !== undefined && typeof child.thinking !== \"string\")\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Invalid async result file '${resultPath}': results[${index}].thinking must be a string.`,\n\t\t\t\t\t\t);\n\t\t\t\t\treturn child;\n\t\t\t\t})\n\t\t\t: undefined;\n\t\treturn { state, ...(results ? { results } : {}) };\n\t} catch (error) {\n\t\tif (isNotFoundError(error)) return undefined;\n\t\tthrow new Error(`Failed to read async result file '${resultPath}': ${getErrorMessage(error)}`, {\n\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t});\n\t}\n}\n\nfunction childState(\n\toverallState: ResultRepairData[\"state\"],\n\tchild: ResultChildOutcome | undefined,\n): \"complete\" | \"failed\" | \"paused\" | \"stopped\" {\n\tif (child?.success === true) return \"complete\";\n\tif (child?.success === false) return \"failed\";\n\treturn overallState;\n}\n\nfunction terminalStatusFromResult(status: AsyncStatus, resultPath: string, now: number): AsyncStatus | undefined {\n\tconst repair = readResultRepairData(resultPath);\n\tif (!repair) return undefined;\n\tconst steps = (status.steps ?? []).map((step, index) => {\n\t\tif (step.status !== \"running\" && step.status !== \"pending\") return step;\n\t\tconst child = repair.results?.[index];\n\t\tconst state = childState(repair.state, child);\n\t\tconst model = child?.model ?? step.model;\n\t\tconst thinking = resolveEffectiveThinking(model, child?.thinking ?? step.thinking);\n\t\treturn {\n\t\t\t...step,\n\t\t\tstatus: state === \"complete\" ? (\"complete\" as const) : state,\n\t\t\tendedAt: step.endedAt ?? now,\n\t\t\tdurationMs:\n\t\t\t\tstep.startedAt !== undefined && step.durationMs === undefined\n\t\t\t\t\t? Math.max(0, now - step.startedAt)\n\t\t\t\t\t: step.durationMs,\n\t\t\texitCode: step.exitCode ?? (state === \"complete\" || state === \"paused\" ? 0 : 1),\n\t\t\terror: state === \"failed\" || state === \"stopped\" ? (step.error ?? child?.error) : step.error,\n\t\t\tstopped: state === \"stopped\" ? true : step.stopped,\n\t\t\tsessionFile: step.sessionFile ?? child?.sessionFile,\n\t\t\tmodel,\n\t\t\tthinking,\n\t\t\tattemptedModels: child?.attemptedModels ?? step.attemptedModels,\n\t\t\tmodelAttempts: child?.modelAttempts ?? step.modelAttempts,\n\t\t};\n\t});\n\treturn {\n\t\t...status,\n\t\tstate: repair.state,\n\t\t...(status.lifecycleArtifactVersion === 3 &&\n\t\t(!status.processTerminal || status.processTerminal.state === \"pending\")\n\t\t\t? {\n\t\t\t\t\tprocessTerminal: {\n\t\t\t\t\t\tversion: 1 as const,\n\t\t\t\t\t\tstate: \"unknown\" as const,\n\t\t\t\t\t\trunId: status.runId,\n\t\t\t\t\t\trunnerProcessInstanceId: \"observer-unavailable\",\n\t\t\t\t\t\treason: \"observer-unavailable\" as const,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t\t...(repair.state === \"stopped\" ? { stopped: true } : {}),\n\t\tactivityState: undefined,\n\t\tlastUpdate: now,\n\t\tendedAt: status.endedAt ?? now,\n\t\tsteps,\n\t};\n}\n\nfunction buildStartedStatus(asyncDir: string, startedRun: StartedRunMetadata, now: number): AsyncStatus {\n\tconst startedAt = startedRun.startedAt ?? now;\n\tconst agents = startedRun.agents?.length ? startedRun.agents : [\"subagent\"];\n\tconst chainStepCount = startedRun.chainStepCount;\n\tconst parallelGroups =\n\t\tchainStepCount !== undefined\n\t\t\t? normalizeParallelGroups(startedRun.parallelGroups, agents.length, chainStepCount)\n\t\t\t: [];\n\treturn {\n\t\trunId: startedRun.runId || path.basename(asyncDir),\n\t\t...(startedRun.sessionId ? { sessionId: startedRun.sessionId } : {}),\n\t\tmode: startedRun.mode ?? \"single\",\n\t\tstate: \"running\",\n\t\tpid: startedRun.pid,\n\t\tstartedAt,\n\t\tlastUpdate: now,\n\t\tcurrentStep: 0,\n\t\t...(chainStepCount !== undefined ? { chainStepCount } : {}),\n\t\t...(parallelGroups.length ? { parallelGroups } : {}),\n\t\tsteps: agents.map((agent) => ({\n\t\t\tagent,\n\t\t\tstatus: \"running\" as const,\n\t\t\tstartedAt,\n\t\t})),\n\t\t...(startedRun.sessionFile ? { sessionFile: startedRun.sessionFile } : {}),\n\t};\n}\n\nfunction buildFailedRepair(\n\tstatus: AsyncStatus,\n\tasyncDir: string,\n\tnow: number,\n\treason?: string,\n): { status: AsyncStatus; result: object; message: string } {\n\tconst runId = status.runId || path.basename(asyncDir);\n\tconst pid = typeof status.pid === \"number\" ? status.pid : \"unknown\";\n\tconst baseMessage =\n\t\treason ??\n\t\t`Async runner process ${pid} exited or disappeared before writing a result. Marked run failed by stale-run reconciliation.`;\n\tconst diagnostics = readRunnerStartupDiagnostics(asyncDir);\n\tconst message = diagnostics ? `${baseMessage}\\n\\nRunner stderr tail:\\n${diagnostics}` : baseMessage;\n\tconst steps = status.steps?.length ? status.steps : [{ agent: \"subagent\", status: \"running\" as const }];\n\tconst repairedSteps = steps.map((step) =>\n\t\tstep.status === \"running\" || step.status === \"pending\"\n\t\t\t? {\n\t\t\t\t\t...step,\n\t\t\t\t\tstatus: \"failed\" as const,\n\t\t\t\t\tactivityState: undefined,\n\t\t\t\t\tendedAt: step.endedAt ?? now,\n\t\t\t\t\tdurationMs:\n\t\t\t\t\t\tstep.startedAt !== undefined && step.durationMs === undefined\n\t\t\t\t\t\t\t? Math.max(0, now - step.startedAt)\n\t\t\t\t\t\t\t: step.durationMs,\n\t\t\t\t\texitCode: step.exitCode ?? 1,\n\t\t\t\t\terror: step.error ?? message,\n\t\t\t\t}\n\t\t\t: step,\n\t);\n\tconst repairedStatus: AsyncStatus = {\n\t\t...status,\n\t\tstate: \"failed\",\n\t\t...(status.lifecycleArtifactVersion === 3 &&\n\t\t(!status.processTerminal || status.processTerminal.state === \"pending\")\n\t\t\t? {\n\t\t\t\t\tprocessTerminal: {\n\t\t\t\t\t\tversion: 1 as const,\n\t\t\t\t\t\tstate: \"unknown\" as const,\n\t\t\t\t\t\trunId,\n\t\t\t\t\t\trunnerProcessInstanceId: \"observer-unavailable\",\n\t\t\t\t\t\treason: \"stale-repair\" as const,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t\tactivityState: undefined,\n\t\tlastUpdate: now,\n\t\tendedAt: now,\n\t\tsteps: repairedSteps,\n\t};\n\tconst resultAgent = repairedSteps[status.currentStep ?? 0]?.agent ?? repairedSteps[0]?.agent ?? \"subagent\";\n\treturn {\n\t\tstatus: repairedStatus,\n\t\tmessage,\n\t\tresult: {\n\t\t\tid: runId,\n\t\t\tagent: resultAgent,\n\t\t\tmode: status.mode,\n\t\t\tsuccess: false,\n\t\t\tstate: \"failed\",\n\t\t\tsummary: message,\n\t\t\tresults: repairedSteps.map((step) => ({\n\t\t\t\tagent: step.agent,\n\t\t\t\toutput: step.status === \"complete\" || step.status === \"completed\" ? \"\" : message,\n\t\t\t\terror: step.status === \"complete\" || step.status === \"completed\" ? undefined : (step.error ?? message),\n\t\t\t\tsuccess: step.status === \"complete\" || step.status === \"completed\",\n\t\t\t\tmodel: step.model,\n\t\t\t\tattemptedModels: step.attemptedModels,\n\t\t\t\tmodelAttempts: step.modelAttempts,\n\t\t\t\tsessionFile: step.sessionFile,\n\t\t\t})),\n\t\t\texitCode: 1,\n\t\t\ttimestamp: now,\n\t\t\tdurationMs: Math.max(0, now - status.startedAt),\n\t\t\tasyncDir,\n\t\t\tsessionId: status.sessionId,\n\t\t\tsessionFile: status.sessionFile,\n\t\t},\n\t};\n}\n\nfunction writeFailedRepair(\n\tasyncDir: string,\n\tstatus: AsyncStatus,\n\tresultPath: string,\n\tnow: number,\n\treason?: string,\n): ReconcileAsyncRunResult {\n\tconst repair = buildFailedRepair(status, asyncDir, now, reason);\n\twriteAtomicJson(resultPath, repair.result);\n\twriteAtomicJson(path.join(asyncDir, \"status.json\"), repair.status);\n\tappendJsonlBestEffort(path.join(asyncDir, \"events.jsonl\"), {\n\t\ttype: \"subagent.run.repaired_stale\",\n\t\tts: now,\n\t\trunId: repair.status.runId,\n\t\tpid: status.pid,\n\t\tresultPath,\n\t\tmessage: repair.message,\n\t});\n\treturn { status: repair.status, repaired: true, resultPath, message: repair.message };\n}\n\nfunction terminal(state: AsyncStatus[\"state\"]): boolean {\n\treturn state === \"complete\" || state === \"failed\" || state === \"paused\" || state === \"stopped\";\n}\n\nfunction* nestedRuns(children: NestedRunSummary[] | undefined): Generator<NestedRunSummary> {\n\tfor (const child of children ?? []) {\n\t\tyield child;\n\t\tyield* nestedRuns(child.children);\n\t\tyield* nestedRuns(child.steps?.flatMap((step) => step.children ?? []));\n\t}\n}\n\nexport function reconcileNestedAsyncDescendants(route: NestedRoute, options: ReconcileAsyncRunOptions = {}): void {\n\tconst registry = projectNestedEvents(route);\n\tfor (const run of nestedRuns(registry.children)) {\n\t\tif (run.state !== \"running\" && run.state !== \"queued\") continue;\n\t\tconst asyncDir = resolveNestedAsyncDir(route.rootRunId, run);\n\t\tif (!asyncDir) continue;\n\t\tconst result = reconcileAsyncRun(asyncDir, {\n\t\t\t...options,\n\t\t\tresultsDir: path.join(options.resultsDir ?? DIRS.results, \"nested\", route.rootRunId),\n\t\t});\n\t\tconst status = result.status;\n\t\tif (!status) continue;\n\t\tif (!result.repaired && !terminal(status.state)) continue;\n\t\tconst ts = options.now?.() ?? Date.now();\n\t\twriteNestedEvent(route, {\n\t\t\ttype: terminal(status.state) ? \"subagent.nested.completed\" : \"subagent.nested.updated\",\n\t\t\tts,\n\t\t\tparentRunId: run.parentRunId,\n\t\t\tparentStepIndex: run.parentStepIndex,\n\t\t\tchild: nestedSummaryFromAsyncStatus(status, asyncDir, {\n\t\t\t\tid: run.id,\n\t\t\t\tparentRunId: run.parentRunId,\n\t\t\t\tparentStepIndex: run.parentStepIndex,\n\t\t\t\tdepth: run.depth,\n\t\t\t\tpath: run.path,\n\t\t\t\tmode: run.mode,\n\t\t\t\tts,\n\t\t\t}),\n\t\t});\n\t}\n}\n\nexport function checkPidLiveness(pid: number, kill: KillFn = process.kill): PidLiveness {\n\ttry {\n\t\tkill(pid, 0);\n\t\treturn \"alive\";\n\t} catch (error) {\n\t\tconst code =\n\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t? (error as NodeJS.ErrnoException).code\n\t\t\t\t: undefined;\n\t\tif (code === \"ESRCH\") return \"dead\";\n\t\tif (code === \"EPERM\") return \"unknown\";\n\t\treturn \"unknown\";\n\t}\n}\n\nexport function reconcileAsyncRun(asyncDir: string, options: ReconcileAsyncRunOptions = {}): ReconcileAsyncRunResult {\n\tconst now = options.now?.() ?? Date.now();\n\tconst status = readStatusFile(asyncDir);\n\tconst startedStatus =\n\t\t!status && options.startedRun ? buildStartedStatus(asyncDir, options.startedRun, now) : undefined;\n\tconst effectiveStatus = status ?? startedStatus;\n\tif (!effectiveStatus) return { status: null, repaired: false };\n\tconst statusPath = path.join(asyncDir, \"status.json\");\n\tfor (const [index, step] of (effectiveStatus.steps ?? []).entries()) {\n\t\tconst stepRecord = step as Record<string, unknown>;\n\t\tif (stepRecord.model !== undefined && typeof stepRecord.model !== \"string\")\n\t\t\tthrow new Error(`Invalid async status file '${statusPath}': steps[${index}].model must be a string.`);\n\t\tif (stepRecord.thinking !== undefined && typeof stepRecord.thinking !== \"string\")\n\t\t\tthrow new Error(`Invalid async status file '${statusPath}': steps[${index}].thinking must be a string.`);\n\t}\n\n\tconst runId = effectiveStatus.runId || path.basename(asyncDir);\n\tconst resultPath = path.join(options.resultsDir ?? DIRS.results, `${runId}.json`);\n\tif (fs.existsSync(resultPath)) {\n\t\tconst terminalStatus =\n\t\t\teffectiveStatus.state === \"running\" || effectiveStatus.state === \"queued\"\n\t\t\t\t? terminalStatusFromResult(effectiveStatus, resultPath, now)\n\t\t\t\t: undefined;\n\t\tif (terminalStatus) {\n\t\t\twriteAtomicJson(path.join(asyncDir, \"status.json\"), terminalStatus);\n\t\t\treturn {\n\t\t\t\tstatus: terminalStatus,\n\t\t\t\trepaired: true,\n\t\t\t\tresultPath,\n\t\t\t\tmessage: \"Existing async result file was used to repair stale running status.\",\n\t\t\t};\n\t\t}\n\t\treturn { status: effectiveStatus, repaired: false, resultPath };\n\t}\n\n\tif (effectiveStatus.state !== \"running\" || typeof effectiveStatus.pid !== \"number\") {\n\t\treturn { status: status ?? null, repaired: false, resultPath };\n\t}\n\n\tif (!status) {\n\t\tconst startedAt = options.startedRun?.startedAt ?? effectiveStatus.startedAt;\n\t\tif (now - startedAt < (options.missingStatusGraceMs ?? 1000)) {\n\t\t\treturn { status: null, repaired: false, resultPath };\n\t\t}\n\t}\n\n\tconst liveness = checkPidLiveness(effectiveStatus.pid, options.kill);\n\tif (liveness !== \"dead\") {\n\t\tconst staleAfterMs = options.staleAlivePidMs ?? 24 * 60 * 60 * 1000;\n\t\tconst lastUpdate = effectiveStatus.lastUpdate ?? effectiveStatus.startedAt;\n\t\tif (now - lastUpdate <= staleAfterMs) return { status: status ?? null, repaired: false, resultPath };\n\t\tconst message = `Async runner process ${effectiveStatus.pid} still has a live PID, but status has not updated for ${now - lastUpdate}ms. Marked run failed by stale-run reconciliation because PID ownership cannot be verified.`;\n\t\treturn writeFailedRepair(asyncDir, effectiveStatus, resultPath, now, message);\n\t}\n\n\treturn writeFailedRepair(asyncDir, effectiveStatus, resultPath, now);\n}\n"]}