// tool-registrar.ts - MCP content transformation
// NOTE: Tools are NOT registered with Pi - only the unified `mcp` proxy tool is registered.
// This keeps the LLM context small (1 tool instead of 100s).
/**
 * Maximum characters per text block from MCP tool results.
 *
 * MCP tools (especially Playwright's browser_snapshot, browser_evaluate,
 * browser_network_requests) can return massive content — accessibility
 * trees of 100-500KB, large JSON arrays, etc. When passed untruncated into
 * spectral's ToolResultMessage.content, they blow up the LLM context window,
 * causing API errors (400/413) and dead sessions ("skipping empty
 * intermediate message" loops).
 *
 * 20,000 characters ≈ 5,000-8,000 tokens depending on the model, which
 * keeps individual tool results manageable even across multiple MCP calls
 * in the same turn.
 */
const MAX_MCP_TEXT_LENGTH = 100_000;
function truncateTextBlock(block) {
    if (block.type !== "text")
        return block;
    if (block.text.length <= MAX_MCP_TEXT_LENGTH)
        return block;
    const omitted = block.text.length - MAX_MCP_TEXT_LENGTH;
    return {
        type: "text",
        text: block.text.slice(0, MAX_MCP_TEXT_LENGTH) +
            `\n\n[truncated — ${omitted.toLocaleString()} bytes omitted. ` +
            `Use more specific tool calls or narrower queries to get complete results.]`,
    };
}
/**
 * Transform MCP content types to Pi content blocks.
 *
 * Text blocks exceeding MAX_MCP_TEXT_LENGTH are truncated to prevent
 * context-window overflow in downstream LLM calls.
 */
export function transformMcpContent(content) {
    return content.map(c => {
        if (c.type === "text") {
            return truncateTextBlock({ type: "text", text: c.text ?? "" });
        }
        if (c.type === "image") {
            // Most LLM APIs reject ImageContent blocks inside tool-result
            // messages (even multimodal models often only accept images in
            // user-turn content, not in tool results).  Passing through
            // base64 images here causes 400 errors and dead sessions.
            // Convert to a text placeholder that preserves metadata.
            const sizeBytes = c.data?.length ?? 0;
            return {
                type: "text",
                text: `[Image: ${c.mimeType ?? "image/png"}, ${sizeBytes.toLocaleString()} bytes base64]` +
                    `\n(Image data is available in the raw MCP result — use direct tool\n` +
                    `calls like browser_take_screenshot or browser_run_code to\n` +
                    `save/process images to disk for further analysis.)`,
            };
        }
        if (c.type === "resource") {
            const resourceUri = c.resource?.uri ?? "(no URI)";
            // Blob resources contain base64 binary data — never serialize the raw
            // blob into the model context (causes context overflow and dead sessions).
            const resourceContent = c.resource?.text ??
                ("blob" in (c.resource ?? {}) && c.resource.blob
                    ? `[Binary blob: ${c.resource.mimeType ?? "application/octet-stream"}]`
                    : c.resource
                        ? JSON.stringify(c.resource)
                        : "(no content)");
            return truncateTextBlock({
                type: "text",
                text: `[Resource: ${resourceUri}]\n${resourceContent}`,
            });
        }
        if (c.type === "resource_link") {
            const linkName = c.name ?? c.uri ?? "unknown";
            const linkUri = c.uri ?? "(no URI)";
            return {
                type: "text",
                text: `[Resource Link: ${linkName}]\nURI: ${linkUri}`,
            };
        }
        if (c.type === "audio") {
            return {
                type: "text",
                text: `[Audio content: ${c.mimeType ?? "audio/*"}]`,
            };
        }
        // Unknown content type — serialize as text but strip any potentially
        // large binary fields (data, blob) to avoid context overflow.
        const safe = { ...c };
        if ("data" in safe && typeof safe.data === "string" && safe.data.length > 200) {
            safe.data = `[${safe.data.length.toLocaleString()} bytes base64 omitted]`;
        }
        if ("blob" in safe && typeof safe.blob === "string" && safe.blob.length > 200) {
            safe.blob = `[${safe.blob.length.toLocaleString()} bytes base64 omitted]`;
        }
        return truncateTextBlock({ type: "text", text: JSON.stringify(safe) });
    });
}
