"use strict";
/**
 * Cursor Protobuf Decoder
 * Implements ConnectRPC protobuf wire format decoding
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractTextFromResponse = exports.parseConnectRPCFrame = exports.decodeMessage = exports.decodeField = exports.decodeVarint = void 0;
const zlib = __importStar(require("zlib"));
const cursor_protobuf_schema_js_1 = require("./cursor-protobuf-schema.js");
/**
 * Decode a varint from buffer
 * Returns [value, newOffset]
 */
function decodeVarint(buffer, offset) {
    let result = 0;
    let shift = 0;
    let pos = offset;
    const maxBytes = 5;
    while (pos < buffer.length && pos - offset < maxBytes) {
        const b = buffer[pos];
        result |= (b & 0x7f) << shift;
        pos++;
        if (!(b & 0x80))
            break;
        shift += 7;
    }
    return [result >>> 0, pos]; // Ensure unsigned
}
exports.decodeVarint = decodeVarint;
/**
 * Decode a single protobuf field
 * Returns [fieldNum, wireType, value, newOffset]
 */
function decodeField(buffer, offset) {
    if (offset >= buffer.length) {
        return [null, null, null, offset];
    }
    const [tag, pos1] = decodeVarint(buffer, offset);
    const fieldNum = tag >> 3;
    const wireType = (tag & 0x07);
    let value;
    let pos = pos1;
    if (wireType === cursor_protobuf_schema_js_1.WIRE_TYPE.VARINT) {
        [value, pos] = decodeVarint(buffer, pos);
    }
    else if (wireType === cursor_protobuf_schema_js_1.WIRE_TYPE.LEN) {
        const [length, pos2] = decodeVarint(buffer, pos);
        if (pos2 + length > buffer.length) {
            return [null, null, null, buffer.length];
        }
        value = buffer.slice(pos2, pos2 + length);
        pos = pos2 + length;
    }
    else if (wireType === cursor_protobuf_schema_js_1.WIRE_TYPE.FIXED64) {
        if (pos + 8 > buffer.length) {
            return [null, null, null, buffer.length];
        }
        value = buffer.slice(pos, pos + 8);
        pos += 8;
    }
    else if (wireType === cursor_protobuf_schema_js_1.WIRE_TYPE.FIXED32) {
        if (pos + 4 > buffer.length) {
            return [null, null, null, buffer.length];
        }
        value = buffer.slice(pos, pos + 4);
        pos += 4;
    }
    else {
        value = null;
    }
    return [fieldNum, wireType, value, pos];
}
exports.decodeField = decodeField;
/**
 * Decode a protobuf message into a map of fields
 */
function decodeMessage(data) {
    const fields = new Map();
    let pos = 0;
    // NOTE: If two fields share the same field number but different wire types, later values overwrite earlier ones.
    while (pos < data.length) {
        const [fieldNum, wireType, value, newPos] = decodeField(data, pos);
        if (fieldNum === null || wireType === null || value === null)
            break;
        if (!fields.has(fieldNum)) {
            fields.set(fieldNum, []);
        }
        const fieldArray = fields.get(fieldNum);
        if (fieldArray) {
            fieldArray.push({ wireType, value: value });
        }
        pos = newPos;
    }
    return fields;
}
exports.decodeMessage = decodeMessage;
/**
 * Parse ConnectRPC frame from buffer
 * Returns frame data or null if incomplete
 */
function parseConnectRPCFrame(buffer) {
    if (buffer.length < 5)
        return null;
    const flags = buffer[0];
    const length = (buffer[1] << 24) | (buffer[2] << 16) | (buffer[3] << 8) | buffer[4];
    if (buffer.length < 5 + length)
        return null;
    let payload = buffer.slice(5, 5 + length);
    // Decompress if gzip
    if ((0, cursor_protobuf_schema_js_1.isCompressedConnectFrame)(flags)) {
        try {
            payload = Buffer.from(zlib.gunzipSync(payload));
        }
        catch (err) {
            if (process.env.CCS_DEBUG) {
                console.error('[cursor] parseConnectRPCFrame decompression failed:', err);
            }
            // Decompression failed, use raw payload
        }
    }
    return {
        flags,
        length,
        payload: new Uint8Array(payload),
        consumed: 5 + length,
    };
}
exports.parseConnectRPCFrame = parseConnectRPCFrame;
/**
 * Extract tool call from protobuf data
 */
function extractToolCall(toolCallData) {
    const toolCall = decodeMessage(toolCallData);
    let toolCallId = '';
    let toolName = '';
    let rawArgs = '';
    let isLast = false;
    // Extract tool call ID
    if (toolCall.has(cursor_protobuf_schema_js_1.FIELD.ToolCall.ID)) {
        const idField = toolCall.get(cursor_protobuf_schema_js_1.FIELD.ToolCall.ID);
        if (idField && idField[0]) {
            const fullId = new TextDecoder().decode(idField[0].value);
            toolCallId = fullId.split('\n')[0]; // Take first line
        }
    }
    // Extract tool name
    if (toolCall.has(cursor_protobuf_schema_js_1.FIELD.ToolCall.NAME)) {
        const nameField = toolCall.get(cursor_protobuf_schema_js_1.FIELD.ToolCall.NAME);
        if (nameField && nameField[0]) {
            toolName = new TextDecoder().decode(nameField[0].value);
        }
    }
    // Extract is_last flag
    if (toolCall.has(cursor_protobuf_schema_js_1.FIELD.ToolCall.IS_LAST)) {
        const lastField = toolCall.get(cursor_protobuf_schema_js_1.FIELD.ToolCall.IS_LAST);
        if (lastField && lastField[0]) {
            isLast = lastField[0].value !== 0;
        }
    }
    // Extract MCP params - nested real tool info
    if (toolCall.has(cursor_protobuf_schema_js_1.FIELD.ToolCall.MCP_PARAMS)) {
        try {
            const mcpField = toolCall.get(cursor_protobuf_schema_js_1.FIELD.ToolCall.MCP_PARAMS);
            if (!mcpField || !mcpField[0])
                return null;
            const mcpParams = decodeMessage(mcpField[0].value);
            if (mcpParams.has(cursor_protobuf_schema_js_1.FIELD.McpParams.TOOLS_LIST)) {
                const toolsList = mcpParams.get(cursor_protobuf_schema_js_1.FIELD.McpParams.TOOLS_LIST);
                if (!toolsList || !toolsList[0])
                    return null;
                const tool = decodeMessage(toolsList[0].value);
                if (tool.has(cursor_protobuf_schema_js_1.FIELD.McpNested.NAME)) {
                    const nestedName = tool.get(cursor_protobuf_schema_js_1.FIELD.McpNested.NAME);
                    if (nestedName && nestedName[0]) {
                        toolName = new TextDecoder().decode(nestedName[0].value);
                    }
                }
                if (tool.has(cursor_protobuf_schema_js_1.FIELD.McpNested.PARAMS)) {
                    const nestedParams = tool.get(cursor_protobuf_schema_js_1.FIELD.McpNested.PARAMS);
                    if (nestedParams && nestedParams[0]) {
                        rawArgs = new TextDecoder().decode(nestedParams[0].value);
                    }
                }
            }
        }
        catch (err) {
            if (process.env.CCS_DEBUG) {
                console.error('[cursor] extractToolCall MCP parsing failed:', err);
            }
            // MCP parse error, continue
        }
    }
    // Fallback to raw_args
    if (!rawArgs && toolCall.has(cursor_protobuf_schema_js_1.FIELD.ToolCall.RAW_ARGS)) {
        const rawArgsField = toolCall.get(cursor_protobuf_schema_js_1.FIELD.ToolCall.RAW_ARGS);
        if (rawArgsField && rawArgsField[0]) {
            rawArgs = new TextDecoder().decode(rawArgsField[0].value);
        }
    }
    if (toolCallId && toolName) {
        return {
            id: toolCallId,
            type: 'function',
            function: {
                name: toolName,
                arguments: rawArgs || '{}',
            },
            isLast,
        };
    }
    return null;
}
/**
 * Extract text and thinking from response data
 */
function extractTextAndThinking(responseData) {
    const nested = decodeMessage(responseData);
    let text = null;
    let thinking = null;
    // Extract text
    if (nested.has(cursor_protobuf_schema_js_1.FIELD.ChatResponse.TEXT)) {
        const textField = nested.get(cursor_protobuf_schema_js_1.FIELD.ChatResponse.TEXT);
        if (textField && textField[0]) {
            text = new TextDecoder().decode(textField[0].value);
        }
    }
    // Extract thinking
    if (nested.has(cursor_protobuf_schema_js_1.FIELD.ChatResponse.THINKING)) {
        try {
            const thinkingField = nested.get(cursor_protobuf_schema_js_1.FIELD.ChatResponse.THINKING);
            if (thinkingField && thinkingField[0]) {
                const thinkingMsg = decodeMessage(thinkingField[0].value);
                if (thinkingMsg.has(cursor_protobuf_schema_js_1.FIELD.Thinking.TEXT)) {
                    const thinkingTextField = thinkingMsg.get(cursor_protobuf_schema_js_1.FIELD.Thinking.TEXT);
                    if (thinkingTextField && thinkingTextField[0]) {
                        thinking = new TextDecoder().decode(thinkingTextField[0].value);
                    }
                }
            }
        }
        catch (err) {
            if (process.env.CCS_DEBUG) {
                console.error('[cursor] extractTextAndThinking parsing failed:', err);
            }
            // Thinking parse error, continue
        }
    }
    return { text, thinking };
}
/**
 * Extract text and tool calls from response payload
 */
function extractTextFromResponse(payload) {
    try {
        const fields = decodeMessage(payload);
        // Field 1: ClientSideToolV2Call
        if (fields.has(cursor_protobuf_schema_js_1.FIELD.Response.TOOL_CALL)) {
            const toolCallField = fields.get(cursor_protobuf_schema_js_1.FIELD.Response.TOOL_CALL);
            if (toolCallField && toolCallField[0]) {
                const toolCall = extractToolCall(toolCallField[0].value);
                if (toolCall) {
                    return { text: null, error: null, toolCall, thinking: null };
                }
            }
        }
        // Field 2: StreamUnifiedChatResponse
        if (fields.has(cursor_protobuf_schema_js_1.FIELD.Response.RESPONSE)) {
            const responseField = fields.get(cursor_protobuf_schema_js_1.FIELD.Response.RESPONSE);
            if (responseField && responseField[0]) {
                const { text, thinking } = extractTextAndThinking(responseField[0].value);
                if (text || thinking) {
                    return { text, error: null, toolCall: null, thinking };
                }
            }
        }
        if (payload.length > 0) {
            return { text: null, error: 'Malformed protobuf response', toolCall: null, thinking: null };
        }
        return { text: null, error: null, toolCall: null, thinking: null };
    }
    catch (err) {
        if (process.env.CCS_DEBUG) {
            console.error('[cursor] extractTextFromResponse parsing failed:', err);
        }
        return { text: null, error: 'Malformed protobuf response', toolCall: null, thinking: null };
    }
}
exports.extractTextFromResponse = extractTextFromResponse;
//# sourceMappingURL=cursor-protobuf-decoder.js.map