"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.LlmRequestFailedError = exports.LlmRetryStoppedError = void 0;
exports.evaluateDiligenceAutoContinueGate = evaluateDiligenceAutoContinueGate;
exports.maybePrepareDiligenceAutoContinuePrompt = maybePrepareDiligenceAutoContinuePrompt;
exports.suspendForKeepGoingBudgetExhausted = suspendForKeepGoingBudgetExhausted;
exports.validateFuncToolArguments = validateFuncToolArguments;
exports.runLlmRequestWithRetry = runLlmRequestWithRetry;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const diligence_1 = require("@longrun-ai/kernel/diligence");
const id_1 = require("@longrun-ai/kernel/utils/id");
const time_1 = require("@longrun-ai/kernel/utils/time");
const dialog_1 = require("../../dialog");
const dialog_display_state_1 = require("../../dialog-display-state");
const evt_registry_1 = require("../../evt-registry");
const log_1 = require("../../log");
const persistence_1 = require("../../persistence");
const problems_1 = require("../../problems");
const driver_messages_1 = require("../../runtime/driver-messages");
const work_language_1 = require("../../runtime/work-language");
const tool_1 = require("../../tool");
const api_quirks_1 = require("../api-quirks");
const gen_1 = require("../gen");
const stop_reason_i18n_1 = require("../stop-reason-i18n");
class LlmRetryStoppedError extends Error {
    reason;
    constructor(reason, message) {
        super(message);
        this.name = 'LlmRetryStoppedError';
        this.reason = reason;
    }
}
exports.LlmRetryStoppedError = LlmRetryStoppedError;
class LlmRequestFailedError extends Error {
    detail;
    streamErrorEmitted;
    i18nStopReason;
    constructor(args) {
        super(args.message);
        this.name = 'LlmRequestFailedError';
        this.detail = args.detail;
        this.streamErrorEmitted = args.streamErrorEmitted;
        this.i18nStopReason = args.i18nStopReason;
    }
}
exports.LlmRequestFailedError = LlmRequestFailedError;
function isNodeErrorWithCode(error) {
    return error instanceof Error && 'code' in error;
}
function stripMarkdownFrontmatter(raw) {
    const match = raw.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/);
    return match ? (match[1] ?? '') : raw;
}
async function resolveRtwsDiligenceConfig() {
    const workLanguage = (0, work_language_1.getWorkLanguage)();
    const langSpecificPath = path.resolve(process.cwd(), '.minds', `diligence.${workLanguage}.md`);
    const genericPath = path.resolve(process.cwd(), '.minds', 'diligence.md');
    async function resolveFromFile(filePath) {
        let raw;
        try {
            raw = await fs.promises.readFile(filePath, 'utf-8');
        }
        catch (error) {
            if (isNodeErrorWithCode(error) && error.code === 'ENOENT') {
                return null;
            }
            log_1.log.warn('Failed to read rtws diligence file; falling back to built-in defaults', error, {
                filePath,
            });
            return {
                kind: 'enabled',
                diligenceText: diligence_1.DILIGENCE_FALLBACK_TEXT[workLanguage],
            };
        }
        const trimmed = raw.trim();
        if (trimmed === '') {
            return { kind: 'disabled', reason: 'empty_file' };
        }
        const bodyTrimmed = stripMarkdownFrontmatter(raw).trim();
        if (bodyTrimmed === '') {
            return { kind: 'disabled', reason: 'empty_body' };
        }
        return { kind: 'enabled', diligenceText: bodyTrimmed };
    }
    const langSpecific = await resolveFromFile(langSpecificPath);
    if (langSpecific) {
        return langSpecific;
    }
    const generic = await resolveFromFile(genericPath);
    if (generic) {
        return generic;
    }
    return {
        kind: 'enabled',
        diligenceText: diligence_1.DILIGENCE_FALLBACK_TEXT[workLanguage],
    };
}
function normalizeDiligenceRemainingBudget(remainingBudget) {
    return typeof remainingBudget === 'number' && Number.isFinite(remainingBudget)
        ? Math.max(0, Math.floor(remainingBudget))
        : 0;
}
async function evaluateDiligenceAutoContinueGate(options) {
    if (!(options.dlg instanceof dialog_1.MainDialog)) {
        return { kind: 'blocked', reason: 'not_main_dialog' };
    }
    if (options.requireIdleRunSlot) {
        if ((0, dialog_display_state_1.hasActiveRun)(options.dlg.id)) {
            return { kind: 'blocked', reason: 'active_run' };
        }
        if ((0, dialog_display_state_1.getStopRequestedReason)(options.dlg.id) !== undefined) {
            return { kind: 'blocked', reason: 'stop_requested' };
        }
        const executionMarker = await (0, dialog_display_state_1.loadDialogExecutionMarker)(options.dlg.id, 'running');
        if (executionMarker?.kind === 'interrupted') {
            return { kind: 'blocked', reason: 'execution_interrupted' };
        }
        if (executionMarker?.kind === 'dead') {
            return { kind: 'blocked', reason: 'execution_dead' };
        }
    }
    const suspension = await options.dlg.getSuspensionStatus();
    if (!suspension.canDrive) {
        return { kind: 'blocked', reason: 'q4h' };
    }
    const activeReplyObligation = await persistence_1.DialogPersistence.loadActiveTellaskReplyObligation(options.dlg.id, options.dlg.status);
    if (activeReplyObligation !== undefined) {
        return { kind: 'blocked', reason: 'active_reply_obligation' };
    }
    if (suspension.backgroundCalleeDialogs) {
        return { kind: 'blocked', reason: 'active_callee_dispatches' };
    }
    return { kind: 'allowed' };
}
async function maybePrepareDiligenceAutoContinuePrompt(options) {
    if (!(options.dlg instanceof dialog_1.MainDialog)) {
        return {
            kind: 'disabled',
            nextRemainingBudget: normalizeDiligenceRemainingBudget(options.remainingBudget),
        };
    }
    if (options.dlg.disableDiligencePush || options.suppressDiligencePush === true) {
        return {
            kind: 'disabled',
            nextRemainingBudget: normalizeDiligenceRemainingBudget(options.remainingBudget),
        };
    }
    const activeReplyObligation = await persistence_1.DialogPersistence.loadActiveTellaskReplyObligation(options.dlg.id, options.dlg.status);
    if (activeReplyObligation !== undefined) {
        return {
            kind: 'disabled',
            nextRemainingBudget: normalizeDiligenceRemainingBudget(options.remainingBudget),
        };
    }
    const resolved = await resolveRtwsDiligenceConfig();
    if (resolved.kind === 'disabled') {
        return {
            kind: 'disabled',
            nextRemainingBudget: normalizeDiligenceRemainingBudget(options.remainingBudget),
        };
    }
    const maxInjectCount = typeof options.diligencePushMax === 'number' && Number.isFinite(options.diligencePushMax)
        ? Math.floor(options.diligencePushMax)
        : 0;
    const normalizedRemaining = normalizeDiligenceRemainingBudget(options.remainingBudget);
    const bypassBudget = options.ignoreBudgetExhaustion === true;
    // `diligencePushMax` is only the per-member default used when a dialog instance is created or
    // reset. Runtime business decisions must be based on this dialog's persisted remaining budget so
    // operator-added budget (for example 51/0) keeps working even when the team default is zero.
    if (normalizedRemaining < 1 && !bypassBudget) {
        if (maxInjectCount > 0) {
            return { kind: 'budget_exhausted', maxInjectCount, nextRemainingBudget: 0 };
        }
        return { kind: 'disabled', nextRemainingBudget: 0 };
    }
    if (normalizedRemaining < 1) {
        const prompt = {
            content: (0, driver_messages_1.formatDiligenceAutoContinuePrompt)((0, work_language_1.getWorkLanguage)(), resolved.diligenceText),
            msgId: (0, id_1.generateShortId)(),
            grammar: 'markdown',
            origin: 'diligence_push',
        };
        return {
            kind: 'prompt',
            prompt,
            maxInjectCount: 0,
            nextRemainingBudget: 0,
        };
    }
    const prompt = {
        content: (0, driver_messages_1.formatDiligenceAutoContinuePrompt)((0, work_language_1.getWorkLanguage)(), resolved.diligenceText),
        msgId: (0, id_1.generateShortId)(),
        grammar: 'markdown',
        origin: 'diligence_push',
    };
    return {
        kind: 'prompt',
        prompt,
        maxInjectCount,
        nextRemainingBudget: Math.max(0, normalizedRemaining - 1),
    };
}
async function suspendForKeepGoingBudgetExhausted(options) {
    const { dlg, maxInjectCount } = options;
    const language = dlg.getLastUserLanguageCode();
    const content = (0, driver_messages_1.formatQ4HDiligencePushBudgetExhausted)(language, { maxInjectCount });
    const genseq = dlg.activeGenSeqOrUndefined ?? 1;
    // This is informational only: it stops further automatic diligence pushes, but does not create
    // a Q4H wait state and does not participate in drive gating.
    await dlg.persistUiOnlyMarkdown(content, genseq);
    (0, evt_registry_1.postDialogEvent)(dlg, {
        type: 'ui_only_markdown_evt',
        content,
        course: dlg.currentCourse,
        genseq,
    });
}
function isPlainObject(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
const FUNC_TOOL_ARG_ALIASES = {
    readonly_shell: {
        timeout: 'timeout_ms',
    },
};
function normalizeFuncToolArguments(toolName, args) {
    const aliases = FUNC_TOOL_ARG_ALIASES[toolName];
    if (!aliases)
        return args;
    let normalized;
    for (const [aliasKey, canonicalKey] of Object.entries(aliases)) {
        if (!(aliasKey in args))
            continue;
        if (!normalized)
            normalized = { ...args };
        if (!(canonicalKey in normalized)) {
            normalized[canonicalKey] = normalized[aliasKey];
        }
        delete normalized[aliasKey];
    }
    return (normalized ?? args);
}
function validateFuncToolArguments(tool, rawArgs) {
    if (!isPlainObject(rawArgs)) {
        return { ok: false, error: 'Arguments must be an object' };
    }
    const normalizedArgs = normalizeFuncToolArguments(tool.name, rawArgs);
    if (tool.argsValidation === 'passthrough') {
        return { ok: true, args: normalizedArgs };
    }
    const validation = (0, tool_1.validateArgs)(tool.parameters, normalizedArgs);
    return validation.ok
        ? { ok: true, args: normalizedArgs }
        : { ok: false, error: validation.error };
}
const SMART_RATE_DECAY_WINDOW_MS = 5 * 60 * 1000;
const smartRateAdaptiveStateByKey = new Map();
const AGGRESSIVE_RETRY_LLM_ERROR_CODES = new Set([
    'ETIMEDOUT',
    'ECONNRESET',
    'ECONNREFUSED',
    'EAI_AGAIN',
    'ENOTFOUND',
    'ENETUNREACH',
    'EHOSTUNREACH',
    'UND_ERR_CONNECT_TIMEOUT',
    'UND_ERR_HEADERS_TIMEOUT',
    'UND_ERR_BODY_TIMEOUT',
    'UND_ERR_SOCKET',
]);
function readErrorCause(error) {
    if (error instanceof Error) {
        const withCause = error;
        return withCause.cause;
    }
    if (isPlainObject(error) && 'cause' in error) {
        return error.cause;
    }
    return undefined;
}
function readErrorCode(error) {
    if (!isPlainObject(error)) {
        return undefined;
    }
    if ('code' in error && typeof error.code === 'string') {
        return error.code;
    }
    if ('errno' in error && typeof error.errno === 'string') {
        return error.errno;
    }
    return undefined;
}
function readErrorStringField(error, field) {
    if (!isPlainObject(error)) {
        return undefined;
    }
    const value = error[field];
    return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
}
function isRetriableLlmErrorCode(code) {
    if (!code)
        return false;
    return AGGRESSIVE_RETRY_LLM_ERROR_CODES.has(code);
}
function isRetriableLlmMessage(message) {
    const lower = message.toLowerCase();
    if (lower.includes('fetch failed') || lower.includes('socket hang up')) {
        return true;
    }
    if (lower.includes('terminated')) {
        return true;
    }
    if (lower.includes('timeout') || lower.includes('timed out')) {
        return true;
    }
    return false;
}
function readGenericRetriableMessage(err) {
    if (err instanceof Error) {
        const message = err.message.trim();
        if (message !== '')
            return message;
        const name = err.name.trim();
        return name !== '' ? name : undefined;
    }
    if (typeof err === 'string') {
        const trimmed = err.trim();
        return trimmed !== '' ? trimmed : undefined;
    }
    if (!isPlainObject(err)) {
        return undefined;
    }
    if ('message' in err && typeof err.message === 'string') {
        const trimmed = err.message.trim();
        if (trimmed !== '')
            return trimmed;
    }
    if ('error' in err &&
        isPlainObject(err.error) &&
        'message' in err.error &&
        typeof err.error.message === 'string') {
        const trimmed = err.error.message.trim();
        if (trimmed !== '')
            return trimmed;
    }
    if ('cause' in err &&
        isPlainObject(err.cause) &&
        'message' in err.cause &&
        typeof err.cause.message === 'string') {
        const trimmed = err.cause.message.trim();
        if (trimmed !== '')
            return trimmed;
    }
    return undefined;
}
function normalizeFailureDisposition(err, disposition) {
    const fallbackMessage = err instanceof Error
        ? err.message || err.name
        : typeof err === 'string'
            ? err
            : JSON.stringify(err);
    const errCode = readErrorCode(err);
    const causeCode = readErrorCode(readErrorCause(err));
    return {
        kind: disposition.kind,
        message: disposition.message.trim().length > 0 ? disposition.message : fallbackMessage,
        status: disposition.status,
        code: disposition.code ?? errCode ?? causeCode,
        retryStrategy: disposition.retryStrategy,
        retryAfterMs: disposition.retryAfterMs,
    };
}
function classifyGenericLlmFailure(err) {
    // Ownership boundary:
    // - Wrapper classifiers own provider/API semantics such as HTTP status handling, response-body
    //   rejection semantics, rate limits, overload signals, and provider-advised backoff headers.
    // - The runtime generic classifier only owns provider-agnostic transport/runtime failures such
    //   as local timeout / socket / fetch failures and Dominds internal retriable error codes.
    // When a new unexpected pattern appears, keep it here only if it is clearly transport/runtime
    // scoped and not tied to any specific upstream API contract.
    const fallbackMessage = err instanceof Error
        ? err.message || err.name
        : typeof err === 'string'
            ? err
            : JSON.stringify(err);
    const errCode = readErrorCode(err);
    const cause = readErrorCause(err);
    const causeCode = readErrorCode(cause);
    if (err instanceof Error && err.message === 'AbortError') {
        return { kind: 'fatal', message: 'Aborted.' };
    }
    if (isRetriableLlmErrorCode(errCode)) {
        return {
            kind: 'retriable',
            code: errCode,
            message: fallbackMessage,
            retryStrategy: 'aggressive',
        };
    }
    if (isRetriableLlmErrorCode(causeCode)) {
        return {
            kind: 'retriable',
            code: causeCode,
            message: fallbackMessage,
            retryStrategy: 'aggressive',
        };
    }
    const msg = readGenericRetriableMessage(err);
    if (typeof msg === 'string' && isRetriableLlmMessage(msg)) {
        return {
            kind: 'retriable',
            code: errCode ?? causeCode,
            message: msg,
            retryStrategy: 'aggressive',
        };
    }
    if (isPlainObject(err) && 'cause' in err) {
        const causeMessage = readGenericRetriableMessage(err.cause);
        if (typeof causeMessage === 'string' && isRetriableLlmMessage(causeMessage)) {
            return {
                kind: 'retriable',
                code: errCode ?? causeCode,
                message: causeMessage,
                retryStrategy: 'aggressive',
            };
        }
    }
    return {
        kind: 'retriable',
        code: errCode ?? causeCode,
        message: fallbackMessage,
        retryStrategy: 'conservative',
    };
}
function classifyLlmFailure(err, classifyFailure) {
    const providerDisposition = classifyFailure?.(err);
    if (providerDisposition) {
        return normalizeFailureDisposition(err, providerDisposition);
    }
    return classifyGenericLlmFailure(err);
}
function applyQuirkFailureHandling(failure, handling) {
    const nextMessage = 'message' in handling && typeof handling.message === 'string' && handling.message.trim() !== ''
        ? handling.message.trim()
        : failure.message;
    switch (handling.kind) {
        case 'default':
            return { failure, handling };
        case 'give_up':
            return {
                failure: {
                    ...failure,
                    message: nextMessage,
                },
                handling,
            };
        case 'retry_strategy':
            return {
                failure: {
                    ...failure,
                    kind: 'retriable',
                    message: nextMessage,
                    retryStrategy: handling.retryStrategy,
                },
                handling,
            };
        case 'single_retry':
            return {
                failure: {
                    ...failure,
                    kind: 'retriable',
                    message: nextMessage,
                    retryStrategy: failure.retryStrategy ?? 'conservative',
                },
                handling,
            };
        default: {
            const _exhaustive = handling;
            return _exhaustive;
        }
    }
}
async function sleepWithAbort(ms, abortSignal) {
    if (abortSignal?.aborted) {
        throw new Error('AbortError');
    }
    await new Promise((resolve, reject) => {
        const timer = setTimeout(() => {
            abortSignal?.removeEventListener('abort', onAbort);
            resolve();
        }, ms);
        const onAbort = () => {
            clearTimeout(timer);
            abortSignal?.removeEventListener('abort', onAbort);
            reject(new Error('AbortError'));
        };
        abortSignal?.addEventListener('abort', onAbort, { once: true });
    });
}
function normalizeRetryInitialDelayMs(value) {
    if (!Number.isFinite(value))
        return 1000;
    const normalized = Math.floor(value);
    return normalized >= 0 ? normalized : 1000;
}
function normalizeRetryConservativeDelayMs(value, fallbackMin) {
    if (!Number.isFinite(value))
        return Math.max(30_000, fallbackMin);
    const normalized = Math.floor(value);
    if (normalized < 0)
        return Math.max(30_000, fallbackMin);
    return Math.max(fallbackMin, normalized);
}
function normalizeRetryBackoffMultiplier(value) {
    if (!Number.isFinite(value))
        return 2;
    return value >= 1 ? value : 2;
}
function normalizeRetryMaxDelayMs(value, fallbackMin) {
    if (!Number.isFinite(value))
        return Math.max(30_000, fallbackMin);
    const normalized = Math.floor(value);
    if (normalized < 0)
        return Math.max(30_000, fallbackMin);
    return Math.max(fallbackMin, normalized);
}
function buildSmartRateAdaptiveKey(params) {
    return `${params.providerId}::${params.modelId}`;
}
function readSmartRateAdaptiveState(key, nowMs) {
    const existing = smartRateAdaptiveStateByKey.get(key);
    if (!existing)
        return undefined;
    const decayWindows = Math.max(0, Math.floor((nowMs - existing.updatedAtMs) / SMART_RATE_DECAY_WINDOW_MS));
    if (decayWindows <= 0) {
        return existing;
    }
    const penaltyLevel = Math.max(0, existing.penaltyLevel - decayWindows);
    let rememberedDelayMs = existing.rememberedDelayMs;
    for (let index = 0; index < decayWindows && rememberedDelayMs !== undefined; index += 1) {
        rememberedDelayMs =
            rememberedDelayMs <= 1000 ? undefined : Math.max(1000, Math.floor(rememberedDelayMs * 0.75));
    }
    if (penaltyLevel === 0 && rememberedDelayMs === undefined) {
        smartRateAdaptiveStateByKey.delete(key);
        return undefined;
    }
    const decayed = {
        penaltyLevel,
        rememberedDelayMs,
        updatedAtMs: nowMs,
    };
    smartRateAdaptiveStateByKey.set(key, decayed);
    return decayed;
}
function recordSmartRateFailureAndResolveBackoffMs(args) {
    const nowMs = Date.now();
    const key = buildSmartRateAdaptiveKey({
        providerId: args.providerId,
        modelId: args.modelId,
    });
    const current = readSmartRateAdaptiveState(key, nowMs);
    const nextPenaltyLevel = (current?.penaltyLevel ?? 0) + 1;
    const adaptiveDelayMs = Math.min(args.maxDelayMs, Math.max(args.baseDelayMs, Math.floor(args.baseDelayMs * 2 ** Math.max(0, nextPenaltyLevel - 1))));
    const rememberedDelayMs = current?.rememberedDelayMs === undefined
        ? undefined
        : Math.max(args.baseDelayMs, current.rememberedDelayMs);
    const providerSuggestedDelayMs = args.retryAfterMs === undefined ? undefined : Math.max(0, Math.floor(args.retryAfterMs));
    const backoffMs = Math.max(args.baseDelayMs, adaptiveDelayMs, rememberedDelayMs ?? 0, providerSuggestedDelayMs ?? 0);
    smartRateAdaptiveStateByKey.set(key, {
        penaltyLevel: nextPenaltyLevel,
        rememberedDelayMs: providerSuggestedDelayMs === undefined
            ? Math.max(backoffMs, rememberedDelayMs ?? 0)
            : Math.max(backoffMs, providerSuggestedDelayMs),
        updatedAtMs: nowMs,
    });
    return backoffMs;
}
function decaySmartRateAdaptiveState(args) {
    const nowMs = Date.now();
    const key = buildSmartRateAdaptiveKey({
        providerId: args.providerId,
        modelId: args.modelId,
    });
    const current = readSmartRateAdaptiveState(key, nowMs);
    if (!current)
        return;
    const nextPenaltyLevel = Math.max(0, current.penaltyLevel - 1);
    const nextRememberedDelayMs = current.rememberedDelayMs === undefined
        ? undefined
        : current.rememberedDelayMs <= 1000
            ? undefined
            : Math.max(1000, Math.floor(current.rememberedDelayMs * 0.75));
    if (nextPenaltyLevel === 0 && nextRememberedDelayMs === undefined) {
        smartRateAdaptiveStateByKey.delete(key);
        return;
    }
    smartRateAdaptiveStateByKey.set(key, {
        penaltyLevel: nextPenaltyLevel,
        rememberedDelayMs: nextRememberedDelayMs,
        updatedAtMs: nowMs,
    });
}
function resolveRetryModeFromHandling(handling) {
    return handling.kind === 'single_retry' || handling.kind === 'give_up'
        ? 'quirk_single'
        : 'policy';
}
function resolveEffectivePolicyRetryStrategy(args) {
    if (args.retryStrategy !== 'aggressive') {
        return args.retryStrategy;
    }
    if (args.aggressiveRetryCount < args.aggressiveRetryMaxRetries) {
        return 'aggressive';
    }
    return 'conservative';
}
function emitLlmRetryEventBestEffort(args) {
    const rawCourse = args.dlg.activeGenCourseOrUndefined ?? args.dlg.currentCourse;
    const rawGenseq = args.dlg.activeGenSeq;
    if (!Number.isFinite(rawCourse) || rawCourse <= 0)
        return;
    if (!Number.isFinite(rawGenseq) || rawGenseq <= 0)
        return;
    switch (args.phase) {
        case 'waiting':
            (0, evt_registry_1.postDialogEvent)(args.dlg, {
                type: 'llm_retry_evt',
                course: Math.floor(rawCourse),
                genseq: Math.floor(rawGenseq),
                phase: 'waiting',
                display: args.display,
                error: args.errorText,
                nextRetryAtMs: args.nextRetryAtMs,
            });
            return;
        case 'running':
            (0, evt_registry_1.postDialogEvent)(args.dlg, {
                type: 'llm_retry_evt',
                course: Math.floor(rawCourse),
                genseq: Math.floor(rawGenseq),
                phase: 'running',
                display: args.display,
                error: args.errorText,
            });
            return;
        case 'resolved':
            (0, evt_registry_1.postDialogEvent)(args.dlg, {
                type: 'llm_retry_evt',
                course: Math.floor(rawCourse),
                genseq: Math.floor(rawGenseq),
                phase: 'resolved',
                display: args.display,
            });
            return;
        case 'stopped':
            (0, evt_registry_1.postDialogEvent)(args.dlg, {
                type: 'llm_retry_evt',
                course: Math.floor(rawCourse),
                genseq: Math.floor(rawGenseq),
                phase: 'stopped',
                continueEnabled: args.continueEnabled,
                reason: args.reason,
            });
            return;
        default: {
            const _exhaustive = args;
            return _exhaustive;
        }
    }
}
function buildLlmRetryExhaustedReason(args) {
    return {
        kind: 'llm_retry_stopped',
        error: args.errorText,
        display: args.display,
        recoveryAction: args.recoveryAction ?? { kind: 'none' },
    };
}
async function runLlmRequestWithRetry(params) {
    const providerProblemId = `llm/provider_rejected/${params.dlg.id.valueOf()}`;
    const retryInitialDelayMs = normalizeRetryInitialDelayMs(params.retryInitialDelayMs);
    const retryConservativeDelayMs = normalizeRetryConservativeDelayMs(params.retryConservativeDelayMs, retryInitialDelayMs);
    const retryBackoffMultiplier = normalizeRetryBackoffMultiplier(params.retryBackoffMultiplier);
    const retryMaxDelayMs = normalizeRetryMaxDelayMs(params.retryMaxDelayMs, Math.max(retryInitialDelayMs, retryConservativeDelayMs));
    const retryFlowStartedAtMs = Date.now();
    let activeRetryContext;
    const quirkFailureHandler = params.quirkFailureHandlerSession ?? (0, api_quirks_1.createLlmFailureQuirkHandlerSession)(params.providerConfig);
    let aggressiveRetryCount = 0;
    for (let attempt = 0;; attempt++) {
        try {
            if (attempt > 0 && activeRetryContext) {
                const retryStrategy = activeRetryContext.failure.retryStrategy ?? 'conservative';
                const retryMode = resolveRetryModeFromHandling(activeRetryContext.handling);
                emitLlmRetryEventBestEffort({
                    dlg: params.dlg,
                    phase: 'running',
                    display: buildRetryProgressDisplay({
                        phase: 'running',
                        provider: params.provider,
                        retryMode,
                        retryStrategy,
                        attemptNumber: attempt + 1,
                    }),
                    errorText: activeRetryContext.errorText,
                });
            }
            const res = await params.doRequest();
            quirkFailureHandler?.onRequestSucceeded?.();
            decaySmartRateAdaptiveState({
                providerId: params.provider,
                modelId: params.modelId,
            });
            if (attempt > 0 && activeRetryContext) {
                const retryStrategy = activeRetryContext.failure.retryStrategy ?? 'conservative';
                const retryMode = resolveRetryModeFromHandling(activeRetryContext.handling);
                emitLlmRetryEventBestEffort({
                    dlg: params.dlg,
                    phase: 'resolved',
                    display: buildRetryResolvedDisplay({
                        provider: params.provider,
                        retryMode,
                        retryStrategy,
                        attemptNumber: attempt + 1,
                    }),
                });
            }
            (0, problems_1.removeProblem)(providerProblemId);
            return res;
        }
        catch (err) {
            if (params.abortSignal?.aborted) {
                throw err;
            }
            if (err instanceof gen_1.LlmStreamErrorEmittedError) {
                throw err;
            }
            const defaultFailure = classifyLlmFailure(err, params.classifyFailure);
            const handledFailure = applyQuirkFailureHandling(defaultFailure, quirkFailureHandler?.onFailure({
                provider: params.provider,
                providerConfig: params.providerConfig,
                failure: defaultFailure,
                error: err,
            }) ?? { kind: 'default' });
            const failure = handledFailure.failure;
            const detail = (0, log_1.extractErrorDetails)(err).message;
            const errorCode = readErrorCode(err);
            const cause = readErrorCause(err);
            const causeCode = readErrorCode(cause);
            const causeMessage = cause === undefined || cause === null ? undefined : (0, log_1.extractErrorDetails)(cause).message;
            const debugCaptureError = readErrorStringField(err, 'debugCaptureError');
            const debugPath = readErrorStringField(err, 'debugPath');
            const requestPayloadPath = readErrorStringField(err, 'requestPayloadPath');
            const attemptNo = attempt + 1;
            const retryStrategy = failure.kind === 'retriable' ? (failure.retryStrategy ?? 'conservative') : undefined;
            const retryMode = resolveRetryModeFromHandling(handledFailure.handling);
            log_1.log.warn('LLM request attempt failed', err, {
                provider: params.provider,
                failureKind: failure.kind,
                status: failure.status,
                code: failure.code,
                debugPath,
                requestPayloadPath,
                debugCaptureError,
                retryStrategy,
                retryMode,
                attemptNumber: attemptNo,
                dialogId: params.dlg.id.valueOf(),
                rootId: params.dlg.id.rootId,
                selfId: params.dlg.id.selfId,
                aggressiveRetryCount,
                aggressiveRetryMaxRetries: params.aggressiveRetryMaxRetries,
                quirkHandling: handledFailure.handling.kind,
                quirkSource: handledFailure.handling.sourceQuirk,
                errorCode,
                causeCode,
                causeMessage,
                errorText: detail,
            });
            if (failure.kind === 'rejected') {
                (0, problems_1.upsertProblem)({
                    kind: 'llm_provider_rejected_request',
                    source: 'llm',
                    id: providerProblemId,
                    severity: 'error',
                    timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
                    message: `LLM provider rejected the request`,
                    detail: {
                        dialogId: params.dlg.id.valueOf(),
                        provider: params.provider,
                        debugPath,
                        requestPayloadPath,
                        debugCaptureError,
                        errorText: detail,
                    },
                });
            }
            if (failure.kind === 'rejected' && handledFailure.handling.kind !== 'give_up') {
                let streamErrorEmitted = false;
                try {
                    await params.dlg.streamError(detail);
                    streamErrorEmitted = true;
                }
                catch {
                    // best-effort
                }
                throw new LlmRequestFailedError({
                    message: `Provider '${params.provider}' rejected the request: ${failure.message}`,
                    detail,
                    streamErrorEmitted,
                    i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({
                        detail,
                        providerName: params.providerConfig.name.trim().length > 0
                            ? params.providerConfig.name
                            : params.provider,
                        kind: 'provider_rejected',
                    }),
                });
            }
            const canRetry = failure.kind === 'retriable' && params.canRetry();
            const stopRetryByQuirk = handledFailure.handling.kind === 'give_up';
            const canScheduleAnotherAttempt = handledFailure.handling.kind === 'single_retry' || retryMode === 'policy';
            if (!canRetry || stopRetryByQuirk || !canScheduleAnotherAttempt) {
                if (params.onGiveUp) {
                    await params.onGiveUp();
                }
                let retryStoppedError;
                if (failure.kind === 'retriable' || stopRetryByQuirk) {
                    const summaryTextI18nOverride = handledFailure.handling.kind === 'give_up'
                        ? handledFailure.handling.summaryTextI18n
                        : undefined;
                    const display = buildRetryExhaustedDisplay({
                        provider: params.provider,
                        attemptsMade: attemptNo,
                        aggressiveRetryMaxRetries: params.aggressiveRetryMaxRetries,
                        elapsedMs: Date.now() - retryFlowStartedAtMs,
                        retryInitialDelayMs,
                        retryConservativeDelayMs,
                        retryBackoffMultiplier,
                        retryMaxDelayMs,
                        suppressRetryTuningHint: handledFailure.handling.kind === 'give_up',
                        summaryTextI18nOverride,
                    });
                    const interruptionReason = buildLlmRetryExhaustedReason({
                        errorText: detail,
                        display,
                        recoveryAction: handledFailure.handling.kind === 'give_up'
                            ? handledFailure.handling.recoveryAction
                            : undefined,
                    });
                    const retryStoppedDecision = params.onRetryStopped
                        ? await params.onRetryStopped(interruptionReason)
                        : 'stop';
                    if (retryStoppedDecision === 'continue') {
                        quirkFailureHandler?.onRecoveryActionUsed?.({
                            action: interruptionReason.recoveryAction,
                            sourceQuirk: handledFailure.handling.sourceQuirk,
                        });
                    }
                    if (retryStoppedDecision !== 'continue') {
                        // Keep the retry-stop progress event non-resumable by default. Do not flip this to
                        // true just because the eventual finalized stopped state may allow manual Continue:
                        // this event is emitted before the driver has fully unwound, cleared interrupted
                        // markers, and persisted the terminal projection, so enabling Continue here would
                        // advertise a resume action that is not yet safe or actually available.
                        emitLlmRetryEventBestEffort({
                            dlg: params.dlg,
                            phase: 'stopped',
                            continueEnabled: false,
                            reason: interruptionReason,
                        });
                    }
                    log_1.log.warn('LLM failure stopped retry flow', undefined, {
                        provider: params.provider,
                        dialogId: params.dlg.id.valueOf(),
                        rootId: params.dlg.id.rootId,
                        selfId: params.dlg.id.selfId,
                        retryStrategy,
                        retryMode,
                        attemptNumber: attemptNo,
                        aggressiveRetryCount,
                        aggressiveRetryMaxRetries: params.aggressiveRetryMaxRetries,
                        retryInitialDelayMs,
                        retryConservativeDelayMs,
                        retryBackoffMultiplier,
                        retryMaxDelayMs,
                        stopRetryByQuirk,
                        errorText: detail,
                        retryStoppedDecision,
                        quirkSource: handledFailure.handling.sourceQuirk,
                        summaryTextI18nOverride,
                    });
                    const interruptionDetail = formatLlmRetryExhaustedInterruptionDetail({
                        language: params.dlg.getLastUserLanguageCode(),
                        summaryTextI18n: interruptionReason.display.summaryTextI18n,
                        errorText: detail,
                    });
                    retryStoppedError = new LlmRetryStoppedError(interruptionReason, interruptionDetail);
                }
                let streamErrorEmitted = false;
                try {
                    await params.dlg.streamError(detail);
                    streamErrorEmitted = true;
                }
                catch {
                    // best-effort
                }
                if (retryStoppedError) {
                    throw retryStoppedError;
                }
                throw new LlmRequestFailedError({
                    message: `LLM failed: ${failure.message}`,
                    detail,
                    streamErrorEmitted,
                    i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({
                        detail,
                        providerName: params.providerConfig.name.trim().length > 0
                            ? params.providerConfig.name
                            : params.provider,
                        fallbackKind: 'request_failed',
                    }),
                });
            }
            const effectiveRetryStrategy = handledFailure.handling.kind === 'single_retry'
                ? (failure.retryStrategy ?? 'conservative')
                : resolveEffectivePolicyRetryStrategy({
                    retryStrategy: failure.retryStrategy ?? 'conservative',
                    aggressiveRetryCount,
                    aggressiveRetryMaxRetries: params.aggressiveRetryMaxRetries,
                });
            const conservativeRampAttempt = Math.max(0, Math.floor(attempt / 10));
            const backoffMs = handledFailure.handling.kind === 'single_retry'
                ? handledFailure.handling.delayMs
                : effectiveRetryStrategy === 'conservative'
                    ? Math.min(retryMaxDelayMs, Math.max(0, Math.floor(retryConservativeDelayMs * retryBackoffMultiplier ** conservativeRampAttempt)))
                    : effectiveRetryStrategy === 'smart_rate'
                        ? recordSmartRateFailureAndResolveBackoffMs({
                            providerId: params.provider,
                            modelId: params.modelId,
                            baseDelayMs: retryConservativeDelayMs,
                            maxDelayMs: retryMaxDelayMs,
                            retryAfterMs: failure.retryAfterMs,
                        })
                        : Math.min(retryMaxDelayMs, Math.max(0, Math.floor(retryInitialDelayMs * retryBackoffMultiplier ** attempt)));
            emitLlmRetryEventBestEffort({
                dlg: params.dlg,
                phase: 'waiting',
                display: buildRetryProgressDisplay({
                    phase: 'waiting',
                    provider: params.provider,
                    retryMode,
                    retryStrategy: effectiveRetryStrategy,
                    attemptNumber: attemptNo,
                }),
                errorText: detail,
                nextRetryAtMs: Date.now() + backoffMs,
            });
            activeRetryContext = {
                failure: {
                    ...failure,
                    retryStrategy: effectiveRetryStrategy,
                },
                errorText: detail,
                handling: handledFailure.handling,
            };
            log_1.log.warn(`Retrying LLM request after retriable error`, undefined, {
                provider: params.provider,
                retryStrategy: effectiveRetryStrategy,
                classifiedRetryStrategy: failure.retryStrategy,
                retryMode,
                attemptNumber: attemptNo,
                aggressiveRetryCount,
                aggressiveRetryMaxRetries: params.aggressiveRetryMaxRetries,
                backoffMs,
                retryInitialDelayMs,
                retryConservativeDelayMs,
                retryBackoffMultiplier,
                retryMaxDelayMs,
                failure,
            });
            if (handledFailure.handling.kind !== 'single_retry' &&
                effectiveRetryStrategy === 'aggressive') {
                aggressiveRetryCount += 1;
            }
            if (params.onRetry) {
                await params.onRetry();
            }
            await sleepWithAbort(backoffMs, params.abortSignal);
            continue;
        }
    }
    throw new Error('LLM failed.');
}
function formatRetryElapsedDuration(language, elapsedMsRaw) {
    const elapsedMs = Math.max(0, Math.floor(elapsedMsRaw));
    if (elapsedMs < 1000) {
        return `${elapsedMs}ms`;
    }
    const totalSeconds = elapsedMs / 1000;
    if (totalSeconds < 60) {
        const text = totalSeconds >= 10 ? totalSeconds.toFixed(1) : totalSeconds.toFixed(2);
        return language === 'zh' ? `${text} 秒` : `${text}s`;
    }
    const wholeSeconds = Math.round(totalSeconds);
    const minutes = Math.floor(wholeSeconds / 60);
    const seconds = wholeSeconds % 60;
    if (language === 'zh') {
        return seconds === 0 ? `${minutes} 分钟` : `${minutes} 分 ${seconds} 秒`;
    }
    return seconds === 0 ? `${minutes}m` : `${minutes}m ${seconds}s`;
}
function buildRetryDisplayTextI18n(args) {
    return {
        zh: args.zh,
        en: args.en,
    };
}
function resolveDisplayTextI18n(texts, language) {
    const localized = texts[language];
    if (typeof localized === 'string' && localized.trim() !== '') {
        return localized.trim();
    }
    const fallback = texts.zh ?? texts.en;
    return typeof fallback === 'string' && fallback.trim() !== '' ? fallback.trim() : null;
}
function formatRetryStrategyForDisplay(args) {
    if (args.language === 'zh') {
        switch (args.retryStrategy) {
            case 'aggressive':
                return 'aggressive';
            case 'conservative':
                return 'conservative';
            case 'smart_rate':
                return 'smart_rate（限频自适应）';
            default: {
                const _exhaustive = args.retryStrategy;
                return _exhaustive;
            }
        }
    }
    switch (args.retryStrategy) {
        case 'aggressive':
            return 'aggressive';
        case 'conservative':
            return 'conservative';
        case 'smart_rate':
            return 'smart_rate (adaptive rate backoff)';
        default: {
            const _exhaustive = args.retryStrategy;
            return _exhaustive;
        }
    }
}
function buildRetryProgressDisplay(args) {
    const providerTextZh = args.provider.trim() === '' ? '当前模型服务' : args.provider.trim();
    const providerTextEn = args.provider.trim() === '' ? 'the current provider' : args.provider.trim();
    if (args.retryMode === 'quirk_single') {
        if (args.phase === 'waiting') {
            return {
                titleTextI18n: buildRetryDisplayTextI18n({
                    zh: '临时重试',
                    en: 'Temporary retry',
                }),
                summaryTextI18n: buildRetryDisplayTextI18n({
                    zh: `${providerTextZh} 返回了需要 quirk 处理的可重试错误。Dominds 正在执行一次临时退避，并将在退避结束后再次尝试。`,
                    en: `${providerTextEn} returned a quirk-handled retriable failure. Dominds is performing a temporary backoff and will try once more after that backoff.`,
                }),
            };
        }
        return {
            titleTextI18n: buildRetryDisplayTextI18n({
                zh: '临时重试',
                en: 'Temporary retry',
            }),
            summaryTextI18n: buildRetryDisplayTextI18n({
                zh: `${providerTextZh} 的临时重试正在执行中。`,
                en: `${providerTextEn} temporary retry is now running.`,
            }),
        };
    }
    if (args.phase === 'waiting') {
        const strategyZh = formatRetryStrategyForDisplay({
            language: 'zh',
            retryStrategy: args.retryStrategy,
        });
        const strategyEn = formatRetryStrategyForDisplay({
            language: 'en',
            retryStrategy: args.retryStrategy,
        });
        return {
            titleTextI18n: buildRetryDisplayTextI18n({
                zh: '正在重试',
                en: 'Retrying',
            }),
            summaryTextI18n: buildRetryDisplayTextI18n({
                zh: `${providerTextZh} 遇到可重试错误，Dominds 正在退避并将在退避结束后自动继续重试。` +
                    `当前失败发生在第 ${String(args.attemptNumber)} 次请求，策略=${strategyZh}。`,
                en: `${providerTextEn} hit a retriable failure. Dominds is backing off and will retry automatically when the backoff window ends. ` +
                    `The failure occurred on request ${String(args.attemptNumber)}, strategy=${strategyEn}.`,
            }),
        };
    }
    const strategyZh = formatRetryStrategyForDisplay({
        language: 'zh',
        retryStrategy: args.retryStrategy,
    });
    const strategyEn = formatRetryStrategyForDisplay({
        language: 'en',
        retryStrategy: args.retryStrategy,
    });
    return {
        titleTextI18n: buildRetryDisplayTextI18n({
            zh: '正在重试',
            en: 'Retrying',
        }),
        summaryTextI18n: buildRetryDisplayTextI18n({
            zh: `${providerTextZh} 的第 ${String(args.attemptNumber)} 次请求正在执行中，策略=${strategyZh}。即将验证是否恢复。`,
            en: `${providerTextEn} request ${String(args.attemptNumber)} is now running, strategy=${strategyEn}. Recovery is being verified now.`,
        }),
    };
}
function buildRetryExhaustedSummaryTextI18n(args) {
    if (args.summaryTextI18nOverride) {
        return args.summaryTextI18nOverride;
    }
    const providerPath = `providers.${args.provider}`;
    const durationTextZh = formatRetryElapsedDuration('zh', args.elapsedMs);
    const durationTextEn = formatRetryElapsedDuration('en', args.elapsedMs);
    const actualRetryCount = Math.max(0, args.attemptsMade - 1);
    const zhBase = `LLM 自动重试已停止：provider=${args.provider}，共尝试 ${args.attemptsMade} 次` +
        `（初始请求 1 次 + 重试 ${actualRetryCount} 次），总耗时 ${durationTextZh}。` +
        `当前重试配置：llm_retry_aggressive_max_retries=${args.aggressiveRetryMaxRetries}，` +
        `llm_retry_initial_delay_ms=${args.retryInitialDelayMs}，` +
        `llm_retry_conservative_delay_ms=${args.retryConservativeDelayMs}，` +
        `llm_retry_backoff_multiplier=${args.retryBackoffMultiplier}，` +
        `llm_retry_max_delay_ms=${args.retryMaxDelayMs}。` +
        'Dominds 只会在高置信度判断继续原样自动重试已无真实进展时停止自动重试。';
    const zhTuningHint = args.suppressRetryTuningHint
        ? ''
        : `若想调整前期快速重试窗口或后续慢速退避节奏，请编辑 \`.minds/llm.yaml\` 中的 ` +
            `\`${providerPath}.llm_retry_aggressive_max_retries\`、` +
            `\`${providerPath}.llm_retry_initial_delay_ms\`、` +
            `\`${providerPath}.llm_retry_conservative_delay_ms\`、` +
            `\`${providerPath}.llm_retry_backoff_multiplier\`、` +
            `\`${providerPath}.llm_retry_max_delay_ms\`，并检查 provider / network 稳定性。`;
    const enBase = `LLM automatic retries stopped: provider=${args.provider}, ${args.attemptsMade} attempts total ` +
        `(1 initial request + ${actualRetryCount} retries), elapsed ${durationTextEn}. ` +
        `Current retry config: llm_retry_aggressive_max_retries=${args.aggressiveRetryMaxRetries}, ` +
        `llm_retry_initial_delay_ms=${args.retryInitialDelayMs}, ` +
        `llm_retry_conservative_delay_ms=${args.retryConservativeDelayMs}, ` +
        `llm_retry_backoff_multiplier=${args.retryBackoffMultiplier}, ` +
        `llm_retry_max_delay_ms=${args.retryMaxDelayMs}. ` +
        `Dominds stops automatic retry only when it has high confidence that repeating the same retry path is no longer making real progress. `;
    const enTuningHint = args.suppressRetryTuningHint
        ? ''
        : `If you want to tune the front-loaded fast retry burst or the later slow backoff cadence, edit ` +
            `\`.minds/llm.yaml\`: \`${providerPath}.llm_retry_aggressive_max_retries\`, ` +
            `\`${providerPath}.llm_retry_initial_delay_ms\`, ` +
            `\`${providerPath}.llm_retry_conservative_delay_ms\`, ` +
            `\`${providerPath}.llm_retry_backoff_multiplier\`, ` +
            `\`${providerPath}.llm_retry_max_delay_ms\`, and verify provider/network stability. `;
    return buildRetryDisplayTextI18n({
        zh: zhBase + zhTuningHint,
        en: enBase + enTuningHint,
    });
}
function buildRetryResolvedDisplay(args) {
    const providerTextZh = args.provider.trim() === '' ? '当前模型服务' : args.provider.trim();
    const providerTextEn = args.provider.trim() === '' ? 'the current provider' : args.provider.trim();
    if (args.retryMode === 'quirk_single') {
        return {
            titleTextI18n: buildRetryDisplayTextI18n({
                zh: '临时重试已恢复',
                en: 'Temporary retry recovered',
            }),
            summaryTextI18n: buildRetryDisplayTextI18n({
                zh: `${providerTextZh} 的临时重试已成功恢复，本次生成将继续进行。`,
                en: `${providerTextEn} temporary retry recovered successfully and generation will continue.`,
            }),
        };
    }
    return {
        titleTextI18n: buildRetryDisplayTextI18n({
            zh: '重试已恢复',
            en: 'Retry recovered',
        }),
        summaryTextI18n: buildRetryDisplayTextI18n({
            zh: `${providerTextZh} 的重试已成功恢复，本次生成将继续进行。恢复发生在第 ${String(args.attemptNumber)} 次请求，策略=${formatRetryStrategyForDisplay({
                language: 'zh',
                retryStrategy: args.retryStrategy,
            })}。`,
            en: `${providerTextEn} retry recovered successfully and generation will continue. Recovery happened on request ${String(args.attemptNumber)}, strategy=${formatRetryStrategyForDisplay({
                language: 'en',
                retryStrategy: args.retryStrategy,
            })}.`,
        }),
    };
}
function buildRetryExhaustedDisplay(args) {
    return {
        titleTextI18n: buildRetryDisplayTextI18n({
            zh: '重试已停止',
            en: 'Retry stopped',
        }),
        summaryTextI18n: buildRetryExhaustedSummaryTextI18n(args),
    };
}
function formatLlmRetryExhaustedInterruptionDetail(args) {
    const summaryText = resolveDisplayTextI18n(args.summaryTextI18n, args.language);
    const trimmedError = args.errorText.trim();
    if (summaryText === null || summaryText === '') {
        return trimmedError;
    }
    if (trimmedError === '') {
        return summaryText;
    }
    if (args.language === 'zh') {
        return `${summaryText}最后错误：${trimmedError}`;
    }
    return `${summaryText} Last error: ${trimmedError}`;
}
