{"version":3,"file":"async-steering-action.d.ts","sourceRoot":"","sources":["../../../../src/runs/foreground/async-steering-action.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAe,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGrH,OAAO,EAKN,KAAK,iBAAiB,EAEtB,MAAM,kCAAkC,CAAC;AAY1C,wBAAsB,aAAa,CAAC,KAAK,EAAE;IAC1C,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,OAAO,CAAC;IAC7D,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACzE,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACvE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,UAAU,CAAC,EAAE,gBAAgB,CAAC;QAC9B,UAAU,CAAC,EAAE,gBAAgB,CAAC;KAC9B,KAAK,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;CACxC,GAAG,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CA+dpC","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { AgentToolResult } from \"@lpb-work/pi-agent-core\";\nimport { writeAtomicJson } from \"../../shared/atomic-json.ts\";\nimport type { AsyncStatus, Details, SubagentState, ToolBudgetConfig, TurnBudgetConfig } from \"../../shared/types.ts\";\nimport { readStatus } from \"../../shared/utils.ts\";\nimport { resolveAsyncResumeTarget } from \"../background/async-resume.ts\";\nimport {\n\tconsumeSteerAcks,\n\tdeliverInterruptRequest,\n\tqueueRevivalBrief,\n\trequestAsyncSteer,\n\ttype SteerDeliveryMode,\n\ttype SteerRequest,\n} from \"../background/control-channel.ts\";\nimport { reconcileAsyncRun } from \"../background/stale-run-reconciler.ts\";\nimport {\n\tactionResultFromSteeringStatus,\n\tclaimSteeringRecovery,\n\tcreateSteeringStatus,\n\trecordSteeringRequest,\n\tremainingSteeringRecoveryLimits,\n\tupdateSteeringTarget,\n\twaitForSteeringAction,\n} from \"../background/steering.ts\";\n\nexport async function steerAsyncRun(input: {\n\tstate: SubagentState;\n\trunId: string;\n\tmessage: string;\n\tmode?: SteerDeliveryMode;\n\tindex?: number;\n\tkill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;\n\tlocation: { asyncDir: string | null };\n\tsignal?: AbortSignal;\n\tackTimeoutMs?: number;\n\trecoveryTimeoutMs?: number;\n\tonRequestQueued?: (requestPath: string) => void;\n\tonBeforeRecoveryClaim?: (requestId: string, committedAt: number) => void;\n\tonRecoveryCommitted?: (requestId: string, committedAt: number) => void;\n\trecover?: (limits: {\n\t\ttimeoutMs?: number;\n\t\tabsoluteDeadlineAt?: number;\n\t\tturnBudget?: TurnBudgetConfig;\n\t\ttoolBudget?: ToolBudgetConfig;\n\t}) => Promise<AgentToolResult<Details>>;\n}): Promise<AgentToolResult<Details>> {\n\tif (!input.location.asyncDir) {\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `Async run '${input.runId}' has no live run directory to steer.` }],\n\t\t\tisError: true,\n\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t};\n\t}\n\tconst asyncDir = input.location.asyncDir;\n\tconst status = reconcileAsyncRun(asyncDir, { kill: input.kill }).status;\n\tif (input.state.currentSessionId && status?.sessionId !== input.state.currentSessionId) {\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `Async run '${input.runId}' was not found in the active session.` }],\n\t\t\tisError: true,\n\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t};\n\t}\n\tif (!status) {\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `Async run '${input.runId}' was not found.` }],\n\t\t\tisError: true,\n\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t};\n\t}\n\tconst steps = status.steps ?? [];\n\tif (status.state !== \"running\" && status.state !== \"queued\") {\n\t\tconst retained =\n\t\t\tinput.mode === \"follow_up\" &&\n\t\t\tstatus.state === \"complete\" &&\n\t\t\tBoolean(status.parentWorkflowRunId) &&\n\t\t\tsteps.length === 1 &&\n\t\t\t(steps[0]?.status === \"complete\" || steps[0]?.status === \"completed\") &&\n\t\t\tBoolean(steps[0]?.sessionFile ?? status.sessionFile);\n\t\tif (!retained) {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{ type: \"text\", text: `Async run '${input.runId}' is not running or queued and cannot be steered.` },\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t\t};\n\t\t}\n\t\tif (input.index !== undefined && input.index !== 0) {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Retained async run '${status.runId}' has one child. Index ${input.index} is out of range.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t\t};\n\t\t}\n\t\tconst request: SteerRequest = {\n\t\t\ttype: \"steer\",\n\t\t\tid: randomUUID(),\n\t\t\tts: Date.now(),\n\t\t\tmessage: input.message.trim(),\n\t\t\tmode: \"follow_up\",\n\t\t\ttargetIndex: 0,\n\t\t\tsource: \"steer-action\",\n\t\t};\n\t\ttry {\n\t\t\tqueueRevivalBrief(asyncDir, request);\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Failed to queue retained follow-up for async run ${status.runId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t\t};\n\t\t}\n\t\tstatus.steering ??= createSteeringStatus();\n\t\trecordSteeringRequest(status.steering, {\n\t\t\tid: request.id,\n\t\t\trequestedAt: request.ts,\n\t\t\tsource: request.source,\n\t\t\tmessage: request.message,\n\t\t\ttargets: [{ index: 0, state: \"scheduled\" }],\n\t\t});\n\t\twriteAtomicJson(path.join(asyncDir, \"status.json\"), status);\n\t\tconst queued = {\n\t\t\trequestId: request.id,\n\t\t\tstate: \"scheduled\" as const,\n\t\t\tdeliveryStatus: \"queued\" as const,\n\t\t\tsourceRunId: status.runId,\n\t\t\ttargets: [{ index: 0, state: \"scheduled\" as const }],\n\t\t};\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext: `Follow-up queued for the next resume of retained async run ${status.runId} (request ${request.id}).`,\n\t\t\t\t},\n\t\t\t],\n\t\t\tdetails: { mode: \"management\", results: [], steering: queued },\n\t\t};\n\t}\n\tif (input.index !== undefined) {\n\t\tif (input.index < 0 || input.index >= steps.length) {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Async run '${status.runId}' has ${steps.length} children. Index ${input.index} is out of range.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t\t};\n\t\t}\n\t\tconst targetStep = steps[input.index];\n\t\tif (targetStep && targetStep.status !== \"running\" && targetStep.status !== \"pending\") {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Async run '${status.runId}' child ${input.index} is ${targetStep.status} and cannot be steered.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t\t};\n\t\t}\n\t} else {\n\t\tconst running = steps.filter((step) => step.status === \"running\");\n\t\tif (running.length === 0 && steps.length > 1) {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Async run '${status.runId}' has no running child yet. Provide index to steer a queued child.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t\t};\n\t\t}\n\t}\n\tconst runningIndexes = steps\n\t\t.map((step, index) => (step.status === \"running\" ? index : undefined))\n\t\t.filter((index): index is number => index !== undefined);\n\tconst effectiveTargetIndex =\n\t\tinput.index ??\n\t\t(status.mode === \"single\" && runningIndexes.length === 0 && steps[0]?.status === \"pending\" ? 0 : undefined);\n\tconst targetIndexes = effectiveTargetIndex !== undefined ? [effectiveTargetIndex] : runningIndexes;\n\tif (targetIndexes.length === 0) {\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `Async run '${status.runId}' has no running child to steer.` }],\n\t\t\tisError: true,\n\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t};\n\t}\n\tconst requestId = randomUUID();\n\tlet requestPath: string;\n\ttry {\n\t\trequestPath = requestAsyncSteer(asyncDir, {\n\t\t\tmessage: input.message,\n\t\t\tmode: input.mode,\n\t\t\t...(effectiveTargetIndex !== undefined ? { targetIndex: effectiveTargetIndex } : { targetIndexes }),\n\t\t\tsource: \"steer-action\",\n\t\t\tid: requestId,\n\t\t});\n\t} catch (error) {\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext: `Failed to queue steering for async run ${status.runId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t},\n\t\t\t],\n\t\t\tisError: true,\n\t\t\tdetails: { mode: \"management\", results: [] },\n\t\t};\n\t}\n\tinput.onRequestQueued?.(requestPath);\n\tconst tracked = input.state.asyncJobs.get(status.runId);\n\tif (tracked) tracked.updatedAt = Date.now();\n\tconst targets = targetIndexes.map((index) => ({\n\t\tindex,\n\t\tstate: status.steps?.[index]?.status === \"pending\" ? (\"scheduled\" as const) : (\"pending\" as const),\n\t}));\n\tif (targets.every((target) => target.state === \"scheduled\")) {\n\t\tconst scheduled = {\n\t\t\trequestId,\n\t\t\tstate: \"scheduled\" as const,\n\t\t\tdeliveryStatus: \"queued\" as const,\n\t\t\tsourceRunId: status.runId,\n\t\t\ttargets,\n\t\t};\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `Steering scheduled for async run ${status.runId} (request ${requestId}).` }],\n\t\t\tdetails: { mode: \"management\", results: [], steering: scheduled },\n\t\t};\n\t}\n\tconst waited = await waitForSteeringAction({\n\t\tasyncDir,\n\t\tsourceRunId: status.runId,\n\t\trequestId,\n\t\ttimeoutMs: input.ackTimeoutMs ?? 3_000,\n\t\tsignal: input.signal,\n\t});\n\tconst result = waited ?? {\n\t\trequestId,\n\t\tstate: \"pending\" as const,\n\t\tdeliveryStatus: \"queued\" as const,\n\t\tsourceRunId: status.runId,\n\t\ttargets,\n\t};\n\tif (input.signal?.aborted) {\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext: `Steering pending for async run ${status.runId} (request ${requestId}); caller aborted before recovery.`,\n\t\t\t\t},\n\t\t\t],\n\t\t\tdetails: { mode: \"management\", results: [], steering: result },\n\t\t};\n\t}\n\tconst finalStatus = readStatus(asyncDir);\n\tconst finalResult = finalStatus?.steering\n\t\t? actionResultFromSteeringStatus(finalStatus.steering, status.runId, requestId)\n\t\t: undefined;\n\tif (finalResult?.state === \"delivered\") {\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `Steering delivered for async run ${status.runId} (request ${requestId}).` }],\n\t\t\tdetails: { mode: \"management\", results: [], steering: finalResult },\n\t\t};\n\t}\n\tconst running = (finalStatus?.steps ?? status.steps ?? []).filter((step) => step.status === \"running\");\n\tconst recoveryAllowed =\n\t\t(input.mode ?? \"steer\") === \"steer\" &&\n\t\tstatus.mode === \"single\" &&\n\t\tstatus.isNested !== true &&\n\t\trunning.length === 1 &&\n\t\tBoolean(finalStatus?.steering) &&\n\t\t(input.index === undefined || input.index === 0);\n\tif (recoveryAllowed && finalResult?.state !== \"scheduled\" && input.recover) {\n\t\tconst appendSteeringNotice = (state: \"failed\" | \"recovered\", message: string): void => {\n\t\t\ttry {\n\t\t\t\tfs.appendFileSync(\n\t\t\t\t\tpath.join(asyncDir, \"events.jsonl\"),\n\t\t\t\t\t`${JSON.stringify({ type: \"subagent.steering.notice\", ts: Date.now(), runId: status.runId, requestId, state, message, ...(status.sessionId ? { currentSessionId: status.sessionId } : {}) })}\\n`,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// The action result and status remain authoritative if diagnostic notification persistence fails.\n\t\t\t}\n\t\t};\n\t\ttry {\n\t\t\tconst latest = readStatus(asyncDir);\n\t\t\tconst latestResult = latest?.steering\n\t\t\t\t? actionResultFromSteeringStatus(latest.steering, status.runId, requestId)\n\t\t\t\t: undefined;\n\t\t\tif (latestResult?.state === \"delivered\")\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{ type: \"text\", text: `Steering delivered for async run ${status.runId} (request ${requestId}).` },\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { mode: \"management\", results: [], steering: latestResult },\n\t\t\t\t};\n\t\t\tconst committedAt = Date.now();\n\t\t\tinput.onBeforeRecoveryClaim?.(requestId, committedAt);\n\t\t\tconst { claimPath, markerPath } = claimSteeringRecovery(asyncDir, {\n\t\t\t\trequestId,\n\t\t\t\tsourceRunId: status.runId,\n\t\t\t\tcommittedAt,\n\t\t\t});\n\t\t\tinput.onRecoveryCommitted?.(requestId, committedAt);\n\t\t\tconst preCommitStatus = readStatus(asyncDir);\n\t\t\tconst preCommitResult = preCommitStatus?.steering\n\t\t\t\t? actionResultFromSteeringStatus(preCommitStatus.steering, status.runId, requestId)\n\t\t\t\t: undefined;\n\t\t\tif (\n\t\t\t\tpreCommitResult?.state === \"delivered\" &&\n\t\t\t\tpreCommitResult.targets.every(\n\t\t\t\t\t(target) => target.deliveredAt !== undefined && target.deliveredAt <= committedAt,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tfs.rmSync(markerPath, { force: true });\n\t\t\t\tfs.rmSync(claimPath, { force: true });\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{ type: \"text\", text: `Steering delivered for async run ${status.runId} (request ${requestId}).` },\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { mode: \"management\", results: [], steering: preCommitResult },\n\t\t\t\t};\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tdeliverInterruptRequest({\n\t\t\t\t\tasyncDir,\n\t\t\t\t\tpid: latest?.pid ?? status.pid,\n\t\t\t\t\tkill: input.kill,\n\t\t\t\t\tsource: \"steering-recovery\",\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tfs.rmSync(markerPath, { force: true });\n\t\t\t\tfs.rmSync(claimPath, { force: true });\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to commit steering recovery interrupt: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst pauseDeadline = Date.now() + (input.recoveryTimeoutMs ?? 15_000);\n\t\t\tlet paused: AsyncStatus | null = null;\n\t\t\twhile (Date.now() <= pauseDeadline) {\n\t\t\t\tif (input.signal?.aborted) break;\n\t\t\t\tconst candidate = readStatus(asyncDir);\n\t\t\t\tif (candidate?.state === \"paused\" && candidate.endedAt !== undefined) {\n\t\t\t\t\tpaused = candidate;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tawait new Promise<void>((resolve) => setTimeout(resolve, 50));\n\t\t\t}\n\t\t\tif (!paused)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Source run did not reach confirmed paused state within 15 seconds; no replacement was launched and the recovery claim remains committed to prevent a delayed duplicate.\",\n\t\t\t\t);\n\t\t\tlet lateAckRecorded = false;\n\t\t\tfor (const ack of consumeSteerAcks(asyncDir)) {\n\t\t\t\tif (\n\t\t\t\t\t!paused.steering?.recent.some(\n\t\t\t\t\t\t(request) =>\n\t\t\t\t\t\t\trequest.id === ack.requestId && request.targets.some((target) => target.index === ack.index),\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t\tcontinue;\n\t\t\t\tconst state = ack.state === \"delivered\" ? \"late\" : \"failed\";\n\t\t\t\tconst reason = ack.state === \"delivered\" ? \"acknowledged after recovery commit\" : ack.message;\n\t\t\t\tupdateSteeringTarget(paused.steering, ack.requestId, ack.index, state, ack.ts, { reason });\n\t\t\t\tconst stepSteering = paused.steps?.[ack.index]?.steering;\n\t\t\t\tif (stepSteering) updateSteeringTarget(stepSteering, ack.requestId, ack.index, state, ack.ts, { reason });\n\t\t\t\tlateAckRecorded = true;\n\t\t\t\ttry {\n\t\t\t\t\tfs.appendFileSync(\n\t\t\t\t\t\tpath.join(asyncDir, \"events.jsonl\"),\n\t\t\t\t\t\t`${JSON.stringify({ type: ack.state === \"delivered\" ? \"subagent.steer.delivered\" : \"subagent.steer.failed\", ts: ack.ts, runId: status.runId, requestId: ack.requestId, index: ack.index, late: true, message: ack.message })}\\n`,\n\t\t\t\t\t);\n\t\t\t\t} catch {\n\t\t\t\t\t// Status remains authoritative when diagnostic event persistence fails.\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (lateAckRecorded) writeAtomicJson(path.join(asyncDir, \"status.json\"), paused);\n\t\t\tlet recoveryTarget;\n\t\t\ttry {\n\t\t\t\trecoveryTarget = resolveAsyncResumeTarget(\n\t\t\t\t\t{ id: status.runId },\n\t\t\t\t\t{ kill: input.kill },\n\t\t\t\t\t{ sessionId: input.state.currentSessionId ?? undefined },\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Source run remains paused and cannot be revived safely: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (recoveryTarget.kind !== \"revive\" || !recoveryTarget.sessionFile)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Source run has no valid persisted child session; it remains paused and requires a new run.\",\n\t\t\t\t);\n\t\t\tif (!recoveryTarget.recoveryDescriptor)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Source run has no private recovery descriptor; it remains paused and is not safely resumable.\",\n\t\t\t\t);\n\t\t\tconst limits = remainingSteeringRecoveryLimits(recoveryTarget.recoveryDescriptor, paused);\n\t\t\tconst revived = await input.recover(limits);\n\t\t\tif (revived.isError || !revived.details.asyncId)\n\t\t\t\tthrow new Error(\n\t\t\t\t\trevived.content[0]?.type === \"text\"\n\t\t\t\t\t\t? revived.content[0].text\n\t\t\t\t\t\t: \"Replacement launch failed; source run remains paused.\",\n\t\t\t\t);\n\t\t\tconst sourceStatus = readStatus(asyncDir);\n\t\t\tconst targetIndex = input.index ?? status.steps?.findIndex((step) => step.status === \"running\") ?? -1;\n\t\t\tif (sourceStatus?.state === \"paused\" && sourceStatus.steering && targetIndex >= 0) {\n\t\t\t\tupdateSteeringTarget(sourceStatus.steering, requestId, targetIndex, \"recovered\", Date.now(), {\n\t\t\t\t\treplacementRunId: revived.details.asyncId,\n\t\t\t\t});\n\t\t\t\tconst stepSteering = sourceStatus.steps?.[targetIndex]?.steering;\n\t\t\t\tif (stepSteering)\n\t\t\t\t\tupdateSteeringTarget(stepSteering, requestId, targetIndex, \"recovered\", Date.now(), {\n\t\t\t\t\t\treplacementRunId: revived.details.asyncId,\n\t\t\t\t\t});\n\t\t\t\twriteAtomicJson(path.join(asyncDir, \"status.json\"), sourceStatus);\n\t\t\t}\n\t\t\tconst recovered = sourceStatus?.steering\n\t\t\t\t? actionResultFromSteeringStatus(sourceStatus.steering, status.runId, requestId, revived.details.asyncId)\n\t\t\t\t: undefined;\n\t\t\tappendSteeringNotice(\n\t\t\t\t\"recovered\",\n\t\t\t\t`Steering recovered for run ${status.runId}; replacement ${revived.details.asyncId} launched.`,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Steering recovered for async run ${status.runId}; replacement ${revived.details.asyncId} launched after the source paused.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdetails: {\n\t\t\t\t\tmode: \"management\",\n\t\t\t\t\tresults: [],\n\t\t\t\t\tsteering: recovered ?? {\n\t\t\t\t\t\trequestId,\n\t\t\t\t\t\tstate: \"recovered\",\n\t\t\t\t\t\tsourceRunId: status.runId,\n\t\t\t\t\t\treplacementRunId: revived.details.asyncId,\n\t\t\t\t\t\tdeliveryStatus: \"delivered\",\n\t\t\t\t\t\ttargets: [{ index: input.index ?? 0, state: \"recovered\" }],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\tconst failedStatus = readStatus(asyncDir);\n\t\t\tconst targetIndex = input.index ?? status.steps?.findIndex((step) => step.status === \"running\") ?? -1;\n\t\t\tif (\n\t\t\t\tfailedStatus &&\n\t\t\t\tfailedStatus.state !== \"running\" &&\n\t\t\t\tfailedStatus.state !== \"queued\" &&\n\t\t\t\tfailedStatus.steering &&\n\t\t\t\ttargetIndex >= 0\n\t\t\t) {\n\t\t\t\tupdateSteeringTarget(failedStatus.steering, requestId, targetIndex, \"failed\", Date.now(), { reason });\n\t\t\t\tconst stepSteering = failedStatus.steps?.[targetIndex]?.steering;\n\t\t\t\tif (stepSteering)\n\t\t\t\t\tupdateSteeringTarget(stepSteering, requestId, targetIndex, \"failed\", Date.now(), { reason });\n\t\t\t\tfailedStatus.activityState = \"needs_attention\";\n\t\t\t\twriteAtomicJson(path.join(asyncDir, \"status.json\"), failedStatus);\n\t\t\t}\n\t\t\tconst failed = failedStatus?.steering\n\t\t\t\t? actionResultFromSteeringStatus(failedStatus.steering, status.runId, requestId)\n\t\t\t\t: undefined;\n\t\t\tappendSteeringNotice(\"failed\", `Steering failed for run ${status.runId}: ${reason}`);\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Steering failed for async run ${status.runId} (request ${requestId}): ${reason}`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t\tdetails: {\n\t\t\t\t\tmode: \"management\",\n\t\t\t\t\tresults: [],\n\t\t\t\t\tsteering: failed ?? {\n\t\t\t\t\t\trequestId,\n\t\t\t\t\t\tstate: \"failed\",\n\t\t\t\t\t\tsourceRunId: status.runId,\n\t\t\t\t\t\tdeliveryStatus: \"queued\",\n\t\t\t\t\t\ttargets: [{ index: input.index ?? 0, state: \"failed\", reason }],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\tconst stateText =\n\t\tresult.state === \"failed\"\n\t\t\t? \"failed\"\n\t\t\t: result.state === \"partial\"\n\t\t\t\t? \"partial\"\n\t\t\t\t: result.deliveryStatus === \"queued\"\n\t\t\t\t\t? \"queued\"\n\t\t\t\t\t: result.state === \"delivered\"\n\t\t\t\t\t\t? \"delivered\"\n\t\t\t\t\t\t: result.state === \"scheduled\"\n\t\t\t\t\t\t\t? \"scheduled\"\n\t\t\t\t\t\t\t: result.state === \"recovered\"\n\t\t\t\t\t\t\t\t? \"recovered\"\n\t\t\t\t\t\t\t\t: \"pending\";\n\tconst isError = result.state === \"failed\" || result.state === \"partial\";\n\treturn {\n\t\tcontent: [{ type: \"text\", text: `Steering ${stateText} for async run ${status.runId} (request ${requestId}).` }],\n\t\t...(isError ? { isError: true } : {}),\n\t\tdetails: { mode: \"management\", results: [], steering: result },\n\t};\n}\n"]}