"use strict";
/**
 * OpenAI to Cursor Request Translator
 * Converts OpenAI messages to Cursor format.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildCursorRequest = void 0;
const MAX_TOOL_RESULT_CHARS = 12000;
const TOOL_RESULT_SERIALIZATION_FALLBACK = '[unserializable content]';
const TOOL_USE_ARGUMENTS_FALLBACK = '{}';
const TOOL_CALL_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
function isTextPart(part) {
    return part.type === 'text';
}
function isToolUsePart(part) {
    return part.type === 'tool_use';
}
function isToolResultPart(part) {
    return part.type === 'tool_result';
}
function extractTextContent(content, separator = '') {
    if (typeof content === 'string') {
        return content;
    }
    const parts = [];
    for (const part of content) {
        if (isTextPart(part) && part.text) {
            parts.push(part.text);
        }
    }
    return parts.join(separator);
}
function stringifyUnknown(value, fallback = '') {
    try {
        const serialized = JSON.stringify(value);
        return typeof serialized === 'string' ? serialized : fallback;
    }
    catch {
        return fallback;
    }
}
function sanitizeToolResultText(text) {
    return text.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '');
}
function truncateToolResultText(text) {
    if (text.length <= MAX_TOOL_RESULT_CHARS) {
        return text;
    }
    let omittedChars = text.length - MAX_TOOL_RESULT_CHARS;
    while (true) {
        const suffix = `\n[truncated ${omittedChars} chars]`;
        const keepLength = Math.max(MAX_TOOL_RESULT_CHARS - suffix.length, 0);
        const nextOmittedChars = text.length - keepLength;
        if (nextOmittedChars === omittedChars) {
            return `${text.slice(0, keepLength)}${suffix}`;
        }
        omittedChars = nextOmittedChars;
    }
}
function escapeXml(text) {
    return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function buildToolResultBlock(toolName, toolCallId, resultText) {
    // Truncate raw tool output before XML escaping so the cap reflects original content.
    const cleanResult = escapeXml(truncateToolResultText(sanitizeToolResultText(resultText)));
    return [
        '<tool_result>',
        `<tool_name>${escapeXml(toolName || 'tool')}</tool_name>`,
        `<tool_call_id>${escapeXml(toolCallId)}</tool_call_id>`,
        `<result>${cleanResult}</result>`,
        '</tool_result>',
    ].join('\n');
}
function normalizeToolCallId(toolCallId) {
    return typeof toolCallId === 'string' ? toolCallId.split('\n')[0] : '';
}
function sanitizeToolCallId(toolCallId) {
    const normalizedId = normalizeToolCallId(toolCallId).trim();
    if (!normalizedId) {
        return '';
    }
    if (TOOL_CALL_ID_PATTERN.test(normalizedId)) {
        return normalizedId;
    }
    return normalizedId.replace(/[^a-zA-Z0-9_-]/g, '');
}
function extractToolResultText(content) {
    if (content === undefined) {
        return '';
    }
    if (typeof content === 'string') {
        return content;
    }
    if (Array.isArray(content)) {
        return content
            .filter(isTextPart)
            .map((part) => part.text || '')
            .join('\n');
    }
    return stringifyUnknown(content, TOOL_RESULT_SERIALIZATION_FALLBACK);
}
function createFallbackToolUseId(messageIndex, partIndex) {
    return `toolu_cursor_fallback_${messageIndex}_${partIndex}`;
}
function resolveToolUseId(part, messageIndex, partIndex) {
    const sanitizedId = sanitizeToolCallId(part.id);
    return sanitizedId || createFallbackToolUseId(messageIndex, partIndex);
}
function requireToolResultId(toolCallId, location) {
    const sanitizedId = sanitizeToolCallId(toolCallId);
    if (sanitizedId) {
        return sanitizedId;
    }
    throw new Error(`${location} must include a valid tool result id`);
}
function normalizeAssistantToolCalls(toolCalls, messageIndex) {
    return toolCalls.map((toolCall, toolCallIndex) => ({
        ...toolCall,
        id: sanitizeToolCallId(toolCall.id) || createFallbackToolUseId(messageIndex, toolCallIndex),
        function: {
            name: toolCall.function?.name || 'tool',
            arguments: typeof toolCall.function?.arguments === 'string'
                ? toolCall.function.arguments
                : stringifyUnknown(toolCall.function?.arguments ?? {}, TOOL_USE_ARGUMENTS_FALLBACK),
        },
    }));
}
function rememberToolCallMeta(toolCallMetaMap, toolCalls) {
    for (const toolCall of toolCalls) {
        const toolCallId = toolCall.id || '';
        const toolName = toolCall.function?.name || 'tool';
        if (!toolCallId) {
            continue;
        }
        toolCallMetaMap.set(toolCallId, { name: toolName });
        const normalizedId = normalizeToolCallId(toolCallId);
        if (normalizedId && normalizedId !== toolCallId) {
            toolCallMetaMap.set(normalizedId, { name: toolName });
        }
    }
}
function rememberToolUseParts(toolCallMetaMap, content, messageIndex) {
    if (!Array.isArray(content)) {
        return;
    }
    for (let partIndex = 0; partIndex < content.length; partIndex++) {
        const part = content[partIndex];
        if (!isToolUsePart(part)) {
            continue;
        }
        const toolCallId = resolveToolUseId(part, messageIndex, partIndex);
        const toolName = part.name || 'tool';
        toolCallMetaMap.set(toolCallId, { name: toolName });
        const normalizedId = normalizeToolCallId(toolCallId);
        if (normalizedId && normalizedId !== toolCallId) {
            toolCallMetaMap.set(normalizedId, { name: toolName });
        }
    }
}
function extractToolCallsFromContent(content, messageIndex) {
    if (!Array.isArray(content)) {
        return [];
    }
    return content.flatMap((part, partIndex) => {
        if (!isToolUsePart(part)) {
            return [];
        }
        return [
            {
                id: resolveToolUseId(part, messageIndex, partIndex),
                type: 'function',
                function: {
                    name: part.name || 'tool',
                    arguments: stringifyUnknown(part.input ?? {}, TOOL_USE_ARGUMENTS_FALLBACK),
                },
            },
        ];
    });
}
function mergeAssistantToolCalls(primaryToolCalls, secondaryToolCalls) {
    const merged = [];
    const seenIds = new Set();
    for (const toolCall of [...primaryToolCalls, ...secondaryToolCalls]) {
        if (seenIds.has(toolCall.id)) {
            continue;
        }
        seenIds.add(toolCall.id);
        merged.push(toolCall);
    }
    return merged;
}
function renderUserContent(content, toolCallMetaMap, messageIndex) {
    if (typeof content === 'string') {
        return content;
    }
    const parts = [];
    let textBuffer = '';
    for (let partIndex = 0; partIndex < content.length; partIndex++) {
        const part = content[partIndex];
        if (isTextPart(part) && part.text) {
            textBuffer += part.text;
            continue;
        }
        if (!isToolResultPart(part)) {
            continue;
        }
        if (textBuffer) {
            parts.push(textBuffer);
            textBuffer = '';
        }
        const toolCallId = requireToolResultId(part.tool_use_id, `messages[${messageIndex}].content[${partIndex}]`);
        const normalizedId = normalizeToolCallId(toolCallId);
        const toolName = toolCallMetaMap.get(toolCallId)?.name || toolCallMetaMap.get(normalizedId)?.name || 'tool';
        parts.push(buildToolResultBlock(toolName, toolCallId, extractToolResultText(part.content)));
    }
    if (textBuffer) {
        parts.push(textBuffer);
    }
    return parts.join('\n');
}
/**
 * Convert OpenAI messages to Cursor format with a safer tool-result strategy.
 * - system → user with [System Instructions] prefix
 * - tool → flatten into a structured user text block for Cursor compatibility
 * - assistant with tool_calls → keep tool_calls in the translated shape for metadata recovery
 */
function convertMessages(messages) {
    const result = [];
    const toolCallMetaMap = new Map();
    for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
        const msg = messages[messageIndex];
        if (msg.role === 'system') {
            result.push({
                role: 'user',
                content: `[System Instructions]\n${extractTextContent(msg.content)}`,
            });
            continue;
        }
        if (msg.role === 'tool') {
            const toolCallId = requireToolResultId(msg.tool_call_id, `messages[${messageIndex}].tool_call_id`);
            const normalizedToolCallId = normalizeToolCallId(toolCallId);
            const rememberedToolName = toolCallMetaMap.get(toolCallId)?.name || toolCallMetaMap.get(normalizedToolCallId)?.name;
            const toolName = msg.name || rememberedToolName || 'tool';
            result.push({
                role: 'user',
                content: buildToolResultBlock(toolName, toolCallId, extractTextContent(msg.content, '\n')),
            });
            continue;
        }
        if (msg.role === 'user' || msg.role === 'assistant') {
            if (msg.role === 'assistant') {
                const normalizedToolCalls = normalizeAssistantToolCalls(msg.tool_calls || [], messageIndex);
                const assistantToolCalls = mergeAssistantToolCalls(normalizedToolCalls, extractToolCallsFromContent(msg.content, messageIndex));
                if (normalizedToolCalls.length > 0) {
                    rememberToolCallMeta(toolCallMetaMap, normalizedToolCalls);
                }
                rememberToolUseParts(toolCallMetaMap, msg.content, messageIndex);
                const content = extractTextContent(msg.content);
                if (assistantToolCalls.length > 0) {
                    result.push({
                        role: 'assistant',
                        content,
                        tool_calls: assistantToolCalls,
                    });
                }
                else if (content) {
                    result.push({
                        role: 'assistant',
                        content,
                    });
                }
            }
            else {
                const content = renderUserContent(msg.content, toolCallMetaMap, messageIndex);
                if (content) {
                    result.push({
                        role: 'user',
                        content,
                    });
                }
            }
            continue;
        }
        if (process.env.CCS_DEBUG) {
            console.error(`[cursor] Unknown message role: ${msg.role}, skipping`);
        }
    }
    return result;
}
function buildCursorRequest(_model, body, _stream, _credentials) {
    return {
        ...body,
        messages: convertMessages(body.messages || []),
    };
}
exports.buildCursorRequest = buildCursorRequest;
//# sourceMappingURL=cursor-translator.js.map