import fs from "node:fs";
import { z } from "zod";
import { runSideCall } from "../agent/model-provider.js";
import { logger } from "../logger.js";
import { getVoiceDb } from "./context.js";
const SUMMARY_TIMEOUT_MS = 45_000;
const SUMMARY_MAX_TOKENS = 1200;
const MAX_LIST_ITEMS = 10;
const VoiceSessionSummarySchema = z.object({
    headline: z.string().describe("A one-line summary of the voice session."),
    userRequests: z
        .array(z.object({
        text: z.string().describe("The user's request."),
        status: z.enum(["open", "done"]).describe("'done' only if completed via a realtime tool during the session; otherwise 'open'."),
    }))
        .describe("User requests captured during the session."),
    assistantCommitments: z
        .array(z.string())
        .describe("Follow-ups the assistant committed to handling."),
    durableFacts: z
        .array(z.string())
        .describe("New user preferences or facts to remember (informational only)."),
    noActionable: z
        .boolean()
        .describe("True if the session was just small talk with no useful follow-up."),
});
const SYSTEM_PROMPT = `You summarize short realtime voice sessions for the main VisionClaw agent.

Rules:
- Mark requests done only if the assistant completed them during the voice session with a realtime tool.
- Mark requests open if they should be handed to the main agent.
- If the session was just small talk or had no useful follow-up, set noActionable true and keep arrays empty.
- Do not include markdown.`;
function fallbackSummary(transcript) {
    const userTexts = transcript.filter((entry) => entry.role === "user").map((entry) => entry.text.trim()).filter(Boolean);
    return {
        headline: userTexts[0] ? `Voice session: ${userTexts[0].slice(0, 120)}` : "Voice session ended",
        userRequests: userTexts.slice(0, 5).map((text) => ({ text, status: "open" })),
        assistantCommitments: [],
        durableFacts: [],
        noActionable: userTexts.length === 0,
    };
}
function formatTranscript(transcript) {
    return transcript.map((entry) => `${entry.role}: ${entry.text}`).join("\n");
}
function clampSummary(parsed) {
    return {
        headline: parsed.headline.trim() || "Voice session summary",
        userRequests: parsed.userRequests.slice(0, MAX_LIST_ITEMS),
        assistantCommitments: parsed.assistantCommitments.slice(0, MAX_LIST_ITEMS),
        durableFacts: parsed.durableFacts.slice(0, MAX_LIST_ITEMS),
        noActionable: parsed.noActionable,
    };
}
function renderSummaryText(summary) {
    const lines = [summary.headline];
    if (summary.noActionable) {
        lines.push("No actionable follow-up.");
        return lines.join("\n");
    }
    if (summary.userRequests.length > 0) {
        lines.push("Requests:");
        for (const req of summary.userRequests)
            lines.push(`- [${req.status}] ${req.text}`);
    }
    if (summary.assistantCommitments.length > 0) {
        lines.push("Assistant commitments:");
        for (const item of summary.assistantCommitments)
            lines.push(`- ${item}`);
    }
    if (summary.durableFacts.length > 0) {
        lines.push("New facts/preferences:");
        for (const item of summary.durableFacts)
            lines.push(`- ${item}`);
    }
    return lines.join("\n");
}
function writeSidecar(pathname, sidecar) {
    fs.writeFileSync(pathname, JSON.stringify(sidecar, null, 2));
}
export async function summarizeVoiceSession(config, sidecarPath) {
    if (!fs.existsSync(sidecarPath))
        return null;
    const sidecar = JSON.parse(fs.readFileSync(sidecarPath, "utf-8"));
    const transcript = Array.isArray(sidecar.transcript) ? sidecar.transcript : [];
    if (!sidecar.sessionId || transcript.length === 0)
        return null;
    try {
        const parsed = await runSideCall(config, {
            systemPrompt: SYSTEM_PROMPT,
            userMessage: `Summarize this realtime voice transcript:\n\n${formatTranscript(transcript)}`,
            schema: VoiceSessionSummarySchema,
            schemaName: "voice_session_summary",
            maxTokens: SUMMARY_MAX_TOKENS,
            timeoutMs: SUMMARY_TIMEOUT_MS,
        });
        const summary = clampSummary(parsed);
        const summaryText = renderSummaryText(summary);
        sidecar.summary = summary;
        sidecar.summaryText = summaryText;
        sidecar.summaryStatus = "ready";
        delete sidecar.summaryError;
        writeSidecar(sidecarPath, sidecar);
        getVoiceDb().prepare("UPDATE voice_sessions SET summary = ? WHERE session_id = ?").run(summaryText, sidecar.sessionId);
        logger.system(`[Realtime] Voice summary ready: ${sidecar.sessionId}`);
        return summary;
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        const summary = fallbackSummary(transcript);
        const summaryText = renderSummaryText(summary);
        sidecar.summary = summary;
        sidecar.summaryText = summaryText;
        sidecar.summaryStatus = "failed";
        sidecar.summaryError = message;
        writeSidecar(sidecarPath, sidecar);
        getVoiceDb().prepare("UPDATE voice_sessions SET summary = ? WHERE session_id = ?").run(summaryText, sidecar.sessionId);
        logger.warn(`[Realtime] Voice summary fallback used: ${message}`);
        return summary;
    }
}
//# sourceMappingURL=voice-summarizer.js.map