import { streamSimpleAnthropic } from '../ai/providers/anthropic.js';
import { createShortcutXLModelCallError, ShortcutXLModelCallErrorCategory, ShortcutXLModelCallErrorEnvelope, ShortcutXLModelCallTransportKind, ShortcutXLProviderUsageStatus } from '../contracts/model-call.js';
import { ShortcutXLAgentAssistantContentKind, ShortcutXLModelStreamEventKind, ShortcutXLModelStreamMessageRole, ShortcutXLModelStreamStopReason } from '../contracts/model-stream.js';
import { aiUserContentToAnthropicContent } from './ai-content.js';
function mapHttpStatusToErrorCategory(status) {
    if (status === 401 || status === 403)
        return ShortcutXLModelCallErrorCategory.Auth;
    if (status === 404)
        return ShortcutXLModelCallErrorCategory.Configuration;
    if (status === 408 || status === 409 || status >= 500) {
        return ShortcutXLModelCallErrorCategory.TransientNetwork;
    }
    if (status === 429)
        return ShortcutXLModelCallErrorCategory.RateLimited;
    return ShortcutXLModelCallErrorCategory.ProviderRejected;
}
async function throwAnthropicHttpError(response, operation) {
    const body = await response.text().catch(() => '');
    let providerErrorType;
    let providerMessage;
    try {
        const parsed = body
            ? JSON.parse(body)
            : {};
        providerErrorType = typeof parsed.error?.type === 'string' ? parsed.error.type : undefined;
        providerMessage = typeof parsed.error?.message === 'string' ? parsed.error.message : undefined;
    }
    catch {
        providerMessage = body || undefined;
    }
    throw new ShortcutXLModelCallErrorEnvelope(createShortcutXLModelCallError(mapHttpStatusToErrorCategory(response.status), providerMessage ?? `Anthropic Messages ${operation} failed: HTTP ${response.status}`, {
        providerErrorType,
        retryable: response.status === 429 || response.status === 408 || response.status >= 500,
        retryAfterMs: parseRetryAfterMs(response.headers.get('retry-after')),
        status: response.status
    }));
}
function parseRetryAfterMs(header) {
    if (!header)
        return undefined;
    const seconds = Number(header);
    if (Number.isFinite(seconds))
        return Math.max(0, seconds * 1000);
    const dateMs = Date.parse(header);
    if (!Number.isNaN(dateMs))
        return Math.max(0, dateMs - Date.now());
    return undefined;
}
function throwInvalidResponse(message) {
    throw new ShortcutXLModelCallErrorEnvelope(createShortcutXLModelCallError(ShortcutXLModelCallErrorCategory.InvalidResponse, message));
}
function throwProviderStreamError(error) {
    throw new ShortcutXLModelCallErrorEnvelope(createShortcutXLModelCallError(ShortcutXLModelCallErrorCategory.ProviderRejected, error?.message ?? 'Anthropic Messages stream error.', { providerErrorType: error?.type }));
}
function isAbortError(error) {
    return error instanceof DOMException && error.name === 'AbortError';
}
function normalizeFetchError(error, operation) {
    if (error instanceof ShortcutXLModelCallErrorEnvelope)
        throw error;
    if (isAbortError(error)) {
        throw new ShortcutXLModelCallErrorEnvelope(createShortcutXLModelCallError(ShortcutXLModelCallErrorCategory.Cancelled, `Anthropic Messages ${operation} was cancelled.`));
    }
    throw new ShortcutXLModelCallErrorEnvelope(createShortcutXLModelCallError(ShortcutXLModelCallErrorCategory.TransientNetwork, error instanceof Error
        ? error.message
        : `Anthropic Messages ${operation} failed before receiving a response.`, { retryable: true }));
}
const AnthropicRuntimeApi = 'anthropic-messages';
function normalizeBaseUrl(baseUrl) {
    return baseUrl.replace(/\/+$/, '');
}
function createAuthHeader(auth) {
    if (auth.headerName === 'Authorization') {
        return {
            Authorization: auth.token.startsWith('Bearer ') ? auth.token : `Bearer ${auth.token}`
        };
    }
    return { 'x-api-key': auth.token };
}
function normalizeAiAnthropicBaseUrl(baseUrl) {
    const normalized = normalizeBaseUrl(baseUrl);
    return normalized.endsWith('/v1') ? normalized.slice(0, -'/v1'.length) : normalized;
}
function buildGatewayAiModel(request, baseUrl) {
    return {
        api: 'anthropic-messages',
        baseUrl: normalizeAiAnthropicBaseUrl(baseUrl),
        contextWindow: request.model.contextWindow,
        cost: { cacheRead: 0, cacheWrite: 0, input: 0, output: 0 },
        id: request.model.id,
        input: request.model.input,
        maxTokens: request.model.maxTokens,
        name: request.model.name,
        provider: request.model.provider,
        reasoning: request.model.reasoning
    };
}
function buildGatewayAiStreamOptions(request, options) {
    const configuredHeaders = {
        ...request.options?.headers,
        ...options.headers
    };
    const apiKey = options.auth.token;
    if (options.auth.headerName === 'Authorization') {
        configuredHeaders.Authorization = options.auth.token.startsWith('Bearer ')
            ? options.auth.token
            : `Bearer ${options.auth.token}`;
        configuredHeaders['x-api-key'] = null;
    }
    return {
        apiKey,
        cacheRetention: request.options?.cacheRetention,
        fetchOptions: options.fetchOptions,
        headers: configuredHeaders,
        maxRetryDelayMs: request.options?.maxRetryDelayMs,
        maxTokens: request.options?.maxTokens,
        metadata: request.options?.metadata,
        reasoning: request.options?.reasoning,
        signal: options.signal,
        temperature: request.options?.temperature,
        transport: request.options?.transport
    };
}
function usesLegacyGatewayThinking(modelId) {
    const normalized = modelId.toLowerCase();
    return (normalized.startsWith('claude-3-') ||
        normalized.startsWith('claude-3.') ||
        normalized.startsWith('claude-opus-4-5') ||
        normalized.startsWith('claude-opus-4.5') ||
        normalized.startsWith('claude-sonnet-4-5') ||
        normalized.startsWith('claude-sonnet-4.5') ||
        normalized.startsWith('claude-sonnet-4-202') ||
        normalized.startsWith('claude-sonnet-4.202'));
}
function mapGatewayThinkingEffort(reasoning, modelId) {
    const normalized = modelId.toLowerCase();
    switch (reasoning) {
        case 'minimal':
        case 'low':
            return 'low';
        case 'medium':
            return 'medium';
        case 'xhigh':
            return normalized.includes('opus') ? 'max' : 'high';
        case 'high':
            return 'high';
    }
}
function withAdaptiveGatewayThinkingPayloadOverride(modelId, options) {
    if (!options.reasoning || usesLegacyGatewayThinking(modelId))
        return options;
    const effort = mapGatewayThinkingEffort(options.reasoning, modelId);
    const onPayload = options.onPayload;
    return {
        ...options,
        onPayload: (payload) => {
            if (payload && typeof payload === 'object') {
                Object.assign(payload, {
                    output_config: { effort },
                    thinking: { type: 'adaptive' }
                });
            }
            onPayload?.(payload);
        }
    };
}
function streamGatewayAiAnthropic(model, request, options) {
    return streamSimpleAnthropic(model, request.context, withAdaptiveGatewayThinkingPayloadOverride(model.id, options));
}
function buildUsage(usage) {
    const output = usage?.output_tokens ?? 0;
    const cacheRead = usage?.cache_read_input_tokens ?? 0;
    const cacheWrite = usage?.cache_creation_input_tokens ?? 0;
    const input = (usage?.input_tokens ?? 0) + cacheRead + cacheWrite;
    return {
        cacheRead,
        cacheWrite,
        cost: { cacheRead: 0, cacheWrite: 0, input: 0, output: 0, total: 0 },
        input,
        output,
        totalTokens: input + output
    };
}
function buildUsageFromAi(usage) {
    return {
        cacheRead: usage?.cacheRead ?? 0,
        cacheWrite: usage?.cacheWrite ?? 0,
        cost: { cacheRead: 0, cacheWrite: 0, input: 0, output: 0, total: 0 },
        input: usage?.input ?? 0,
        output: usage?.output ?? 0,
        totalTokens: usage?.totalTokens ?? 0
    };
}
function toProviderUsage(usage) {
    if (!usage)
        return undefined;
    const cacheRead = usage.cache_read_input_tokens ?? 0;
    const cacheWrite = usage.cache_creation_input_tokens ?? 0;
    const input = (usage.input_tokens ?? 0) + cacheRead + cacheWrite;
    return {
        cacheCreationInputTokens: usage.cache_creation_input_tokens,
        cacheReadInputTokens: usage.cache_read_input_tokens,
        inputTokens: input,
        outputTokens: usage.output_tokens,
        totalTokens: input + (usage.output_tokens ?? 0)
    };
}
export function toProviderUsageFromAi(usage) {
    if (!usage || usage.totalTokens === 0)
        return undefined;
    return {
        cacheCreationInputTokens: usage.cacheWrite,
        cacheReadInputTokens: usage.cacheRead,
        inputTokens: usage.input,
        outputTokens: usage.output,
        totalTokens: usage.totalTokens
    };
}
function cloneMessage(message) {
    return {
        ...message,
        content: message.content.map((content) => ({ ...content })),
        usage: {
            ...message.usage,
            cost: { ...message.usage.cost }
        }
    };
}
function toShortcutXLAssistantContent(content) {
    switch (content.type) {
        case 'text':
            return {
                text: content.text,
                type: ShortcutXLAgentAssistantContentKind.Text
            };
        case 'thinking':
            return {
                redacted: content.redacted ?? false,
                thinking: content.thinking,
                thinkingSignature: content.thinkingSignature ?? '',
                type: ShortcutXLAgentAssistantContentKind.Thinking
            };
        case 'toolCall':
            return {
                arguments: content.arguments,
                id: content.id,
                name: content.name,
                type: ShortcutXLAgentAssistantContentKind.ToolCall
            };
    }
}
function toShortcutXLStopReason(stopReason) {
    switch (stopReason) {
        case 'aborted':
            return ShortcutXLModelStreamStopReason.Aborted;
        case 'error':
            return ShortcutXLModelStreamStopReason.Error;
        case 'length':
            return ShortcutXLModelStreamStopReason.Length;
        case 'toolUse':
            return ShortcutXLModelStreamStopReason.ToolUse;
        case 'stop':
            return ShortcutXLModelStreamStopReason.Stop;
    }
}
function toShortcutXLDoneReason(stopReason) {
    switch (stopReason) {
        case 'length':
            return ShortcutXLModelStreamStopReason.Length;
        case 'toolUse':
            return ShortcutXLModelStreamStopReason.ToolUse;
        case 'stop':
            return ShortcutXLModelStreamStopReason.Stop;
    }
}
function toShortcutXLErrorReason(stopReason) {
    return stopReason === 'aborted'
        ? ShortcutXLModelStreamStopReason.Aborted
        : ShortcutXLModelStreamStopReason.Error;
}
function toShortcutXLAssistantMessage(message) {
    return {
        api: message.api,
        content: message.content.map((content) => toShortcutXLAssistantContent(content)),
        errorMessage: message.errorMessage,
        model: message.model,
        provider: message.provider,
        role: 'assistant',
        stopReason: toShortcutXLStopReason(message.stopReason),
        timestamp: message.timestamp,
        usage: buildUsageFromAi(message.usage)
    };
}
export function toShortcutXLModelStreamEvent(event) {
    const partial = 'partial' in event ? toShortcutXLAssistantMessage(event.partial) : undefined;
    switch (event.type) {
        case 'start':
            return {
                kind: ShortcutXLModelStreamEventKind.Start,
                partial: partial
            };
        case 'text_start':
            return {
                contentIndex: event.contentIndex,
                kind: ShortcutXLModelStreamEventKind.TextStart,
                partial: partial
            };
        case 'text_delta':
            return {
                contentIndex: event.contentIndex,
                delta: event.delta,
                kind: ShortcutXLModelStreamEventKind.TextDelta,
                partial: partial
            };
        case 'text_end':
            return {
                contentIndex: event.contentIndex,
                kind: ShortcutXLModelStreamEventKind.TextEnd,
                partial: partial
            };
        case 'thinking_start':
            return {
                contentIndex: event.contentIndex,
                kind: ShortcutXLModelStreamEventKind.ThinkingStart,
                partial: partial
            };
        case 'thinking_delta':
            return {
                contentIndex: event.contentIndex,
                delta: event.delta,
                kind: ShortcutXLModelStreamEventKind.ThinkingDelta,
                partial: partial
            };
        case 'thinking_end':
            return {
                content: event.content,
                contentIndex: event.contentIndex,
                kind: ShortcutXLModelStreamEventKind.ThinkingEnd,
                partial: partial
            };
        case 'toolcall_start':
            return {
                contentIndex: event.contentIndex,
                kind: ShortcutXLModelStreamEventKind.ToolCallStart,
                partial: partial
            };
        case 'toolcall_delta':
            return {
                contentIndex: event.contentIndex,
                delta: event.delta,
                kind: ShortcutXLModelStreamEventKind.ToolCallDelta,
                partial: partial
            };
        case 'toolcall_end':
            return {
                contentIndex: event.contentIndex,
                kind: ShortcutXLModelStreamEventKind.ToolCallEnd,
                partial: partial,
                toolCall: toShortcutXLAssistantContent(event.toolCall)
            };
        case 'done': {
            const message = toShortcutXLAssistantMessage(event.message);
            return {
                kind: ShortcutXLModelStreamEventKind.Done,
                message,
                partial: message,
                reason: toShortcutXLDoneReason(event.reason)
            };
        }
        case 'error': {
            const error = toShortcutXLAssistantMessage(event.error);
            return {
                error,
                kind: ShortcutXLModelStreamEventKind.Error,
                partial: error,
                reason: toShortcutXLErrorReason(event.reason)
            };
        }
    }
}
function normalizeStopReason(stopReason) {
    if (stopReason === 'tool_use')
        return ShortcutXLModelStreamStopReason.ToolUse;
    if (stopReason === 'max_tokens')
        return ShortcutXLModelStreamStopReason.Length;
    return ShortcutXLModelStreamStopReason.Stop;
}
function toAnthropicRole(role) {
    return role === ShortcutXLModelStreamMessageRole.Ai ? 'assistant' : 'user';
}
function buildMessages(messages) {
    const system = messages
        .filter((message) => message.role === ShortcutXLModelStreamMessageRole.System)
        .map((message) => message.content)
        .join('\n\n');
    const anthropicMessages = messages
        .filter((message) => message.role !== ShortcutXLModelStreamMessageRole.System)
        .map((message) => ({
        content: message.content,
        role: toAnthropicRole(message.role)
    }));
    return {
        messages: anthropicMessages,
        ...(system ? { system } : {})
    };
}
function buildRequestBody(call) {
    const messages = buildMessages(call.payload.messages);
    return {
        max_tokens: call.payload.maxTokens ?? 4096,
        ...messages,
        model: call.payload.model,
        stream: true,
        ...(call.payload.tools
            ? {
                tools: call.payload.tools.map((tool) => ({
                    description: tool.description,
                    input_schema: tool.parameters,
                    name: tool.name
                }))
            }
            : {})
    };
}
function parseToolArguments(inputJson) {
    if (!inputJson)
        return {};
    const parsed = JSON.parse(inputJson);
    return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
        ? parsed
        : {};
}
async function readSseEvents(response, onEvent) {
    if (!response.body)
        throwInvalidResponse('Anthropic Messages stream response had no body.');
    const decoder = new TextDecoder();
    const reader = response.body.getReader();
    let buffer = '';
    for (;;) {
        const { done, value } = await reader.read();
        if (done)
            break;
        buffer += decoder.decode(value, { stream: true });
        for (;;) {
            const boundary = buffer.indexOf('\n\n');
            if (boundary === -1)
                break;
            const frame = buffer.slice(0, boundary);
            buffer = buffer.slice(boundary + 2);
            const data = frame
                .split('\n')
                .find((line) => line.startsWith('data:'))
                ?.slice('data:'.length)
                .trim();
            if (!data || data === '[DONE]')
                continue;
            try {
                await onEvent(JSON.parse(data));
            }
            catch (error) {
                if (error instanceof ShortcutXLModelCallErrorEnvelope)
                    throw error;
                throwInvalidResponse(error instanceof SyntaxError
                    ? 'Anthropic Messages stream emitted malformed JSON.'
                    : error instanceof Error
                        ? error.message
                        : String(error));
            }
        }
    }
}
async function emitProviderUsage(logger, event) {
    await logger?.recordProviderUsage(event);
}
async function streamAnthropicMessagesResponse(options) {
    const blocks = new Map();
    let latestUsage;
    let stopReason;
    const emit = (event) => options.onEvent(event);
    emit({
        kind: ShortcutXLModelStreamEventKind.Start,
        partial: cloneMessage(options.message)
    });
    await readSseEvents(options.response, async (event) => {
        switch (event.type) {
            case 'message_start':
                latestUsage = { ...latestUsage, ...event.message?.usage };
                return;
            case 'content_block_start': {
                const block = event.content_block;
                if (!block)
                    return;
                if (block.type === 'text') {
                    blocks.set(event.index, {
                        inputJson: '',
                        signature: '',
                        text: block.text ?? '',
                        type: 'text'
                    });
                    options.message.content[event.index] = {
                        text: block.text ?? '',
                        type: ShortcutXLAgentAssistantContentKind.Text
                    };
                    emit({
                        contentIndex: event.index,
                        kind: ShortcutXLModelStreamEventKind.TextStart,
                        partial: cloneMessage(options.message)
                    });
                    return;
                }
                if (block.type === 'tool_use') {
                    blocks.set(event.index, {
                        id: block.id,
                        inputJson: '',
                        name: block.name,
                        signature: '',
                        text: '',
                        type: 'tool_use'
                    });
                    options.message.content[event.index] = {
                        arguments: {},
                        id: block.id ?? '',
                        name: block.name ?? '',
                        type: ShortcutXLAgentAssistantContentKind.ToolCall
                    };
                    emit({
                        contentIndex: event.index,
                        kind: ShortcutXLModelStreamEventKind.ToolCallStart,
                        partial: cloneMessage(options.message)
                    });
                    return;
                }
                if (block.type === 'thinking') {
                    blocks.set(event.index, {
                        inputJson: '',
                        signature: '',
                        text: block.thinking ?? '',
                        type: 'thinking'
                    });
                    options.message.content[event.index] = {
                        redacted: false,
                        thinking: block.thinking ?? '',
                        thinkingSignature: '',
                        type: ShortcutXLAgentAssistantContentKind.Thinking
                    };
                    emit({
                        contentIndex: event.index,
                        kind: ShortcutXLModelStreamEventKind.ThinkingStart,
                        partial: cloneMessage(options.message)
                    });
                }
                return;
            }
            case 'content_block_delta': {
                const block = blocks.get(event.index);
                if (!block || !event.delta)
                    return;
                if (event.delta.type === 'text_delta') {
                    const delta = event.delta.text ?? '';
                    block.text += delta;
                    options.message.content[event.index] = {
                        text: block.text,
                        type: ShortcutXLAgentAssistantContentKind.Text
                    };
                    emit({
                        contentIndex: event.index,
                        delta,
                        kind: ShortcutXLModelStreamEventKind.TextDelta,
                        partial: cloneMessage(options.message)
                    });
                    return;
                }
                if (event.delta.type === 'input_json_delta') {
                    const delta = event.delta.partial_json ?? '';
                    block.inputJson += delta;
                    emit({
                        contentIndex: event.index,
                        delta,
                        kind: ShortcutXLModelStreamEventKind.ToolCallDelta,
                        partial: cloneMessage(options.message)
                    });
                    return;
                }
                if (event.delta.type === 'thinking_delta') {
                    const delta = event.delta.thinking ?? '';
                    block.text += delta;
                    options.message.content[event.index] = {
                        redacted: false,
                        thinking: block.text,
                        thinkingSignature: block.signature,
                        type: ShortcutXLAgentAssistantContentKind.Thinking
                    };
                    emit({
                        contentIndex: event.index,
                        delta,
                        kind: ShortcutXLModelStreamEventKind.ThinkingDelta,
                        partial: cloneMessage(options.message)
                    });
                    return;
                }
                if (event.delta.type === 'signature_delta') {
                    block.signature += event.delta.signature ?? '';
                }
                return;
            }
            case 'content_block_stop': {
                const block = blocks.get(event.index);
                if (!block)
                    return;
                const content = options.message.content[event.index];
                if (block.type === 'text') {
                    emit({
                        contentIndex: event.index,
                        kind: ShortcutXLModelStreamEventKind.TextEnd,
                        partial: cloneMessage(options.message)
                    });
                    return;
                }
                if (block.type === 'tool_use') {
                    const toolCall = {
                        arguments: parseToolArguments(block.inputJson),
                        id: block.id ?? '',
                        name: block.name ?? '',
                        type: ShortcutXLAgentAssistantContentKind.ToolCall
                    };
                    options.message.content[event.index] = toolCall;
                    emit({
                        contentIndex: event.index,
                        kind: ShortcutXLModelStreamEventKind.ToolCallEnd,
                        partial: cloneMessage(options.message),
                        toolCall
                    });
                    return;
                }
                if (block.type === 'thinking' &&
                    content?.type === ShortcutXLAgentAssistantContentKind.Thinking) {
                    options.message.content[event.index] = { ...content, thinkingSignature: block.signature };
                    emit({
                        content: block.text,
                        contentIndex: event.index,
                        kind: ShortcutXLModelStreamEventKind.ThinkingEnd,
                        partial: cloneMessage(options.message)
                    });
                }
                return;
            }
            case 'message_delta':
                stopReason = event.delta?.stop_reason ?? stopReason;
                latestUsage = { ...latestUsage, ...event.usage };
                return;
            case 'message_stop':
                options.message.stopReason = normalizeStopReason(stopReason);
                options.message.usage = buildUsage(latestUsage);
                emit({
                    kind: ShortcutXLModelStreamEventKind.Done,
                    message: cloneMessage(options.message),
                    partial: cloneMessage(options.message),
                    reason: options.message.stopReason
                });
                await emitProviderUsage(options.usageStatusLogger, {
                    callId: options.callId,
                    model: options.model,
                    status: latestUsage
                        ? ShortcutXLProviderUsageStatus.UsageAvailable
                        : ShortcutXLProviderUsageStatus.Unavailable,
                    transport: ShortcutXLModelCallTransportKind.AnthropicMessages,
                    usage: toProviderUsage(latestUsage)
                });
                return;
            case 'error':
                options.message.stopReason = ShortcutXLModelStreamStopReason.Error;
                options.message.errorMessage = event.error?.message ?? 'Anthropic Messages stream error.';
                emit({
                    error: cloneMessage(options.message),
                    kind: ShortcutXLModelStreamEventKind.Error,
                    partial: cloneMessage(options.message),
                    reason: ShortcutXLModelStreamStopReason.Error
                });
                throwProviderStreamError(event.error);
        }
    });
}
function aiToolResultContentToAnthropic(content) {
    const hasMedia = content.some((block) => block.type === 'image' || block.type === 'document');
    if (!hasMedia) {
        return content
            .filter((block) => block.type === 'text')
            .map((block) => block.text)
            .join('\n');
    }
    const blocks = content.map((block) => {
        if (block.type === 'text') {
            return { text: block.text, type: 'text' };
        }
        if (block.type === 'document') {
            return {
                source: {
                    data: block.data,
                    media_type: block.mimeType,
                    type: 'base64'
                },
                type: 'document'
            };
        }
        return {
            source: {
                data: block.data,
                media_type: block.mimeType,
                type: 'base64'
            },
            type: 'image'
        };
    });
    if (!blocks.some((block) => block.type === 'text')) {
        blocks.unshift({ text: '(see attached media)', type: 'text' });
    }
    return blocks.length > 0 ? blocks : [{ text: '', type: 'text' }];
}
function aiAssistantContentToAnthropic(content) {
    return content
        .map((block) => {
        if (block.type === 'text') {
            const text = block.text;
            return text.trim().length > 0 ? { text, type: 'text' } : undefined;
        }
        if (block.type === 'toolCall') {
            const toolCall = block;
            return {
                id: toolCall.id,
                input: toolCall.arguments,
                name: toolCall.name,
                type: 'tool_use'
            };
        }
        return undefined;
    })
        .filter((block) => block !== undefined);
}
function getAnthropicCacheControl(baseUrl, cacheRetention) {
    const retention = cacheRetention ??
        (typeof process !== 'undefined' && process.env.PI_CACHE_RETENTION === 'long'
            ? 'long'
            : 'short');
    if (retention === 'none')
        return undefined;
    return {
        type: 'ephemeral',
        ...(retention === 'long' && normalizeBaseUrl(baseUrl).includes('api.anthropic.com')
            ? { ttl: '1h' }
            : {})
    };
}
function buildInvokeRequestBody(request, baseUrl) {
    const messages = [];
    for (let i = 0; i < request.context.messages.length; i += 1) {
        const message = request.context.messages[i];
        if (message.role === 'user') {
            const content = aiUserContentToAnthropicContent(message.content, request.model.input);
            if (typeof content === 'string' && content.trim().length === 0)
                continue;
            if (Array.isArray(content) && content.length === 0)
                continue;
            messages.push({
                content,
                role: 'user'
            });
            continue;
        }
        if (message.role === 'assistant') {
            const content = aiAssistantContentToAnthropic(message.content);
            if (content.length > 0) {
                messages.push({ content, role: 'assistant' });
            }
            continue;
        }
        const toolResults = [
            {
                content: aiToolResultContentToAnthropic(message.content),
                is_error: message.isError,
                tool_use_id: message.toolCallId,
                type: 'tool_result'
            }
        ];
        let nextIndex = i + 1;
        while (nextIndex < request.context.messages.length &&
            request.context.messages[nextIndex]?.role === 'toolResult') {
            const nextMessage = request.context.messages[nextIndex];
            toolResults.push({
                content: aiToolResultContentToAnthropic(nextMessage.content),
                is_error: nextMessage.isError,
                tool_use_id: nextMessage.toolCallId,
                type: 'tool_result'
            });
            nextIndex += 1;
        }
        i = nextIndex - 1;
        messages.push({
            content: toolResults,
            role: 'user'
        });
    }
    const cacheControl = getAnthropicCacheControl(baseUrl, request.options?.cacheRetention);
    if (cacheControl && messages.length > 0) {
        const lastMessage = messages[messages.length - 1];
        if (lastMessage.role === 'user') {
            if (Array.isArray(lastMessage.content)) {
                const lastBlock = lastMessage.content[lastMessage.content.length - 1];
                if (lastBlock &&
                    (lastBlock.type === 'text' ||
                        lastBlock.type === 'image' ||
                        lastBlock.type === 'tool_result')) {
                    lastBlock.cache_control = cacheControl;
                }
            }
            else if (typeof lastMessage.content === 'string') {
                lastMessage.content = [
                    {
                        cache_control: cacheControl,
                        text: lastMessage.content,
                        type: 'text'
                    }
                ];
            }
        }
    }
    return {
        max_tokens: request.options?.maxTokens ?? 4096,
        messages,
        model: request.model.id,
        stream: false,
        ...(request.context.systemPrompt
            ? {
                system: [
                    {
                        ...(cacheControl ? { cache_control: cacheControl } : {}),
                        text: request.context.systemPrompt,
                        type: 'text'
                    }
                ]
            }
            : {}),
        ...(request.context.tools
            ? {
                tools: request.context.tools.map((tool) => ({
                    description: tool.description,
                    input_schema: {
                        properties: 'properties' in tool.parameters && tool.parameters.properties
                            ? tool.parameters.properties
                            : {},
                        required: 'required' in tool.parameters && Array.isArray(tool.parameters.required)
                            ? tool.parameters.required
                            : [],
                        type: 'object'
                    },
                    name: tool.name
                }))
            }
            : {}),
        ...(typeof request.options?.toolChoice === 'object'
            ? { tool_choice: request.options.toolChoice }
            : typeof request.options?.toolChoice === 'string'
                ? { tool_choice: { type: request.options.toolChoice } }
                : {})
    };
}
function concreteHeaders(headers) {
    const result = {};
    for (const [key, value] of Object.entries(headers ?? {})) {
        if (value !== null)
            result[key] = value;
    }
    return result;
}
function parseInvokeResponse(data) {
    if (!data || typeof data !== 'object') {
        throwInvalidResponse('Anthropic Messages invoke response must be a JSON object.');
    }
    const response = data;
    const content = Array.isArray(response.content) ? response.content : [];
    const text = content
        .filter((block) => {
        return block.type === 'text';
    })
        .map((block) => block.text ?? '')
        .join('');
    const toolCalls = content
        .filter((block) => {
        return block.type === 'tool_use';
    })
        .map((block) => ({
        arguments: JSON.stringify(block.input ?? {}),
        id: block.id ?? '',
        name: block.name ?? ''
    }));
    return {
        stopReason: response.stop_reason ?? undefined,
        text: text || undefined,
        toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
        usageStatus: response.usage
            ? ShortcutXLProviderUsageStatus.UsageAvailable
            : ShortcutXLProviderUsageStatus.Unavailable
    };
}
export async function invokeAnthropicMessagesModel(request, options) {
    const fetchImpl = options.fetchImpl ?? fetch;
    let response;
    try {
        response = await fetchImpl(`${normalizeBaseUrl(options.baseUrl)}/v1/messages`, {
            body: JSON.stringify(buildInvokeRequestBody(request, options.baseUrl)),
            headers: {
                'anthropic-version': '2023-06-01',
                'content-type': 'application/json',
                ...concreteHeaders(options.headers),
                ...createAuthHeader(options.auth)
            },
            method: 'POST',
            credentials: options.fetchOptions?.credentials,
            signal: options.signal
        });
    }
    catch (error) {
        normalizeFetchError(error, 'invoke');
    }
    if (!response.ok) {
        await throwAnthropicHttpError(response, 'invoke');
    }
    let data;
    try {
        data = (await response.json());
    }
    catch {
        throwInvalidResponse('Anthropic Messages invoke response was not valid JSON.');
    }
    const parsed = parseInvokeResponse(data);
    const usage = data.usage;
    await emitProviderUsage(options.usageStatusLogger, {
        callId: options.usageRequestId,
        model: request.model.id,
        status: parsed.usageStatus ?? ShortcutXLProviderUsageStatus.Unavailable,
        transport: ShortcutXLModelCallTransportKind.AnthropicMessages,
        usage: toProviderUsage(usage)
    });
    return parsed;
}
export async function streamAiAnthropicMessagesModelRequest(request, options) {
    if (options.fetchImpl && options.fetchImpl !== fetch) {
        throw new Error('owned AI Anthropic Messages streaming does not support per-call fetchImpl.');
    }
    const model = buildGatewayAiModel(request, options.baseUrl);
    const stream = streamGatewayAiAnthropic(model, request, buildGatewayAiStreamOptions(request, options));
    let finalMessage;
    for await (const event of stream) {
        if (event.type === 'done') {
            finalMessage = event.message;
        }
        else if (event.type === 'error') {
            finalMessage = event.error;
        }
        const shortcutEvent = toShortcutXLModelStreamEvent(event);
        if (shortcutEvent)
            options.onEvent(shortcutEvent);
    }
    const result = finalMessage ?? (await stream.result().catch(() => undefined));
    await emitProviderUsage(options.usageStatusLogger, {
        callId: request.requestId,
        model: model.id,
        status: result && result.usage.totalTokens > 0
            ? ShortcutXLProviderUsageStatus.UsageAvailable
            : ShortcutXLProviderUsageStatus.Unavailable,
        transport: ShortcutXLModelCallTransportKind.AnthropicMessages,
        usage: toProviderUsageFromAi(result?.usage)
    });
}
export function createAnthropicMessagesModelStreamTransport(options) {
    const fetchImpl = options.fetchImpl ?? fetch;
    return {
        kind: ShortcutXLModelCallTransportKind.AnthropicMessages,
        async stream(call, streamOptions) {
            const message = {
                api: AnthropicRuntimeApi,
                content: [],
                model: call.payload.model,
                provider: ShortcutXLModelCallTransportKind.AnthropicMessages,
                role: 'assistant',
                stopReason: ShortcutXLModelStreamStopReason.Stop,
                timestamp: Date.now(),
                usage: buildUsage(undefined)
            };
            let response;
            try {
                response = await fetchImpl(`${normalizeBaseUrl(options.baseUrl)}/v1/messages`, {
                    body: JSON.stringify(buildRequestBody(call)),
                    headers: {
                        'anthropic-version': '2023-06-01',
                        'content-type': 'application/json',
                        ...concreteHeaders(options.headers),
                        ...createAuthHeader(options.auth)
                    },
                    method: 'POST',
                    credentials: options.fetchOptions?.credentials,
                    signal: streamOptions.signal
                });
            }
            catch (error) {
                normalizeFetchError(error, 'stream');
            }
            if (!response.ok) {
                await throwAnthropicHttpError(response, 'stream');
            }
            await streamAnthropicMessagesResponse({
                callId: call.context.requestId,
                message,
                model: call.payload.model,
                onEvent: streamOptions.onEvent,
                response,
                usageStatusLogger: options.usageStatusLogger
            });
        }
    };
}
//# sourceMappingURL=anthropic-messages-transport.js.map