import { truncateText } from "@best-agent/foundation";
import { buildProviderTurnBudgetFinalizationPrompt } from "../../prompt/runtime-prompts.js";
import { classifyRuntimeFailure } from "../failure-classification.js";
import { buildProviderToolLoopContext } from "./provider-tool-loop-context.js";
import { appendRuntimeObservation, terminalOutputInvalidObservation, } from "./runtime-observation.js";
export async function finalizeProviderToolBudgetExhausted(input) {
    const maxProviderTurns = input.invokeContext.budget.max;
    const systemPrompt = mergeSystemPrompts(input.followUpDecisionSystemPrompt, buildProviderTurnBudgetFinalizationPrompt(maxProviderTurns));
    const generate = (messages, inputOverrides = {}) => input.runtime.provider.generateUnbudgetedFinalization(input.turn.threadId, input.turn.id, {
        cacheControl: input.context.cacheControl,
        messages,
        systemPrompt,
    }, inputOverrides.operation ?? "generate", buildProviderToolLoopContext(input.invokeContext, {
        correctionTrace: inputOverrides.correctionTrace,
        escalationLevel: input.advancedPhaseEscalationLevel,
        requiredToolConstraint: input.unresolvedRequiredToolConstraint,
        routeConfigOverride: input.followUpRouteConfig,
        toolStepCount: input.consumedToolStepCount,
    }));
    const finalize = (messages, text) => input.runtime.responses.finalizeGeneratedResponse({
        availableTools: input.context.availableTools,
        messages,
        objective: input.invokeContext.objective ?? input.context.objective,
        text,
    });
    const response = await generate(input.followUpMessages);
    try {
        return await finalize(input.followUpMessages, response.text);
    }
    catch (error) {
        const failure = classifyRuntimeFailure(error);
        if (failure.errorCode !== "TERMINAL_OUTPUT_INVALID") {
            throw error;
        }
        const repairMessages = appendRuntimeObservation(input.followUpMessages, terminalOutputInvalidObservation({
            actionPolicy: {
                allowedActions: ["respond"],
                directResponseAllowed: true,
            },
            invalidKind: "generic-control-payload",
            toolNames: input.context.availableTools.map((tool) => tool.name),
        }));
        const correctionTrace = input.runtime.provider.createCorrectionTrace({
            code: "TERMINAL_OUTPUT_INVALID",
            error: {
                errorCode: failure.errorCode,
                message: error instanceof Error ? error.message : String(error),
                recoverability: failure.recoverability,
                scope: failure.scope,
            },
            messages: input.followUpMessages,
            operation: "generate",
            systemPrompt,
        });
        const repairedResponse = await generate(repairMessages, {
            correctionTrace,
            operation: "repair",
        });
        try {
            return await finalize(repairMessages, repairedResponse.text);
        }
        catch (repairError) {
            const repairFailure = classifyRuntimeFailure(repairError);
            if (repairFailure.errorCode !== "TERMINAL_OUTPUT_INVALID") {
                throw repairError;
            }
            return finalize(repairMessages, buildBudgetExhaustedFallbackText(repairMessages));
        }
    }
}
function buildBudgetExhaustedFallbackText(messages) {
    const unavailableProcessSession = findLatestUnavailableProcessSessionRead(messages);
    if (unavailableProcessSession) {
        return [
            `The previous process session ${unavailableProcessSession} is no longer available, so I cannot report live progress from it.`,
            "It likely ended or was terminated when the prior runtime/thread was closed. I did not restart it.",
            "Tell me if you want me to start a new run.",
        ].join("\n");
    }
    const toolMessages = messages.filter((message) => message.role === "tool").slice(-5);
    const lines = [
        "I did not complete this turn cleanly because the model exhausted its tool-decision budget before producing a valid final answer.",
    ];
    if (toolMessages.length > 0) {
        lines.push("", "Recent tool evidence:");
        for (const message of toolMessages) {
            const status = message.outcome === "error" ? "failed" : "succeeded";
            const detail = truncateText(message.content.replace(/\s+/g, " ").trim(), 240);
            lines.push(`- ${message.toolName} ${status}${message.errorCode ? ` (${message.errorCode})` : ""}: ${detail}`);
        }
    }
    return lines.join("\n");
}
function findLatestUnavailableProcessSessionRead(messages) {
    for (let index = messages.length - 1; index >= 0; index -= 1) {
        const message = messages[index];
        if (message?.role !== "tool" ||
            message.toolName !== "process-session-read" ||
            !message.content.includes("status: unavailable")) {
            continue;
        }
        return message.content.match(/\bsession_id:\s*(proc_[A-Za-z0-9_-]+)/u)?.[1];
    }
    return undefined;
}
function mergeSystemPrompts(...parts) {
    const content = parts
        .map((value) => value?.trim())
        .filter((value) => Boolean(value && value.length > 0));
    if (content.length === 0) {
        return undefined;
    }
    return content.join("\n\n");
}
//# sourceMappingURL=provider-tool-budget-finalizer.js.map