import { setTimeout as sleep } from "node:timers/promises";
import { modelRefForPrompt } from "../model-routing/index.js";
import { formatSafeTranscript } from "../safe-transcript/index.js";
export async function launchOpenCodeDelegation(ctx, toolContext, input) {
    const openCodeAgent = input.openCodeAgent ?? input.targetAgent;
    const parent = await getOpenCodeSession(ctx, input.parentSessionId).catch(() => undefined);
    const directory = stringValue(parent?.directory) ?? toolContext.directory ?? ctx.directory;
    const child = await createOpenCodeSession(ctx, {
        parentID: input.parentSessionId,
        title: `${input.promptSummary} (@${input.targetAgent})`,
        agent: openCodeAgent,
        directory,
    });
    const sessionId = requireSessionId(child);
    const parts = [{
            type: "text",
            text: input.prompt,
            metadata: {
                seed_mode: input.mode,
                seed_target_agent: input.targetAgent,
                seed_opencode_agent: openCodeAgent,
                seed_prompt_summary: input.promptSummary,
                seed_model_profile: input.modelRoute.model_profile,
                seed_requested_model: input.modelRoute.requested_model,
            },
        }];
    const body = {
        agent: openCodeAgent,
        tools: {
            task: true,
            seed_delegate: false,
            seed_delegate_background: false,
            seed_delegate_followup: false,
            ...(input.sessionTools ?? {}),
        },
        parts,
    };
    if (input.mode === "background") {
        const prompted = await promptOpenCodeSessionWithModelRoute(ctx, sessionId, body, directory, true, input.modelRoute);
        return { sessionId, resolvedModel: prompted.resolvedModel, modelAttempts: prompted.modelAttempts };
    }
    const prompted = await promptOpenCodeSessionWithModelRoute(ctx, sessionId, body, directory, false, input.modelRoute);
    const promptResult = prompted.result;
    const immediate = messageText(promptResult);
    if (immediate) {
        return {
            sessionId,
            output: immediate,
            resolvedModel: prompted.resolvedModel,
            modelAttempts: prompted.modelAttempts,
            tokenUsage: promptResult.info?.tokens,
        };
    }
    const messages = await getOpenCodeMessages(ctx, sessionId, 50);
    const lastAssistant = [...messages].reverse().find((message) => message.info?.role === "assistant");
    return {
        sessionId,
        output: lastAssistant ? messageText(lastAssistant) : undefined,
        resolvedModel: prompted.resolvedModel ?? modelLabelFromMessage(lastAssistant),
        modelAttempts: prompted.modelAttempts,
        tokenUsage: lastAssistant?.info?.tokens,
    };
}
export async function promptOpenCodeSessionWithModelRoute(ctx, sessionId, body, directory, asyncMode, modelRoute) {
    const attempts = modelRoute.attempts.length ? modelRoute.attempts : [undefined];
    const modelAttempts = [];
    let attemptNumber = 0;
    for (let index = 0; index < attempts.length; index += 1) {
        const routeAttempt = attempts[index];
        const maxSameModelRetries = 1;
        for (let retry = 0; retry <= maxSameModelRetries; retry += 1) {
            attemptNumber += 1;
            try {
                const result = await promptOpenCodeSession(ctx, sessionId, bodyWithModelAttempt(body, routeAttempt), directory, asyncMode);
                const resolvedModel = asyncMode ? routeAttempt?.model : modelLabelFromMessage(result) ?? routeAttempt?.model;
                modelAttempts.push(agentModelAttempt(attemptNumber, routeAttempt, "succeeded", sessionId, resolvedModel));
                return { result, resolvedModel, modelAttempts };
            }
            catch (error) {
                const message = error instanceof Error ? error.message : String(error);
                modelAttempts.push(agentModelAttempt(attemptNumber, routeAttempt, "failed", sessionId, routeAttempt?.model, message));
                if (retry < maxSameModelRetries && isTransientModelFailure(error)) {
                    await sleep(25 * (retry + 1));
                    continue;
                }
                const hasFallback = index < attempts.length - 1;
                if (!hasFallback || !isRetryableModelFailure(error))
                    throw modelRouteError(error, modelAttempts);
                break;
            }
        }
    }
    throw modelRouteError(new Error("SEED model route exhausted without a prompt attempt."), modelAttempts);
}
export function plannedModelAttempts(route) {
    const attempts = route.attempts.length ? route.attempts : [undefined];
    return attempts.map((attempt, index) => agentModelAttempt(index + 1, attempt, "pending"));
}
export function modelAttemptsFromError(error) {
    if (!error || typeof error !== "object")
        return [];
    const attempts = error.modelAttempts;
    return Array.isArray(attempts) ? attempts : [];
}
export async function createOpenCodeSession(ctx, input) {
    const session = sessionApi(ctx);
    const result = isV2SessionApi(session)
        ? await session.create({
            directory: input.directory ?? ctx.directory,
            workspace: ctx.worktree,
            parentID: input.parentID,
            title: input.title,
            agent: input.agent,
        })
        : await session.create({
            query: { directory: input.directory ?? ctx.directory },
            body: {
                parentID: input.parentID,
                title: input.title,
                agent: input.agent,
            },
        });
    return unwrapSdkData(result, "create OpenCode session");
}
export async function getOpenCodeSession(ctx, sessionId) {
    const session = sessionApi(ctx);
    const result = isV2SessionApi(session)
        ? await session.get({ sessionID: sessionId, directory: ctx.directory, workspace: ctx.worktree })
        : await session.get({ path: { id: sessionId }, query: { directory: ctx.directory } });
    return unwrapSdkData(result, `read OpenCode session ${sessionId}`);
}
export async function listOpenCodeSessions(ctx, input) {
    const session = sessionApi(ctx);
    const result = isV2SessionApi(session)
        ? await session.list({ directory: ctx.directory, workspace: ctx.worktree, search: input.search, limit: input.limit, roots: input.roots })
        : await session.list({ query: { directory: ctx.directory } });
    const sessions = unwrapSdkData(result, "list OpenCode sessions");
    const searched = input.search && !isV2SessionApi(session)
        ? sessions.filter((item) => JSON.stringify(compactSession(item)).toLowerCase().includes(input.search.toLowerCase()))
        : sessions;
    return typeof input.limit === "number" ? searched.slice(0, input.limit) : searched;
}
export async function getOpenCodeMessages(ctx, sessionId, limit) {
    const session = sessionApi(ctx);
    const result = isV2SessionApi(session)
        ? await session.messages({ sessionID: sessionId, directory: ctx.directory, workspace: ctx.worktree, limit })
        : await session.messages({ path: { id: sessionId }, query: { directory: ctx.directory, limit } });
    return unwrapSdkData(result, `read OpenCode messages ${sessionId}`);
}
export async function promptOpenCodeSession(ctx, sessionId, body, directory, asyncMode) {
    const session = sessionApi(ctx);
    const result = asyncMode
        ? isV2SessionApi(session)
            ? await session.promptAsync({ sessionID: sessionId, directory, workspace: ctx.worktree, ...body })
            : await session.promptAsync({ path: { id: sessionId }, query: { directory }, body })
        : isV2SessionApi(session)
            ? await session.prompt({ sessionID: sessionId, directory, workspace: ctx.worktree, ...body })
            : await session.prompt({ path: { id: sessionId }, query: { directory }, body });
    return asyncMode ? result : unwrapSdkData(result, `prompt OpenCode session ${sessionId}`);
}
export async function abortOpenCodeSession(ctx, sessionId) {
    const session = sessionApi(ctx);
    const result = isV2SessionApi(session)
        ? await session.abort({ sessionID: sessionId, directory: ctx.directory, workspace: ctx.worktree })
        : await session.abort({ path: { id: sessionId }, query: { directory: ctx.directory } });
    unwrapSdkData(result, `abort OpenCode session ${sessionId}`);
}
export async function getOpenCodeSessionStatus(ctx, sessionId) {
    const session = sessionApi(ctx);
    if (typeof session.status !== "function")
        return undefined;
    const result = isV2SessionApi(session)
        ? await session.status({ directory: ctx.directory, workspace: ctx.worktree })
        : await session.status({ query: { directory: ctx.directory } });
    const statuses = unwrapSdkData(result, "read OpenCode session status");
    return statuses[sessionId]?.type;
}
export function compactSession(session) {
    return {
        id: session.id,
        title: session.title,
        parentID: session.parentID,
        directory: session.directory,
        agent: session.agent,
        model: session.model,
        time: session.time,
    };
}
export function compactMessage(message) {
    const transcript = formatSafeTranscript(message);
    return {
        id: message.info?.id,
        role: message.info?.role,
        agent: message.info?.agent,
        finish: message.info?.finish,
        providerID: message.info?.providerID,
        modelID: message.info?.modelID,
        error: message.info?.error ? formatUnknownError(message.info.error) : undefined,
        text: transcript.text,
        reasoning_parts_omitted: transcript.reasoning_parts_omitted,
    };
}
export function messageText(message) {
    return formatSafeTranscript(message).text;
}
export function isActiveSessionStatus(status) {
    return status === "busy" || status === "running" || status === "retry";
}
function bodyWithModelAttempt(body, attempt) {
    const ref = attempt ? modelRefForPrompt(attempt) : undefined;
    return {
        ...body,
        ...(ref ? { model: ref } : {}),
        ...(attempt?.variant ? { variant: attempt.variant } : {}),
    };
}
function agentModelAttempt(attempt, routeAttempt, status, childSessionId, resolvedModel, error) {
    const parsed = resolvedModel && !routeAttempt?.model ? parseProviderModel(resolvedModel) : undefined;
    return {
        attempt,
        source: routeAttempt?.source ?? "opencode_current",
        model: routeAttempt?.model ?? resolvedModel,
        providerID: routeAttempt?.providerID ?? parsed?.providerID,
        modelID: routeAttempt?.modelID ?? parsed?.modelID,
        variant: routeAttempt?.variant,
        model_profile: routeAttempt?.model_profile,
        child_session_id: childSessionId,
        status,
        error,
    };
}
function modelRouteError(error, modelAttempts) {
    const wrapped = error instanceof Error ? error : new Error(String(error));
    return Object.assign(wrapped, { modelAttempts });
}
function isRetryableModelFailure(error) {
    const message = formatUnknownError(error).toLowerCase();
    if (/permission|policy|agent not found|unauthori[sz]ed|forbidden|auth/.test(message))
        return false;
    return /model|provider|rate limit|429|quota|overload|timeout|temporar|unavailable|503|econnreset|network|socket|transport/.test(message);
}
function isTransientModelFailure(error) {
    const message = formatUnknownError(error).toLowerCase();
    if (/permission|policy|agent not found|model not found|not found|unauthori[sz]ed|forbidden|auth/.test(message))
        return false;
    return /rate limit|429|overload|timeout|temporar|unavailable|503|econnreset|network|socket|transport/.test(message);
}
function modelLabelFromMessage(message) {
    const providerID = stringValue(message?.info?.providerID);
    const modelID = stringValue(message?.info?.modelID);
    return providerID && modelID ? `${providerID}/${modelID}` : undefined;
}
function parseProviderModel(model) {
    const slash = model.indexOf("/");
    if (slash <= 0 || slash === model.length - 1)
        return undefined;
    return { providerID: model.slice(0, slash), modelID: model.slice(slash + 1) };
}
function sessionApi(ctx) {
    const client = ctx.client;
    if (!client.session)
        throw new Error("OpenCode SDK session API is unavailable in this plugin context.");
    return client.session;
}
function isV2SessionApi(session) {
    return "client" in session && !("_client" in session);
}
function unwrapSdkData(result, action) {
    if (result && typeof result === "object") {
        const shaped = result;
        if (shaped.error)
            throw new Error(`Failed to ${action}: ${formatUnknownError(shaped.error)}`);
        if ("data" in shaped)
            return shaped.data;
    }
    return result;
}
function requireSessionId(session) {
    const id = stringValue(session.id);
    if (!id)
        throw new Error("OpenCode did not return a child session id.");
    return id;
}
function formatUnknownError(error) {
    if (error instanceof Error)
        return error.message;
    if (typeof error === "string")
        return error;
    try {
        return JSON.stringify(error);
    }
    catch {
        return String(error);
    }
}
function stringValue(value) {
    return typeof value === "string" && value.length > 0 ? value : undefined;
}
//# sourceMappingURL=manager.js.map