"use strict";
/**
 * Model ID normalization helpers.
 *
 * Handles provider-aware compatibility between dotted and hyphenated Claude
 * model version formats (e.g., 4.6 vs 4-6).
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeModelEnvVarsForProvider = exports.normalizeModelIdForRouting = exports.canonicalizeModelIdForProvider = exports.normalizeModelIdForProvider = exports.getDeniedModelIdReasonForProvider = exports.isDeniedAntigravityModelId = exports.migrateDeniedAntigravityModelAliases = exports.normalizeDeprecatedAntigravityModelAliases = exports.normalizeClaudeDottedThinkingMajorMinor = exports.normalizeClaudeDottedMajorMinor = exports.normalizeIFlowLegacyModelAliases = exports.normalizeCodexLegacyModelAliases = exports.stripCodexEffortSuffix = exports.isIFlowProvider = exports.isCodexProvider = exports.isAntigravityProvider = exports.extractProviderFromPathname = exports.parseCodexModelTuningAlias = exports.MODEL_ENV_VAR_KEYS = void 0;
const extended_context_utils_1 = require("../../shared/extended-context-utils");
/** Env vars that carry model identifiers. */
exports.MODEL_ENV_VAR_KEYS = extended_context_utils_1.ANTHROPIC_MODEL_ENV_KEYS;
const CLAUDE_DOTTED_VERSION_REGEX = /claude-(sonnet|opus|haiku)-(\d+)\.(\d+)(?=(?:$|-|\[|\(|\/))/gi;
const CLAUDE_DOTTED_THINKING_REGEX = /claude-(sonnet|opus|haiku)-(\d+)\.(\d+)-thinking(?=(?:$|-|\[|\(|\/))/gi;
const DEPRECATED_ANTIGRAVITY_SONNET_46_THINKING_REGEX = /claude-sonnet-4(?:[.-])6-thinking(?=(?:$|-|\[|\(|\/))/gi;
const CANONICAL_ANTIGRAVITY_SONNET_46_MODEL = 'claude-sonnet-4-6';
const DENIED_ANTIGRAVITY_MODEL_REGEX = /claude-(?:opus|sonnet)-4(?:[.-])5(?:-thinking)?(?=(?:$|[^a-z0-9]))/i;
const DENIED_ANTIGRAVITY_OPUS_45_REGEX = /claude-opus-4(?:[.-])5(?:-thinking)?(?=(?:$|[^a-z0-9]))/gi;
const DENIED_ANTIGRAVITY_SONNET_45_REGEX = /claude-sonnet-4(?:[.-])5(?:-thinking)?(?=(?:$|[^a-z0-9]))/gi;
const CANONICAL_ANTIGRAVITY_OPUS_46_MODEL = 'claude-opus-4-6-thinking';
const CODEX_TUNING_SUFFIX_TOKEN_REGEX = /-(minimal|low|medium|high|xhigh|fast)$/i;
const CODEX_LEGACY_MODEL_ALIASES = Object.freeze({
    'gpt-5-codex': 'gpt-5.4',
    'gpt-5-codex-mini': 'gpt-5.4-mini',
    'gpt-5-mini': 'gpt-5.4-mini',
    'gpt-5.1-codex': 'gpt-5.2',
    'gpt-5.1-codex-mini': 'gpt-5.4-mini',
    'gpt-5.1-codex-max': 'gpt-5.4',
    'gpt-5.2-codex': 'gpt-5.2',
    'gpt-5.2-codex-mini': 'gpt-5.4-mini',
});
const IFLOW_LEGACY_MODEL_ALIASES = Object.freeze({
    'iflow-default': 'qwen3-coder-plus',
    'kimi-k2.5': 'kimi-k2',
    'deepseek-v3.2-chat': 'deepseek-v3.2',
    'glm-4.7': 'glm-4.6',
    'minimax-m2.5': 'qwen3-coder-plus',
});
function trimModelId(model) {
    return model.trim();
}
function splitBaseModelAndSuffix(model) {
    const openParenIndex = model.indexOf('(');
    const openBracketIndex = model.indexOf('[');
    const suffixIndexCandidates = [openParenIndex, openBracketIndex].filter((index) => index >= 0);
    if (suffixIndexCandidates.length === 0) {
        return { baseModel: model, suffix: '' };
    }
    const suffixStart = Math.min(...suffixIndexCandidates);
    return {
        baseModel: model.slice(0, suffixStart),
        suffix: model.slice(suffixStart),
    };
}
function splitCodexTuningSuffix(model) {
    let baseModel = model;
    let effort = null;
    let serviceTier = null;
    for (let consumed = 0; consumed < 2; consumed += 1) {
        const match = baseModel.match(CODEX_TUNING_SUFFIX_TOKEN_REGEX);
        if (!match?.[1])
            break;
        const token = match[1].toLowerCase();
        if (token === 'fast') {
            if (serviceTier)
                break;
            serviceTier = 'fast';
        }
        else {
            if (effort)
                break;
            effort = token;
        }
        baseModel = baseModel.slice(0, -match[0].length);
    }
    const suffix = [effort, serviceTier].filter(Boolean).join('-');
    return {
        baseModel,
        suffix: suffix ? `-${suffix}` : '',
    };
}
function parseCodexModelTuningAlias(model) {
    const trimmed = trimModelId(model);
    const { baseModel, suffix } = splitBaseModelAndSuffix(trimmed);
    if (suffix)
        return null;
    const parsed = splitCodexTuningSuffix(baseModel);
    if (!parsed.suffix)
        return null;
    const tokens = parsed.suffix.slice(1).split('-');
    const effort = tokens.find((token) => token !== 'fast');
    return {
        baseModel: parsed.baseModel.trim(),
        effort: effort ?? null,
        serviceTier: tokens.includes('fast') ? 'fast' : null,
    };
}
exports.parseCodexModelTuningAlias = parseCodexModelTuningAlias;
/**
 * Extract provider segment from `/api/provider/{provider}` request paths.
 *
 * @example
 * extractProviderFromPathname('/api/provider/agy/v1/messages')
 * // => 'agy'
 */
function extractProviderFromPathname(pathname) {
    const match = pathname.match(/\/api\/provider\/([^/]+)/i);
    if (!match?.[1])
        return null;
    return match[1].toLowerCase();
}
exports.extractProviderFromPathname = extractProviderFromPathname;
/** Whether the provider uses Antigravity model routing conventions. */
function isAntigravityProvider(provider) {
    if (typeof provider !== 'string')
        return false;
    const normalized = provider.trim().toLowerCase();
    return normalized === 'agy' || normalized === 'antigravity';
}
exports.isAntigravityProvider = isAntigravityProvider;
/** Whether provider maps to Codex model canonicalization rules. */
function isCodexProvider(provider) {
    if (typeof provider !== 'string')
        return false;
    return provider.trim().toLowerCase() === 'codex';
}
exports.isCodexProvider = isCodexProvider;
/** Whether provider maps to iFlow model alias canonicalization rules. */
function isIFlowProvider(provider) {
    if (typeof provider !== 'string')
        return false;
    return provider.trim().toLowerCase() === 'iflow';
}
exports.isIFlowProvider = isIFlowProvider;
/** Strip Codex effort/service-tier suffixes while preserving trailing config suffixes. */
function stripCodexEffortSuffix(model) {
    const trimmed = trimModelId(model);
    const { baseModel, suffix } = splitBaseModelAndSuffix(trimmed);
    const { baseModel: withoutEffort } = splitCodexTuningSuffix(baseModel);
    return `${withoutEffort}${suffix}`;
}
exports.stripCodexEffortSuffix = stripCodexEffortSuffix;
/** Normalize legacy Codex aliases to the current public Codex model IDs. */
function normalizeCodexLegacyModelAliases(model) {
    const trimmed = trimModelId(model);
    const { baseModel, suffix } = splitBaseModelAndSuffix(trimmed);
    const { baseModel: baseWithoutEffort, suffix: tuningSuffix } = splitCodexTuningSuffix(baseModel);
    const replacement = CODEX_LEGACY_MODEL_ALIASES[baseWithoutEffort.trim().toLowerCase()];
    if (!replacement) {
        return trimmed;
    }
    return `${replacement}${tuningSuffix}${suffix}`;
}
exports.normalizeCodexLegacyModelAliases = normalizeCodexLegacyModelAliases;
/**
 * Normalize known legacy iFlow model aliases to current upstream model IDs.
 * Preserves suffixes such as (budget) and [1m].
 */
function normalizeIFlowLegacyModelAliases(model) {
    const trimmed = trimModelId(model);
    const { baseModel, suffix } = splitBaseModelAndSuffix(trimmed);
    const replacement = IFLOW_LEGACY_MODEL_ALIASES[baseModel.trim().toLowerCase()];
    if (!replacement) {
        return trimmed;
    }
    return `${replacement}${suffix}`;
}
exports.normalizeIFlowLegacyModelAliases = normalizeIFlowLegacyModelAliases;
/** Normalize Claude dotted major.minor IDs to hyphenated format. */
function normalizeClaudeDottedMajorMinor(model) {
    return model.replace(CLAUDE_DOTTED_VERSION_REGEX, (_match, family, major, minor) => `claude-${family.toLowerCase()}-${major}-${minor}`);
}
exports.normalizeClaudeDottedMajorMinor = normalizeClaudeDottedMajorMinor;
/**
 * Normalize only dotted Claude thinking IDs to hyphenated format.
 * Keeps non-thinking dotted IDs unchanged.
 */
function normalizeClaudeDottedThinkingMajorMinor(model) {
    return model.replace(CLAUDE_DOTTED_THINKING_REGEX, (_match, family, major, minor) => `claude-${family.toLowerCase()}-${major}-${minor}-thinking`);
}
exports.normalizeClaudeDottedThinkingMajorMinor = normalizeClaudeDottedThinkingMajorMinor;
/**
 * Antigravity no longer exposes `claude-sonnet-4-6-thinking`.
 * Canonicalize legacy aliases to `claude-sonnet-4-6` while preserving suffixes.
 */
function normalizeDeprecatedAntigravityModelAliases(model) {
    return model.replace(DEPRECATED_ANTIGRAVITY_SONNET_46_THINKING_REGEX, CANONICAL_ANTIGRAVITY_SONNET_46_MODEL);
}
exports.normalizeDeprecatedAntigravityModelAliases = normalizeDeprecatedAntigravityModelAliases;
/**
 * Migrate denylisted Antigravity Claude 4.5 aliases to supported Claude 4.6 IDs.
 * This is intended for repairing stale local settings at runtime.
 */
function migrateDeniedAntigravityModelAliases(model) {
    const normalized = normalizeClaudeDottedMajorMinor(trimModelId(model));
    return normalized
        .replace(DENIED_ANTIGRAVITY_OPUS_45_REGEX, CANONICAL_ANTIGRAVITY_OPUS_46_MODEL)
        .replace(DENIED_ANTIGRAVITY_SONNET_45_REGEX, CANONICAL_ANTIGRAVITY_SONNET_46_MODEL);
}
exports.migrateDeniedAntigravityModelAliases = migrateDeniedAntigravityModelAliases;
/**
 * Deprecated Antigravity models that are denylisted and should not be used.
 */
function isDeniedAntigravityModelId(model) {
    return DENIED_ANTIGRAVITY_MODEL_REGEX.test(trimModelId(model));
}
exports.isDeniedAntigravityModelId = isDeniedAntigravityModelId;
/**
 * Returns deny reason when model ID is blocked for the given provider.
 */
function getDeniedModelIdReasonForProvider(model, provider) {
    if (!isAntigravityProvider(provider))
        return null;
    const normalized = normalizeClaudeDottedMajorMinor(trimModelId(model));
    if (!isDeniedAntigravityModelId(normalized))
        return null;
    return 'Antigravity denylist: Claude Opus 4.5 and Claude Sonnet 4.5 are deprecated.';
}
exports.getDeniedModelIdReasonForProvider = getDeniedModelIdReasonForProvider;
/**
 * Normalize model ID for a specific provider.
 * Antigravity requires hyphenated Claude major.minor model IDs.
 *
 * @example
 * normalizeModelIdForProvider('claude-opus-4.6-thinking', 'agy')
 * // => 'claude-opus-4-6-thinking'
 *
 * @example
 * normalizeModelIdForProvider('claude-opus-4.6-thinking', 'gemini')
 * // => 'claude-opus-4.6-thinking'
 */
function normalizeModelIdForProvider(model, provider) {
    const trimmedModel = trimModelId(model);
    if (isIFlowProvider(provider)) {
        return normalizeIFlowLegacyModelAliases(trimmedModel);
    }
    if (isCodexProvider(provider)) {
        return normalizeCodexLegacyModelAliases(trimmedModel);
    }
    if (!isAntigravityProvider(provider))
        return trimmedModel;
    const normalizedDottedVersion = normalizeClaudeDottedMajorMinor(trimmedModel);
    return normalizeDeprecatedAntigravityModelAliases(normalizedDottedVersion);
}
exports.normalizeModelIdForProvider = normalizeModelIdForProvider;
/**
 * Canonicalize model ID for provider-specific compatibility.
 * - Codex: preserve valid effort suffixes while normalizing legacy aliases.
 * - Antigravity: normalize dotted/historical aliases.
 */
function canonicalizeModelIdForProvider(model, provider) {
    const trimmedModel = trimModelId(model);
    return normalizeModelIdForProvider(trimmedModel, provider);
}
exports.canonicalizeModelIdForProvider = canonicalizeModelIdForProvider;
/**
 * Normalize model ID for request routing.
 * - Antigravity routes: normalize all dotted Claude major.minor forms.
 * - Root/composite routes: normalize only thinking forms to avoid mutating
 *   valid non-thinking dotted IDs used by other providers.
 *
 * @example
 * normalizeModelIdForRouting('claude-sonnet-4.6-thinking', null)
 * // => 'claude-sonnet-4-6'
 *
 * @example
 * normalizeModelIdForRouting('claude-sonnet-4.6', null)
 * // => 'claude-sonnet-4.6'
 */
function normalizeModelIdForRouting(model, provider) {
    const trimmedModel = trimModelId(model);
    if (isAntigravityProvider(provider)) {
        return normalizeModelIdForProvider(trimmedModel, provider);
    }
    // Explicit provider routes still apply provider-specific canonicalization.
    if (typeof provider === 'string' && provider.trim().length > 0) {
        return normalizeModelIdForProvider(trimmedModel, provider);
    }
    const normalizedThinking = normalizeClaudeDottedThinkingMajorMinor(trimmedModel);
    return normalizeDeprecatedAntigravityModelAliases(normalizedThinking);
}
exports.normalizeModelIdForRouting = normalizeModelIdForRouting;
/**
 * Normalize model-related env vars for a provider.
 * Returns original object when no changes are required.
 *
 * @example
 * normalizeModelEnvVarsForProvider(
 *   { ANTHROPIC_MODEL: 'claude-sonnet-4.6-thinking' },
 *   'agy'
 * )
 * // => { ANTHROPIC_MODEL: 'claude-sonnet-4-6' }
 */
function normalizeModelEnvVarsForProvider(envVars, provider, keys = exports.MODEL_ENV_VAR_KEYS) {
    let nextEnv = null;
    for (const key of keys) {
        const value = envVars[key];
        if (typeof value !== 'string' || value.trim().length === 0)
            continue;
        const normalizedValue = normalizeModelIdForProvider(value, provider);
        if (normalizedValue === value)
            continue;
        if (!nextEnv)
            nextEnv = { ...envVars };
        nextEnv[key] = normalizedValue;
    }
    return nextEnv ?? envVars;
}
exports.normalizeModelEnvVarsForProvider = normalizeModelEnvVarsForProvider;
//# sourceMappingURL=model-id-normalizer.js.map