import type { createOpencodeClient } from "@opencode-ai/sdk" import { createInternalAgentContinuationTextPart, isAmbiguousPostDispatchPromptFailure, isRealUserMessage, resolveInheritedPromptTools, } from "../../shared" import { dispatchInternalPrompt, isInternalPromptDispatchAccepted } from "../shared/prompt-async-gate" import type { MessageData, ResumeConfig } from "./types" const RECOVERY_RESUME_TEXT = "[session recovered - continuing previous task]" type Client = ReturnType export function findLastUserMessage(messages: MessageData[]): MessageData | undefined { for (let i = messages.length - 1; i >= 0; i--) { const message = messages[i] if (message !== undefined && isRealUserMessage(message)) { return message } } return undefined } export function extractResumeConfig(userMessage: MessageData | undefined, sessionID: string): ResumeConfig { return { sessionID, agent: userMessage?.info?.agent, model: userMessage?.info?.model, tools: userMessage?.info?.tools, } } export async function resumeSession(client: Client, config: ResumeConfig): Promise { try { const inheritedTools = resolveInheritedPromptTools(config.sessionID, config.tools) const launchModel = config.model ? { providerID: config.model.providerID, modelID: config.model.modelID } : undefined const launchVariant = config.model?.variant const promptResult = await dispatchInternalPrompt({ mode: "async", client, sessionID: config.sessionID, source: "session-recovery", queueBehavior: "defer", input: { path: { id: config.sessionID }, body: { parts: [createInternalAgentContinuationTextPart(RECOVERY_RESUME_TEXT)], agent: config.agent, ...(launchModel ? { model: launchModel } : {}), ...(launchVariant ? { variant: launchVariant } : {}), ...(inheritedTools ? { tools: inheritedTools } : {}), }, }, }) if (promptResult.status === "failed" && isAmbiguousPostDispatchPromptFailure(promptResult)) { return true } return isInternalPromptDispatchAccepted(promptResult) } catch { return false } }