import { startExecutionChildObservation, updateExecutionObservation, updateExecutionTraceIO } from "./langfuse.js";
function parseTs(value) {
    if (!value)
        return undefined;
    const parsed = new Date(value);
    return Number.isNaN(parsed.getTime()) ? undefined : parsed;
}
function appendTranscriptText(current, next, isDelta = false) {
    if (!next)
        return current;
    if (isDelta)
        return current + next;
    if (next.startsWith(current))
        return next;
    if (current.endsWith(next) || current.includes(next))
        return current;
    return current + next;
}
function formatTodoListOutput(entry) {
    return entry.items
        .map((item) => `${item.status === "completed" ? "[x]" : item.status === "in_progress" ? "[~]" : "[ ]"} ${item.text}`)
        .join("\n");
}
function appendTurnInput(current, next) {
    const normalized = next.trim();
    if (!normalized)
        return current;
    if (typeof current === "string" && current.trim())
        return appendTranscriptText(current, `\n\n${normalized}`);
    if (current == null)
        return normalized;
    return current;
}
function usageDetailsFromTokens(inputTokens, outputTokens) {
    return {
        input: inputTokens,
        output: outputTokens,
    };
}
function normalizeToolKey(entry, fallbackIndex) {
    return entry.toolUseId?.trim() || entry.name?.trim() || `tool:${fallbackIndex}`;
}
function createTranscriptEvent(parentObservation, context, input) {
    const eventObservation = startExecutionChildObservation(parentObservation, context, {
        name: input.name,
        asType: "event",
        startTime: parseTs(input.ts),
        level: input.level,
        output: input.output,
        metadata: input.metadata,
    });
    eventObservation?.end(parseTs(input.ts));
}
export function emitExecutionTranscriptTree(input) {
    if (!input.parentObservation) {
        return {
            turnCount: 0,
            toolCount: 0,
            eventCount: 0,
            finalOutput: null,
            finalModel: null,
            finalUsage: null,
            finalSessionId: null,
            hasError: false,
        };
    }
    let turnCount = 0;
    let toolCount = 0;
    let eventCount = 0;
    let fallbackToolIndex = 0;
    let pendingModel = input.fallbackResult?.model ?? null;
    let pendingSessionId = input.context.sessionKey ?? null;
    let pendingTurnInput = input.initialTurnInput;
    let finalOutput = null;
    let finalModel = pendingModel ?? null;
    let finalUsage = null;
    let finalSessionId = pendingSessionId ?? null;
    let finalHasError = false;
    let latestTranscriptTs = null;
    let activeTurn = null;
    const pendingTools = new Map();
    const closeTool = (key, ts, inputData) => {
        const tool = pendingTools.get(key);
        if (!tool)
            return;
        updateExecutionObservation(tool.observation, input.context, {
            output: inputData.output,
            level: inputData.isError ? "ERROR" : "DEFAULT",
            statusMessage: inputData.statusMessage ?? (inputData.isError ? "tool_error" : "tool_completed"),
            metadata: {
                turnIndex: tool.turnIndex,
                toolUseId: key,
                toolName: inputData.toolName ?? tool.name,
                isError: inputData.isError,
            },
        });
        tool.observation?.end(parseTs(ts));
        pendingTools.delete(key);
        if (activeTurn) {
            activeTurn.pendingToolKeys.delete(key);
            if (inputData.isError)
                activeTurn.hasError = true;
        }
    };
    const ensureTurn = (ts) => {
        if (activeTurn) {
            activeTurn.lastTs = ts;
            return activeTurn;
        }
        turnCount += 1;
        const turnObservation = startExecutionChildObservation(input.parentObservation, input.context, {
            name: `model_turn:${turnCount}`,
            asType: "generation",
            startTime: parseTs(ts),
            input: pendingTurnInput,
            model: pendingModel ?? undefined,
            metadata: {
                turnIndex: turnCount,
                sessionId: pendingSessionId,
            },
        });
        updateExecutionTraceIO(turnObservation, { input: pendingTurnInput });
        activeTurn = {
            index: turnCount,
            observation: turnObservation,
            assistantText: "",
            thinkingText: "",
            startedAt: ts,
            lastTs: ts,
            toolCallCount: 0,
            hasError: false,
            model: pendingModel,
            sessionId: pendingSessionId,
            input: pendingTurnInput,
            pendingToolKeys: new Set(),
        };
        pendingTurnInput = undefined;
        return activeTurn;
    };
    const finalizeTurn = (turn, ts, result = null) => {
        for (const toolKey of [...turn.pendingToolKeys]) {
            closeTool(toolKey, ts, {
                output: "Tool result missing before turn completion",
                isError: true,
                statusMessage: "tool_result_missing",
            });
        }
        const output = result?.output?.trim() || turn.assistantText.trim() || null;
        const errors = result?.errors?.length ? result.errors : null;
        const usage = result?.usage
            ? {
                ...usageDetailsFromTokens(result.usage.inputTokens, result.usage.outputTokens),
                ...(typeof result.usage.cachedInputTokens === "number" && result.usage.cachedInputTokens > 0
                    ? { cachedInput: result.usage.cachedInputTokens }
                    : {}),
            }
            : null;
        const costUsd = typeof result?.costUsd === "number" && Number.isFinite(result.costUsd)
            ? result.costUsd
            : null;
        const hasError = turn.hasError || result?.isError === true || Boolean(errors?.length);
        updateExecutionObservation(turn.observation, input.context, {
            input: turn.input,
            output,
            model: turn.model ?? result?.model ?? undefined,
            usageDetails: usage ?? undefined,
            costDetails: costUsd !== null ? { totalCost: costUsd } : undefined,
            level: hasError ? "ERROR" : "DEFAULT",
            statusMessage: result?.subtype ?? (hasError ? "generation_failed" : "generation_completed"),
            metadata: {
                turnIndex: turn.index,
                assistantText: output,
                thinkingText: turn.thinkingText.trim() || null,
                toolCallCount: turn.toolCallCount,
                hasError,
                sessionId: turn.sessionId,
                errors,
            },
        });
        updateExecutionTraceIO(turn.observation, {
            input: turn.input,
            output,
        });
        turn.observation?.end(parseTs(ts));
        finalOutput = output;
        finalModel = turn.model ?? result?.model ?? finalModel;
        finalUsage = result?.usage ?? finalUsage;
        finalHasError = hasError;
        activeTurn = null;
    };
    for (const entry of input.transcript) {
        latestTranscriptTs = entry.ts;
        switch (entry.kind) {
            case "init": {
                pendingModel = entry.model;
                pendingSessionId = entry.sessionId;
                finalSessionId = entry.sessionId;
                const turn = activeTurn;
                if (turn) {
                    turn.model = entry.model;
                    turn.sessionId = entry.sessionId;
                }
                createTranscriptEvent(input.parentObservation, input.context, {
                    name: "runtime.init",
                    ts: entry.ts,
                    output: `model=${entry.model}\nsession=${entry.sessionId}`,
                    metadata: {
                        model: entry.model,
                        sessionId: entry.sessionId,
                    },
                });
                eventCount += 1;
                break;
            }
            case "assistant": {
                const turn = ensureTurn(entry.ts);
                turn.assistantText = appendTranscriptText(turn.assistantText, entry.text, entry.delta === true);
                turn.lastTs = entry.ts;
                break;
            }
            case "thinking": {
                const turn = ensureTurn(entry.ts);
                turn.thinkingText = appendTranscriptText(turn.thinkingText, entry.text, entry.delta === true);
                turn.lastTs = entry.ts;
                break;
            }
            case "tool_call": {
                const turn = ensureTurn(entry.ts);
                const toolKey = normalizeToolKey(entry, ++fallbackToolIndex);
                if (pendingTools.has(toolKey)) {
                    closeTool(toolKey, entry.ts, {
                        output: "Tool call restarted before receiving a result",
                        isError: true,
                        toolName: entry.name,
                        statusMessage: "tool_restarted",
                    });
                }
                const toolObservation = startExecutionChildObservation(turn.observation, input.context, {
                    name: entry.name,
                    asType: "tool",
                    startTime: parseTs(entry.ts),
                    input: entry.input,
                    metadata: {
                        turnIndex: turn.index,
                        toolUseId: toolKey,
                        toolName: entry.name,
                    },
                });
                pendingTools.set(toolKey, {
                    observation: toolObservation,
                    turnIndex: turn.index,
                    name: entry.name,
                });
                turn.pendingToolKeys.add(toolKey);
                turn.toolCallCount += 1;
                turn.lastTs = entry.ts;
                toolCount += 1;
                break;
            }
            case "tool_result": {
                const turn = ensureTurn(entry.ts);
                const toolKey = normalizeToolKey(entry, ++fallbackToolIndex);
                if (!pendingTools.has(toolKey)) {
                    const fallbackToolObservation = startExecutionChildObservation(turn.observation, input.context, {
                        name: entry.toolName ?? "tool_result",
                        asType: "tool",
                        startTime: parseTs(entry.ts),
                        metadata: {
                            turnIndex: turn.index,
                            toolUseId: toolKey,
                            toolName: entry.toolName ?? null,
                            synthetic: true,
                        },
                    });
                    pendingTools.set(toolKey, {
                        observation: fallbackToolObservation,
                        turnIndex: turn.index,
                        name: entry.toolName ?? null,
                    });
                    turn.pendingToolKeys.add(toolKey);
                    toolCount += 1;
                }
                closeTool(toolKey, entry.ts, {
                    output: entry.content,
                    isError: entry.isError,
                    toolName: entry.toolName ?? null,
                });
                turn.lastTs = entry.ts;
                break;
            }
            case "todo_list": {
                createTranscriptEvent(input.parentObservation, input.context, {
                    name: "todo_list",
                    ts: entry.ts,
                    output: formatTodoListOutput(entry),
                    metadata: {
                        todoListId: entry.todoListId ?? null,
                        itemCount: entry.items.length,
                        completedCount: entry.items.filter((item) => item.status === "completed").length,
                    },
                });
                eventCount += 1;
                const turn = activeTurn;
                if (turn)
                    turn.lastTs = entry.ts;
                break;
            }
            case "result": {
                const turn = ensureTurn(entry.ts);
                finalizeTurn(turn, entry.ts, {
                    ts: entry.ts,
                    output: entry.text,
                    model: turn.model ?? pendingModel,
                    usage: {
                        inputTokens: entry.inputTokens,
                        outputTokens: entry.outputTokens,
                        cachedInputTokens: entry.cachedTokens,
                    },
                    costUsd: entry.costUsd,
                    subtype: entry.subtype,
                    isError: entry.isError,
                    errors: entry.errors,
                });
                break;
            }
            case "stderr": {
                createTranscriptEvent(input.parentObservation, input.context, {
                    name: "stderr",
                    ts: entry.ts,
                    level: "ERROR",
                    output: entry.text,
                });
                eventCount += 1;
                const turn = activeTurn;
                if (turn)
                    turn.hasError = true;
                break;
            }
            case "stdout": {
                createTranscriptEvent(input.parentObservation, input.context, {
                    name: "stdout",
                    ts: entry.ts,
                    level: "DEBUG",
                    output: entry.text,
                });
                eventCount += 1;
                break;
            }
            case "system": {
                createTranscriptEvent(input.parentObservation, input.context, {
                    name: "system",
                    ts: entry.ts,
                    output: entry.text,
                });
                eventCount += 1;
                break;
            }
            case "user":
                {
                    const turn = activeTurn;
                    if (turn && !turn.assistantText.trim() && !turn.thinkingText.trim() && turn.toolCallCount === 0) {
                        turn.input = appendTurnInput(turn.input, entry.text);
                    }
                    else {
                        pendingTurnInput = appendTurnInput(pendingTurnInput, entry.text);
                    }
                }
                break;
        }
    }
    if (!activeTurn &&
        turnCount === 0 &&
        (pendingTurnInput !== undefined || (input.fallbackResult !== null && input.fallbackResult !== undefined))) {
        const syntheticTs = input.fallbackResult?.ts ?? latestTranscriptTs ?? new Date().toISOString();
        const syntheticTurn = ensureTurn(syntheticTs);
        finalizeTurn(syntheticTurn, syntheticTs, input.fallbackResult ?? null);
    }
    const finalTurn = activeTurn;
    if (finalTurn) {
        finalizeTurn(finalTurn, input.fallbackResult?.ts ?? finalTurn.lastTs, input.fallbackResult ?? null);
    }
    updateExecutionObservation(input.parentObservation, input.context, {
        metadata: {
            transcriptEntryCount: input.transcript.length,
            transcriptTurnCount: turnCount,
            transcriptToolCount: toolCount,
            transcriptEventCount: eventCount,
        },
    });
    return {
        turnCount,
        toolCount,
        eventCount,
        finalOutput,
        finalModel,
        finalUsage,
        finalSessionId,
        hasError: finalHasError,
    };
}
//# sourceMappingURL=langfuse-transcript.js.map