"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnthropicGen = void 0;
exports.buildAnthropicRequestMessagesWrapper = buildAnthropicRequestMessagesWrapper;
exports.consumeAnthropicStream = consumeAnthropicStream;
exports.reconstructAnthropicContextWrapper = reconstructAnthropicContextWrapper;
exports.reconstructAnthropicContextWrapperAsync = reconstructAnthropicContextWrapperAsync;
const sdk_1 = require("@anthropic-ai/sdk");
const events_1 = require("events");
const fs_1 = require("fs");
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const log_1 = require("../../log");
const i18n_text_1 = require("../../runtime/i18n-text");
const work_language_1 = require("../../runtime/work-language");
const artifacts_1 = require("./artifacts");
const failure_classifier_1 = require("./failure-classifier");
const tool_call_context_1 = require("./tool-call-context");
const tool_output_limit_1 = require("./tool-output-limit");
const tool_result_image_ingest_1 = require("./tool-result-image-ingest");
const log = (0, log_1.createLogger)('llm/anthropic');
const ANTHROPIC_JSON_RESPONSE_TOOL_NAME = 'dominds_json_response';
const ANTHROPIC_JSON_RESPONSE_TOOL_DESCRIPTION = 'Return the final answer as a JSON object. Do not include any non-JSON text.';
const ANTHROPIC_COMPAT_CAPTURE_SSE_ENV = 'DOMINDS_ANTHROPIC_COMPAT_CAPTURE_SSE';
const ANTHROPIC_COMPAT_CAPTURE_DIR_ENV = 'DOMINDS_ANTHROPIC_COMPAT_CAPTURE_DIR';
const ANTHROPIC_JSON_RESPONSE_TOOL_INPUT_SCHEMA = {
    type: 'object',
    additionalProperties: true,
};
function resolveAnthropicToolChoice(tools, requestContext, forceJsonResponse) {
    if (forceJsonResponse) {
        return {
            type: 'tool',
            name: ANTHROPIC_JSON_RESPONSE_TOOL_NAME,
            disable_parallel_tool_use: true,
        };
    }
    const requirement = requestContext.toolUseRequirement ?? 'auto';
    if (tools.length === 0) {
        if (requirement === 'required') {
            throw new Error(`Anthropic request invariant violation: toolUseRequirement=required but no tools are available (dialog=${requestContext.dialogSelfId})`);
        }
        return undefined;
    }
    if (requirement === 'none')
        return { type: 'none' };
    if (requirement === 'required')
        return { type: 'any' };
    return { type: 'auto' };
}
class AnthropicCompatibleHttpError extends Error {
    status;
    headers;
    responseBody;
    constructor(args) {
        const bodyPreview = args.responseBody.length > 500 ? `${args.responseBody.slice(0, 500)}...` : args.responseBody;
        super(`Anthropic-compatible HTTP request failed: ${args.status} ${args.statusText}; body=${bodyPreview}`);
        this.name = 'AnthropicCompatibleHttpError';
        this.status = args.status;
        this.headers = args.headers;
        this.responseBody = args.responseBody;
    }
}
function isAnthropicCompatibleSseCaptureEnabled() {
    const configured = process.env[ANTHROPIC_COMPAT_CAPTURE_SSE_ENV]?.trim().toLowerCase();
    return configured === '1' || configured === 'true' || configured === 'yes' || configured === 'on';
}
function resolveAnthropicCompatibleCaptureDir() {
    const configured = process.env[ANTHROPIC_COMPAT_CAPTURE_DIR_ENV]?.trim();
    if (configured && configured.length > 0)
        return path_1.default.resolve(configured);
    return path_1.default.resolve(process.cwd(), '.dialogs', 'debug', 'anthropic-compatible-sse');
}
function sanitizeCapturePathPart(value) {
    const sanitized = value.replace(/[^a-zA-Z0-9_.-]+/g, '_').replace(/^_+|_+$/g, '');
    if (sanitized.length === 0)
        return 'unknown';
    return sanitized.slice(0, 96);
}
function buildAnthropicCompatibleCaptureId(context) {
    const now = new Date().toISOString().replace(/[:.]/g, '-');
    const suffix = Math.random().toString(36).slice(2, 10);
    return [
        now,
        sanitizeCapturePathPart(context.providerKey ?? context.providerName),
        sanitizeCapturePathPart(context.model),
        sanitizeCapturePathPart(context.dialogSelfId),
        context.requestKind,
        suffix,
    ].join('__');
}
function redactHttpHeader(name, value) {
    const normalized = name.toLowerCase();
    if (normalized === 'authorization' ||
        normalized === 'x-api-key' ||
        normalized === 'api-key' ||
        normalized === 'anthropic-api-key' ||
        normalized.includes('token') ||
        normalized.includes('secret')) {
        return '<redacted>';
    }
    return value;
}
function headersToRedactedRecord(headers) {
    const entries = {};
    headers.forEach((value, name) => {
        entries[name] = redactHttpHeader(name, value);
    });
    return entries;
}
function mergeRequestHeaders(input, init) {
    const headers = new Headers(input instanceof Request ? input.headers : undefined);
    if (init?.headers !== undefined) {
        new Headers(init.headers).forEach((value, name) => headers.set(name, value));
    }
    return headers;
}
function requestMethod(input, init) {
    if (init?.method !== undefined)
        return init.method;
    if (input instanceof Request)
        return input.method;
    return 'GET';
}
function requestUrl(input) {
    if (typeof input === 'string')
        return input;
    if (input instanceof URL)
        return input.toString();
    return input.url;
}
function decodeArrayBufferView(view) {
    return new TextDecoder().decode(view);
}
async function readCaptureRequestBody(input, init) {
    const body = init?.body;
    if (typeof body === 'string')
        return body;
    if (body instanceof URLSearchParams)
        return body.toString();
    if (body instanceof Blob)
        return await body.text();
    if (body instanceof ArrayBuffer)
        return new TextDecoder().decode(body);
    if (ArrayBuffer.isView(body))
        return decodeArrayBufferView(body);
    if (body === null)
        return undefined;
    if (body !== undefined)
        return `[unreadable RequestInit body: ${Object.prototype.toString.call(body)}]`;
    if (input instanceof Request) {
        try {
            return await input.clone().text();
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            return `[failed to clone/read Request body: ${message}]`;
        }
    }
    return undefined;
}
async function writeCaptureJson(pathname, value) {
    await promises_1.default.writeFile(pathname, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
}
async function startAnthropicCompatibleCapture(context, input, init) {
    const captureRoot = resolveAnthropicCompatibleCaptureDir();
    const id = buildAnthropicCompatibleCaptureId(context);
    const dir = path_1.default.join(captureRoot, id);
    await promises_1.default.mkdir(dir, { recursive: true });
    const record = {
        id,
        dir,
        metaPath: path_1.default.join(dir, 'meta.json'),
        requestBodyPath: path_1.default.join(dir, 'request-body.txt'),
        responseBodyPath: path_1.default.join(dir, 'response-body.raw'),
        framesPath: path_1.default.join(dir, 'sse-frames.jsonl'),
        summaryPath: path_1.default.join(dir, 'summary.json'),
        context,
    };
    const bodyText = await readCaptureRequestBody(input, init);
    if (bodyText !== undefined) {
        await promises_1.default.writeFile(record.requestBodyPath, bodyText, 'utf-8');
    }
    else {
        await promises_1.default.writeFile(record.requestBodyPath, '', 'utf-8');
    }
    await writeCaptureJson(record.metaPath, {
        id: record.id,
        capturedAt: new Date().toISOString(),
        context,
        env: {
            enabledBy: ANTHROPIC_COMPAT_CAPTURE_SSE_ENV,
            captureDirEnv: process.env[ANTHROPIC_COMPAT_CAPTURE_DIR_ENV]
                ? ANTHROPIC_COMPAT_CAPTURE_DIR_ENV
                : undefined,
        },
        request: {
            method: requestMethod(input, init),
            url: requestUrl(input),
            headers: headersToRedactedRecord(mergeRequestHeaders(input, init)),
            bodyPath: record.requestBodyPath,
        },
        response: {
            bodyPath: record.responseBodyPath,
            framesPath: record.framesPath,
            summaryPath: record.summaryPath,
        },
    });
    log.info('ANTH compatible SSE capture started', undefined, {
        captureDir: record.dir,
        providerKey: context.providerKey,
        providerName: context.providerName,
        model: context.model,
        rootId: context.dialogRootId,
        selfId: context.dialogSelfId,
        requestKind: context.requestKind,
    });
    return record;
}
function parseSseFrameData(frame) {
    const lines = frame.split(/\r?\n/);
    const eventLine = lines.find((line) => line.startsWith('event:'));
    const eventName = eventLine ? eventLine.slice('event:'.length).trim() : '';
    const dataLines = lines
        .filter((line) => line.startsWith('data:'))
        .map((line) => (line.startsWith('data: ') ? line.slice(6) : line.slice(5)));
    if (dataLines.length === 0)
        return { eventName, data: undefined };
    return { eventName, data: dataLines.join('\n') };
}
function parseSseFrameJson(frame) {
    const { data } = parseSseFrameData(frame);
    if (data === undefined)
        return { kind: 'no_data' };
    if (data === '[DONE]')
        return { kind: 'done' };
    try {
        JSON.parse(data);
        return { kind: 'json_ok' };
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        return { kind: 'invalid_json', message, data };
    }
}
function describeCaptureError(error) {
    return error instanceof Error ? error.message : String(error);
}
function buildSseFrameLogLine(frame, state) {
    const { eventName, data } = parseSseFrameData(frame);
    const result = parseSseFrameJson(frame);
    if (result.kind === 'done')
        state.doneSeen = true;
    if (result.kind === 'json_ok')
        state.jsonFrameCount += 1;
    if (result.kind === 'invalid_json') {
        state.invalidJsonFrameCount += 1;
        state.invalidFrames.push({
            frameIndex: state.frameCount,
            eventName,
            message: result.message,
            data: result.data,
        });
    }
    return `${JSON.stringify({
        frameIndex: state.frameCount,
        eventName,
        dataBytes: data === undefined ? 0 : Buffer.byteLength(data),
        parse: result.kind,
        error: result.kind === 'invalid_json' ? result.message : undefined,
        data: result.kind === 'invalid_json' ? result.data : undefined,
    })}\n`;
}
async function writeAndDrain(stream, chunk) {
    if (!stream.write(chunk)) {
        await Promise.race([
            (0, events_1.once)(stream, 'drain'),
            (0, events_1.once)(stream, 'error').then(([error]) => {
                throw error;
            }),
        ]);
    }
}
async function endWriteStream(stream) {
    await new Promise((resolve, reject) => {
        stream.once('error', reject);
        stream.end(resolve);
    });
}
async function cancelReadableStreamReader(reader, context) {
    try {
        await reader.cancel();
    }
    catch (error) {
        log.warn('ANTH response reader cancel failed during cleanup', error, context);
    }
}
async function captureAnthropicCompatibleResponseBody(record, response) {
    const responseClone = response.clone();
    const rawStream = (0, fs_1.createWriteStream)(record.responseBodyPath);
    const framesStream = (0, fs_1.createWriteStream)(record.framesPath);
    const decoder = new TextDecoder();
    const parseAsSse = record.context.requestKind === 'stream';
    let captureError;
    const state = {
        buffer: '',
        responseBytes: 0,
        frameCount: 0,
        doneSeen: false,
        jsonFrameCount: 0,
        invalidJsonFrameCount: 0,
        invalidFrames: [],
    };
    try {
        if (responseClone.body) {
            const reader = responseClone.body.getReader();
            let done = false;
            try {
                for (;;) {
                    const readResult = await reader.read();
                    if (readResult.done) {
                        done = true;
                        break;
                    }
                    state.responseBytes += readResult.value.byteLength;
                    await writeAndDrain(rawStream, readResult.value);
                    if (parseAsSse) {
                        state.buffer += decoder.decode(readResult.value, { stream: true });
                        for (;;) {
                            const separator = state.buffer.match(/\r?\n\r?\n/);
                            if (!separator || separator.index === undefined)
                                break;
                            const frame = state.buffer.slice(0, separator.index);
                            state.buffer = state.buffer.slice(separator.index + separator[0].length);
                            if (frame.trim().length === 0)
                                continue;
                            state.frameCount += 1;
                            await writeAndDrain(framesStream, buildSseFrameLogLine(frame, state));
                        }
                    }
                }
                if (parseAsSse) {
                    const rest = decoder.decode();
                    if (rest.length > 0)
                        state.buffer += rest;
                    if (state.buffer.trim().length > 0) {
                        state.frameCount += 1;
                        await writeAndDrain(framesStream, buildSseFrameLogLine(state.buffer, state));
                    }
                }
            }
            finally {
                if (!done) {
                    await cancelReadableStreamReader(reader, {
                        captureDir: record.dir,
                        providerKey: record.context.providerKey,
                        model: record.context.model,
                    });
                }
                reader.releaseLock();
            }
        }
    }
    catch (error) {
        captureError = describeCaptureError(error);
    }
    finally {
        await Promise.all([endWriteStream(rawStream), endWriteStream(framesStream)]);
    }
    await writeCaptureJson(record.summaryPath, {
        id: record.id,
        completedAt: new Date().toISOString(),
        status: response.status,
        ok: response.ok,
        statusText: response.statusText,
        requestKind: record.context.requestKind,
        captureError,
        headers: headersToRedactedRecord(response.headers),
        responseBytes: state.responseBytes,
        frameCount: state.frameCount,
        doneSeen: state.doneSeen,
        jsonFrameCount: state.jsonFrameCount,
        invalidJsonFrameCount: state.invalidJsonFrameCount,
        invalidFrames: state.invalidFrames,
    });
    if (state.invalidJsonFrameCount > 0) {
        log.warn('ANTH compatible SSE capture found invalid JSON data frame', undefined, {
            captureDir: record.dir,
            invalidJsonFrameCount: state.invalidJsonFrameCount,
            invalidFrames: state.invalidFrames,
            providerKey: record.context.providerKey,
            model: record.context.model,
            rootId: record.context.dialogRootId,
            selfId: record.context.dialogSelfId,
        });
    }
    else if (captureError === undefined) {
        log.info('ANTH compatible SSE capture completed', undefined, {
            captureDir: record.dir,
            responseBytes: state.responseBytes,
            frameCount: state.frameCount,
            doneSeen: state.doneSeen,
            providerKey: record.context.providerKey,
            model: record.context.model,
            rootId: record.context.dialogRootId,
            selfId: record.context.dialogSelfId,
        });
    }
    if (captureError !== undefined) {
        throw new Error(`ANTH compatible SSE capture failed while reading response clone: ${captureError}`);
    }
}
function buildAnthropicCompatibleCaptureFetch(context) {
    return async (input, init) => {
        const record = await startAnthropicCompatibleCapture(context, input, init);
        try {
            const response = await fetch(input, init);
            void captureAnthropicCompatibleResponseBody(record, response).catch((error) => {
                log.error('ANTH compatible SSE capture failed while reading response clone', error, {
                    captureDir: record.dir,
                    providerKey: context.providerKey,
                    model: context.model,
                    rootId: context.dialogRootId,
                    selfId: context.dialogSelfId,
                });
            });
            return response;
        }
        catch (error) {
            await writeCaptureJson(record.summaryPath, {
                id: record.id,
                completedAt: new Date().toISOString(),
                fetchError: error instanceof Error ? error.message : String(error),
            });
            throw error;
        }
    };
}
function createAnthropicClient(args) {
    const options = {
        apiKey: args.apiKey,
        baseURL: args.providerConfig.baseUrl,
    };
    if (args.providerConfig.apiType === 'anthropic-compatible' &&
        isAnthropicCompatibleSseCaptureEnabled()) {
        options.fetch = buildAnthropicCompatibleCaptureFetch({
            providerKey: args.requestContext.providerKey,
            providerName: args.providerConfig.name,
            model: args.agent.model ?? args.requestContext.modelKey ?? 'unknown',
            dialogRootId: args.requestContext.dialogRootId,
            dialogSelfId: args.requestContext.dialogSelfId,
            requestKind: args.requestKind,
        });
    }
    return new sdk_1.Anthropic(options);
}
function buildAnthropicCompatibleMessagesUrl(providerConfig) {
    if (!providerConfig.baseUrl) {
        throw new Error(`Provider '${providerConfig.name}' is missing baseUrl.`);
    }
    const baseUrl = providerConfig.baseUrl.endsWith('/')
        ? providerConfig.baseUrl
        : `${providerConfig.baseUrl}/`;
    return new URL('v1/messages', baseUrl).toString();
}
function stringifySseDataPreview(data) {
    return data.length > 500 ? `${data.slice(0, 500)}...` : data;
}
function parseAnthropicCompatibleSseFrame(frame) {
    const { eventName, data } = parseSseFrameData(frame);
    if (data === undefined)
        return null;
    if (data === '[DONE]')
        return null;
    if (eventName === 'ping')
        return null;
    if (eventName === 'error') {
        throw new Error(`Anthropic-compatible SSE error event: ${stringifySseDataPreview(data)}`);
    }
    try {
        return JSON.parse(data);
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        throw new Error(`Anthropic-compatible SSE data is not valid JSON (event=${eventName || '<none>'}): ${message}; data=${JSON.stringify(stringifySseDataPreview(data))}`);
    }
}
async function* streamAnthropicCompatibleRawSse(args) {
    const { signal, ...bodyParams } = args.params;
    const headers = new Headers({
        accept: 'application/json, text/event-stream',
        'anthropic-version': '2023-06-01',
        'content-type': 'application/json',
        'x-api-key': args.apiKey,
    });
    const fetchImpl = args.providerConfig.apiType === 'anthropic-compatible' &&
        isAnthropicCompatibleSseCaptureEnabled()
        ? buildAnthropicCompatibleCaptureFetch({
            providerKey: args.requestContext.providerKey,
            providerName: args.providerConfig.name,
            model: args.agent.model ?? args.requestContext.modelKey ?? 'unknown',
            dialogRootId: args.requestContext.dialogRootId,
            dialogSelfId: args.requestContext.dialogSelfId,
            requestKind: 'stream',
        })
        : fetch;
    const response = await fetchImpl(buildAnthropicCompatibleMessagesUrl(args.providerConfig), {
        method: 'POST',
        headers,
        body: JSON.stringify(bodyParams),
        ...(signal ? { signal } : {}),
    });
    if (!response.ok) {
        const responseBody = await response.text();
        throw new AnthropicCompatibleHttpError({
            status: response.status,
            statusText: response.statusText,
            headers: headersToRedactedRecord(response.headers),
            responseBody,
        });
    }
    if (!response.body) {
        throw new Error('Anthropic-compatible SSE response body is empty.');
    }
    const decoder = new TextDecoder();
    const reader = response.body.getReader();
    let buffer = '';
    let done = false;
    let sawSseDataFrame = false;
    try {
        for (;;) {
            const readResult = await reader.read();
            if (readResult.done) {
                done = true;
                break;
            }
            buffer += decoder.decode(readResult.value, { stream: true });
            for (;;) {
                const separator = buffer.match(/\r?\n\r?\n/);
                if (!separator || separator.index === undefined)
                    break;
                const frame = buffer.slice(0, separator.index);
                buffer = buffer.slice(separator.index + separator[0].length);
                if (frame.trim().length === 0)
                    continue;
                if (parseSseFrameData(frame).data !== undefined)
                    sawSseDataFrame = true;
                const event = parseAnthropicCompatibleSseFrame(frame);
                if (event)
                    yield event;
            }
        }
        const rest = decoder.decode();
        if (rest.length > 0)
            buffer += rest;
        if (buffer.trim().length > 0) {
            if (parseSseFrameData(buffer).data !== undefined)
                sawSseDataFrame = true;
            const event = parseAnthropicCompatibleSseFrame(buffer);
            if (event)
                yield event;
        }
        if (!sawSseDataFrame) {
            throw new Error('Anthropic-compatible SSE stream ended without any data frames.');
        }
    }
    finally {
        if (!done) {
            await cancelReadableStreamReader(reader, {
                providerKey: args.requestContext.providerKey,
                model: args.agent.model ?? args.requestContext.modelKey,
            });
        }
        reader.releaseLock();
    }
}
function limitAnthropicToolOutputText(text, msg, limitChars) {
    const limited = (0, tool_output_limit_1.truncateProviderToolOutputText)(text, limitChars);
    if (limited.truncated) {
        log.warn('ANTH tool output truncated before provider request', undefined, {
            callId: msg.id,
            toolName: msg.name,
            originalChars: limited.originalChars,
            limitChars: limited.limitChars,
        });
    }
    return limited.text;
}
function limitAnthropicToolOutputBlocks(content, msg, limitChars) {
    let remainingChars = limitChars;
    let truncated = false;
    const limited = [];
    for (const block of content) {
        if (block.type !== 'text') {
            limited.push(block);
            continue;
        }
        if (remainingChars <= 0) {
            truncated = true;
            break;
        }
        const next = (0, tool_output_limit_1.truncateProviderToolOutputText)(block.text, remainingChars);
        limited.push({ type: 'text', text: next.text });
        remainingChars -= next.text.length;
        if (next.truncated) {
            truncated = true;
            break;
        }
    }
    if (truncated) {
        const originalChars = content.reduce((sum, block) => sum + (block.type === 'text' ? block.text.length : 0), 0);
        log.warn('ANTH tool output blocks truncated before provider request', undefined, {
            callId: msg.id,
            toolName: msg.name,
            originalTextChars: originalChars,
            limitChars,
        });
    }
    return limited;
}
function countAnthropicTextBlockChars(content) {
    return content.reduce((sum, block) => {
        if (block.type !== 'text') {
            return sum;
        }
        return sum + block.text.length;
    }, 0);
}
function isRecord(value) {
    return typeof value === 'object' && value !== null;
}
function isNonArrayRecord(value) {
    return isRecord(value) && !Array.isArray(value);
}
function isLlmRequestContext(value) {
    return (isNonArrayRecord(value) &&
        typeof value.dialogSelfId === 'string' &&
        typeof value.dialogRootId === 'string' &&
        typeof value.providerKey === 'string' &&
        typeof value.modelKey === 'string');
}
function tryExtractApiReturnedModel(value) {
    // NOTE: External API payload; a runtime check is unavoidable.
    if (!isRecord(value))
        return undefined;
    if (!('model' in value))
        return undefined;
    const model = value.model;
    if (typeof model !== 'string')
        return undefined;
    const trimmed = model.trim();
    return trimmed.length > 0 ? trimmed : undefined;
}
function isToolUseBlock(value) {
    return (isRecord(value) &&
        value.type === 'tool_use' &&
        typeof value.id === 'string' &&
        typeof value.name === 'string');
}
function isTextOrImageBlockParam(value) {
    return isRecord(value) && (value.type === 'text' || value.type === 'image');
}
function funcToolToAnthropic(funcTool) {
    // MCP schemas are passed through to providers. Anthropic's SDK types expect a narrower schema
    // shape; runtime compatibility is handled by provider validation + the driver stop policy.
    const input_schema = funcTool.parameters;
    const description = (0, i18n_text_1.getTextForLanguage)({ i18n: funcTool.descriptionI18n, fallback: funcTool.description }, (0, work_language_1.getWorkLanguage)());
    return {
        name: funcTool.name,
        description,
        input_schema,
    };
}
function resolveAnthropicParams(providerConfig, agent) {
    if (providerConfig.apiType === 'anthropic-compatible') {
        return agent.model_params?.['anthropic-compatible'] || {};
    }
    return agent.model_params?.anthropic || {};
}
function resolveAnthropicJsonResponseEnabled(providerConfig, agent) {
    const anthropicParams = resolveAnthropicParams(providerConfig, agent);
    const providerSpecific = anthropicParams.json_response;
    if (providerSpecific !== undefined)
        return providerSpecific;
    return agent.model_params?.json_response === true;
}
function buildAnthropicForcedJsonTool() {
    return {
        name: ANTHROPIC_JSON_RESPONSE_TOOL_NAME,
        description: ANTHROPIC_JSON_RESPONSE_TOOL_DESCRIPTION,
        input_schema: ANTHROPIC_JSON_RESPONSE_TOOL_INPUT_SCHEMA,
    };
}
function buildAnthropicToolList(funcTools, forceJsonResponse) {
    const tools = funcTools.map(funcToolToAnthropic);
    if (!forceJsonResponse)
        return tools;
    if (tools.some((tool) => tool.name === ANTHROPIC_JSON_RESPONSE_TOOL_NAME)) {
        throw new Error(`Anthropic tool name collision: '${ANTHROPIC_JSON_RESPONSE_TOOL_NAME}' is reserved for json_response mode.`);
    }
    tools.push(buildAnthropicForcedJsonTool());
    return tools;
}
function buildAnthropicThinkingConfig(anthropicParams, providerConfig) {
    const configured = anthropicParams.thinking;
    if (configured === undefined)
        return undefined;
    if (providerConfig.apiType === 'anthropic-compatible') {
        if (configured === true)
            return { type: 'enabled' };
        if (configured === false)
            return { type: 'disabled' };
        throw new Error(`Invalid model_params.anthropic-compatible.thinking for provider '${providerConfig.name}' (apiType=anthropic-compatible): expected boolean.`);
    }
    if (typeof configured === 'boolean') {
        throw new Error(`Invalid model_params.anthropic.thinking for provider '${providerConfig.name}' (apiType=anthropic): expected Anthropic thinking object.`);
    }
    return configured;
}
function serializeAnthropicForcedJsonObject(input, at) {
    if (!isNonArrayRecord(input)) {
        throw new Error(`Invalid ${at}: expected JSON object output in json_response mode.`);
    }
    return JSON.stringify(input);
}
function extractJsonObjectCandidate(text) {
    const markdownMatch = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
    if (markdownMatch && typeof markdownMatch[1] === 'string' && markdownMatch[1].trim().length > 0) {
        return markdownMatch[1].trim();
    }
    const firstObject = text.indexOf('{');
    const lastObject = text.lastIndexOf('}');
    if (firstObject !== -1 && lastObject !== -1 && lastObject > firstObject) {
        return text.slice(firstObject, lastObject + 1).trim();
    }
    return null;
}
function parseForcedJsonToolInput(rawJson, fallbackInput, at) {
    const trimmed = rawJson.trim();
    if (trimmed.length === 0)
        return fallbackInput;
    const candidates = [trimmed];
    const extracted = extractJsonObjectCandidate(trimmed);
    if (extracted && extracted !== trimmed) {
        candidates.push(extracted);
    }
    if (trimmed.startsWith('{{') && trimmed.endsWith('}}') && trimmed.length > 4) {
        candidates.push(trimmed.slice(1, -1).trim());
    }
    let lastError = null;
    for (const candidate of candidates) {
        try {
            return JSON.parse(candidate);
        }
        catch (error) {
            lastError = error;
        }
    }
    const errorText = lastError instanceof Error ? lastError.message : String(lastError);
    const preview = trimmed.length > 200 ? `${trimmed.slice(0, 200)}...` : trimmed;
    if (isNonArrayRecord(fallbackInput)) {
        log.warn('ANTH malformed forced-json tool input; using initialInput fallback', new Error(errorText), {
            at,
            rawPreview: preview,
        });
        return fallbackInput;
    }
    throw new Error(`Invalid ${at}: ${errorText}; raw=${JSON.stringify(preview)}`);
}
async function funcResultToAnthropicToolResultBlock(chatMsg, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest) {
    const items = chatMsg.contentItems;
    if (!Array.isArray(items) || items.length === 0) {
        return {
            type: 'tool_result',
            tool_use_id: chatMsg.id,
            content: limitAnthropicToolOutputText(chatMsg.content, chatMsg, limitChars),
        };
    }
    const content = [];
    for (const [itemIndex, item] of items.entries()) {
        if (item.type === 'input_text') {
            content.push({ type: 'text', text: item.text });
            continue;
        }
        if (item.type === 'input_image') {
            if (!supportsImageInput) {
                if (onToolResultImageIngest) {
                    await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
                        requestContext,
                        toolCallId: chatMsg.id,
                        toolName: chatMsg.name,
                        artifact: item.artifact,
                        disposition: 'filtered_model_unsupported',
                        providerPathLabel: 'Anthropic Messages path',
                    }));
                }
                content.push({
                    type: 'text',
                    text: `[image not sent: current model does not support image input]`,
                });
                continue;
            }
            if (!(0, artifacts_1.isVisionImageMimeType)(item.mimeType)) {
                if (onToolResultImageIngest) {
                    await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
                        requestContext,
                        toolCallId: chatMsg.id,
                        toolName: chatMsg.name,
                        artifact: item.artifact,
                        disposition: 'filtered_mime_unsupported',
                        mimeType: item.mimeType,
                        providerPathLabel: 'Anthropic Messages path',
                    }));
                }
                content.push({
                    type: 'text',
                    text: `[image omitted: unsupported mimeType=${item.mimeType}]`,
                });
                continue;
            }
            if (!allowedImageKeys.has((0, tool_result_image_ingest_1.buildImageBudgetKeyForContentItem)({ msg: chatMsg, itemIndex, artifact: item.artifact }))) {
                if (onToolResultImageIngest) {
                    await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
                        requestContext,
                        toolCallId: chatMsg.id,
                        toolName: chatMsg.name,
                        artifact: item.artifact,
                        disposition: 'filtered_size_limit',
                        detail: (0, tool_result_image_ingest_1.buildImageBudgetLimitDetail)({
                            byteLength: item.byteLength,
                            budgetBytes: tool_result_image_ingest_1.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES,
                        }),
                        providerPathLabel: 'Anthropic Messages path',
                    }));
                }
                content.push({
                    type: 'text',
                    text: `[image omitted: request image budget exceeded bytes=${String(item.byteLength)} budget=${String(tool_result_image_ingest_1.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES)}]`,
                });
                continue;
            }
            const bytesResult = await (0, tool_result_image_ingest_1.readToolResultImageBytesSafe)(item.artifact);
            if (bytesResult.kind === 'missing') {
                if (onToolResultImageIngest) {
                    await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
                        requestContext,
                        toolCallId: chatMsg.id,
                        toolName: chatMsg.name,
                        artifact: item.artifact,
                        disposition: 'filtered_missing',
                        providerPathLabel: 'Anthropic Messages path',
                    }));
                }
                content.push({ type: 'text', text: `[image missing: ${item.artifact.relPath}]` });
                continue;
            }
            if (bytesResult.kind === 'read_failed') {
                if (onToolResultImageIngest) {
                    await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
                        requestContext,
                        toolCallId: chatMsg.id,
                        toolName: chatMsg.name,
                        artifact: item.artifact,
                        disposition: 'filtered_read_failed',
                        detail: bytesResult.detail,
                        providerPathLabel: 'Anthropic Messages path',
                    }));
                }
                content.push({ type: 'text', text: `[image unreadable: ${item.artifact.relPath}]` });
                continue;
            }
            if (onToolResultImageIngest) {
                await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
                    requestContext,
                    toolCallId: chatMsg.id,
                    toolName: chatMsg.name,
                    artifact: item.artifact,
                    disposition: 'fed_native',
                    providerPathLabel: 'Anthropic Messages path',
                }));
            }
            const bytes = bytesResult.bytes;
            const base64 = bytes.toString('base64');
            content.push({
                type: 'image',
                source: {
                    type: 'base64',
                    media_type: item.mimeType,
                    data: base64,
                },
            });
            continue;
        }
        const _exhaustive = item;
        content.push({ type: 'text', text: `[unknown content item: ${String(_exhaustive)}]` });
    }
    if (content.length === 0) {
        return {
            type: 'tool_result',
            tool_use_id: chatMsg.id,
            content: limitAnthropicToolOutputText(chatMsg.content, chatMsg, limitChars),
        };
    }
    return {
        type: 'tool_result',
        tool_use_id: chatMsg.id,
        content: limitAnthropicToolOutputBlocks(content, chatMsg, limitChars),
    };
}
async function chatMessageToContentBlocksAsync(chatMsg, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest, onUserImageIngest) {
    if ((chatMsg.type === 'prompting_msg' ||
        chatMsg.type === 'tellask_result_msg' ||
        chatMsg.type === 'tellask_carryover_msg') &&
        Array.isArray(chatMsg.contentItems) &&
        chatMsg.contentItems.length > 0) {
        const content = [
            { type: 'text', text: chatMsg.content },
        ];
        for (const [itemIndex, item] of chatMsg.contentItems.entries()) {
            if (item.type === 'input_text') {
                content.push({ type: 'text', text: item.text });
                continue;
            }
            if (item.type === 'input_image') {
                if (!supportsImageInput) {
                    if (onUserImageIngest) {
                        await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
                            requestContext,
                            ...(chatMsg.type === 'prompting_msg' ? { msgId: chatMsg.msgId } : {}),
                            artifact: item.artifact,
                            disposition: 'filtered_model_unsupported',
                            providerPathLabel: 'Anthropic Messages path',
                        }));
                    }
                    content.push({
                        type: 'text',
                        text: `[image not sent: current model does not support image input]`,
                    });
                    continue;
                }
                if (!(0, artifacts_1.isVisionImageMimeType)(item.mimeType)) {
                    if (onUserImageIngest) {
                        await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
                            requestContext,
                            ...(chatMsg.type === 'prompting_msg' ? { msgId: chatMsg.msgId } : {}),
                            artifact: item.artifact,
                            disposition: 'filtered_mime_unsupported',
                            mimeType: item.mimeType,
                            providerPathLabel: 'Anthropic Messages path',
                        }));
                    }
                    content.push({
                        type: 'text',
                        text: `[image not sent: unsupported mimeType=${item.mimeType}]`,
                    });
                    continue;
                }
                if (!allowedImageKeys.has((0, tool_result_image_ingest_1.buildImageBudgetKeyForContentItem)({ msg: chatMsg, itemIndex, artifact: item.artifact }))) {
                    if (onUserImageIngest) {
                        await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
                            requestContext,
                            ...(chatMsg.type === 'prompting_msg' ? { msgId: chatMsg.msgId } : {}),
                            artifact: item.artifact,
                            disposition: 'filtered_size_limit',
                            detail: (0, tool_result_image_ingest_1.buildImageBudgetLimitDetail)({
                                byteLength: item.byteLength,
                                budgetBytes: tool_result_image_ingest_1.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES,
                            }),
                            providerPathLabel: 'Anthropic Messages path',
                        }));
                    }
                    content.push({
                        type: 'text',
                        text: `[image not sent: request image budget exceeded bytes=${String(item.byteLength)} budget=${String(tool_result_image_ingest_1.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES)}]`,
                    });
                    continue;
                }
                const bytesResult = await (0, tool_result_image_ingest_1.readToolResultImageBytesSafe)(item.artifact);
                if (bytesResult.kind === 'missing') {
                    if (onUserImageIngest) {
                        await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
                            requestContext,
                            ...(chatMsg.type === 'prompting_msg' ? { msgId: chatMsg.msgId } : {}),
                            artifact: item.artifact,
                            disposition: 'filtered_missing',
                            providerPathLabel: 'Anthropic Messages path',
                        }));
                    }
                    content.push({ type: 'text', text: `[image missing: ${item.artifact.relPath}]` });
                    continue;
                }
                if (bytesResult.kind === 'read_failed') {
                    if (onUserImageIngest) {
                        await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
                            requestContext,
                            ...(chatMsg.type === 'prompting_msg' ? { msgId: chatMsg.msgId } : {}),
                            artifact: item.artifact,
                            disposition: 'filtered_read_failed',
                            detail: bytesResult.detail,
                            providerPathLabel: 'Anthropic Messages path',
                        }));
                    }
                    content.push({ type: 'text', text: `[image unreadable: ${item.artifact.relPath}]` });
                    continue;
                }
                if (onUserImageIngest) {
                    await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
                        requestContext,
                        ...(chatMsg.type === 'prompting_msg' ? { msgId: chatMsg.msgId } : {}),
                        artifact: item.artifact,
                        disposition: 'fed_native',
                        providerPathLabel: 'Anthropic Messages path',
                    }));
                }
                content.push({
                    type: 'image',
                    source: {
                        type: 'base64',
                        media_type: item.mimeType,
                        data: bytesResult.bytes.toString('base64'),
                    },
                });
                continue;
            }
            const _exhaustive = item;
            throw new Error(`Unsupported user content item: ${String(_exhaustive)}`);
        }
        return content;
    }
    if (chatMsg.type !== 'func_result_msg') {
        return chatMessageToContentBlocks(chatMsg);
    }
    return [
        await funcResultToAnthropicToolResultBlock(chatMsg, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest),
    ];
}
async function chatMessageToAnthropicAsync(chatMsg, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest, onUserImageIngest) {
    const contentBlocks = await chatMessageToContentBlocksAsync(chatMsg, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest, onUserImageIngest);
    if (contentBlocks.length === 0) {
        throw new Error(`No content blocks generated for message: ${JSON.stringify(chatMsg)}`);
    }
    let role = 'assistant';
    if ('role' in chatMsg) {
        role = chatMsg.role === 'tool' ? 'user' : chatMsg.role;
    }
    return {
        role,
        content: contentBlocks.length === 1 ? contentBlocks : contentBlocks,
    };
}
async function funcResultToAnthropicTextBlocks(result, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest) {
    const resultText = limitAnthropicToolOutputText(formatFunctionCallResultForAnthropicTextContext(result), result, limitChars);
    const contentBlocks = [
        {
            type: 'text',
            text: resultText,
        },
    ];
    if (!Array.isArray(result.contentItems) || result.contentItems.length === 0) {
        return contentBlocks;
    }
    const remainingTextChars = Math.max(0, limitChars - countAnthropicTextBlockChars(contentBlocks));
    const toolResultBlock = await funcResultToAnthropicToolResultBlock(result, remainingTextChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest);
    const toolResultContent = toolResultBlock.content;
    if (typeof toolResultContent === 'string') {
        if (toolResultContent.length > 0) {
            contentBlocks.push({ type: 'text', text: toolResultContent });
        }
        return contentBlocks;
    }
    if (!Array.isArray(toolResultContent)) {
        return contentBlocks;
    }
    contentBlocks.push(...toolResultContent.filter(isTextOrImageBlockParam));
    return contentBlocks;
}
async function anthropicProjectionToMessageAsync(projection, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest, onUserImageIngest) {
    switch (projection.kind) {
        case 'chat':
            return await chatMessageToAnthropicAsync(projection.msg, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest, onUserImageIngest);
        case 'function_call_text':
            return {
                role: 'assistant',
                content: [{ type: 'text', text: formatFunctionCallForAnthropicTextContext(projection) }],
            };
        case 'function_result_text':
            return {
                role: 'user',
                content: await funcResultToAnthropicTextBlocks(projection.result, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest),
            };
        default: {
            const _exhaustive = projection;
            return _exhaustive;
        }
    }
}
async function buildAnthropicRequestMessages(context, requestContext, providerConfig, onToolResultImageIngest, onUserImageIngest) {
    // Anthropic's native tool_use.input requires structured JSON. Historical Dominds function-call
    // arguments are opaque raw strings, so persisted call/result pairs are projected as text while
    // preserving the original tool-result image handling path.
    const normalized = (0, tool_call_context_1.normalizeToolCallPairs)(context);
    const violation = (0, tool_call_context_1.findFirstToolCallAdjacencyViolation)(normalized);
    if (violation) {
        const detail = (0, tool_call_context_1.formatToolCallAdjacencyViolation)(violation, 'ANTH provider projection');
        log.error(detail, new Error('anthropic_tool_call_adjacency_violation'), {
            callId: violation.callId,
            toolName: violation.toolName,
            violationKind: violation.kind,
            index: violation.index,
        });
        throw new Error(detail);
    }
    const messages = [];
    const toolResultMaxChars = (0, tool_output_limit_1.resolveProviderToolResultMaxChars)(providerConfig);
    const projectedContext = projectFunctionCallPairsForAnthropicTextContext(normalized);
    const allowedImageKeys = (0, tool_result_image_ingest_1.selectLatestImagesWithinBudget)(normalized, tool_result_image_ingest_1.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES);
    const supportsImageInput = (0, tool_result_image_ingest_1.resolveModelImageInputSupport)(requestContext.modelKey === undefined
        ? undefined
        : providerConfig?.models[requestContext.modelKey], true);
    for (const msg of projectedContext) {
        messages.push(await anthropicProjectionToMessageAsync(msg, toolResultMaxChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest, onUserImageIngest));
    }
    return assembleAnthropicTurns(messages);
}
async function buildAnthropicRequestMessagesWrapper(context, requestContextOrProviderConfig, providerConfigMaybe) {
    const requestContext = isLlmRequestContext(requestContextOrProviderConfig)
        ? requestContextOrProviderConfig
        : {
            dialogSelfId: '',
            dialogRootId: '',
            providerKey: 'anthropic',
            modelKey: 'unknown',
        };
    const providerConfig = isLlmRequestContext(requestContextOrProviderConfig)
        ? providerConfigMaybe
        : requestContextOrProviderConfig;
    return await buildAnthropicRequestMessages(context, requestContext, providerConfig);
}
/**
 * Reconstruct Anthropic context from persisted messages.
 * Relies on natural storage order - func_result always follows func_call.
 */
function reconstructAnthropicContext(persistedMessages) {
    return buildAnthropicRequestMessagesSync(persistedMessages);
}
function contentToBlocks(content) {
    if (typeof content === 'string') {
        return [{ type: 'text', text: content }];
    }
    return content;
}
function assembleAnthropicTurns(messages) {
    // Provider payload projection (turn assembly)
    //
    // Dominds persists fine-grained events (thinking/saying/tool-use/tool-result as separate
    // ChatMessage entries). Many Anthropic-compatible endpoints are strict about role alternation and
    // reject consecutive messages with the same role.
    //
    // Instead of treating persisted entries as 1:1 provider messages, we assemble them into provider
    // turns by coalescing consecutive messages with the same role.
    //
    // Ideal future: provider SDKs should support a dedicated role='environment' for environment/system
    // messages. Today most providers accept only user/assistant (and tool via special-casing), so those
    // messages must be projected as role='user'.
    const turns = [];
    for (const msg of messages) {
        const contentBlocks = contentToBlocks(msg.content);
        if (contentBlocks.length === 0)
            continue;
        const prev = turns.length > 0 ? turns[turns.length - 1] : null;
        if (prev && prev.role === msg.role) {
            const prevBlocks = contentToBlocks(prev.content);
            prev.content = [...prevBlocks, ...contentBlocks];
            continue;
        }
        turns.push({ role: msg.role, content: contentBlocks });
    }
    return turns;
}
function formatFunctionCallForAnthropicTextContext(args) {
    return ('Function call emitted by the assistant.\n' +
        `Tool name: ${args.call.name}\n` +
        `Call ID: ${args.call.id}\n` +
        'Raw arguments, verbatim:\n' +
        '<raw_arguments>\n' +
        `${args.call.arguments}\n` +
        '</raw_arguments>');
}
function formatFunctionCallResultForAnthropicTextContext(result) {
    return ('Function call result.\n' +
        `Tool name: ${result.name}\n` +
        `Call ID: ${result.id}\n` +
        'Result content:\n' +
        result.content);
}
function projectFunctionCallPairsForAnthropicTextContext(context) {
    const projected = [];
    for (let index = 0; index < context.length; index += 1) {
        const msg = context[index];
        if (msg.type !== 'func_call_msg') {
            projected.push({ kind: 'chat', msg });
            continue;
        }
        const result = context[index + 1];
        if (result === undefined || result.type !== 'func_result_msg' || result.id !== msg.id) {
            throw new Error(`ANTH function call text projection invariant violation: missing adjacent result for callId=${msg.id}, tool=${msg.name}`);
        }
        projected.push({ kind: 'function_call_text', call: msg });
        projected.push({ kind: 'function_result_text', result });
        index += 1;
    }
    return projected;
}
function anthropicProjectionToMessageSync(projection, toolResultMaxChars) {
    switch (projection.kind) {
        case 'chat':
            return chatMessageToAnthropic(projection.msg);
        case 'function_call_text':
            return {
                role: 'assistant',
                content: [{ type: 'text', text: formatFunctionCallForAnthropicTextContext(projection) }],
            };
        case 'function_result_text':
            return {
                role: 'user',
                content: [
                    {
                        type: 'text',
                        text: limitAnthropicToolOutputText(formatFunctionCallResultForAnthropicTextContext(projection.result), projection.result, toolResultMaxChars),
                    },
                ],
            };
        default: {
            const _exhaustive = projection;
            return _exhaustive;
        }
    }
}
function buildAnthropicRequestMessagesSync(context) {
    const normalized = (0, tool_call_context_1.normalizeToolCallPairs)(context);
    const violation = (0, tool_call_context_1.findFirstToolCallAdjacencyViolation)(normalized);
    if (violation) {
        const detail = (0, tool_call_context_1.formatToolCallAdjacencyViolation)(violation, 'ANTH sync context reconstruction');
        log.error(detail, new Error('anthropic_tool_call_adjacency_violation'), {
            callId: violation.callId,
            toolName: violation.toolName,
            violationKind: violation.kind,
            index: violation.index,
        });
        throw new Error(detail);
    }
    const toolResultMaxChars = (0, tool_output_limit_1.resolveProviderToolResultMaxChars)(undefined);
    const projectedContext = projectFunctionCallPairsForAnthropicTextContext(normalized);
    const messages = [];
    for (const msg of projectedContext) {
        messages.push(anthropicProjectionToMessageSync(msg, toolResultMaxChars));
    }
    return assembleAnthropicTurns(messages);
}
function applyInputJsonDelta(state, partialJson) {
    if (partialJson.length === 0)
        return;
    if (state.inputJson.length === 0) {
        state.inputJson = partialJson;
        return;
    }
    // Some Anthropic-compatible providers stream `partial_json` as the full JSON accumulated so far
    // (cumulative), while Anthropic streams deltas. Support both.
    if (partialJson.startsWith(state.inputJson)) {
        state.inputJson = partialJson;
        return;
    }
    if (state.inputJson.startsWith(partialJson)) {
        return;
    }
    state.inputJson += partialJson;
}
function stringifyToolUseInitialInput(input) {
    const stringified = JSON.stringify(input);
    return typeof stringified === 'string' && stringified.length > 0 ? stringified : '{}';
}
function resolveToolUseArgumentsJson(state) {
    const trimmed = state.inputJson.trim();
    if (trimmed.length === 0) {
        return stringifyToolUseInitialInput(state.initialInput);
    }
    try {
        JSON.parse(trimmed);
        return state.inputJson;
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        log.warn('ANTH malformed tool_use input_json_delta; preserving raw arguments for tool feedback', error, {
            callId: state.id,
            toolName: state.name,
            rawPreview: trimmed.length > 200 ? `${trimmed.slice(0, 200)}...` : trimmed,
            parseError: message,
        });
        return state.inputJson;
    }
}
function assertValidAnthropicStreamGenseq(genseq) {
    if (genseq === undefined)
        return;
    if (!Number.isInteger(genseq) || genseq <= 0) {
        throw new Error(`Invalid Anthropic stream genseq for tool-call correlation: ${String(genseq)}`);
    }
}
/**
 * Convert a single ChatMessage to content blocks for Anthropic SDK.
 * Returns array of content blocks (may contain multiple for complex messages).
 * Call/result pairing is handled before this point by Anthropic context projection.
 */
function chatMessageToContentBlocks(chatMsg) {
    // Handle TransientGuide messages as text content
    if (chatMsg.type === 'transient_guide_msg') {
        const block = { type: 'text', text: chatMsg.content };
        return [block];
    }
    // Handle prompting and reporting messages
    if (chatMsg.type === 'prompting_msg' || chatMsg.type === 'environment_msg') {
        const block = { type: 'text', text: chatMsg.content };
        return [block];
    }
    // Handle saying and thinking messages from assistant
    if (chatMsg.type === 'saying_msg' || chatMsg.type === 'thinking_msg') {
        const block = { type: 'text', text: chatMsg.content };
        return [block];
    }
    // Handle function calls
    if (chatMsg.type === 'func_call_msg') {
        const block = {
            type: 'text',
            text: formatFunctionCallForAnthropicTextContext({ call: chatMsg }),
        };
        return [block];
    }
    // Fallback for direct conversion; normal persisted call/result pairs are projected as text first.
    if (chatMsg.type === 'func_result_msg') {
        const block = {
            type: 'tool_result',
            tool_use_id: chatMsg.id,
            content: chatMsg.content,
        };
        return [block];
    }
    // Handle tellask call results (NOT LLM-native tool use; represented as role='user' text)
    if (chatMsg.type === 'tellask_result_msg' || chatMsg.type === 'tellask_carryover_msg') {
        const msg = {
            type: 'text',
            text: chatMsg.content,
        };
        return [msg];
    }
    // Exhaustiveness check - ensure all ChatMessage types are handled
    throw new Error(`Unsupported ChatMessage type: ${JSON.stringify(chatMsg)}`);
}
function chatMessageToAnthropic(chatMsg) {
    const contentBlocks = chatMessageToContentBlocks(chatMsg);
    if (contentBlocks.length === 0) {
        throw new Error(`No content blocks generated for message: ${JSON.stringify(chatMsg)}`);
    }
    // Determine the role, handling cases where role might not exist
    let role = 'assistant'; // default
    if ('role' in chatMsg) {
        role = chatMsg.role === 'tool' ? 'user' : chatMsg.role;
    }
    return {
        role,
        content: contentBlocks.length === 1 ? contentBlocks : contentBlocks,
    };
}
function anthropicToChatMessages(message, genseq, forcedJsonToolName) {
    const results = [];
    if (!isRecord(message)) {
        throw new Error('Invalid Anthropic message: expected object');
    }
    const role = message.role;
    const content = message.content;
    if (role !== 'assistant' && role !== 'user') {
        throw new Error('Invalid Anthropic message: missing role');
    }
    const blocks = Array.isArray(content) ? content : [];
    const thinkingBlocks = blocks.filter((block) => isRecord(block) && block.type === 'thinking');
    if (thinkingBlocks.length > 0 && role === 'assistant') {
        const thinkingText = thinkingBlocks
            .map((block) => (typeof block.thinking === 'string' ? block.thinking : ''))
            .join('');
        if (thinkingText) {
            results.push({
                type: 'thinking_msg',
                role: 'assistant',
                content: thinkingText,
                genseq: genseq,
            });
        }
    }
    const pushAssistantSaying = (contentText) => {
        if (contentText.length === 0)
            return;
        const previous = results.length > 0 ? results[results.length - 1] : undefined;
        if (previous?.type === 'saying_msg' && previous.role === 'assistant') {
            results[results.length - 1] = {
                ...previous,
                content: `${previous.content}${contentText}`,
            };
            return;
        }
        results.push({
            type: 'saying_msg',
            role: 'assistant',
            content: contentText,
            genseq,
        });
    };
    if (role === 'assistant') {
        for (const block of blocks) {
            if (isRecord(block) && block.type === 'text' && typeof block.text === 'string') {
                pushAssistantSaying(block.text);
                continue;
            }
            if (isToolUseBlock(block)) {
                if (forcedJsonToolName && block.name === forcedJsonToolName) {
                    const jsonText = serializeAnthropicForcedJsonObject(block.input, `tool_use:${block.id}:${block.name}`);
                    pushAssistantSaying(jsonText);
                    continue;
                }
                results.push({
                    type: 'func_call_msg',
                    id: block.id,
                    name: block.name,
                    arguments: JSON.stringify(block.input),
                    role: 'assistant',
                    genseq,
                });
            }
        }
    }
    return results;
}
async function emitAnthropicStreamReadError(receiver, error) {
    const errorText = error instanceof Error ? error.message : String(error);
    const detail = `ANTH stream read failed: ${errorText}`;
    log.warn(detail, error);
    if (receiver.streamError) {
        await receiver.streamError(detail);
    }
}
async function* streamWithReadDiagnostics(stream, receiver) {
    const iterator = stream[Symbol.asyncIterator]();
    let done = false;
    try {
        for (;;) {
            let next;
            try {
                next = await iterator.next();
            }
            catch (error) {
                await emitAnthropicStreamReadError(receiver, error);
                throw error;
            }
            if (next.done === true) {
                done = true;
                return;
            }
            yield next.value;
        }
    }
    finally {
        if (!done && iterator.return) {
            try {
                await iterator.return();
            }
            catch (error) {
                log.warn('ANTH stream iterator return failed during cleanup', error);
            }
        }
    }
}
/**
 * Validate that reconstructed context produces valid Anthropic SDK MessageParam[].
 * Checks for proper role assignment, content block structure, and tool call/result pairing.
 */
function validateReconstructedContext(messages) {
    for (const msg of messages) {
        // Validate role
        if (msg.role !== 'user' && msg.role !== 'assistant') {
            throw new Error(`Invalid message role: ${msg.role}. Must be 'user' or 'assistant'.`);
        }
        // Validate content blocks
        if (!Array.isArray(msg.content) || msg.content.length === 0) {
            throw new Error('Message must have non-empty content array.');
        }
        for (const block of msg.content) {
            // Validate content block type
            if (!['text', 'thinking', 'tool_use', 'tool_result'].includes(block.type)) {
                throw new Error(`Invalid content block type: ${block.type}`);
            }
            // Validate thinking blocks have signature
            if (block.type === 'thinking' && !block.signature) {
                throw new Error('Thinking blocks must have a signature.');
            }
            // Validate tool_use blocks have required fields
            if (block.type === 'tool_use') {
                if (!block.id || !block.name || block.input === undefined) {
                    throw new Error('Tool_use blocks must have id, name, and input fields.');
                }
            }
            // Validate tool_result blocks have required fields
            if (block.type === 'tool_result') {
                // Check for tool_use_id field (Anthropic SDK uses 'tool_use_id' for ToolResultBlockParam)
                const hasToolUseId = 'tool_use_id' in block &&
                    typeof block.tool_use_id === 'string' &&
                    block.tool_use_id.length > 0;
                const hasContent = 'content' in block && block.content !== undefined;
                if (!hasToolUseId || !hasContent) {
                    throw new Error('Tool_result blocks must have tool_use_id (reference to tool_use) and content fields.');
                }
            }
        }
    }
}
async function consumeAnthropicStream(stream, receiver, options = {}) {
    const { abortSignal, forcedJsonToolName, genseq } = options;
    assertValidAnthropicStreamGenseq(genseq);
    // Stream lifecycle management using SDK start/stop events
    const activeContentBlocks = new Map();
    const activeToolUses = new Map();
    let sayingStarted = false;
    let thinkingStarted = false;
    let messageStarted = false;
    let messageStopped = false;
    let usage = { kind: 'unavailable' };
    let returnedModel;
    for await (const event of streamWithReadDiagnostics(stream, receiver)) {
        if (abortSignal?.aborted) {
            throw new Error('AbortError');
        }
        switch (event.type) {
            case 'content_block_start': {
                const blockIndex = event.index;
                const contentBlock = event.content_block;
                const existingBlock = activeContentBlocks.get(blockIndex);
                if (existingBlock) {
                    log.warn('ANTH content_block_start replacing active content block at index', new Error('content_block_start_without_stop'), {
                        index: blockIndex,
                        prevType: existingBlock.type,
                        nextType: contentBlock.type,
                    });
                    activeToolUses.delete(blockIndex);
                }
                activeContentBlocks.set(blockIndex, contentBlock);
                // Track tool use so we can emit function calls once JSON is complete
                if (contentBlock.type === 'tool_use') {
                    activeToolUses.set(blockIndex, {
                        id: contentBlock.id,
                        name: contentBlock.name,
                        inputJson: '',
                        initialInput: contentBlock.input,
                    });
                }
                break;
            }
            case 'content_block_delta': {
                const blockIndex = event.index;
                const activeContentBlock = activeContentBlocks.get(blockIndex);
                // Only process deltas for known content blocks
                if (!activeContentBlock) {
                    log.warn('ANTH unexpected content_block_delta without active content block', new Error('Delta received before content_block_start'), {
                        index: blockIndex,
                        deltaType: event.delta.type,
                    });
                    break;
                }
                const delta = event.delta;
                // Handle all RawContentBlockDelta types from Anthropic SDK
                if (delta.type === 'text_delta') {
                    const textDelta = delta.text ?? '';
                    if (textDelta) {
                        if (thinkingStarted) {
                            log.error('ANTH stream ordering violation: received text_delta while thinking stream still active', new Error('anthropic_stream_order_violation'));
                            await receiver.thinkingFinish();
                            thinkingStarted = false;
                        }
                        if (!sayingStarted) {
                            sayingStarted = true;
                            await receiver.sayingStart();
                        }
                        await receiver.sayingChunk(textDelta);
                    }
                }
                else if (delta.type === 'thinking_delta') {
                    const thinkingDelta = delta.thinking ?? '';
                    if (thinkingDelta) {
                        if (sayingStarted) {
                            log.error('ANTH stream ordering violation: received thinking_delta while saying stream still active', new Error('anthropic_stream_order_violation'));
                            await receiver.sayingFinish();
                            sayingStarted = false;
                        }
                        if (!thinkingStarted) {
                            thinkingStarted = true;
                            await receiver.thinkingStart();
                        }
                        await receiver.thinkingChunk(thinkingDelta);
                    }
                }
                else if (delta.type === 'citations_delta') {
                    // Handle CitationsDelta - typically just logging for now
                }
                else if (delta.type === 'signature_delta') {
                    // Handle SignatureDelta - typically just logging for now
                }
                else if (delta.type === 'input_json_delta') {
                    const partialJson = delta.partial_json;
                    const activeToolUse = activeToolUses.get(blockIndex);
                    if (activeToolUse) {
                        applyInputJsonDelta(activeToolUse, partialJson);
                    }
                    else if (partialJson.length > 0) {
                        log.warn('ANTH input_json_delta without active tool_use', new Error('Input JSON delta received without active tool_use block'), {
                            hasCurrentBlock: true,
                            blockIndex,
                            blockType: activeContentBlock.type,
                        });
                    }
                }
                break;
            }
            case 'content_block_stop': {
                const blockIndex = event.index;
                const activeContentBlock = activeContentBlocks.get(blockIndex);
                if (!activeContentBlock) {
                    break;
                }
                // Close thinking as soon as the thinking block ends so downstream UI/persistence reflects
                // strict generation order (thinking first, then saying). This also avoids emitting
                // thinking_finish after the main message has already completed.
                if (activeContentBlock.type === 'tool_use') {
                    const activeToolUse = activeToolUses.get(blockIndex);
                    if (!activeToolUse) {
                        log.warn('ANTH tool_use stop without active tool_use', new Error('Tool_use block stopped without active tool tracking'), {
                            blockIndex,
                        });
                    }
                    else {
                        if (forcedJsonToolName && activeToolUse.name === forcedJsonToolName) {
                            const forcedInput = parseForcedJsonToolInput(activeToolUse.inputJson, activeToolUse.initialInput, `tool_use:${activeToolUse.id}:${activeToolUse.name}`);
                            const jsonText = serializeAnthropicForcedJsonObject(forcedInput, `tool_use:${activeToolUse.id}:${activeToolUse.name}`);
                            if (!sayingStarted) {
                                sayingStarted = true;
                                await receiver.sayingStart();
                            }
                            await receiver.sayingChunk(jsonText);
                            await receiver.sayingFinish();
                            sayingStarted = false;
                        }
                        else {
                            const argsJson = resolveToolUseArgumentsJson(activeToolUse);
                            await receiver.funcCall(activeToolUse.id, activeToolUse.name, argsJson);
                        }
                    }
                    activeToolUses.delete(blockIndex);
                }
                else if (activeContentBlock.type === 'thinking') {
                    if (thinkingStarted) {
                        await receiver.thinkingFinish();
                        thinkingStarted = false;
                    }
                }
                activeContentBlocks.delete(blockIndex);
                break;
            }
            case 'message_start': {
                messageStarted = true;
                messageStopped = false;
                if (returnedModel === undefined) {
                    returnedModel = tryExtractApiReturnedModel(event.message);
                }
                const startUsage = event.message.usage;
                const cacheCreation = typeof startUsage.cache_creation_input_tokens === 'number'
                    ? startUsage.cache_creation_input_tokens
                    : 0;
                const cacheRead = typeof startUsage.cache_read_input_tokens === 'number'
                    ? startUsage.cache_read_input_tokens
                    : 0;
                const promptTokens = startUsage.input_tokens + cacheCreation + cacheRead;
                const completionTokens = startUsage.output_tokens;
                usage = {
                    kind: 'available',
                    promptTokens,
                    completionTokens,
                    totalTokens: promptTokens + completionTokens,
                };
                break;
            }
            case 'message_delta': {
                const deltaUsage = event.usage;
                const inputTokens = typeof deltaUsage.input_tokens === 'number' ? deltaUsage.input_tokens : null;
                const cacheCreation = typeof deltaUsage.cache_creation_input_tokens === 'number'
                    ? deltaUsage.cache_creation_input_tokens
                    : 0;
                const cacheRead = typeof deltaUsage.cache_read_input_tokens === 'number'
                    ? deltaUsage.cache_read_input_tokens
                    : 0;
                if (usage.kind === 'available') {
                    const promptTokens = inputTokens !== null ? inputTokens + cacheCreation + cacheRead : usage.promptTokens;
                    const completionTokens = deltaUsage.output_tokens;
                    usage = {
                        kind: 'available',
                        promptTokens,
                        completionTokens,
                        totalTokens: promptTokens + completionTokens,
                    };
                }
                else if (inputTokens !== null) {
                    const promptTokens = inputTokens + cacheCreation + cacheRead;
                    const completionTokens = deltaUsage.output_tokens;
                    usage = {
                        kind: 'available',
                        promptTokens,
                        completionTokens,
                        totalTokens: promptTokens + completionTokens,
                    };
                }
                break;
            }
            case 'message_stop': {
                messageStopped = true;
                activeContentBlocks.clear();
                activeToolUses.clear();
                if (thinkingStarted) {
                    await receiver.thinkingFinish();
                    thinkingStarted = false;
                }
                if (sayingStarted) {
                    await receiver.sayingFinish();
                    sayingStarted = false;
                }
                break;
            }
            // Note: input_json_delta is handled within content_block_delta as part of input_json_delta delta type
            default: {
                // Handle unexpected events with proper type checking
                const unknownEvent = event;
                const eventType = isRecord(unknownEvent) && typeof unknownEvent.type === 'string' ? unknownEvent.type : '';
                log.warn('ANTH unexpected llm event', new Error('Unknown event type'), {
                    eventType: eventType.length > 0 ? eventType : 'unknown',
                });
                break;
            }
        }
    }
    if ((messageStarted && !messageStopped) ||
        activeContentBlocks.size > 0 ||
        activeToolUses.size > 0 ||
        thinkingStarted ||
        sayingStarted) {
        const detail = 'ANTH incomplete stream: provider event stream ended before a complete message lifecycle ' +
            `(messageStarted=${String(messageStarted)}, messageStopped=${String(messageStopped)}, ` +
            `activeContentBlocks=${String(activeContentBlocks.size)}, activeToolUses=${String(activeToolUses.size)}, thinkingStarted=${String(thinkingStarted)}, sayingStarted=${String(sayingStarted)})`;
        log.error(detail, new Error('anthropic_incomplete_stream_state'));
        if (receiver.streamError) {
            await receiver.streamError(detail);
        }
        throw new Error(detail);
    }
    return { usage, llmGenModel: returnedModel };
}
/**
 * Reconstruct Anthropic context from persisted messages with genseq tracking.
 * This function groups messages by generation sequence and converts them to
 * Anthropic SDK MessageParam[] format for context restoration.
 *
 * @param persistedMessages - Array of ChatMessage objects with genseq tracking
 * @returns Array of MessageParam objects in Anthropic SDK format
 */
function reconstructAnthropicContextWrapper(persistedMessages) {
    const reconstructed = reconstructAnthropicContext(persistedMessages);
    // Validate the reconstructed context
    try {
        validateReconstructedContext(reconstructed);
    }
    catch (error) {
        log.error('Context reconstruction validation failed:', error);
        throw new Error(`Invalid reconstructed context: ${error}`);
    }
    return reconstructed;
}
async function reconstructAnthropicContextWrapperAsync(persistedMessages) {
    const reconstructed = await buildAnthropicRequestMessages(persistedMessages, {
        dialogSelfId: '',
        dialogRootId: '',
        providerKey: 'anthropic',
        modelKey: 'unknown',
    });
    // Validate the reconstructed context
    try {
        validateReconstructedContext(reconstructed);
    }
    catch (error) {
        log.error('Context reconstruction validation failed:', error);
        throw new Error(`Invalid reconstructed context: ${error}`);
    }
    return reconstructed;
}
/**
 * AnthropicGen
 *
 * Implements `LlmGenerator` for Anthropic, mapping tool calls and text deltas
 * and providing both streaming and non-streaming generation.
 */
class AnthropicGen {
    generatorApiType;
    constructor(generatorApiType = 'anthropic') {
        this.generatorApiType = generatorApiType;
    }
    get apiType() {
        return this.generatorApiType;
    }
    classifyFailure(error) {
        return (0, failure_classifier_1.classifyAnthropicFailure)(error);
    }
    async genToReceiver(providerConfig, agent, systemPrompt, funcTools, requestContext, context, receiver, genseq, abortSignal) {
        const apiKey = process.env[providerConfig.apiKeyEnvVar];
        if (!apiKey)
            throw new Error(`Missing API key env var ${providerConfig.apiKeyEnvVar}`);
        const requestMessages = await buildAnthropicRequestMessages(context, requestContext, providerConfig, receiver.toolResultImageIngest, receiver.userImageIngest);
        const anthropicParams = resolveAnthropicParams(providerConfig, agent);
        const forceJsonResponse = resolveAnthropicJsonResponseEnabled(providerConfig, agent);
        // Safety check: model should never be undefined at this point due to validation in driver
        if (!agent.model) {
            throw new Error(`Internal error: Model is undefined for agent '${agent.id}' after validation`);
        }
        // Get model info from provider config for output_length
        const modelInfo = providerConfig.models[agent.model];
        const outputLength = modelInfo?.output_length;
        const maxTokens = anthropicParams.max_tokens ?? outputLength ?? 1024;
        const thinking = buildAnthropicThinkingConfig(anthropicParams, providerConfig);
        const anthropicTools = buildAnthropicToolList(funcTools, forceJsonResponse);
        const toolChoice = resolveAnthropicToolChoice(anthropicTools, requestContext, forceJsonResponse);
        const baseParams = {
            model: agent.model,
            messages: requestMessages,
            system: systemPrompt.length > 0 ? systemPrompt : undefined,
            max_tokens: maxTokens,
            ...(thinking !== undefined && { thinking }),
            ...(anthropicTools.length > 0 && { tools: anthropicTools }),
            ...(toolChoice !== undefined && { tool_choice: toolChoice }),
            ...(anthropicParams.temperature !== undefined && {
                temperature: anthropicParams.temperature,
            }),
            ...(anthropicParams.top_p !== undefined && { top_p: anthropicParams.top_p }),
            ...(anthropicParams.top_k !== undefined && { top_k: anthropicParams.top_k }),
            ...(anthropicParams.stop_sequences !== undefined && {
                stop_sequences: anthropicParams.stop_sequences,
            }),
            ...(anthropicParams.reasoning_split !== undefined && {
                reasoning_split: anthropicParams.reasoning_split,
            }),
        };
        const streamParams = {
            ...baseParams,
            stream: true,
            ...(abortSignal ? { signal: abortSignal } : {}),
        };
        const stream = providerConfig.apiType === 'anthropic-compatible'
            ? streamAnthropicCompatibleRawSse({
                apiKey,
                providerConfig,
                agent,
                requestContext,
                params: streamParams,
            })
            : createAnthropicClient({
                apiKey,
                providerConfig,
                agent,
                requestContext,
                requestKind: 'stream',
            }).messages.stream(streamParams);
        return consumeAnthropicStream(stream, receiver, {
            abortSignal,
            forcedJsonToolName: forceJsonResponse ? ANTHROPIC_JSON_RESPONSE_TOOL_NAME : undefined,
            genseq,
        });
    }
    async genMoreMessages(providerConfig, agent, systemPrompt, funcTools, requestContext, context, genseq, abortSignal) {
        const apiKey = process.env[providerConfig.apiKeyEnvVar];
        if (!apiKey)
            throw new Error(`Missing API key env var ${providerConfig.apiKeyEnvVar}`);
        const client = createAnthropicClient({
            apiKey,
            providerConfig,
            agent,
            requestContext,
            requestKind: 'batch',
        });
        const outputs = [];
        const requestMessages = await buildAnthropicRequestMessages(context, requestContext, providerConfig, async (ingest) => {
            outputs.push({ kind: 'tool_result_image_ingest', ingest });
        }, async (ingest) => {
            outputs.push({ kind: 'user_image_ingest', ingest });
        });
        const anthropicParams = resolveAnthropicParams(providerConfig, agent);
        const forceJsonResponse = resolveAnthropicJsonResponseEnabled(providerConfig, agent);
        // Safety check: model should never be undefined at this point due to validation in driver
        if (!agent.model) {
            throw new Error(`Internal error: Model is undefined for agent '${agent.id}' after validation`);
        }
        // Get model info from provider config for output_length
        const modelInfo = providerConfig.models[agent.model];
        const outputLength = modelInfo?.output_length;
        const maxTokens = anthropicParams.max_tokens ?? outputLength ?? 1024;
        const thinking = buildAnthropicThinkingConfig(anthropicParams, providerConfig);
        const anthropicTools = buildAnthropicToolList(funcTools, forceJsonResponse);
        const toolChoice = resolveAnthropicToolChoice(anthropicTools, requestContext, forceJsonResponse);
        const baseParams = {
            model: agent.model,
            messages: requestMessages,
            system: systemPrompt.length > 0 ? systemPrompt : undefined,
            max_tokens: maxTokens,
            ...(thinking !== undefined && { thinking }),
            ...(anthropicTools.length > 0 && { tools: anthropicTools }),
            ...(toolChoice !== undefined && { tool_choice: toolChoice }),
            ...(anthropicParams.temperature !== undefined && {
                temperature: anthropicParams.temperature,
            }),
            ...(anthropicParams.top_p !== undefined && { top_p: anthropicParams.top_p }),
            ...(anthropicParams.top_k !== undefined && { top_k: anthropicParams.top_k }),
            ...(anthropicParams.stop_sequences !== undefined && {
                stop_sequences: anthropicParams.stop_sequences,
            }),
        };
        const createParams = {
            ...baseParams,
            stream: false,
            ...(abortSignal ? { signal: abortSignal } : {}),
        };
        const response = await client.messages.create(createParams);
        if (!response) {
            throw new Error('No response from Anthropic API');
        }
        const returnedModel = typeof response.model === 'string' ? response.model : undefined;
        const responseUsage = response.usage;
        const cacheCreation = typeof responseUsage.cache_creation_input_tokens === 'number'
            ? responseUsage.cache_creation_input_tokens
            : 0;
        const cacheRead = typeof responseUsage.cache_read_input_tokens === 'number'
            ? responseUsage.cache_read_input_tokens
            : 0;
        const promptTokens = responseUsage.input_tokens + cacheCreation + cacheRead;
        const completionTokens = responseUsage.output_tokens;
        const usage = {
            kind: 'available',
            promptTokens,
            completionTokens,
            totalTokens: promptTokens + completionTokens,
        };
        const messages = anthropicToChatMessages(response, genseq, forceJsonResponse ? ANTHROPIC_JSON_RESPONSE_TOOL_NAME : undefined);
        const orderedOutputs = outputs.length > 0
            ? [...outputs, ...messages.map((message) => ({ kind: 'message', message }))]
            : [];
        return {
            messages,
            ...(orderedOutputs.length > 0 ? { outputs: orderedOutputs } : {}),
            usage,
            llmGenModel: returnedModel,
        };
    }
}
exports.AnthropicGen = AnthropicGen;
