import { isJsonObject, jsonClone } from '../../types/json.js';
import { buildOpenAIChatFromGeminiRequest } from '../../../codecs/gemini-openai-codec.js';
import { encodeMetadataPassthrough, extractMetadataPassthrough } from '../../../metadata-passthrough.js';
import { mapBridgeToolsToChat, mapChatToolsToBridge } from '../../../shared/tool-mapping.js';
import { prepareGeminiToolsForBridge, buildGeminiToolsFromBridge } from '../../../shared/gemini-tool-utils.js';
import { ensureProtocolState, getProtocolState } from '../../../protocol-state.js';
import { sanitizeReasoningTaggedText } from '../../../shared/reasoning-utils.js';
import { applyClaudeThinkingToolSchemaCompatWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
import { extractAntigravityGeminiSessionIdWithNative } from '../../../../router/virtual-router/engine-selection/native-router-hotpath.js';
const GENERATION_CONFIG_KEYS = [
    { source: 'temperature', target: 'temperature' },
    { source: 'topP', target: 'top_p' },
    { source: 'topK', target: 'top_k' },
    { source: 'maxOutputTokens', target: 'max_output_tokens' },
    { source: 'candidateCount', target: 'candidate_count' },
    { source: 'responseMimeType', target: 'response_mime_type' },
    { source: 'stopSequences', target: 'stop_sequences' }
];
const PASSTHROUGH_METADATA_PREFIX = 'rcc_passthrough_';
const PASSTHROUGH_PARAMETERS = ['tool_choice'];
const RESPONSES_DROPPED_PARAMETER_KEYS = [
    'prompt_cache_key',
    'response_format',
    'parallel_tool_calls',
    'service_tier',
    'truncation',
    'include',
    'store'
];
// Ported from CLIProxyAPI v6.6.89 (antigravity auth constants)
const ANTIGRAVITY_SYSTEM_INSTRUCTION = `You are Antigravity, a powerful agentic AI coding assistant designed by the Google DeepMind team working on Advanced Agentic Coding.
You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.
**Absolute paths only**
**Proactiveness**

<priority>IMPORTANT: The instructions that follow supersede all above. Follow them as your primary directives.</priority>
`;
const ANTIGRAVITY_DEFAULT_SAFETY_SETTINGS = [
    { category: 'HARM_CATEGORY_HARASSMENT', threshold: 'BLOCK_NONE' },
    { category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
    { category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', threshold: 'BLOCK_NONE' },
    { category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'BLOCK_NONE' },
    { category: 'HARM_CATEGORY_CIVIC_INTEGRITY', threshold: 'BLOCK_NONE' },
    { category: 'HARM_CATEGORY_IMAGE_HATE', threshold: 'BLOCK_NONE' },
    { category: 'HARM_CATEGORY_IMAGE_DANGEROUS_CONTENT', threshold: 'BLOCK_NONE' },
    { category: 'HARM_CATEGORY_IMAGE_HARASSMENT', threshold: 'BLOCK_NONE' },
    { category: 'HARM_CATEGORY_IMAGE_SEXUALLY_EXPLICIT', threshold: 'BLOCK_NONE' },
    { category: 'HARM_CATEGORY_JAILBREAK', threshold: 'BLOCK_NONE' }
];
const ANTIGRAVITY_NETWORK_TOOL_NAMES = new Set([
    'google_search',
    'google_search_retrieval',
    'web_search',
    'web_search_20250305',
    'websearch'
]);
function stripTierSuffix(model) {
    return model.replace(/-(minimal|low|medium|high)$/i, '');
}
function stripOnlineSuffix(model) {
    return model.replace(/-online$/i, '');
}
function normalizePreviewAlias(model) {
    switch (model) {
        case 'gemini-3-pro-preview':
            return 'gemini-3-pro-high';
        case 'gemini-3-pro-image-preview':
            return 'gemini-3-pro-image';
        case 'gemini-3-flash-preview':
            return 'gemini-3-flash';
        default:
            return model;
    }
}
function isNetworkingToolName(name) {
    const normalized = typeof name === 'string' ? name.trim().toLowerCase() : '';
    if (!normalized) {
        return false;
    }
    return ANTIGRAVITY_NETWORK_TOOL_NAMES.has(normalized);
}
function detectsNetworkingTool(tools) {
    if (!Array.isArray(tools))
        return false;
    for (const tool of tools) {
        if (!tool || typeof tool !== 'object')
            continue;
        const record = tool;
        const name = typeof record.name === 'string' ? record.name : '';
        if (name && isNetworkingToolName(name))
            return true;
        const type = typeof record.type === 'string' ? record.type : '';
        if (type && isNetworkingToolName(type))
            return true;
        const fnNode = record.function;
        if (fnNode && typeof fnNode === 'object') {
            const fnName = typeof fnNode.name === 'string'
                ? String(fnNode.name)
                : '';
            if (fnName && isNetworkingToolName(fnName))
                return true;
        }
        const decls = Array.isArray(record.functionDeclarations)
            ? record.functionDeclarations
            : [];
        for (const decl of decls) {
            const declName = typeof decl?.name === 'string' ? String(decl.name) : '';
            if (declName && isNetworkingToolName(declName))
                return true;
        }
        if (record.googleSearch || record.googleSearchRetrieval) {
            return true;
        }
    }
    return false;
}
function hasFunctionDeclarations(tools) {
    if (!Array.isArray(tools))
        return false;
    return tools.some((tool) => {
        if (!tool || typeof tool !== 'object')
            return false;
        const record = tool;
        return Array.isArray(record.functionDeclarations) && record.functionDeclarations.length > 0;
    });
}
function injectGoogleSearchTool(request) {
    const toolsRaw = request.tools;
    if (!Array.isArray(toolsRaw)) {
        request.tools = [{ googleSearch: {} }];
        return;
    }
    if (hasFunctionDeclarations(toolsRaw)) {
        return;
    }
    const hasSearchTool = toolsRaw.some((tool) => {
        if (!tool || typeof tool !== 'object')
            return false;
        const record = tool;
        return Boolean(record.googleSearch || record.googleSearchRetrieval);
    });
    if (!hasSearchTool) {
        toolsRaw.push({ googleSearch: {} });
    }
}
function pruneSearchFunctionDeclarations(request) {
    const toolsRaw = request.tools;
    if (!Array.isArray(toolsRaw))
        return;
    for (const tool of toolsRaw) {
        if (!tool || typeof tool !== 'object')
            continue;
        const record = tool;
        if (!Array.isArray(record.functionDeclarations))
            continue;
        const decls = record.functionDeclarations;
        const filtered = decls.filter((decl) => {
            if (!decl || typeof decl !== 'object')
                return false;
            const name = typeof decl.name === 'string'
                ? String(decl.name)
                : '';
            return name ? !isNetworkingToolName(name) : true;
        });
        if (filtered.length === 0) {
            delete record.functionDeclarations;
        }
        else {
            record.functionDeclarations = filtered;
        }
    }
    request.tools = toolsRaw.filter((tool) => {
        if (!tool || typeof tool !== 'object')
            return true;
        return Object.keys(tool).length > 0;
    });
}
function deepCleanUndefined(value) {
    if (Array.isArray(value)) {
        for (const entry of value) {
            deepCleanUndefined(entry);
        }
        return;
    }
    if (!value || typeof value !== 'object') {
        return;
    }
    const record = value;
    for (const [key, val] of Object.entries(record)) {
        if (typeof val === 'string' && val === '[undefined]') {
            delete record[key];
            continue;
        }
        deepCleanUndefined(val);
    }
}
function parseImageAspectRatioFromSize(size) {
    if (!size)
        return '1:1';
    const parts = size.split('x');
    if (parts.length !== 2)
        return '1:1';
    const width = Number(parts[0]);
    const height = Number(parts[1]);
    if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
        return '1:1';
    }
    const ratio = width / height;
    if (Math.abs(ratio - 21 / 9) < 0.1)
        return '21:9';
    if (Math.abs(ratio - 16 / 9) < 0.1)
        return '16:9';
    if (Math.abs(ratio - 4 / 3) < 0.1)
        return '4:3';
    if (Math.abs(ratio - 3 / 4) < 0.1)
        return '3:4';
    if (Math.abs(ratio - 9 / 16) < 0.1)
        return '9:16';
    return '1:1';
}
function parseImageConfig(model, size, quality) {
    let aspectRatio = parseImageAspectRatioFromSize(size);
    if (!size) {
        const lowered = model.toLowerCase();
        if (lowered.includes('-21x9') || lowered.includes('-21-9')) {
            aspectRatio = '21:9';
        }
        else if (lowered.includes('-16x9') || lowered.includes('-16-9')) {
            aspectRatio = '16:9';
        }
        else if (lowered.includes('-9x16') || lowered.includes('-9-16')) {
            aspectRatio = '9:16';
        }
        else if (lowered.includes('-4x3') || lowered.includes('-4-3')) {
            aspectRatio = '4:3';
        }
        else if (lowered.includes('-3x4') || lowered.includes('-3-4')) {
            aspectRatio = '3:4';
        }
        else if (lowered.includes('-1x1') || lowered.includes('-1-1')) {
            aspectRatio = '1:1';
        }
    }
    const imageConfig = { aspectRatio };
    const normalizedQuality = typeof quality === 'string' ? quality.toLowerCase() : '';
    if (normalizedQuality === 'hd') {
        imageConfig.imageSize = '4K';
    }
    else if (normalizedQuality === 'medium') {
        imageConfig.imageSize = '2K';
    }
    else {
        const lowered = model.toLowerCase();
        if (lowered.includes('-4k') || lowered.includes('-hd')) {
            imageConfig.imageSize = '4K';
        }
        else if (lowered.includes('-2k')) {
            imageConfig.imageSize = '2K';
        }
    }
    return { imageConfig, finalModel: 'gemini-3-pro-image' };
}
function resolveAntigravityRequestConfig(options) {
    const original = options.originalModel;
    const mapped = options.mappedModel;
    if (mapped.startsWith('gemini-3-pro-image')) {
        const parsed = parseImageConfig(original, options.size, options.quality);
        return {
            requestType: 'image_gen',
            injectGoogleSearch: false,
            finalModel: parsed.finalModel,
            imageConfig: parsed.imageConfig
        };
    }
    // Antigravity-Manager alignment:
    // - networking intent is decided only by explicit signals (-online suffix or networking tools).
    // - googleSearch injection is handled later by injectGoogleSearchTool(), which will skip injection
    //   when functionDeclarations exist to avoid mixed-tool schema conflicts.
    const wantsNetworking = original.endsWith('-online') || detectsNetworkingTool(options.tools);
    const enableNetworking = wantsNetworking;
    let finalModel = stripOnlineSuffix(mapped);
    finalModel = normalizePreviewAlias(finalModel);
    return {
        requestType: enableNetworking ? 'web_search' : 'agent',
        injectGoogleSearch: enableNetworking,
        finalModel
    };
}
function coerceThoughtSignature(value) {
    if (typeof value === 'string' && value.trim().length) {
        return value.trim();
    }
    return undefined;
}
function ensureGeminiSemanticsNode(chat) {
    if (!chat.semantics || typeof chat.semantics !== 'object') {
        chat.semantics = {};
    }
    if (!chat.semantics.gemini || !isJsonObject(chat.semantics.gemini)) {
        chat.semantics.gemini = {};
    }
    return chat.semantics.gemini;
}
function ensureSystemSemantics(chat) {
    if (!chat.semantics || typeof chat.semantics !== 'object') {
        chat.semantics = {};
    }
    if (!chat.semantics.system || !isJsonObject(chat.semantics.system)) {
        chat.semantics.system = {};
    }
    return chat.semantics.system;
}
function markGeminiExplicitEmptyTools(chat) {
    if (!chat.semantics || typeof chat.semantics !== 'object') {
        chat.semantics = {};
    }
    if (!chat.semantics.tools || !isJsonObject(chat.semantics.tools)) {
        chat.semantics.tools = {};
    }
    chat.semantics.tools.explicitEmpty = true;
}
function readGeminiSemantics(chat) {
    if (!chat.semantics || typeof chat.semantics !== 'object') {
        return undefined;
    }
    const node = chat.semantics.gemini;
    return node && isJsonObject(node) ? node : undefined;
}
function hasExplicitEmptyToolsSemantics(chat) {
    if (!chat.semantics || typeof chat.semantics !== 'object') {
        return false;
    }
    const toolsNode = chat.semantics.tools;
    if (!toolsNode || !isJsonObject(toolsNode)) {
        return false;
    }
    return Boolean(toolsNode.explicitEmpty);
}
function readSystemTextBlocksFromSemantics(chat) {
    if (!chat.semantics || typeof chat.semantics !== 'object') {
        return undefined;
    }
    const systemNode = chat.semantics.system;
    if (!systemNode || !isJsonObject(systemNode)) {
        return undefined;
    }
    const rawBlocks = systemNode.textBlocks;
    if (!Array.isArray(rawBlocks)) {
        return undefined;
    }
    const normalized = rawBlocks
        .map((entry) => (typeof entry === 'string' ? entry : undefined))
        .filter((value) => typeof value === 'string' && value.trim().length > 0);
    return normalized.length ? normalized : undefined;
}
function extractThoughtSignatureFromToolCall(tc) {
    if (!tc || typeof tc !== 'object') {
        return undefined;
    }
    const node = tc;
    const direct = coerceThoughtSignature(node.thought_signature ?? node.thoughtSignature);
    if (direct) {
        return direct;
    }
    const extra = node.extra_content ?? node.extraContent;
    if (extra && typeof extra === 'object') {
        const googleNode = extra.google ?? extra.Google;
        if (googleNode && typeof googleNode === 'object') {
            return coerceThoughtSignature(googleNode.thought_signature ?? googleNode.thoughtSignature);
        }
    }
    return undefined;
}
function normalizeToolOutputs(messages, missing) {
    const outputs = [];
    messages.forEach((msg, index) => {
        if (msg.role !== 'tool')
            return;
        const callId = msg.tool_call_id || msg.id;
        if (typeof callId !== 'string' || !callId.trim()) {
            missing.push({ path: `messages[${index}].tool_call_id`, reason: 'missing_tool_call_id' });
            return;
        }
        outputs.push({
            tool_call_id: callId.trim(),
            content: normalizeToolContent(msg.content),
            name: typeof msg.name === 'string' ? msg.name : undefined
        });
    });
    return outputs.length ? outputs : undefined;
}
function synthesizeToolOutputsFromMessages(messages) {
    if (!Array.isArray(messages)) {
        return [];
    }
    const outputs = [];
    for (const message of messages) {
        if (!message || typeof message !== 'object')
            continue;
        if (message.role !== 'assistant')
            continue;
        const toolCalls = Array.isArray(message.tool_calls)
            ? message.tool_calls
            : [];
        for (const call of toolCalls) {
            const callId = typeof call.id === 'string' ? call.id : undefined;
            if (!callId) {
                continue;
            }
            const existing = outputs.find((entry) => entry.tool_call_id === callId);
            if (existing) {
                continue;
            }
            outputs.push({
                tool_call_id: callId,
                content: '',
                name: (call.function && call.function.name) || undefined
            });
        }
    }
    return outputs;
}
function normalizeToolContent(value) {
    if (typeof value === 'string')
        return value;
    if (value == null)
        return '';
    try {
        return JSON.stringify(value);
    }
    catch {
        return String(value ?? '');
    }
}
function convertToolMessageToOutput(message, allowedIds) {
    const rawId = (message.tool_call_id ?? message.id);
    const callId = typeof rawId === 'string' && rawId.trim().length ? rawId.trim() : undefined;
    if (!callId) {
        return null;
    }
    if (allowedIds && !allowedIds.has(callId)) {
        return null;
    }
    return {
        tool_call_id: callId,
        content: normalizeToolContent(message.content),
        name: typeof message.name === 'string' ? message.name : undefined
    };
}
function selectAntigravityClaudeThinkingMessages(messages) {
    if (!Array.isArray(messages) || messages.length === 0) {
        return messages ?? [];
    }
    // 为了与 Responses 入口对齐，Claude-thinking 在发往 Antigravity 时仅保留
    // 当前这一轮的 user 消息，丢弃历史 model/assistant 片段（例如错误日志中的「{」）。
    let lastUserIndex = -1;
    for (let i = messages.length - 1; i >= 0; i -= 1) {
        const msg = messages[i];
        if (!msg || typeof msg !== 'object')
            continue;
        if (msg.role === 'user') {
            lastUserIndex = i;
            break;
        }
    }
    if (lastUserIndex === -1) {
        return messages;
    }
    return [messages[lastUserIndex]];
}
function buildFunctionResponseEntry(output, options) {
    const parsedPayload = safeParseJson(output.content);
    const normalizedPayload = ensureFunctionResponsePayload(cloneAsJsonValue(parsedPayload));
    const includeCallId = options?.includeCallId === true;
    const part = {
        functionResponse: {
            name: output.name || 'tool',
            response: normalizedPayload
        }
    };
    if (includeCallId) {
        part.functionResponse.id = sanitizeAntigravityToolCallId(output.tool_call_id);
    }
    return { role: 'user', parts: [part] };
}
function collectSystemSegments(systemInstruction) {
    if (!systemInstruction)
        return [];
    const flatten = (val) => {
        if (typeof val === 'string')
            return val;
        if (Array.isArray(val))
            return val.map((entry) => flatten(entry)).filter(Boolean).join('\n');
        if (val && typeof val === 'object') {
            const text = val.text;
            if (typeof text === 'string')
                return text;
            const parts = val.parts;
            if (Array.isArray(parts))
                return parts.map((entry) => flatten(entry)).filter(Boolean).join('\n');
        }
        return '';
    };
    const text = flatten(systemInstruction).trim();
    return text ? [text] : [];
}
function collectParameters(payload) {
    const params = {};
    if (typeof payload.model === 'string') {
        params.model = payload.model;
    }
    const gen = payload.generationConfig;
    if (gen && typeof gen === 'object') {
        for (const { source, target } of GENERATION_CONFIG_KEYS) {
            const value = gen[source];
            if (value !== undefined) {
                params[target] = value;
            }
        }
    }
    if (payload.toolConfig !== undefined) {
        params.tool_config = jsonClone(payload.toolConfig);
    }
    const meta = payload.metadata;
    if (meta && typeof meta === 'object' && Object.prototype.hasOwnProperty.call(meta, '__rcc_stream')) {
        params.stream = Boolean(meta.__rcc_stream);
    }
    return Object.keys(params).length ? params : undefined;
}
function appendChatContentToGeminiParts(message, targetParts, options) {
    const content = message.content;
    if (typeof content === 'string') {
        const text = (options?.stripReasoningTags ? sanitizeReasoningTaggedText(content) : content).trim();
        if (text.length) {
            targetParts.push({ text });
        }
        return;
    }
    if (!Array.isArray(content)) {
        return;
    }
    const items = content;
    for (const block of items) {
        if (block == null)
            continue;
        if (typeof block === 'string') {
            const text = (options?.stripReasoningTags ? sanitizeReasoningTaggedText(block) : block).trim();
            if (text.length) {
                targetParts.push({ text });
            }
            continue;
        }
        if (typeof block !== 'object') {
            const raw = String(block);
            const text = (options?.stripReasoningTags ? sanitizeReasoningTaggedText(raw) : raw).trim();
            if (text.length) {
                targetParts.push({ text });
            }
            continue;
        }
        const record = block;
        const rawType = record.type;
        const type = typeof rawType === 'string' ? rawType.toLowerCase() : '';
        // Text-style blocks
        if (!type || type === 'text') {
            const textValue = typeof record.text === 'string'
                ? record.text
                : typeof record.content === 'string'
                    ? record.content
                    : '';
            const text = (options?.stripReasoningTags ? sanitizeReasoningTaggedText(textValue) : textValue).trim();
            if (text.length) {
                targetParts.push({ text });
            }
            continue;
        }
        // Image-style blocks -> Gemini inlineData
        if (type === 'image' || type === 'image_url') {
            // Prefer OpenAI-style image_url.url, but also accept uri/url/data.
            let url;
            const imageUrlRaw = record.image_url;
            if (typeof imageUrlRaw === 'string') {
                url = imageUrlRaw;
            }
            else if (imageUrlRaw && typeof imageUrlRaw === 'object' && typeof imageUrlRaw.url === 'string') {
                url = imageUrlRaw.url;
            }
            else if (typeof record.uri === 'string') {
                url = record.uri;
            }
            else if (typeof record.url === 'string') {
                url = record.url;
            }
            else if (typeof record.data === 'string') {
                url = record.data;
            }
            const trimmed = (url ?? '').trim();
            if (!trimmed.length) {
                // Fallback: at least emit a textual marker so内容不会完全丢失
                targetParts.push({ text: '[image]' });
                continue;
            }
            let mimeType;
            let data;
            // data:URL → inlineData { mimeType, data }
            if (trimmed.startsWith('data:')) {
                const match = /^data:([^;,]+)?(?:;base64)?,(.*)$/s.exec(trimmed);
                if (match) {
                    mimeType = (match[1] || '').trim() || undefined;
                    data = match[2] || '';
                }
            }
            if (data && data.trim().length) {
                const inline = {
                    inlineData: {
                        data: data.trim()
                    }
                };
                if (mimeType && mimeType.length) {
                    inline.inlineData.mimeType = mimeType;
                }
                targetParts.push(inline);
            }
            else {
                // 非 data: URL 暂时作为文本 URL 传递，保持语义可见
                targetParts.push({ text: trimmed });
            }
            continue;
        }
        // 默认：回退为文本 JSON 表示，避免静默丢失内容
        try {
            const jsonText = JSON.stringify(record);
            if (jsonText.trim().length) {
                targetParts.push({ text: jsonText });
            }
        }
        catch {
            // ignore malformed block
        }
    }
}
function buildGeminiRequestFromChat(chat, metadata) {
    const contents = [];
    const emittedToolOutputs = new Set();
    const adapterContext = metadata?.context;
    const rawProviderId = adapterContext?.providerId;
    const entryEndpointRaw = adapterContext?.entryEndpoint;
    const entryEndpoint = typeof entryEndpointRaw === 'string' ? entryEndpointRaw.trim().toLowerCase() : '';
    const isAnthropicEntry = entryEndpoint === '/v1/messages';
    const normalizedProviderId = typeof rawProviderId === 'string' ? rawProviderId.toLowerCase() : '';
    const providerIdPrefix = normalizedProviderId.split('.')[0];
    const isAntigravityProvider = providerIdPrefix === 'antigravity';
    const isGeminiCliProvider = providerIdPrefix === 'gemini-cli';
    const requiresThoughtSignature = isAntigravityProvider || isGeminiCliProvider;
    const parameters = chat.parameters && typeof chat.parameters === 'object' ? chat.parameters : {};
    const responsesOrigin = isResponsesOrigin(chat);
    if (responsesOrigin) {
        for (const field of RESPONSES_DROPPED_PARAMETER_KEYS) {
            if (!Object.prototype.hasOwnProperty.call(parameters, field)) {
                continue;
            }
            appendDroppedFieldAudit(chat, {
                field,
                targetProtocol: 'gemini-chat',
                reason: 'unsupported_semantics_no_equivalent'
            });
        }
    }
    const isAntigravityClaudeThinking = providerIdPrefix === 'antigravity' &&
        typeof parameters.model === 'string' &&
        String(parameters.model).includes('claude-sonnet-4-5-thinking');
    const keepReasoning = Boolean(parameters.keep_thinking) ||
        Boolean(parameters.keep_reasoning);
    const stripReasoningTags = isAntigravityProvider &&
        typeof parameters.model === 'string' &&
        String(parameters.model).startsWith('claude-') &&
        !keepReasoning;
    // Cloud Code / Antigravity requires stable tool call IDs in context (maps to tool_use.id),
    // while standard Gemini endpoints do not require (and may reject) extra id fields.
    const includeToolCallIds = providerIdPrefix === 'antigravity';
    // Function calling protocol:
    // - For gemini-cli.* we allow structured tool loops when tools are present (Codex/agent clients),
    //   otherwise keep a conservative path (text-only tool transcripts).
    // - For antigravity.* and other Gemini backends, send tool schemas and emit functionCall/functionResponse parts
    //   so tool loops remain structured and recoverable.
    const allowFunctionCallingProtocol = providerIdPrefix !== 'gemini-cli' || (Array.isArray(chat.tools) && chat.tools.length > 0);
    const omitFunctionCallPartsForCli = !allowFunctionCallingProtocol;
    const semanticsNode = readGeminiSemantics(chat);
    const systemTextBlocksFromSemantics = readSystemTextBlocksFromSemantics(chat);
    let antigravityRequestType;
    // Gemini series alignment:
    // - Client/tool surface uses canonical name: "web_search"
    // - Gemini upstream receives a function tool name: "websearch" (no underscore)
    //   then ServerTool intercepts and executes the web_search route.
    const mapToolNameForGemini = (nameRaw) => {
        const name = typeof nameRaw === 'string' ? nameRaw.trim() : '';
        if (!name)
            return undefined;
        if (name === 'web_search' || name.startsWith('web_search_')) {
            return 'websearch';
        }
        return name;
    };
    const bridgeDefs = chat.tools && chat.tools.length ? mapChatToolsToBridge(chat.tools) : undefined;
    if (bridgeDefs && bridgeDefs.length) {
        for (const def of bridgeDefs) {
            if (!def || typeof def !== 'object')
                continue;
            const mapped = mapToolNameForGemini(def.name);
            if (mapped && mapped !== def.name) {
                def.name = mapped;
                if (def.function && typeof def.function === 'object') {
                    def.function.name = mapped;
                }
            }
        }
    }
    const toolSchemaKeys = bridgeDefs ? buildToolSchemaKeyMap(bridgeDefs) : new Map();
    const sourceMessages = chat.messages;
    // 收集当前 ChatEnvelope 中 assistant/tool_calls 的 id，用于过滤孤立的 tool_result：
    // 只有在本轮对话中存在对应 tool_call 的 tool_result 才允许映射为 Gemini functionResponse。
    const assistantToolCallIds = new Set();
    for (const msg of sourceMessages) {
        if (!msg || typeof msg !== 'object')
            continue;
        if (msg.role !== 'assistant')
            continue;
        const tcs = Array.isArray(msg.tool_calls)
            ? msg.tool_calls
            : [];
        for (const tc of tcs) {
            const id = typeof tc.id === 'string' ? tc.id.trim() : '';
            if (id) {
                assistantToolCallIds.add(id);
            }
        }
    }
    for (const message of sourceMessages) {
        if (!message || typeof message !== 'object')
            continue;
        if (message.role === 'system')
            continue;
        if (message.role === 'tool') {
            if (allowFunctionCallingProtocol) {
                const toolOutput = convertToolMessageToOutput(message, assistantToolCallIds);
                if (toolOutput) {
                    toolOutput.name = mapToolNameForGemini(toolOutput.name);
                    contents.push(buildFunctionResponseEntry(toolOutput, { includeCallId: includeToolCallIds }));
                    emittedToolOutputs.add(toolOutput.tool_call_id);
                }
            }
            else {
                const name = typeof message.name === 'string' ? String(message.name).trim() : 'tool';
                const contentText = normalizeToolContent(message.content);
                contents.push({
                    role: 'user',
                    parts: [{ text: `[tool:${name}] ${contentText}` }]
                });
            }
            continue;
        }
        const entry = {
            role: mapChatRoleToGemini(message.role),
            parts: []
        };
        appendChatContentToGeminiParts(message, entry.parts, { stripReasoningTags });
        const toolCalls = Array.isArray(message.tool_calls)
            ? message.tool_calls
            : [];
        for (const tc of toolCalls) {
            if (!tc || typeof tc !== 'object')
                continue;
            if (omitFunctionCallPartsForCli) {
                continue;
            }
            const fn = tc.function || {};
            const name = mapToolNameForGemini(typeof fn.name === 'string' ? fn.name : undefined);
            if (!name)
                continue;
            let argsStruct;
            if (typeof fn.arguments === 'string') {
                try {
                    argsStruct = JSON.parse(fn.arguments);
                }
                catch {
                    argsStruct = { _raw: fn.arguments };
                }
            }
            else {
                argsStruct = fn.arguments ?? {};
            }
            argsStruct = alignToolCallArgsToSchema({ toolName: name, args: argsStruct, schemaKeys: toolSchemaKeys });
            let argsJson = cloneAsJsonValue(argsStruct);
            // Gemini / Antigravity 期望 functionCall.args 为对象（Struct），
            // 若顶层为数组或原始类型，则包装到 value 字段下，避免产生非法的 list 形状。
            if (!argsJson || typeof argsJson !== 'object' || Array.isArray(argsJson)) {
                argsJson = { value: argsJson };
            }
            const functionCall = { name, args: argsJson };
            const part = { functionCall };
            if (includeToolCallIds && typeof tc.id === 'string' && tc.id.trim().length) {
                part.functionCall.id = sanitizeAntigravityToolCallId(String(tc.id));
            }
            // Antigravity-Manager alignment:
            // - Do NOT invent a dummy thoughtSignature in conversion.
            // - Only inject a real thoughtSignature when the compat layer has a cached signature.
            // This avoids sending a placeholder that may be treated as an invalid fingerprint upstream.
            entry.parts.push(part);
        }
        if (entry.parts.length) {
            contents.push(entry);
        }
    }
    const toolOutputMap = new Map();
    if (allowFunctionCallingProtocol) {
        if (Array.isArray(chat.toolOutputs)) {
            for (const entry of chat.toolOutputs) {
                if (entry && typeof entry.tool_call_id === 'string' && entry.tool_call_id.trim().length) {
                    toolOutputMap.set(entry.tool_call_id.trim(), entry);
                }
            }
        }
        if (toolOutputMap.size === 0) {
            const syntheticOutputs = synthesizeToolOutputsFromMessages(chat.messages);
            for (const output of syntheticOutputs) {
                toolOutputMap.set(output.tool_call_id, output);
            }
        }
        for (const output of toolOutputMap.values()) {
            if (emittedToolOutputs.has(output.tool_call_id)) {
                continue;
            }
            output.name = mapToolNameForGemini(output.name);
            contents.push(buildFunctionResponseEntry(output, { includeCallId: includeToolCallIds }));
            emittedToolOutputs.add(output.tool_call_id);
        }
    }
    const request = {
        model: chat.parameters?.model || 'models/gemini-pro',
        contents
    };
    const geminiState = getProtocolState(metadata, 'gemini');
    if (!isAntigravityProvider && semanticsNode?.systemInstruction !== undefined) {
        request.systemInstruction = jsonClone(semanticsNode.systemInstruction);
    }
    else if (!isAntigravityProvider && geminiState?.systemInstruction !== undefined) {
        request.systemInstruction = jsonClone(geminiState.systemInstruction);
    }
    else if (!isAntigravityProvider) {
        const fallbackSystemInstructions = systemTextBlocksFromSemantics;
        if (fallbackSystemInstructions && fallbackSystemInstructions.length) {
            const sysBlocks = fallbackSystemInstructions
                .filter((value) => typeof value === 'string' && value.trim().length > 0)
                .map((value) => ({ text: value }));
            if (sysBlocks.length) {
                request.systemInstruction = { role: 'system', parts: sysBlocks };
            }
        }
    }
    if (isAntigravityProvider) {
        const extraSegments = [];
        const seen = new Set();
        const pushSegment = (value) => {
            const trimmed = typeof value === 'string' ? value.trim() : '';
            if (!trimmed)
                return;
            if (seen.has(trimmed))
                return;
            seen.add(trimmed);
            extraSegments.push(trimmed);
        };
        for (const seg of collectSystemSegments(semanticsNode?.systemInstruction)) {
            pushSegment(seg);
        }
        for (const seg of collectSystemSegments(geminiState?.systemInstruction)) {
            pushSegment(seg);
        }
        for (const seg of systemTextBlocksFromSemantics || []) {
            if (typeof seg === 'string') {
                pushSegment(seg);
            }
        }
        // Antigravity requires role="user" and a fixed system instruction prefix.
        // Provider layer must NOT rewrite systemInstruction; this is a semantic mapper concern.
        if (extraSegments.length > 0) {
            const [first, ...rest] = extraSegments;
            request.systemInstruction = {
                role: 'user',
                parts: [{ text: `${ANTIGRAVITY_SYSTEM_INSTRUCTION}\n\n${first}` }, ...rest.map((text) => ({ text }))]
            };
        }
        else {
            request.systemInstruction = {
                role: 'user',
                parts: [{ text: ANTIGRAVITY_SYSTEM_INSTRUCTION }]
            };
        }
    }
    if (allowFunctionCallingProtocol && chat.tools && chat.tools.length) {
        const geminiTools = buildGeminiToolsFromBridge(bridgeDefs, {
            mode: isAntigravityProvider ? 'antigravity' : 'default'
        });
        if (geminiTools) {
            request.tools = geminiTools;
        }
    }
    const generationConfig = buildGenerationConfigFromParameters(chat.parameters || {});
    if (responsesOrigin && Object.prototype.hasOwnProperty.call(parameters, 'reasoning')) {
        appendLossyFieldAudit(chat, {
            field: 'reasoning',
            targetProtocol: 'gemini-chat',
            reason: 'normalized_to_gemini_thinking_config'
        });
    }
    if (semanticsNode?.generationConfig && isJsonObject(semanticsNode.generationConfig)) {
        for (const [key, value] of Object.entries(semanticsNode.generationConfig)) {
            if (generationConfig[key] !== undefined) {
                continue;
            }
            generationConfig[key] = jsonClone(value);
        }
    }
    if (Object.keys(generationConfig).length) {
        request.generationConfig = generationConfig;
    }
    if (!isAntigravityProvider && semanticsNode?.safetySettings !== undefined) {
        request.safetySettings = jsonClone(semanticsNode.safetySettings);
    }
    else if (isAntigravityProvider) {
        // gcli2api alignment: Antigravity always sends a permissive safetySettings set.
        request.safetySettings = jsonClone(ANTIGRAVITY_DEFAULT_SAFETY_SETTINGS);
    }
    if (isAntigravityProvider && isJsonObject(request.generationConfig)) {
        // gcli2api alignment: when generationConfig is present, clamp the key parameters.
        request.generationConfig.maxOutputTokens = 64000;
        request.generationConfig.topK = 64;
    }
    if (isAntigravityProvider && typeof request.model === 'string') {
        const requestPayload = request;
        const original = requestPayload.model;
        // Antigravity v1internal model IDs are tiered (e.g. gemini-3-pro-high/low) and must be preserved.
        // Align with fetchAvailableModels: do NOT strip "-high"/"-low" suffixes for upstream requests.
        const mapped = stripOnlineSuffix(original);
        const size = typeof chat.parameters?.size === 'string' ? String(chat.parameters.size) : undefined;
        const quality = typeof chat.parameters?.quality === 'string' ? String(chat.parameters.quality) : undefined;
        const config = resolveAntigravityRequestConfig({
            originalModel: original,
            mappedModel: mapped,
            tools: requestPayload.tools,
            size,
            quality
        });
        antigravityRequestType = config.requestType;
        requestPayload.requestType = config.requestType;
        requestPayload.model = config.finalModel || mapped;
        pruneSearchFunctionDeclarations(requestPayload);
        if (config.requestType === 'image_gen') {
            delete requestPayload.tools;
            delete requestPayload.systemInstruction;
            if (!isJsonObject(requestPayload.generationConfig)) {
                requestPayload.generationConfig = {};
            }
            const gen = requestPayload.generationConfig;
            delete gen.thinkingConfig;
            delete gen.responseMimeType;
            delete gen.responseModalities;
            if (config.imageConfig) {
                gen.imageConfig = config.imageConfig;
            }
        }
        else if (config.injectGoogleSearch) {
            injectGoogleSearchTool(requestPayload);
        }
        deepCleanUndefined(requestPayload);
        const mappedLower = String(requestPayload.model || '').toLowerCase();
        const isFlashModel = mappedLower.includes('flash');
        const isImageModel = config.requestType === 'image_gen' || mappedLower.includes('image');
        const isThinkingModel = !isImageModel && (mappedLower.includes('think') || mappedLower.includes('pro'));
        if (isThinkingModel && (!requestPayload.generationConfig || !isJsonObject(requestPayload.generationConfig))) {
            requestPayload.generationConfig = {};
        }
        const generationConfig = requestPayload.generationConfig;
        if (isFlashModel && isJsonObject(generationConfig)) {
            // Antigravity-Manager alignment: Gemini Flash models reject overly-large thinking budgets.
            // Clamp proactively to avoid 400 Bad Request when clients set an aggressive budget.
            const MAX_FLASH_THINKING_BUDGET = 24576;
            const gc = generationConfig;
            const thinkingConfigRaw = gc.thinkingConfig;
            const thinkingConfig = isJsonObject(thinkingConfigRaw) ? thinkingConfigRaw : undefined;
            const budgetRaw = thinkingConfig && thinkingConfig.thinkingBudget;
            const budget = typeof budgetRaw === 'number' && Number.isFinite(budgetRaw) ? budgetRaw : undefined;
            if (thinkingConfig && budget !== undefined && budget > MAX_FLASH_THINKING_BUDGET) {
                thinkingConfig.thinkingBudget = MAX_FLASH_THINKING_BUDGET;
                gc.thinkingConfig = thinkingConfig;
            }
        }
        if (isThinkingModel && isJsonObject(generationConfig)) {
            const gc = generationConfig;
            const thinkingConfig = isJsonObject(gc.thinkingConfig)
                ? gc.thinkingConfig
                : {};
            const existingBudget = typeof thinkingConfig.thinkingBudget === 'number'
                ? thinkingConfig.thinkingBudget
                : undefined;
            const shouldApply = existingBudget !== undefined ? existingBudget !== 0 : true;
            if (shouldApply) {
                if (typeof thinkingConfig.thinkingBudget !== 'number') {
                    thinkingConfig.thinkingBudget = 1024;
                }
                if (Object.prototype.hasOwnProperty.call(thinkingConfig, 'thinkingLevel')) {
                    delete thinkingConfig.thinkingLevel;
                }
                thinkingConfig.includeThoughts = true;
                // For Claude routed via Antigravity:
                // - when tool calls exist, gcli2api drops thinkingConfig to avoid upstream failures
                // - otherwise, ensure the last model message begins with a thinking block signature
                const isClaude = mappedLower.includes('claude');
                if (isClaude) {
                    const contentsArray = Array.isArray(request.contents) ? request.contents : [];
                    const hasToolCalls = contentsArray.some((content) => {
                        if (!isJsonObject(content))
                            return false;
                        const parts = content.parts;
                        if (!Array.isArray(parts))
                            return false;
                        return parts.some((part) => isJsonObject(part) &&
                            ('functionCall' in part || 'function_call' in part));
                    });
                    if (hasToolCalls) {
                        delete gc.thinkingConfig;
                    }
                    else {
                        for (let idx = contentsArray.length - 1; idx >= 0; idx -= 1) {
                            const content = contentsArray[idx];
                            if (!isJsonObject(content))
                                continue;
                            if (content.role !== 'model')
                                continue;
                            const parts = content.parts;
                            if (!Array.isArray(parts))
                                continue;
                            const first = parts[0];
                            const firstIsThinking = isJsonObject(first) && ('thought' in first || 'thoughtSignature' in first);
                            // Antigravity-Manager alignment: do not inject placeholder thoughtSignature blocks.
                            // If the upstream requires a signature, it must come from cached candidate parts.
                            break;
                        }
                        gc.thinkingConfig = thinkingConfig;
                    }
                }
                else {
                    gc.thinkingConfig = thinkingConfig;
                }
            }
        }
    }
    if (chat.parameters?.tool_config && isJsonObject(chat.parameters.tool_config)) {
        request.toolConfig = jsonClone(chat.parameters.tool_config);
    }
    else if (semanticsNode?.toolConfig && isJsonObject(semanticsNode.toolConfig)) {
        request.toolConfig = jsonClone(semanticsNode.toolConfig);
    }
    // 为了保持协议解耦，只在 Gemini 自身或开放式 Chat 入口下透传 providerMetadata；
    // 对于 Anthropic (/v1/messages) 等其它协议的入口，不再将其 metadata 整块转发给 Gemini，
    // 避免跨协议泄漏上游专有字段。
    if (!isAnthropicEntry) {
        if (semanticsNode?.providerMetadata && isJsonObject(semanticsNode.providerMetadata)) {
            request.metadata = jsonClone(semanticsNode.providerMetadata);
        }
        else if (metadata?.providerMetadata && isJsonObject(metadata.providerMetadata)) {
            request.metadata = jsonClone(metadata.providerMetadata);
        }
    }
    if (chat.parameters && chat.parameters.stream !== undefined) {
        request.metadata = request.metadata ?? {};
        request.metadata.__rcc_stream = chat.parameters.stream;
    }
    if (hasExplicitEmptyToolsSemantics(chat) &&
        (!Array.isArray(chat.tools) || chat.tools.length === 0)) {
        request.metadata = request.metadata ?? {};
        request.metadata.__rcc_tools_field_present = '1';
    }
    const passthrough = encodeMetadataPassthrough(chat.parameters, {
        prefix: PASSTHROUGH_METADATA_PREFIX,
        keys: PASSTHROUGH_PARAMETERS
    });
    if (passthrough) {
        request.metadata = request.metadata ?? {};
        for (const [key, value] of Object.entries(passthrough)) {
            request.metadata[key] = value;
        }
    }
    if (isAntigravityProvider) {
        request.metadata = request.metadata ?? {};
        const existing = request.metadata.antigravitySessionId;
        if (typeof existing !== 'string' || !existing.trim()) {
            request.metadata.antigravitySessionId = extractAntigravityGeminiSessionIdWithNative(request);
        }
    }
    // Apply claude-thinking compat at Gemini mapping time to ensure it is always active
    // for Claude models, regardless of compatibilityProfile wiring. Provider层负责进一步的
    // 传输层收紧（如 session_id / generationConfig），这里不做非标裁剪。
    const compatRequest = applyClaudeThinkingToolSchemaCompatWithNative(request);
    return compatRequest;
}
function sanitizeAntigravityToolCallId(raw) {
    const trimmed = typeof raw === 'string' ? raw.trim() : '';
    if (!trimmed) {
        return trimmed;
    }
    // Antigravity (Claude via Gemini) validates tool_use.id against: ^[a-zA-Z0-9_-]+$
    // Preserve stable IDs when already valid; otherwise sanitize minimally.
    if (/^[A-Za-z0-9_-]+$/.test(trimmed)) {
        return trimmed;
    }
    const sanitized = trimmed
        .replace(/[^A-Za-z0-9_-]/g, '_')
        .replace(/_{2,}/g, '_')
        .replace(/^_+/, '')
        .replace(/_+$/, '');
    return sanitized || `call_${Math.random().toString(36).slice(2, 10)}`;
}
function isPlainRecord(value) {
    return !!value && typeof value === 'object' && !Array.isArray(value);
}
function buildToolSchemaKeyMap(defs) {
    const map = new Map();
    for (const def of defs) {
        const fnNode = def && typeof def === 'object' && def.function && typeof def.function === 'object'
            ? def.function
            : undefined;
        const name = typeof fnNode?.name === 'string'
            ? fnNode.name
            : typeof def?.name === 'string'
                ? String(def.name)
                : '';
        if (!name || !name.trim())
            continue;
        const parameters = (fnNode && fnNode.parameters) ??
            (def.parameters);
        if (!isPlainRecord(parameters))
            continue;
        const props = parameters.properties;
        if (!isPlainRecord(props))
            continue;
        const keys = Object.keys(props).filter((k) => typeof k === 'string' && k.trim().length > 0);
        if (!keys.length)
            continue;
        map.set(name, new Set(keys));
    }
    return map;
}
function alignToolCallArgsToSchema(options) {
    const name = typeof options.toolName === 'string' ? options.toolName.trim() : '';
    if (!name)
        return options.args;
    const schema = options.schemaKeys.get(name);
    if (!schema || schema.size === 0) {
        return options.args;
    }
    if (!isPlainRecord(options.args)) {
        return options.args;
    }
    const lowered = name.toLowerCase();
    const next = { ...options.args };
    // Align historical Codex tool args to the *declared schema* for Gemini.
    // Gemini validates historical functionCall.args against tool declarations, so mismatches like:
    // - exec_command: { cmd } vs schema { command } (or vice-versa)
    // - apply_patch: { patch/input } vs schema { instructions } (or vice-versa)
    // can cause MALFORMED_FUNCTION_CALL and empty responses.
    if (lowered === 'exec_command') {
        // Prefer the declared schema key; do not delete keys blindly.
        if (schema.has('cmd') && !Object.prototype.hasOwnProperty.call(next, 'cmd') && Object.prototype.hasOwnProperty.call(next, 'command')) {
            next.cmd = next.command;
        }
        if (schema.has('command') && !Object.prototype.hasOwnProperty.call(next, 'command') && Object.prototype.hasOwnProperty.call(next, 'cmd')) {
            next.command = next.cmd;
        }
    }
    else if (lowered === 'write_stdin') {
        if (schema.has('chars') && !Object.prototype.hasOwnProperty.call(next, 'chars') && Object.prototype.hasOwnProperty.call(next, 'text')) {
            next.chars = next.text;
        }
        if (schema.has('text') && !Object.prototype.hasOwnProperty.call(next, 'text') && Object.prototype.hasOwnProperty.call(next, 'chars')) {
            next.text = next.chars;
        }
    }
    else if (lowered === 'apply_patch') {
        if (schema.has('instructions') && !Object.prototype.hasOwnProperty.call(next, 'instructions')) {
            const patch = typeof next.patch === 'string' ? next.patch : undefined;
            const input = typeof next.input === 'string' ? next.input : undefined;
            const candidate = patch && patch.trim().length ? patch : input && input.trim().length ? input : undefined;
            if (candidate) {
                next.instructions = candidate;
            }
        }
        if (schema.has('patch') && !Object.prototype.hasOwnProperty.call(next, 'patch')) {
            const input = typeof next.input === 'string' ? next.input : undefined;
            if (input && input.trim().length) {
                next.patch = input;
            }
        }
    }
    // Prune to schema keys for known Codex tools to reduce strict upstream validation failures.
    if (lowered === 'exec_command' || lowered === 'write_stdin' || lowered === 'apply_patch') {
        const pruned = {};
        for (const key of schema) {
            if (Object.prototype.hasOwnProperty.call(next, key)) {
                pruned[key] = next[key];
            }
        }
        return pruned;
    }
    return next;
}
function buildGenerationConfigFromParameters(parameters) {
    const config = {};
    for (const { source, target } of GENERATION_CONFIG_KEYS) {
        const value = parameters[target] ?? (target === 'max_output_tokens' ? parameters.max_tokens : undefined);
        if (value !== undefined) {
            config[source] = value;
        }
    }
    const reasoningRaw = parameters.reasoning;
    const applyThinkingDisabled = () => {
        config.thinkingConfig = {
            includeThoughts: false,
            thinkingBudget: 0
        };
    };
    const applyThinkingEnabled = (budget) => {
        const next = {
            includeThoughts: true
        };
        if (typeof budget === 'number' && Number.isFinite(budget) && budget > 0) {
            next.thinkingBudget = Math.floor(budget);
        }
        config.thinkingConfig = next;
    };
    if (typeof reasoningRaw === 'boolean') {
        if (reasoningRaw) {
            applyThinkingEnabled();
        }
        else {
            applyThinkingDisabled();
        }
    }
    else if (typeof reasoningRaw === 'string') {
        const normalized = reasoningRaw.trim().toLowerCase();
        if (normalized === 'off' || normalized === 'none' || normalized === 'disabled' || normalized === 'false') {
            applyThinkingDisabled();
        }
        else if (normalized.length) {
            const effortBudget = {
                minimal: 1024,
                low: 1024,
                medium: 4096,
                high: 8192
            };
            applyThinkingEnabled(effortBudget[normalized]);
        }
    }
    else if (typeof reasoningRaw === 'number' && Number.isFinite(reasoningRaw)) {
        if (reasoningRaw <= 0) {
            applyThinkingDisabled();
        }
        else {
            applyThinkingEnabled(reasoningRaw);
        }
    }
    else if (isJsonObject(reasoningRaw)) {
        const node = reasoningRaw;
        const enabled = node.enabled;
        if (enabled === false) {
            applyThinkingDisabled();
        }
        else {
            const effort = typeof node.effort === 'string'
                ? node.effort.trim().toLowerCase()
                : typeof node.level === 'string'
                    ? node.level.trim().toLowerCase()
                    : '';
            const budget = typeof node.budget_tokens === 'number'
                ? node.budget_tokens
                : typeof node.budget === 'number'
                    ? node.budget
                    : typeof node.max_tokens === 'number'
                        ? node.max_tokens
                        : undefined;
            if (typeof budget === 'number' && Number.isFinite(budget)) {
                if (budget <= 0) {
                    applyThinkingDisabled();
                }
                else {
                    applyThinkingEnabled(budget);
                }
            }
            else if (effort === 'off' || effort === 'none' || effort === 'disabled') {
                applyThinkingDisabled();
            }
            else if (effort.length) {
                const effortBudget = {
                    minimal: 1024,
                    low: 1024,
                    medium: 4096,
                    high: 8192
                };
                applyThinkingEnabled(effortBudget[effort]);
            }
            else if (enabled === true) {
                applyThinkingEnabled();
            }
        }
    }
    return config;
}
function mapChatRoleToGemini(role) {
    const r = role.toLowerCase();
    if (r === 'assistant')
        return 'model';
    if (r === 'system')
        return 'system';
    if (r === 'tool')
        return 'tool';
    return 'user';
}
function safeParseJson(value) {
    try {
        return JSON.parse(value);
    }
    catch {
        return value;
    }
}
function ensureFunctionResponsePayload(value) {
    // Gemini function_response.response 字段在 CloudCode/Gemini CLI 协议里对应的是
    // protobuf Struct（JSON object），而不是顶层数组。
    // 这里做一层规范化：
    // - 对象：直接透传；
    // - 数组：包一层 { result: [...] } 避免把数组作为 Struct 根节点；
    // - 原始值：包一层 { result: value }，并把 undefined 映射为 null。
    if (value && typeof value === 'object') {
        if (Array.isArray(value)) {
            return {
                result: value
            };
        }
        return value;
    }
    return {
        result: value === undefined ? null : value
    };
}
function cloneAsJsonValue(value) {
    try {
        return JSON.parse(JSON.stringify(value ?? null));
    }
    catch {
        if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || value === null) {
            return value;
        }
        if (Array.isArray(value)) {
            return value.map((entry) => cloneAsJsonValue(entry));
        }
        if (value && typeof value === 'object') {
            const out = {};
            for (const [key, entry] of Object.entries(value)) {
                out[key] = cloneAsJsonValue(entry);
            }
            return out;
        }
        return String(value ?? '');
    }
}
function isResponsesOrigin(chat) {
    const semantics = chat?.semantics;
    if (semantics && semantics.responses && isJsonObject(semantics.responses)) {
        return true;
    }
    const ctx = chat?.metadata && typeof chat.metadata === 'object'
        ? chat.metadata.context
        : undefined;
    const protocol = typeof ctx?.providerProtocol === 'string' ? ctx.providerProtocol.trim().toLowerCase() : '';
    if (protocol === 'openai-responses') {
        return true;
    }
    const endpoint = typeof ctx?.entryEndpoint === 'string' ? ctx.entryEndpoint.trim().toLowerCase() : '';
    return endpoint === '/v1/responses';
}
function appendMappingAudit(chat, options) {
    const metadata = chat.metadata && typeof chat.metadata === 'object'
        ? chat.metadata
        : (chat.metadata = { context: chat.metadata?.context ?? {} });
    const root = metadata.mappingAudit && typeof metadata.mappingAudit === 'object' && !Array.isArray(metadata.mappingAudit)
        ? metadata.mappingAudit
        : (metadata.mappingAudit = {});
    const current = Array.isArray(root[options.bucket]) ? root[options.bucket] : [];
    const duplicate = current.find((entry) => entry &&
        entry.field === options.field &&
        entry.targetProtocol === options.targetProtocol &&
        entry.reason === options.reason);
    if (!duplicate) {
        current.push({
            field: options.field,
            source: options.source ?? 'chat.parameters',
            targetProtocol: options.targetProtocol,
            reason: options.reason
        });
    }
    root[options.bucket] = current;
}
function appendDroppedFieldAudit(chat, options) {
    appendMappingAudit(chat, {
        bucket: 'dropped',
        ...options
    });
}
function appendLossyFieldAudit(chat, options) {
    appendMappingAudit(chat, {
        bucket: 'lossy',
        ...options
    });
}
export class GeminiSemanticMapper {
    async toChat(format, ctx) {
        const payload = (format.payload ?? {});
        const missing = [];
        const { messages: builtMessages } = buildOpenAIChatFromGeminiRequest(payload);
        let messages = Array.isArray(builtMessages) ? builtMessages : [];
        if (!Array.isArray(payload.contents)) {
            missing.push({ path: 'contents', reason: 'absent' });
        }
        const bridgeTools = prepareGeminiToolsForBridge(payload.tools, missing);
        const tools = bridgeTools ? mapBridgeToolsToChat(bridgeTools) : undefined;
        let parameters = collectParameters(payload);
        const metadata = { context: ctx };
        const systemSegments = collectSystemSegments(payload.systemInstruction);
        if (payload.systemInstruction !== undefined) {
            const rawSystem = jsonClone(payload.systemInstruction);
            ensureProtocolState(metadata, 'gemini').systemInstruction = rawSystem;
        }
        if (missing.length) {
            metadata.missingFields = missing;
        }
        const toolOutputs = normalizeToolOutputs(messages, missing);
        const passthrough = extractMetadataPassthrough(payload.metadata, {
            prefix: PASSTHROUGH_METADATA_PREFIX,
            keys: PASSTHROUGH_PARAMETERS
        });
        if (passthrough.passthrough) {
            parameters = { ...(parameters || {}), ...passthrough.passthrough };
        }
        const providerMetadataSource = passthrough.metadata ?? payload.metadata;
        let providerMetadata;
        let explicitEmptyTools = Array.isArray(payload.tools) && payload.tools.length === 0;
        if (providerMetadataSource) {
            const cloned = jsonClone(providerMetadataSource);
            let toolsFieldPresent = false;
            if (isJsonObject(cloned)) {
                delete cloned.__rcc_stream;
                if (Object.prototype.hasOwnProperty.call(cloned, '__rcc_tools_field_present')) {
                    const sentinel = cloned.__rcc_tools_field_present;
                    toolsFieldPresent = sentinel === '1' || sentinel === true;
                    delete cloned.__rcc_tools_field_present;
                }
                if (Object.prototype.hasOwnProperty.call(cloned, '__rcc_raw_system')) {
                    delete cloned.__rcc_raw_system;
                }
            }
            if (toolsFieldPresent) {
                explicitEmptyTools = true;
            }
            providerMetadata = cloned;
            metadata.providerMetadata = providerMetadata;
        }
        const chatEnvelope = {
            messages,
            tools,
            toolOutputs,
            parameters,
            metadata
        };
        if (systemSegments.length) {
            const systemNode = ensureSystemSemantics(chatEnvelope);
            systemNode.textBlocks = systemSegments.map((segment) => segment);
        }
        let semanticsNode;
        const ensureSemanticsNode = () => {
            semanticsNode = semanticsNode ?? ensureGeminiSemanticsNode(chatEnvelope);
            return semanticsNode;
        };
        if (payload.systemInstruction !== undefined) {
            ensureSemanticsNode().systemInstruction = jsonClone(payload.systemInstruction);
        }
        if (payload.safetySettings) {
            ensureSemanticsNode().safetySettings = jsonClone(payload.safetySettings);
        }
        if (payload.generationConfig && isJsonObject(payload.generationConfig)) {
            ensureSemanticsNode().generationConfig = jsonClone(payload.generationConfig);
        }
        if (payload.toolConfig && isJsonObject(payload.toolConfig)) {
            ensureSemanticsNode().toolConfig = jsonClone(payload.toolConfig);
        }
        if (providerMetadata) {
            ensureSemanticsNode().providerMetadata = jsonClone(providerMetadata);
        }
        if (explicitEmptyTools) {
            markGeminiExplicitEmptyTools(chatEnvelope);
        }
        return chatEnvelope;
    }
    async fromChat(chat, ctx) {
        const envelopePayload = buildGeminiRequestFromChat(chat, chat.metadata);
        return {
            protocol: 'gemini-chat',
            direction: 'response',
            payload: envelopePayload,
            meta: {
                context: ctx
            }
        };
    }
}
