// Force strict mode and setup for ESM
"use strict";
import {
  retryContext
} from "./chunk-P3QQPMQA.js";
import {
  formatQuotaExhaustedMessage,
  getRateLimitErrorDetails,
  getRetryAfterDelayMs,
  getRetryDelayMs,
  isQuotaExhaustedError,
  isQwenQuotaExceededError,
  isRateLimitError
} from "./chunk-XQK4XXTN.js";
import {
  createDebugLogger,
  getErrorStatus,
  isAbortError
} from "./chunk-E2BHWIDT.js";
import {
  init_esbuild_shims
} from "./chunk-5O2XNYP6.js";
import {
  __name
} from "./chunk-J2S4EL5Y.js";

// packages/core/src/utils/retry.ts
init_esbuild_shims();

// packages/core/src/utils/retryErrorClassification.ts
init_esbuild_shims();
function classifyRetryError(error, context = {}) {
  if (isRetryAbortError(error)) {
    return {
      kind: "abort",
      diagnosis: "fail-fast",
      reason: "aborted"
    };
  }
  const details = getRateLimitErrorDetails(error);
  const statusCode = details.statusCode;
  const providerFields = getProviderFields(error);
  const providerCode = details.providerCode ?? providerFields.providerCode;
  const providerMessage = details.providerMessage ?? providerFields.providerMessage;
  const requestId = details.requestId ?? providerFields.requestId;
  const common = {
    ...statusCode !== void 0 ? { statusCode } : {},
    ...providerCode !== void 0 ? { providerCode } : {},
    ...providerMessage !== void 0 ? { providerMessage } : {},
    ...requestId !== void 0 ? { requestId } : {}
  };
  if (context.authType === "qwen-oauth" /* QWEN_OAUTH */ && isQwenQuotaExceededError(error)) {
    return {
      kind: "provider-business",
      diagnosis: "fail-fast",
      reason: "qwen-oauth-free-tier-quota",
      ...common
    };
  }
  if (isAllocatedQuotaExceeded(providerCode)) {
    return {
      kind: "provider-business",
      diagnosis: "fail-fast",
      reason: "allocated-quota-exceeded",
      ...common
    };
  }
  if (isRateLimitError(error, context.extraRetryErrorCodes)) {
    const kind = details.transport === "sse" ? "sse-provider" : statusCode !== void 0 ? "http" : "provider";
    return {
      kind,
      diagnosis: "retryable",
      reason: "rate-limit",
      ...common
    };
  }
  const transportCode = getTransportCode(error);
  if (transportCode !== void 0 && (statusCode === void 0 || statusCode >= 500)) {
    return {
      kind: "transport",
      diagnosis: "retryable",
      reason: "transport-error",
      transportCode,
      ...statusCode !== void 0 ? { statusCode } : {}
    };
  }
  if (statusCode !== void 0) {
    const kind = details.transport === "sse" ? "sse-provider" : "http";
    if (statusCode === 529) {
      return {
        kind,
        diagnosis: "retryable",
        reason: "capacity-overload",
        ...common
      };
    }
    if (statusCode === 401 || statusCode === 403) {
      return {
        kind,
        diagnosis: "fail-fast",
        reason: "auth-error",
        ...common
      };
    }
    if (statusCode >= 400 && statusCode < 500) {
      return {
        kind,
        diagnosis: "fail-fast",
        reason: "client-error",
        ...common
      };
    }
    if (statusCode >= 500 && statusCode < 600) {
      return {
        kind,
        diagnosis: "retryable",
        reason: "server-error",
        ...common
      };
    }
    return {
      kind,
      diagnosis: "unknown",
      reason: "http-status",
      ...common
    };
  }
  return {
    kind: "unknown",
    diagnosis: "unknown",
    reason: "unclassified",
    ...common
  };
}
__name(classifyRetryError, "classifyRetryError");
function isRetryAbortError(error) {
  if (isAbortError(error)) {
    return true;
  }
  return error instanceof Error && error.name === "CanceledError";
}
__name(isRetryAbortError, "isRetryAbortError");
var MAX_TRANSPORT_CAUSE_DEPTH = 4;
function getTransportCode(error) {
  let current = error;
  for (let depth = 0; depth <= MAX_TRANSPORT_CAUSE_DEPTH; depth++) {
    if (typeof current !== "object" || current === null) {
      return void 0;
    }
    const code = current.code;
    if (typeof code === "string" && isTransportCode(code)) {
      return code;
    }
    current = current instanceof Error ? current.cause : void 0;
  }
  return void 0;
}
__name(getTransportCode, "getTransportCode");
function isTransportCode(code) {
  return TRANSPORT_ERROR_CODES.has(code);
}
__name(isTransportCode, "isTransportCode");
var TRANSPORT_ERROR_CODES = /* @__PURE__ */ new Set([
  "EAI_AGAIN",
  "ECONNABORTED",
  "ECONNREFUSED",
  "ECONNRESET",
  "EHOSTUNREACH",
  "ENETUNREACH",
  "ENOTFOUND",
  "EPIPE",
  "ETIMEDOUT",
  "UND_ERR_BODY_TIMEOUT",
  "UND_ERR_CONNECT_TIMEOUT",
  "UND_ERR_HEADERS_TIMEOUT",
  "UND_ERR_SOCKET"
]);
function isAllocatedQuotaExceeded(providerCode) {
  return providerCode === "Throttling.AllocationQuota";
}
__name(isAllocatedQuotaExceeded, "isAllocatedQuotaExceeded");
function getProviderFields(error) {
  if (typeof error !== "object" || error === null) {
    return {};
  }
  const source = error;
  const rawCode = typeof source.code === "string" || typeof source.code === "number" ? String(source.code) : void 0;
  const isHttpStatusEcho = typeof source.code === "number" && source.code >= 100 && source.code < 600;
  const providerCode = error instanceof Error && rawCode?.startsWith("ERR_") || isHttpStatusEcho ? void 0 : rawCode;
  const requestId = typeof source.request_id === "string" ? source.request_id : typeof source.requestId === "string" ? source.requestId : void 0;
  const providerMessage = typeof source.message === "string" && (!(error instanceof Error) || providerCode !== void 0 || requestId !== void 0) ? source.message : void 0;
  return {
    ...providerCode !== void 0 ? { providerCode } : {},
    ...providerMessage !== void 0 ? { providerMessage } : {},
    ...requestId !== void 0 ? { requestId } : {}
  };
}
__name(getProviderFields, "getProviderFields");
var FALLBACK_ELIGIBLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 503, 529]);
function isFallbackEligible(classification) {
  return classification.kind !== "transport" && classification.statusCode !== void 0 && FALLBACK_ELIGIBLE_STATUS_CODES.has(classification.statusCode) && classification.diagnosis !== "fail-fast" && classification.diagnosis !== "unknown";
}
__name(isFallbackEligible, "isFallbackEligible");

// packages/core/src/utils/retry.ts
var debugLogger = createDebugLogger("RETRY");
var PERSISTENT_MAX_BACKOFF_MS = 5 * 60 * 1e3;
var PERSISTENT_CAP_MS = 6 * 60 * 60 * 1e3;
var HEARTBEAT_INTERVAL_MS = 3e4;
var DEFAULT_RETRY_OPTIONS = {
  maxAttempts: 7,
  initialDelayMs: 1500,
  maxDelayMs: 3e4,
  // 30 seconds
  shouldRetryOnError: defaultShouldRetry
};
function defaultShouldRetry(error, extraRetryErrorCodes) {
  const status = getErrorStatus(error);
  if (isRateLimitError(error, extraRetryErrorCodes) || status !== void 0 && status >= 500 && status < 600) {
    return true;
  }
  return classifyRetryError(error, { extraRetryErrorCodes }).kind === "transport";
}
__name(defaultShouldRetry, "defaultShouldRetry");
function hasRetryAfterStatus(status) {
  return status === 429 || status === 503;
}
__name(hasRetryAfterStatus, "hasRetryAfterStatus");
function isTransientCapacityError(error) {
  const status = getErrorStatus(error);
  return status === 429 || status === 529;
}
__name(isTransientCapacityError, "isTransientCapacityError");
function isUnattendedMode() {
  const val = process.env["QWEN_CODE_UNATTENDED_RETRY"];
  return val === "true" || val === "1";
}
__name(isUnattendedMode, "isUnattendedMode");
function delay(ms, signal) {
  return new Promise((resolve, reject) => {
    if (signal?.aborted) {
      return reject(new Error("Retry aborted by signal"));
    }
    const cleanup = /* @__PURE__ */ __name(() => {
      signal?.removeEventListener("abort", onAbort);
    }, "cleanup");
    function onAbort() {
      clearTimeout(timeout);
      cleanup();
      reject(new Error("Retry aborted by signal"));
    }
    __name(onAbort, "onAbort");
    const timeout = setTimeout(() => {
      cleanup();
      resolve();
    }, ms);
    signal?.addEventListener("abort", onAbort, { once: true });
    if (signal?.aborted) {
      clearTimeout(timeout);
      cleanup();
      reject(new Error("Retry aborted by signal"));
    }
  });
}
__name(delay, "delay");
async function sleepWithHeartbeat(totalMs, ctx) {
  let remaining = totalMs;
  while (remaining > 0) {
    if (ctx.signal?.aborted) {
      throw new Error("Retry aborted by signal");
    }
    const chunk = Math.max(1, Math.min(remaining, ctx.heartbeatInterval));
    await delay(chunk, ctx.signal);
    remaining -= chunk;
    if (remaining > 0 && ctx.heartbeatFn) {
      ctx.heartbeatFn({
        attempt: ctx.attempt,
        remainingMs: remaining,
        error: ctx.error
      });
    }
  }
}
__name(sleepWithHeartbeat, "sleepWithHeartbeat");
async function retryWithBackoff(fn, options) {
  if (options?.maxAttempts !== void 0 && options.maxAttempts <= 0) {
    throw new Error("maxAttempts must be a positive number.");
  }
  const cleanOptions = options ? Object.fromEntries(Object.entries(options).filter(([_, v]) => v != null)) : {};
  const {
    maxAttempts,
    initialDelayMs,
    maxDelayMs,
    authType,
    extraRetryErrorCodes,
    shouldRetryOnError,
    shouldRetryOnContent,
    persistentMode,
    persistentMaxBackoffMs,
    persistentCapMs,
    heartbeatIntervalMs,
    heartbeatFn,
    signal,
    onRetry
  } = {
    ...DEFAULT_RETRY_OPTIONS,
    ...cleanOptions
  };
  const hasCustomShouldRetryOnError = typeof options?.shouldRetryOnError === "function";
  const persistent = persistentMode ?? false;
  const maxBackoff = persistentMaxBackoffMs ?? PERSISTENT_MAX_BACKOFF_MS;
  const capMs = persistentCapMs ?? PERSISTENT_CAP_MS;
  const heartbeatInterval = heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;
  let attempt = 0;
  let persistentAttempt = 0;
  let currentDelay = initialDelayMs;
  const requestEntryTime = Date.now();
  let iterationCount = 0;
  let retryTotalDelayMs = 0;
  let lastContentResult;
  let hadContentRetry = false;
  while (attempt < maxAttempts) {
    attempt++;
    iterationCount++;
    const requestSetupMs = Date.now() - requestEntryTime;
    try {
      const result = await retryContext.run(
        { attempt: iterationCount, retryTotalDelayMs, requestSetupMs },
        () => fn()
      );
      if (shouldRetryOnContent && shouldRetryOnContent(result)) {
        lastContentResult = result;
        hadContentRetry = true;
        const delayMs = getRetryDelayMs({
          // attempt: 1 — currentDelay already tracks exponential growth;
          // getRetryDelayMs is called here only for jitter calculation.
          attempt: 1,
          initialDelayMs: currentDelay,
          maxDelayMs,
          jitterRatio: 0.3
        });
        debugLogger.warn(
          `Attempt ${iterationCount}: response rejected by content check. Retrying with backoff in ${Math.ceil(delayMs / 1e3)}s...`
        );
        await delay(delayMs, signal);
        retryTotalDelayMs += delayMs;
        currentDelay = Math.min(maxDelayMs, currentDelay * 2);
        continue;
      }
      return result;
    } catch (error) {
      const errorStatus = getErrorStatus(error);
      const retryDiagnostics = classifyRetryError(error, {
        authType,
        extraRetryErrorCodes
      });
      if (retryDiagnostics.kind === "abort") {
        throw error;
      }
      if (authType === "qwen-oauth" /* QWEN_OAUTH */ && isQwenQuotaExceededError(error)) {
        debugLogger.error(
          "Qwen OAuth quota exceeded, fast-failing",
          retryDiagnostics,
          error
        );
        throw new Error(
          `Qwen OAuth free tier has been discontinued as of 2026-04-15.

To continue using Qwen Code, try one of these alternatives:
  - OpenRouter:    https://openrouter.ai/docs/quickstart
  - Fireworks AI:  https://docs.fireworks.ai/api-reference/introduction
  - ModelStudio:   https://help.aliyun.com/zh/model-studio/coding-plan

After setting up your API key, run /auth to configure your provider.`
        );
      }
      if (isQuotaExhaustedError(error)) {
        debugLogger.error(
          "Quota exhausted, fast-failing",
          retryDiagnostics,
          error
        );
        throw new Error(formatQuotaExhaustedMessage(error), { cause: error });
      }
      const isTransient = isTransientCapacityError(error);
      const callerAllowsRetry = hasCustomShouldRetryOnError ? shouldRetryOnError(error) : defaultShouldRetry(error, extraRetryErrorCodes);
      const isFailFast = retryDiagnostics.diagnosis === "fail-fast";
      const shouldPersist = persistent && isTransient && callerAllowsRetry && !isFailFast;
      if (!shouldPersist) {
        if (attempt >= maxAttempts || !callerAllowsRetry) {
          throw error;
        }
      }
      let delayMs;
      if (shouldPersist) {
        persistentAttempt++;
        const retryAfterMs = hasRetryAfterStatus(errorStatus) ? getRetryAfterDelayMs(error) : null;
        if (retryAfterMs !== null && retryAfterMs > 0) {
          delayMs = Math.min(retryAfterMs, capMs);
        } else {
          delayMs = getRetryDelayMs({
            attempt: persistentAttempt,
            initialDelayMs,
            maxDelayMs: Math.min(maxBackoff, capMs),
            jitterRatio: 0.25
          });
        }
        const reportedAttempt = persistentAttempt;
        debugLogger.warn(
          `[Persistent] Attempt ${reportedAttempt} failed with status ${errorStatus ?? "unknown"}. Retrying in ${Math.ceil(delayMs / 1e3)}s...`,
          retryDiagnostics,
          error
        );
        if (!signal?.aborted) {
          try {
            onRetry?.({
              attempt: iterationCount,
              error,
              errorStatus,
              delayMs
            });
          } catch (cbError) {
            debugLogger.warn(
              `onRetry callback threw (swallowed): ${cbError instanceof Error ? cbError.message : String(cbError)}`
            );
          }
        }
        await sleepWithHeartbeat(delayMs, {
          attempt: reportedAttempt,
          error,
          heartbeatInterval,
          heartbeatFn,
          signal
        });
        retryTotalDelayMs += delayMs;
        if (attempt >= maxAttempts) {
          attempt = maxAttempts - 1;
        }
      } else {
        const retryAfterMs = hasRetryAfterStatus(errorStatus) ? getRetryAfterDelayMs(error) : null;
        let actualDelayMs;
        if (retryAfterMs !== null && retryAfterMs > 0) {
          actualDelayMs = retryAfterMs;
          currentDelay = initialDelayMs;
          logRetryAtStatusLevel(
            `Attempt ${attempt} failed with status ${errorStatus ?? "unknown"}. Retrying after explicit delay of ${retryAfterMs}ms...`,
            retryDiagnostics,
            error,
            errorStatus
          );
        } else {
          actualDelayMs = getRetryDelayMs({
            // attempt: 1 — currentDelay already tracks exponential growth;
            // getRetryDelayMs is called here only for jitter calculation.
            attempt: 1,
            initialDelayMs: currentDelay,
            maxDelayMs,
            jitterRatio: 0.3
          });
          currentDelay = Math.min(maxDelayMs, currentDelay * 2);
          logRetryAttempt(
            attempt,
            error,
            retryDiagnostics,
            errorStatus,
            actualDelayMs
          );
        }
        if (!signal?.aborted) {
          try {
            onRetry?.({
              attempt: iterationCount,
              error,
              errorStatus,
              delayMs: actualDelayMs
            });
          } catch (cbError) {
            debugLogger.warn(
              `onRetry callback threw (swallowed): ${cbError instanceof Error ? cbError.message : String(cbError)}`
            );
          }
        }
        await delay(actualDelayMs, signal);
        retryTotalDelayMs += actualDelayMs;
      }
    }
  }
  if (hadContentRetry) {
    return lastContentResult;
  }
  throw new Error("Retry attempts exhausted");
}
__name(retryWithBackoff, "retryWithBackoff");
function logRetryAttempt(attempt, error, retryDiagnostics, errorStatus, delayMs) {
  const backoff = delayMs !== void 0 ? `Retrying with backoff in ${Math.ceil(delayMs / 1e3)}s...` : "Retrying with backoff...";
  const message = errorStatus ? `Attempt ${attempt} failed with status ${errorStatus}. ${backoff}` : `Attempt ${attempt} failed. ${backoff}`;
  logRetryAtStatusLevel(message, retryDiagnostics, error, errorStatus);
}
__name(logRetryAttempt, "logRetryAttempt");
function logRetryAtStatusLevel(message, retryDiagnostics, error, errorStatus) {
  if (errorStatus !== void 0 && errorStatus >= 500 && errorStatus < 600) {
    debugLogger.error(message, retryDiagnostics, error);
  } else {
    debugLogger.warn(message, retryDiagnostics, error);
  }
}
__name(logRetryAtStatusLevel, "logRetryAtStatusLevel");

export {
  classifyRetryError,
  isFallbackEligible,
  isUnattendedMode,
  delay,
  retryWithBackoff
};
/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
