/** * Context-overflow detection + recovery. * * Provider error classifier and session-recovery helper used by * AgentExecuteStage to turn a fatal context-overflow error into a * single retry after force-compaction. * * AI SDK v6 error model (researched against /vercel/ai @ ai_6.0.0-beta.128): * - `APICallError.isInstance(error)` is the canonical type guard * (the v4+ replacement for the deprecated `isAPICallError` static). * - APICallError exposes `statusCode`, `responseBody`, `cause`, `url`. * - During `streamText`, errors surface BOTH ways: as a thrown * exception around the iterator AND as `chunk.type === 'error'` * parts inside `stream`. Callers must handle both. * * Provider-specific overflow signatures (researched against current docs + * gh search across 10 production codebases that ship their own classifier): * - OpenAI: `code: 'context_length_exceeded'`, or message containing * 'maximum context length' / 'reduce the length of the * messages' / 'context_length_exceeded'. * - Anthropic: status 400 with message containing 'prompt is too long'. * - Generic: status 400 with message matching * /context.{0,20}(window|length|tokens)/i. * * Why the matching is intentionally OR-shaped (not provider-switched): * proxies (OpenRouter, LiteLLM, Portkey, Bedrock) reshape the original * provider error, so a strict provider-by-provider switch silently * mis-classifies the same overflow on the same model when routed * through a proxy. The cost of a false positive is a wasted retry; the * cost of a false negative is a fatal turn loss. Bias OR. */ import type { Session } from '../types/index.js'; /** * Returns true if the error is a provider context-overflow / "prompt too * long" condition. Matches on AI SDK APICallError + raw provider errors + * proxy-reshaped errors. * * Bias: tolerate false positives (wasted retry), reject false negatives * (fatal turn loss). */ export declare function isContextOverflowError(error: unknown): boolean; export interface OverflowRecoveryResult { /** Whether at least one message was stripped from session.messages. */ stripped: boolean; /** Number of messages stripped. */ strippedCount: number; } /** * Strip partial assistant + tool messages that followed the most recent * user message — these are in-flight work from the turn that overflowed. * The user's own message is PRESERVED so the retry can still answer it. * * Why we keep the user message: * 1. The user asked a question. Throwing it away to "recover" is worse * than just failing — they have to re-type it. * 2. Overflow is almost always caused by accumulated PRIOR-turn weight * (long tool outputs, growing history), not by the new user message * itself. Once compaction trims the history, the same user message * fits. * 3. Matches Flue's recovery semantics: "strip the failed turn" means * strip the partial response, not the prompt. * * Does NOT trigger compaction itself — that's the caller's job, so it * can pass `force: true` and pick the right strategy. This function is * pure session mutation and is safe to call before any awaitable work. * * Returns the count of messages stripped. A retry is valid even when * `strippedCount === 0` (e.g. brand-new turn, nothing in-flight to * strip) — the caller decides based on its overall recovery policy. */ export declare function recoverFromContextOverflow(session: Session): Promise;