/*
 * 文件定位：Agent 执行引擎 / 执行核心逻辑。
 * 核心目的：把模型输出解释为 Praxis 内部 ModelDecision，而不是让 Kernel 直接猜 provider 形状。
 * 边界：保留 provider 原始引用，但不把 Responses 字段提升为 Praxis 核心合同。
 * 对接：mainLoop 根据决策调度 BaseTool、EphemeralProcedure、审批、继续或最终输出。
 * 实现提示：新增 provider shape 时只扩展 extractor，不改变 ModelDecision 主合同。
 */
import { normalizeEphemeralProcedurePlan, } from "./ephemeralProcedure.js";
import { raiseProviderToolCalls, } from "../../agent_modelAdapter/bridgingLayer/toolSchemaCompatibilityLayer.js";
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function readString(value) {
    return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
}
function readStreamDelta(value) {
    return typeof value === "string" && value.length > 0 ? value : undefined;
}
function sseDataObjects(text) {
    const objects = [];
    for (const line of text.split(/\r?\n/u)) {
        if (!line.startsWith("data:"))
            continue;
        const payload = line.slice("data:".length).trim();
        if (payload.length === 0 || payload === "[DONE]")
            continue;
        try {
            objects.push(JSON.parse(payload));
        }
        catch {
            // Ignore non-JSON stream payloads.
        }
    }
    return objects;
}
function extractChatChoiceText(choices) {
    const chunks = [];
    for (const choice of choices) {
        if (!isRecord(choice))
            continue;
        const message = isRecord(choice.message) ? choice.message : undefined;
        const delta = isRecord(choice.delta) ? choice.delta : undefined;
        const content = message?.content ?? delta?.content;
        const contentText = readStreamDelta(content);
        if (contentText !== undefined)
            chunks.push(contentText);
        if (Array.isArray(content)) {
            for (const block of content) {
                if (!isRecord(block))
                    continue;
                const blockText = readStreamDelta(block.text);
                if (blockText !== undefined)
                    chunks.push(blockText);
            }
        }
    }
    return chunks.join("");
}
function extractText(raw) {
    if (typeof raw === "string") {
        const streamChunks = [];
        const completed = [];
        const sseObjects = sseDataObjects(raw);
        for (const object of sseObjects) {
            if (!isRecord(object))
                continue;
            const eventType = readString(object.type);
            const delta = readStreamDelta(object.delta);
            if (delta !== undefined && (eventType === undefined || eventType.includes("output_text"))) {
                streamChunks.push(delta);
            }
            if (Array.isArray(object.choices)) {
                const choiceText = extractChatChoiceText(object.choices);
                if (choiceText.length > 0)
                    streamChunks.push(choiceText);
            }
            if (eventType === "content_block_start" && isRecord(object.content_block) && object.content_block.type === "text") {
                const blockText = readStreamDelta(object.content_block.text);
                if (blockText !== undefined)
                    streamChunks.push(blockText);
            }
            if (eventType === "content_block_delta" && isRecord(object.delta) && object.delta.type === "text_delta") {
                const textDelta = readStreamDelta(object.delta.text);
                if (textDelta !== undefined)
                    streamChunks.push(textDelta);
            }
            if (object.response !== undefined) {
                const text = extractText(object.response);
                if (text.length > 0)
                    completed.push(text);
            }
        }
        const text = streamChunks.join("").trim() || completed.join("\n").trim();
        if (text.length > 0)
            return text;
        return sseObjects.length > 0 ? "" : raw.trim();
    }
    if (!isRecord(raw))
        return "";
    const direct = readString(raw.output_text) ?? readString(raw.text);
    if (direct !== undefined)
        return direct;
    if (Array.isArray(raw.choices)) {
        const text = extractChatChoiceText(raw.choices).trim();
        if (text.length > 0)
            return text;
    }
    if (Array.isArray(raw.content)) {
        const chunks = [];
        for (const block of raw.content) {
            if (!isRecord(block))
                continue;
            const blockText = readString(block.text);
            if (blockText !== undefined)
                chunks.push(blockText);
        }
        const text = chunks.join("\n").trim();
        if (text.length > 0)
            return text;
    }
    const output = raw.output;
    if (!Array.isArray(output))
        return "";
    const chunks = [];
    for (const item of output) {
        if (!isRecord(item))
            continue;
        const itemText = readString(item.output_text) ?? readString(item.text);
        if (itemText !== undefined)
            chunks.push(itemText);
        if (Array.isArray(item.content)) {
            for (const block of item.content) {
                if (!isRecord(block))
                    continue;
                const blockText = readString(block.text) ?? readString(block.output_text);
                if (blockText !== undefined)
                    chunks.push(blockText);
            }
        }
    }
    return chunks.join("\n").trim();
}
function failure(code, message) {
    return {
        ok: false,
        error: { code, message, publicSafe: true },
        events: ["agentCore.execution.modelDecision.rejected"],
    };
}
function readToolContextExpansion(value) {
    const targetKind = readString(value.targetKind);
    if (targetKind !== "family" && targetKind !== "group" && targetKind !== "tool")
        return undefined;
    const family = readString(value.family);
    const group = readString(value.group);
    const toolId = readString(value.toolId);
    if (targetKind === "family" && family === undefined)
        return undefined;
    if (targetKind === "group" && (family === undefined || group === undefined))
        return undefined;
    if (targetKind === "tool" && toolId === undefined)
        return undefined;
    return {
        targetKind,
        ...(family === undefined ? {} : { family }),
        ...(group === undefined ? {} : { group }),
        ...(toolId === undefined ? {} : { toolId }),
        ...(readString(value.reason) === undefined ? {} : { reason: readString(value.reason) }),
    };
}
export function interpretModelDecision(request) {
    const sessionId = request.sessionId.trim();
    if (sessionId.length === 0) {
        return failure("MISSING_SESSION_ID", "ModelDecision interpretation requires a sessionId");
    }
    if (request.raw === undefined || request.raw === null) {
        return failure("MISSING_RAW", "ModelDecision interpretation requires provider output");
    }
    const rawRef = request.providerRawRef ?? `${sessionId}:model:${request.turnIndex + 1}:raw`;
    if (request.raw instanceof Error) {
        return {
            ok: true,
            decisions: [{
                    decisionId: `${sessionId}:turn:${request.turnIndex}:decision:provider-failure`,
                    kind: "fail",
                    failure: {
                        code: "PROVIDER_FAILURE",
                        message: request.raw.message || "model provider failed",
                        publicSafe: true,
                    },
                    providerRawRef: rawRef,
                    observationRefs: [],
                    metadata: { providerRawRef: rawRef },
                }],
            events: ["agentCore.execution.modelDecision.fail"],
        };
    }
    if (isRecord(request.raw) && isRecord(request.raw.error)) {
        return {
            ok: true,
            decisions: [{
                    decisionId: `${sessionId}:turn:${request.turnIndex}:decision:provider-error`,
                    kind: "fail",
                    failure: {
                        code: readString(request.raw.error.code) ?? "PROVIDER_ERROR",
                        message: readString(request.raw.error.message) ?? "model provider returned an error",
                        publicSafe: true,
                    },
                    providerRawRef: rawRef,
                    observationRefs: [],
                    metadata: { providerRawRef: rawRef },
                }],
            events: ["agentCore.execution.modelDecision.fail"],
        };
    }
    const decisions = [];
    const providerToolCalls = raiseProviderToolCalls({
        raw: request.raw,
        providerFamily: request.providerFamily,
        mappings: request.providerToolMappings,
        providerRawRef: rawRef,
    });
    const visiblePreambleText = extractText(request.raw);
    for (const [index, call] of providerToolCalls.entries()) {
        if (call.malformedArguments !== undefined) {
            decisions.push({
                decisionId: `${sessionId}:turn:${request.turnIndex}:decision:${index + 1}`,
                kind: "fail",
                failure: {
                    code: "MALFORMED_PROVIDER_TOOL_ARGUMENTS",
                    message: call.malformedArguments,
                    publicSafe: true,
                },
                providerRawRef: rawRef,
                observationRefs: [],
                metadata: { providerFunctionName: call.providerName, callId: call.callId, providerFamily: call.providerFamily },
            });
            continue;
        }
        if (call.providerName === "praxis_ephemeral_procedure") {
            const procedure = normalizeEphemeralProcedurePlan(call.arguments);
            if (!procedure.ok) {
                decisions.push({
                    decisionId: `${sessionId}:turn:${request.turnIndex}:decision:${index + 1}`,
                    kind: "fail",
                    failure: {
                        code: "INVALID_EPHEMERAL_PROCEDURE",
                        message: procedure.error.message,
                        publicSafe: true,
                    },
                    providerRawRef: rawRef,
                    observationRefs: [],
                    metadata: { providerFunctionName: call.providerName, callId: call.callId, providerFamily: call.providerFamily },
                });
                continue;
            }
            decisions.push({
                decisionId: `${sessionId}:turn:${request.turnIndex}:decision:${index + 1}`,
                kind: "ephemeralProcedurePlan",
                ephemeralProcedurePlan: procedure.plan,
                providerRawRef: rawRef,
                observationRefs: [],
                metadata: { providerFunctionName: call.providerName, callId: call.callId, providerFamily: call.providerFamily },
            });
            continue;
        }
        if (call.providerName === "praxis_request_approval") {
            decisions.push({
                decisionId: `${sessionId}:turn:${request.turnIndex}:decision:${index + 1}`,
                kind: "requestApproval",
                approvalRequest: {
                    reason: readString(call.arguments.reason) ?? "model requested approval",
                    requestedScopes: Array.isArray(call.arguments.requestedScopes)
                        ? call.arguments.requestedScopes.map((scope) => String(scope))
                        : [],
                    riskLevel: readString(call.arguments.riskLevel),
                },
                providerRawRef: rawRef,
                observationRefs: [],
                metadata: { providerFunctionName: call.providerName, callId: call.callId, providerFamily: call.providerFamily },
            });
            continue;
        }
        if (call.providerName === "praxis_expand_tool_context") {
            const expansion = readToolContextExpansion(call.arguments);
            decisions.push({
                decisionId: `${sessionId}:turn:${request.turnIndex}:decision:${index + 1}`,
                kind: expansion === undefined ? "fail" : "continue",
                ...(expansion === undefined
                    ? {
                        failure: {
                            code: "INVALID_TOOL_CONTEXT_EXPANSION",
                            message: "praxis_expand_tool_context requires targetKind plus family/group/toolId matching the target kind",
                            publicSafe: true,
                        },
                    }
                    : { toolContextExpansion: expansion }),
                providerRawRef: rawRef,
                observationRefs: [],
                metadata: {
                    providerFunctionName: call.providerName,
                    callId: call.callId,
                    providerFamily: call.providerFamily,
                    runtimeDecision: "expandToolContext",
                    ...(expansion === undefined ? {} : { toolContextExpansion: expansion }),
                },
            });
            continue;
        }
        decisions.push({
            decisionId: `${sessionId}:turn:${request.turnIndex}:decision:${index + 1}`,
            kind: "toolCall",
            toolCall: {
                callId: call.callId,
                providerToolName: call.providerName,
                toolId: call.toolId,
                arguments: call.arguments,
            },
            ...(index === 0 && visiblePreambleText.length > 0 ? { preambleText: visiblePreambleText } : {}),
            providerRawRef: rawRef,
            observationRefs: [],
            metadata: { providerFunctionName: call.providerName, providerFamily: call.providerFamily },
        });
    }
    if (decisions.length > 0) {
        return { ok: true, decisions, events: ["agentCore.execution.modelDecision.toolIntent"] };
    }
    const text = extractText(request.raw);
    if (text.length > 0) {
        return {
            ok: true,
            decisions: [{
                    decisionId: `${sessionId}:turn:${request.turnIndex}:decision:final`,
                    kind: "finalOutput",
                    finalOutput: text,
                    providerRawRef: rawRef,
                    observationRefs: [],
                    metadata: {},
                }],
            events: ["agentCore.execution.modelDecision.finalOutput"],
        };
    }
    return {
        ok: true,
        decisions: [{
                decisionId: `${sessionId}:turn:${request.turnIndex}:decision:continue`,
                kind: "continue",
                providerRawRef: rawRef,
                observationRefs: [],
                metadata: {},
            }],
        events: ["agentCore.execution.modelDecision.continue"],
    };
}
