/** * @packageDocumentation * CFG-84 — **a short per-minute quota 429 waits out the provider's retry hint.** * * When a Gemini call fails with a 429 whose quota is per-minute, and the provider names a short * wait, wait that long (the LONGER of `RetryInfo.retryDelay` and the prose `Please retry in Ns`), * tell the user we are waiting, then re-send, instead of ending the run. * * ## Design decisions * * - **The longer wait wins (Andrew, 2026-09-27):** The recorded fixture's two waits disagree: * `RetryInfo` says 14s and prose says 51.4s. Retrying early only earns another 429 and burns a * request against a window that is still closed, so we take the longer of the two. * - **Bound: 90 seconds ({@link MAX_QUOTA_COOLDOWN_MS}):** The recorded fixture's longer wait is * 51.4s and must cool down, so the bound has to be above that. A named wait above 90s terminates * as today. * - **At most 2 cooldowns per turn ({@link MAX_QUOTA_COOLDOWNS_PER_TURN}):** A key that stays * exhausted still ends the run. A third 429 in the same turn terminates as today. * - **Per-minute only:** The cooldown fires only when a `QuotaFailure` `quotaId` contains * `PerMinute`, or when there is no `QuotaFailure` but the prose wait is present and within the * bound (matching the 2026-09-27 report which had prose only). A per-day quota returns nothing * and terminates as today. * - **Covered providers:** Google Gemini API (`@langchain/google` / AI Studio). Other providers * (OpenAI, Anthropic, Vertex AI, Groq, Ollama, etc.) keep today's behaviour. * - **Rater calls out of scope:** AI rater calls on the same key are out of scope for this version. * - **EXT-196 balance refusal:** A 403 naming an exhausted balance returns nothing and never * retries; a balance cap does not recover by waiting. */ /** Maximum wait duration in milliseconds for a quota cooldown (90 seconds). */ export declare const MAX_QUOTA_COOLDOWN_MS = 90000; /** Maximum number of quota cooldown retries allowed per turn (2). */ export declare const MAX_QUOTA_COOLDOWNS_PER_TURN = 2; /** Parsed quota retry hint. */ export interface QuotaRetryHint { /** The cooldown duration to wait in milliseconds (the longer of structured and prose waits). */ waitMs: number; /** Whether the quota refusal is per-minute. */ perMinute: boolean; } /** * Format the user-visible notification line for a quota cooldown wait. * * Emitted to `statusUpdate` at `StatusLevel.WARNING` so both plain-CLI and Ink TUI * surfaces present the wait visibly to the user rather than going silent. */ export declare function quotaCooldownWaitMessage(waitMs: number): string; /** Check if a parsed wait duration is within the accepted cooldown bound (0 < waitMs <= 90s). */ export declare function isWithinQuotaCooldownBound(waitMs: number): boolean; /** * Read the quota retry hint from an error thrown by the model call. * * Returns `{ waitMs, perMinute: true }` if the error represents a per-minute quota 429 * from the Gemini API with a retry wait hint. * Returns `null` if: * - the error is not a 429 quota exhaustion (e.g. 403 balance refusal, 400 invalid request); * - the error is not from Gemini API; * - the quota is per-day (e.g. QuotaFailure quotaId contains `PerDay` or lacks `PerMinute`); * - no wait duration could be parsed; * - there is no QuotaFailure and the prose wait is past the bound. */ export declare function readQuotaRetryHint(error: unknown): QuotaRetryHint | null;