import type { NamedError } from "@opencode-ai/util/error" import { MessageV2 } from "./message-v2" export namespace SessionRetry { export const RETRY_INITIAL_DELAY = 2000 export const RETRY_BACKOFF_FACTOR = 2 export const RETRY_MAX_DELAY_NO_HEADERS = 30_000 // 30 seconds export async function sleep(ms: number, signal: AbortSignal): Promise { return new Promise((resolve, reject) => { const timeout = setTimeout(resolve, ms) signal.addEventListener( "abort", () => { clearTimeout(timeout) reject(new DOMException("Aborted", "AbortError")) }, { once: true }, ) }) } export function delay(attempt: number, error?: MessageV2.APIError) { if (error) { const headers = error.data.responseHeaders if (headers) { const retryAfterMs = headers["retry-after-ms"] if (retryAfterMs) { const parsedMs = Number.parseFloat(retryAfterMs) if (!Number.isNaN(parsedMs)) { return parsedMs } } const retryAfter = headers["retry-after"] if (retryAfter) { const parsedSeconds = Number.parseFloat(retryAfter) if (!Number.isNaN(parsedSeconds)) { // convert seconds to milliseconds return Math.ceil(parsedSeconds * 1000) } // Try parsing as HTTP date format const parsed = Date.parse(retryAfter) - Date.now() if (!Number.isNaN(parsed) && parsed > 0) { return Math.ceil(parsed) } } return RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1) } } return Math.min(RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1), RETRY_MAX_DELAY_NO_HEADERS) } export function retryable(error: ReturnType) { if (MessageV2.APIError.isInstance(error)) { if (!error.data.isRetryable) return undefined return error.data.message.includes("Overloaded") ? "Provider is overloaded" : error.data.message } if (typeof error.data?.message === "string") { try { const json = JSON.parse(error.data.message) if (json.type === "error" && json.error?.type === "too_many_requests") { return "Too Many Requests" } if (json.code === "Some resource has been exhausted") { return "Provider is overloaded" } if (json.type === "error" && json.error?.code?.includes("rate_limit")) { return "Rate Limited" } if ( json.error?.message?.includes("no_kv_space") || (json.type === "error" && json.error?.type === "server_error") ) { return "Provider Server Error" } } catch {} } return undefined } }