import type { AIProviderName } from "../constants/enums.js";
import type { EvaluationData } from "../index.js";
import type { NeuroLink } from "../neurolink.js";
import type { FileToolRootPolicy, UnknownRecord, MiddlewareFactoryOptions, StreamOptions, StreamResult, AIProvider, AnalyticsData, EnhancedGenerateResult, TextGenerationOptions, TextGenerationResult, ValidationSchema, EmbedInput, DecisionRequest, DecisionResult, MemoryToolCallRecord, MemoryToolResultRecord, ToolExecutionSummaryInternal } from "../types/index.js";
import { TelemetryHandler } from "./modules/TelemetryHandler.js";
import { ToolsManager } from "./modules/ToolsManager.js";
import type { LanguageModel, ModelMessage, RawUsageObject, Tool, ToolCallRepairFunction, ToolSet } from "../types/index.js";
export declare abstract class BaseProvider implements AIProvider {
    protected modelName: string;
    protected readonly providerName: AIProviderName;
    protected readonly defaultTimeout: number;
    protected middlewareOptions?: MiddlewareFactoryOptions;
    protected readonly directTools: {};
    protected mcpTools?: Record<string, Tool>;
    private fileToolRootsBound;
    protected customTools?: Map<string, unknown>;
    protected toolExecutor?: (toolName: string, params: unknown) => Promise<unknown>;
    protected sessionId?: string;
    protected userId?: string;
    protected neurolink?: NeuroLink;
    setTraceContext(ctx: {
        traceId: string;
        parentSpanId: string;
    } | null): void;
    private messageBuilder;
    private streamHandler;
    protected telemetryHandler: TelemetryHandler;
    private utilities;
    protected readonly toolsManager: ToolsManager;
    constructor(modelName?: string, providerName?: AIProviderName, neurolink?: NeuroLink, middleware?: MiddlewareFactoryOptions);
    /**
     * Update modelName and rebuild composition handlers with the new value.
     *
     * Auto-discovery providers (lm-studio, llamacpp) call this once they have
     * resolved the loaded model from `/v1/models`. Without this, handlers
     * (TelemetryHandler, MessageBuilder, ...) keep the pre-discovery name and
     * pricing / span / log metadata reports the stale value.
     */
    protected refreshHandlersForModel(model: string): void;
    /**
     * Check if this provider supports tool/function calling
     * Override in subclasses to disable tools for specific providers or models
     * @returns the current model's registered capability, or true when unknown
     */
    supportsTools(): boolean;
    /**
     * Whether this provider implements the opt-in `executionControl` contract.
     *
     * Default false, and that default is load-bearing: a provider that has not
     * implemented the contract must REJECT it, not ignore it. Silently dropping
     * a caller-set execution policy is invisible until a long turn dies at a
     * ceiling its owner believed had been removed. Overridden only where the
     * control is genuinely honoured end to end.
     */
    supportsExecutionControl(): boolean;
    /**
     * Apply the shared tool gate and optionally report registry-backed
     * suppression at the request entry point.
     */
    private shouldUseTools;
    /**
     * Primary streaming method - implements AIProvider interface
     * When tools are involved, falls back to generate() with synthetic streaming
     */
    stream(optionsOrPrompt: StreamOptions | string, analysisSchema?: ValidationSchema): Promise<StreamResult>;
    /**
     * Swap in a fallback model when a stream dies on a retired id before it has
     * produced anything.
     *
     * OpenAI-compatible streaming is lazy: executeStream() returns a
     * StreamResult without touching the network, and the request only goes out
     * on the consumer's first pull. A retired model therefore does NOT fail in
     * stream()'s try/catch — it fails deep inside iteration. This wrapper sits
     * UNDER wrapStreamWithLifecycleCallbacks precisely so that a failed first
     * attempt is invisible: onChunk, onFinish and onError all belong to the
     * layer above and never observe it.
     *
     * The `yielded` guards are the safety property. A stream is only retried
     * while it has emitted nothing; once a single chunk has reached the
     * consumer the output is committed, and any later failure propagates
     * untouched rather than replaying a half-delivered response.
     */
    private withStreamModelFallback;
    /**
     * The eager half of the retired-model stream fallback, for providers whose
     * executeStream() reaches the network before returning (see
     * runGenerateWithModelFallback for the generate() half and the reasoning).
     *
     * Returns a working StreamResult when a fallback model succeeds, or
     * undefined to mean "not recoverable — carry on with the original error".
     * Returning rather than throwing is deliberate: every existing path in the
     * caller's catch (the broad fake-streaming fallback, the terminal-error
     * re-throw, the onError firing) is left exactly as it was, so behaviour
     * changes only when a fallback actually succeeds.
     */
    private retryStreamWithFallbackModel;
    /**
     * Wrap a StreamResult with consumer-facing lifecycle callbacks.
     *
     * `options.onChunk`, `options.onFinish`, `options.onError` are translated
     * by NeuroLink.applyStreamLifecycleMiddleware() into
     * `options.middleware.middlewareConfig.lifecycle.config`. The AI SDK's
     * lifecycle middleware only sees these via the wrapped LanguageModel —
     * which is bypassed by providers that stream via raw HTTP fetch (Ollama
     * over /api/chat, custom OpenAI-compatible servers, etc). Wrapping the
     * user-facing stream here ensures the callbacks fire regardless of the
     * underlying transport.
     *
     * `teardownController`, when supplied, is the call's own abort source (see
     * `stream()`): the cancel hook fires it on abandonment so the transport's
     * in-flight HTTP request is cancelled, not just this iterator chain.
     */
    private wrapStreamWithLifecycleCallbacks;
    /**
     * Fire the consumer-supplied onError callback before throwing. Used in
     * error branches inside stream() that re-throw without emitting any
     * stream chunks (which would otherwise hide the failure from a caller
     * that supplied `onError`).
     */
    private fireLifecycleErrorCallback;
    /**
     * Build the fake-stream output and apply the same incremental TTS wrapper
     * used by the standard NeuroLink stream path.
     */
    private createFakeStreamingOutput;
    /**
     * Execute fake streaming - extracted method for reusability
     */
    private executeFakeStreaming;
    /**
     * Apply per-call tool filtering (whitelist/blacklist) to a tools record.
     *
     * All filtering surfaces are merged into one ResolvedToolPolicy by
     * `resolveToolPolicy()` — per-call `toolFilter` (whitelist),
     * `enabledToolNames` (merged into the whitelist, as its docs always
     * promised), `excludeTools` (denylist, applied after the whitelist), and
     * the instance-level `tools` config (enabled/include/exclude, `*` globs) —
     * then applied by `applyToolGate()`. This is the single filter semantics
     * for every generate/stream path.
     */
    private getToolPolicy;
    private applyToolFiltering;
    /**
     * Deterministic name-sorted key order. External MCP servers connect and
     * discover in parallel, so insertion order varies across process restarts;
     * providers serialize this record in key order (and Anthropic pins its
     * cache_control breakpoint to the LAST tool), so an unstable order silently
     * busts provider prompt caches. Runs AFTER the dedup pass — dedup's
     * keep-first policy must see phase order (built-ins first) so duplicate
     * winners don't flip when an MCP tool name sorts earlier.
     */
    private sortToolRecord;
    /**
     * On-demand discovery (`tools.discovery: true`): defer external MCP tool
     * schemas behind one `search_tools` meta-tool. Built-in tools, per-call
     * tools, explicitly whitelisted tools, and session-pinned (previously
     * discovered) tools always stay hot. No-op when discovery is off — with a
     * one-time WARN when the catalog is large enough that selection accuracy
     * measurably degrades.
     */
    private applyToolDiscovery;
    private static warnedLargeCatalog;
    private warnLargeCatalogOnce;
    /**
     * Opt-in signature dedup — runs AFTER whitelist/blacklist filtering and
     * BEFORE the tool set reaches the provider call.  Fails open: any error
     * inside dedupeTools returns the original filtered set unchanged.
     */
    private applyDedupPass;
    /**
     * Prepare generation context including tools and model
     */
    private prepareGenerationContext;
    /**
     * Get merged tools for streaming: combines base tools (MCP/built-in) with
     * user-provided tools (e.g., RAG tools passed via options.tools).
     *
     * This is the canonical tool-merge pattern for executeStream() implementations.
     * All providers should call this instead of getAllTools() directly.
     */
    protected getToolsForStream(options: StreamOptions | TextGenerationOptions): Promise<Record<string, Tool>>;
    /**
     * Create (or reuse) the per-call ToolExecutionRecorder and wrap the final
     * tool record with it. The recorder rides on the options object so provider
     * loops and result assembly observe the same capture state; wrapping is
     * idempotent, so paths that re-enter (stream→generate fallback) never
     * double-record.
     */
    protected wrapToolsForExecutionCapture(tools: Record<string, Tool>, options: StreamOptions | TextGenerationOptions): Record<string, Tool>;
    /**
     * Build messages array for generation - delegated to MessageBuilder
     */
    private buildMessages;
    /**
     * Fold the instance's `conversationMemory.replayToolSteps` into the request
     * when the caller set none, so every path into the message builder — text
     * or multimodal, generate or stream — reads one resolved value. The
     * builder itself defaults to `"marker"` when neither is set.
     *
     * Then degrade `"full"` to `"marker"` for a request that declares no tools:
     * Anthropic rejects a prompt carrying tool_use / tool_result blocks without
     * a `tools` field ("Requests which include tool_use or tool_result blocks
     * must define tools"), so a tools-off call — `disableTools`, or a filter
     * that removed every tool — in a session that already ran one would fail
     * on every later turn. Runs after the provider has merged `options.tools`,
     * which is where every native path calls the builder from.
     */
    protected applyToolReplayDefault(options: StreamOptions | TextGenerationOptions): void;
    /**
     * Build messages array for streaming operations - delegated to MessageBuilder
     * This is a protected helper method that providers can use to build messages
     * with automatic multimodal detection, eliminating code duplication
     *
     * @param options - Stream options or text generation options
     * @returns Promise resolving to ModelMessage array ready for AI SDK
     */
    protected buildMessagesForStream(options: StreamOptions | TextGenerationOptions): Promise<ModelMessage[]>;
    /**
     * Record performance metrics - delegated to TelemetryHandler
     */
    protected recordPerformanceMetrics(usage: RawUsageObject | undefined, responseTime: number): Promise<void>;
    /**
     * Text generation method - implements AIProvider interface
     * Tools are always available unless explicitly disabled
     *
     * Supports Text-to-Speech (TTS) audio generation in two modes:
     * 1. Direct synthesis (default): TTS synthesizes the input text without AI generation
     * 2. AI response synthesis: TTS synthesizes the AI-generated response after generation
     *
     * When TTS is enabled with useAiResponse=false (default), the method returns early with
     * only the audio result, skipping AI generation entirely for optimal performance.
     *
     * When TTS is enabled with useAiResponse=true, the method performs full AI generation
     * and then synthesizes the AI response to audio.
     *
     * @param optionsOrPrompt - Generation options or prompt string
     * @param _analysisSchema - Optional analysis schema (not used)
     * @returns Enhanced result with optional audio field containing TTSResult
     *
     * IMPLEMENTATION NOTE: Uses streamText() under the hood and accumulates results
     * for consistency and better performance
     */
    /**
     * Ensure runtime-discovered model limits (context window, output-token
     * ceiling) are registered before any budget math runs. Generation
     * pipelines await this BEFORE `checkContextBudget`, and generate()/stream()
     * await it before options normalization so `getSafeMaxTokens` sees the
     * discovered output ceiling.
     *
     * Default no-op. Providers with a runtime discovery source override it
     * (e.g. LiteLLM's `/model/info`). Implementations must NEVER reject —
     * discovery failure degrades to the static defaults.
     */
    ensureModelLimits(): Promise<void>;
    generate(optionsOrPrompt: TextGenerationOptions | string, _analysisSchema?: ValidationSchema): Promise<EnhancedGenerateResult | null>;
    /**
     * Models the catalog names are the models a vendor served when the entry was
     * last verified. Vendors retire them without warning, and until this existed
     * the runtime had no answer for that: an InvalidModelError is classified
     * non-retryable (correctly — another *provider* cannot fix a bad model id),
     * so the fallback chain stopped dead and the caller got an error even though
     * the same provider was still serving four other models listed right there
     * in the catalog's `fallbacks`.
     *
     * So on an invalid-model error only, walk this provider's fallbacks. Every
     * switch is a loud WARN: the caller asked for one model and is getting
     * another, which they must be able to see in the logs. Any other error type
     * propagates untouched on the first attempt.
     *
     * stream() gets the same treatment via retryStreamWithFallbackModel, which
     * is safe for the same reason it is cheap: the retry happens before any
     * lifecycle callback has fired and before a chunk has reached the consumer,
     * so there is no observable output to replay.
     */
    /**
     * Protected rather than private so a provider with a native generate path can
     * reuse it. Overriding `generate()` otherwise skips this wrapper silently,
     * and a retired default model stops degrading to the next live one in the
     * catalog — which is exactly what the provider contract gate checks.
     */
    protected runGenerateWithModelFallback(attempt: () => Promise<EnhancedGenerateResult | null>, callerOwnsFallback: boolean, options?: TextGenerationOptions): Promise<EnhancedGenerateResult | null>;
    /**
     * Model ids to try when this provider rejects its current model as invalid.
     * Empty by default; catalog-driven providers return their `fallbacks`.
     */
    protected getModelFallbacks(): string[];
    /**
     * The fallbacks an invalid-model retry may try for this request. A catalog
     * orders `fallbacks` for text, so a request carrying an image keeps only the
     * vision-capable ones: a text-only candidate fails the vision guard, and
     * that error names the fallback instead of the retired model the caller
     * asked for. With no vision-capable candidate left, the caller gets the
     * original invalid-model error.
     */
    protected modelFallbacksFor(options: TextGenerationOptions | StreamOptions, requestedModel: string): string[];
    /**
     * Alias for generate method - implements AIProvider interface
     */
    gen(optionsOrPrompt: TextGenerationOptions | string, analysisSchema?: ValidationSchema): Promise<EnhancedGenerateResult | null>;
    private runGenerateInActiveContext;
    /**
     * Mode 1 — synthesize the input text itself, with no LLM call.
     *
     * This is the documented default for `generate({ tts: { enabled: true } })`
     * and what every "speak this text" caller relies on, so a request that
     * looks like it wanted the model's reply (tools, a system prompt, message
     * history, a schema, conversation memory) is warned about, never rejected:
     * those options have no effect here, and the fix is `tts.mode: "response"`.
     */
    protected handleDirectTTSSynthesis(options: TextGenerationOptions, startTime: number): Promise<EnhancedGenerateResult>;
    /**
     * One warning when a direct-synthesis request carries options that only a
     * model call would honour and the caller never chose a mode. Silence is
     * the wrong answer here — the caller gets `usage` of zero and `content`
     * echoing their prompt, and would otherwise have to guess why.
     *
     * Only options the CALLER supplied count. `conversationMemoryConfig` is
     * attached by NeuroLink to every request on a memory-enabled instance, and
     * on such an instance `conversationMessages` is the stored history
     * NeuroLink injected itself — neither is something the caller set, and a
     * voice bot with memory used to see this warning on every turn after the
     * first for options it never passed.
     */
    private warnIfDirectTTSLooksLikeResponseRequest;
    /**
     * Outer bound on one synthesis call: the provider's request timeout, raised
     * to whatever the handler itself may legitimately spend — `timeoutMs × (1 +
     * retries)` plus retry pauses, with the handler defaults (30 s, one retry)
     * standing in for a knob the caller left unset. Without the raise, a
     * handler's own retry loop could be cut off from outside one attempt in:
     * with the 60 s provider default, a first attempt that hung to its 30 s
     * abort left no room for the documented default retry.
     */
    private resolveTTSSynthesisTimeout;
    /**
     * Apply `tts.sanitize` (off by default — existing callers see byte-identical
     * input). Always runs when configured, regardless of the raw text's
     * length against the provider's cap: sanitizing can shrink text a great
     * deal (the default `codeBlocks: "phrase"` replaces a whole fenced block
     * with one short phrase, `urls: "hostname"` shortens every link), so a
     * reply that is over the cap raw can still fit after sanitizing — a coding
     * answer with a large code block is exactly the reply this option is for.
     * A reply still over the cap afterwards is rejected downstream by
     * `TTSProcessor.synthesize` as `TTS_TEXT_TOO_LONG`, same as today.
     */
    private prepareTTSInput;
    /**
     * One synthesis call under the outer budget. The handler receives a signal
     * that fires when that budget lapses or the caller's own `abortSignal`
     * does, so a handler with a retry loop (ElevenLabs) stops rather than
     * pausing and issuing a further billable request after the caller has
     * already been told the synthesis failed.
     */
    private runTTSSynthesis;
    private handleVideoFrameGeneration;
    /**
     * Close out a turn produced by a provider's own native generate loop.
     *
     * The native loops bypass `executeGeneration`, and with it every post-call
     * step the standard path runs. Each one that was missed had to be found
     * separately — `onFinish` stopped firing, TTS synthesis silently produced no
     * audio, and OTEL saw no usage for any native provider. Routing all of them
     * through one method is what stops the next native path from rediscovering
     * the same list.
     *
     * Order matters and mirrors the standard path: metrics are recorded before
     * synthesis so telemetry sees the model's own usage, and `enhanceResult`
     * runs last so analytics and evaluation observe the final content.
     */
    protected finalizeNativeGenerate(result: EnhancedGenerateResult, options: TextGenerationOptions, startTime: number, toolExecutionSummaries?: ReadonlyArray<ToolExecutionSummaryInternal>): Promise<EnhancedGenerateResult>;
    /**
     * Persist a generate() turn's tool steps into conversation memory.
     *
     * The stream loops store each step's calls and results as they run;
     * generate() never did, so the same session held tool rows for streamed
     * turns and none for generated ones, and a later turn could not replay
     * what generate() had called. Every native generate path closes out
     * through finalizeNativeGenerate, so this is the one place to do it.
     *
     * Gated on a real session id: the storage helper otherwise invents a
     * `session-<nanoid>` key, and on the in-memory backend that creates a
     * throwaway session per tool-using call — evicting real ones once
     * `maxSessions` is reached. A turn with no session has no history to
     * replay into, so there is nothing to persist.
     */
    private persistGenerateToolSteps;
    /**
     * Invoke a caller's `onFinish` for a native turn.
     *
     * On the standard path `onFinish` is converted into lifecycle middleware and
     * applied by wrapping the model — a step the native loops skip, so they have
     * to call it themselves. Failures are logged and swallowed: a caller's
     * callback must not be able to fail their generation.
     */
    protected fireGenerateOnFinish(options: TextGenerationOptions, result: EnhancedGenerateResult | null, startTime: number): void;
    protected synthesizeAIResponseIfNeeded(enhancedResult: EnhancedGenerateResult, options: TextGenerationOptions): Promise<EnhancedGenerateResult>;
    /**
     * Build the public TTS failure detail.
     *
     * The message is redacted through the shared `sanitizeErrorCause` before it
     * leaves this method. It used to be an internal value that only reached the
     * logger; it is now carried on `result.ttsMetadata` and therefore reaches
     * SDK callers, who may forward it to an end user. Provider errors quote the
     * request URL often enough that a key in a query string is a real vector —
     * `redactUrlsInText` strips exactly that while leaving a bare URL readable,
     * so the diagnosis survives and the credential does not.
     */
    private getTTSErrorDetails;
    /**
     * BACKWARD COMPATIBILITY: Legacy generateText method
     * Converts EnhancedGenerateResult to TextGenerationResult format
     * Ensures existing scripts using createAIProvider().generateText() continue to work
     */
    generateText(options: TextGenerationOptions): Promise<TextGenerationResult>;
    /**
     * Generate embeddings for text
     *
     * This is a default implementation that throws an error.
     * Providers that support embeddings (OpenAI, Google Vertex, Amazon Bedrock)
     * should override this method with their specific implementation.
     *
     * @param input - Text string or EmbedInput with text/image/mimeType
     * @param _modelName - Optional embedding model name (provider-specific)
     * @returns Promise resolving to the embedding vector (array of numbers)
     * @throws Error if the provider does not support embeddings
     *
     * @example
     * ```typescript
     * const provider = await ProviderFactory.createProvider('openai', 'text-embedding-3-small');
     * const embedding = await provider.embed('Hello world');
     * console.log(embedding); // [0.123, -0.456, ...]
     *
     * // Multi-modal embedding (Bedrock Titan Image / Nova)
     * const multiEmbedding = await provider.embed({ text: "a photo", image: imageBuffer, mimeType: "image/png" });
     * ```
     */
    embed(input: string | EmbedInput, _modelName?: string): Promise<number[]>;
    /**
     * Generate embeddings for multiple texts in a single batch
     *
     * This is a default implementation that throws an error.
     * Providers that support embeddings should override this method.
     * The AI SDK's embedMany automatically handles chunking for models with batch limits.
     *
     * @param texts - The texts to embed
     * @param _modelName - Optional embedding model name (provider-specific)
     * @returns Promise resolving to an array of embedding vectors
     * @throws Error if the provider does not support embeddings
     */
    embedMany(texts: string[], _modelName?: string): Promise<number[][]>;
    /**
     * Get the default embedding model for this provider
     *
     * Override in subclasses to provide provider-specific defaults.
     * Returns undefined for providers that don't support embeddings.
     *
     * @returns The default embedding model name, or undefined if not supported
     */
    protected getDefaultEmbeddingModel(): string | undefined;
    /**
     * Evaluate a state against a batch of typed questions.
     *
     * Default implementation throws, exactly as `embed()` does: `decide` is an
     * optional capability, declared per provider by `"decide"` in its
     * descriptor's `inferenceKinds`. Callers that want a silent no-op on an
     * unsupporting provider should check the descriptor first rather than
     * catching this.
     *
     * Latency on a decision model is flat in question count while concurrent
     * requests queue, so this method is deliberately batch-shaped: one call
     * carries every question a caller needs. There is no single-question
     * convenience wrapper, because it would invite fanning out.
     *
     * @throws Error if the provider does not serve the `decide` inference type
     */
    decide(_request: DecisionRequest): Promise<DecisionResult>;
    /**
     * Get the default decision model for this provider.
     *
     * Mirrors {@link getDefaultEmbeddingModel}. Returns undefined for providers
     * that do not serve the `decide` inference type.
     */
    protected getDefaultDecisionModel(): string | undefined;
    /**
     * Create an `experimental_repairToolCall` handler for streamText/generateText.
     * Dynamically reads the tool's JSON schema to repair wrong names and params.
     * Returns undefined when repair is disabled via options.
     */
    protected getToolCallRepairFn(options?: StreamOptions | TextGenerationOptions): ToolCallRepairFunction<ToolSet> | undefined;
    /**
     * Opt-in streaming hook. A provider that produces an async iterable of text
     * chunks plus promises for how the turn ended implements this and gets a
     * working `executeStream` for free.
     *
     * Deliberately optional rather than abstract: every provider that already
     * overrides `executeStream` directly — the older and still perfectly valid
     * pattern — never needs it.
     *
     * `finishReason` and `usage` are promises because both are only knowable
     * once the underlying stream has finished. Resolve them when it does; the
     * default `executeStream` chains off them rather than waiting for a
     * consumer, so they must settle even if nobody drains the stream.
     */
    protected doStream?(options: StreamOptions): Promise<{
        stream: AsyncIterable<{
            content: string;
        }>;
        finishReason: Promise<string>;
        usage: Promise<{
            inputTokens: number;
            outputTokens: number;
        }>;
        warnings?: string[];
    }>;
    /**
     * Provider-specific streaming implementation (only used when tools are
     * disabled).
     *
     * This used to be `protected abstract`, which meant a provider had exactly
     * two options: write the whole adapter by hand, or not stream at all. The
     * failure mode that produced was SageMaker's — a complete, working
     * `doStream` sitting one property access away from an `executeStream` that
     * unconditionally threw "not yet fully implemented". Providers that
     * implement `doStream` now inherit a correct implementation, and providers
     * that override this method are unaffected.
     */
    protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
    /**
     * Get the provider name
     */
    protected abstract getProviderName(): AIProviderName;
    /**
     * Get the default model for this provider
     */
    protected abstract getDefaultModel(): string;
    /**
     * REQUIRED: Every provider MUST implement this method
     * Returns the Vercel AI SDK model instance for this provider
     */
    /**
     * Run one whole turn under the caller's timeout contract.
     *
     * Every generate path must go through this, including the native loops that
     * bypass `executeGeneration`. When the Anthropic native path was first added
     * it built its own call and never composed a timer, so `timeout: 1000`
     * against a three-second upstream simply returned the late response — the
     * turn budget silently stopped existing for that provider.
     *
     * An explicit, valid `turnTimeoutMs` is the caller's whole-turn contract and
     * owns this hard abort; `timeout` then keeps its per-model-call meaning (it
     * reaches the model layer via `providerOptions.neurolink`). Before that
     * split, `timeout` alone bounded the ENTIRE multi-step loop, so a caller
     * asking for a 40-minute turn of 5-minute calls was killed at 5 minutes
     * flat — mid-loop, dressed as "Request was aborted.".
     *
     * `descriptorGenerateMs` only ever RAISES the 3-minute floor, never lowers
     * it: several descriptors carry aspirational sub-180s numbers (openai 30s,
     * bedrock 45s) that were never enforced, and enforcing them now would break
     * long-running generations that have always been allowed.
     */
    protected withTurnTimeout<T>(options: TextGenerationOptions, descriptorGenerateMs: number | undefined, run: (timedOptions: TextGenerationOptions) => Promise<T>): Promise<T>;
    /**
     * Emit a `turn:lifecycle` event on the SDK emitter (alongside
     * tool:start/tool:end) so loop conditions that previously only reached
     * process logs — step-cap, time-limit, stall, abort, tool timeouts,
     * malformed-call retries — are observable by consumers' own pipelines.
     *
     * Lives here rather than on one provider because every native agentic loop
     * has the same conditions to report, and a second copy is how two loops
     * come to emit subtly different payloads for the same event name.
     */
    protected emitTurnEvent(payload: {
        phase: "step-cap" | "context-cap" | "time-limit" | "stalled" | "aborted" | "provider-error" | "tool-timeout" | "malformed-retry";
        step?: number;
        maxSteps?: number;
        toolName?: string;
        toolCallCount?: number;
        elapsedMs?: number;
    }): void;
    /**
     * The per-provider generate budget from the descriptor, for native paths
     * that call `withTurnTimeout` directly.
     */
    protected getDescriptorGenerateMs(): number | undefined;
    /** Per-provider `decide` budget from the descriptor, when one is declared. */
    protected getDescriptorDecideMs(): number | undefined;
    protected abstract getAISDKModel(): LanguageModel | Promise<LanguageModel>;
    /**
     * Public handle on this provider's model object.
     *
     * `getAISDKModel()` is protected because only the generation pipeline should
     * drive it. The browser bundle needs the same handle to back its public
     * provider factories without reaching into provider internals, so this is the
     * one sanctioned way out. The union return lets providers that resolve their
     * model synchronously (Anthropic) and asynchronously (the OpenAI-compatible
     * family) both satisfy it.
     */
    getModel(): LanguageModel | Promise<LanguageModel>;
    /**
     * Get AI SDK model with middleware applied
     * This method wraps the base model with any configured middleware
     * TODO(#1953): Implement global level middlewares that can be used
     */
    protected getAISDKModelWithMiddleware(options?: TextGenerationOptions | StreamOptions): Promise<LanguageModel>;
    /**
     * Apply the configured middleware chain to a caller-supplied base model.
     *
     * `getAISDKModelWithMiddleware()` always wraps `getAISDKModel()`, which is
     * the model the non-streaming path drives. Streaming paths build a
     * different base — one whose `doStream` starts the provider's own stream
     * loop — and need the same chain applied to it, so the wrapping is split
     * out here rather than duplicated per provider.
     */
    protected applyMiddlewareToModel(baseModel: LanguageModel, options?: TextGenerationOptions | StreamOptions): Promise<LanguageModel>;
    /**
     * Extract middleware options - delegated to Utilities
     */
    private extractMiddlewareOptions;
    /**
     * Check if a schema is a Zod schema - delegated to Utilities
     */
    private isZodSchema;
    /**
     * Convert tool execution result - delegated to Utilities
     */
    private convertToolResult;
    /**
     * Fix JSON Schema for OpenAI strict mode - delegated to Utilities
     */
    private fixSchemaForOpenAIStrictMode;
    /**
     * Get all available tools - delegated to ToolsManager
     */
    /**
     * Bind the built-in file tools to one request's root policy. NeuroLink
     * calls this before every provider call that carries the caller's options,
     * because several providers override generate()/stream() and assemble
     * their tools without passing through BaseProvider's entry points.
     * Providers are created per request, so a bound policy never reaches
     * another request.
     */
    setFileToolRootPolicy(policy: FileToolRootPolicy | undefined): void;
    /**
     * Emit the start/end pair for a tool call a loop rejected before execute
     * (unknown tool, schema rejection). See ToolsManager.emitRejectedToolCall.
     */
    protected emitRejectedToolCall(toolName: string, error: string, toolCallId?: string): void;
    /**
     * Resolve roots for a provider called directly, without NeuroLink. A
     * policy NeuroLink already bound is left alone unless these options carry
     * their own. An invalid root throws, before any model request.
     */
    private applyFileToolRoots;
    protected getAllTools(): Promise<Record<string, Tool>>;
    /**
     * Calculate actual cost - delegated to TelemetryHandler
     */
    private calculateActualCost;
    /**
     * Create a permissive Zod schema - delegated to Utilities
     */
    private createPermissiveZodSchema;
    /**
     * Set session context for MCP tools - delegated to ToolsManager
     */
    setSessionContext(sessionId?: string, userId?: string): void;
    /**
     * Provider-specific error formatting.
     * Subclasses implement this to produce human-readable error messages
     * (e.g., "❌ Google Vertex AI Provider Error\n\n...").
     */
    protected abstract formatProviderError(error: unknown): Error;
    /**
     * Handle provider errors with abort passthrough.
     * AbortErrors are never wrapped — they must propagate with their
     * original identity so that isAbortError() can detect them in
     * retry/fallback loops (directProviderGeneration, performMCPGenerationRetries).
     */
    /**
     * Classify an error that escaped while the consumer was ITERATING a stream.
     *
     * `stream()` only awaits the CONSTRUCTION of the provider's stream object, and a provider
     * that discovers its failure lazily throws on first pull instead — so the raw upstream
     * error reached the consumer with no provider tag and no classification, while the same
     * failure through `generate()` was classified normally. Measured on OpenAI:
     *
     *   streaming      "You have no credits remaining."
     *   non-streaming  "[openai] OpenAI quota exhausted — this will not resolve by retrying..."
     *
     * Three guards. Only the third is load-bearing against today's code; the first two are
     * deliberate depth against a hazard that is real but not currently reachable. Measured,
     * rather than assumed — see the note after the list.
     *
     * 1. ALREADY STAMPED — everything that went through `handleProviderError` carries the mark,
     *    including the two formatters whose results escape the ProviderError hierarchy
     *    (`amazonSagemaker` returns SageMakerError, `replicate` returns NeuroLinkError; both
     *    extend Error directly). Without this they would be classified a second time and
     *    degraded.
     * 2. ALREADY A ProviderError — covers providers that call `formatProviderError` DIRECTLY,
     *    bypassing `handleProviderError` and therefore the stamp. Anthropic does this in its
     *    own streaming catch (`anthropic/client.ts`), and its result is a ProviderError.
     * 3. HAS AN HTTP STATUS — without this, an ordinary bug is relabelled as a provider
     *    failure. `classifyProviderError` ends in an unconditional catch-all
     *    (`utils/errorClassifier.ts`: `if (!rule) return new ProviderError(...)`) that is not
     *    gated on the error having come off the wire. Measured with the guard absent:
     *      TypeError "Cannot read properties of undefined (reading 'content')"
     *        became  ProviderError "[openai] openai error: Cannot read properties of undefined..."
     *    which would hide a real defect behind a plausible provider message.
     *
     * WHY 1 AND 2 ARE NOT CURRENTLY REACHABLE, and why they stay anyway. A statusCode is
     * attached to a formatted error in exactly one place — `handleProviderError` below — and
     * that same method applies the stamp. So a ProviderError carrying a status is always
     * stamped (caught by guard 1's own condition), and a ProviderError produced by a direct
     * `formatProviderError` call carries no status, so guard 3 already returns it untouched.
     * Verified by disabling guards 1 and 2 together, rebuilding, and re-running: the mocked
     * provider contract suite stayed 64/64 and a mocked Anthropic streaming 429 was
     * byte-identical (`RateLimitError`, one `[anthropic]` prefix).
     *
     * They remain because the hazard they cover is measured and real: `handleProviderError`
     * is NOT idempotent — it copies statusCode onto its own output, so a second pass
     * re-matches the bare 429 rule and DEGRADES the classification:
     *   pass 1  ProviderError  "...quota exhausted — this will not resolve by retrying..."
     *   pass 2  RateLimitError "...rate limit exceeded..."
     * The day any provider attaches a status to an error it formats itself, guard 3 stops
     * covering that case and this degradation becomes live. Cheap insurance, not dead code —
     * but do not cite guards 1 and 2 as proven-by-failure the way guard 3 is.
     */
    protected classifyStreamError(error: unknown): Error;
    protected handleProviderError(error: unknown): Error;
    /**
     * Image generation method. Providers that support it should override this.
     * By default, it throws an error indicating that the functionality is not supported.
     * @param _options The generation options.
     * @returns A promise that resolves to the generation result.
     */
    protected executeImageGeneration(_options: TextGenerationOptions): Promise<EnhancedGenerateResult>;
    /**
     * Execute operation with timeout and proper cleanup
     * Consolidates identical timeout handling from 8/10 providers
     */
    protected executeWithTimeout<T>(operation: () => Promise<T>, options: {
        timeout?: number | string;
        operationType?: string;
    }): Promise<T>;
    /**
     * Validate stream options - delegated to StreamHandler
     */
    protected validateStreamOptions(options: StreamOptions): void;
    /**
     * Create text stream transformation - delegated to StreamHandler.
     * Reviewer follow-up: forwards the optional `getUnderlyingError`
     * callback so providers can capture upstream errors via
     * `streamText`'s `onError` and have them flow into the
     * NoOutputGeneratedError sentinel's `providerError` /
     * `modelResponseRaw`.
     */
    protected createTextStream(result: {
        textStream: AsyncIterable<string>;
        finishReason?: Promise<unknown> | unknown;
        totalUsage?: Promise<unknown> | unknown;
    }, getUnderlyingError?: () => unknown): AsyncGenerator<{
        content: string;
    } | import("../types/index.js").StreamNoOutputSentinel>;
    /**
     * Create standardized stream result - delegated to StreamHandler
     */
    protected createStreamResult(stream: AsyncGenerator<{
        content: string;
    }>, additionalProps?: Partial<StreamResult>): StreamResult;
    /**
     * Create stream analytics - delegated to StreamHandler
     */
    protected createStreamAnalytics(result: UnknownRecord, startTime: number, options: StreamOptions): Promise<UnknownRecord | undefined>;
    /**
     * Handle common error patterns - delegated to Utilities
     */
    protected handleCommonErrors(error: unknown): Error | null;
    /**
     * Set up tool executor - delegated to ToolsManager
     * @param sdk - The NeuroLinkSDK instance for tool execution
     * @param functionTag - Function name for logging
     */
    setupToolExecutor(sdk: {
        customTools: Map<string, unknown>;
        executeTool: (toolName: string, params: unknown) => Promise<unknown>;
    }, functionTag: string): void;
    /**
     * Normalize text generation options - delegated to Utilities
     */
    protected normalizeTextOptions(optionsOrPrompt: TextGenerationOptions | string): TextGenerationOptions;
    /**
     * Normalize stream options - delegated to Utilities
     */
    protected normalizeStreamOptions(optionsOrPrompt: StreamOptions | string): StreamOptions;
    protected enhanceResult(result: EnhancedGenerateResult, options: TextGenerationOptions, startTime: number): Promise<EnhancedGenerateResult>;
    /**
     * Handle video generation mode
     *
     * Generates video from input image + text prompt using Vertex AI Veo 3.1.
     *
     * @param options - Text generation options with video configuration
     * @param startTime - Generation start timestamp for metrics
     * @returns Enhanced result with video data
     *
     * @example
     * ```typescript
     * const result = await provider.generate({
     *   input: { text: "Product showcase", images: [imageBuffer] },
     *   output: { mode: "video", video: { resolution: "1080p" } }
     * });
     * // result.video contains the generated video
     * ```
     */
    protected handleVideoGeneration(options: TextGenerationOptions, startTime: number): Promise<EnhancedGenerateResult>;
    /**
     * Create analytics - delegated to TelemetryHandler
     */
    protected createAnalytics(result: EnhancedGenerateResult, responseTime: number, options: TextGenerationOptions): Promise<AnalyticsData>;
    /**
     * Create evaluation - delegated to TelemetryHandler
     */
    protected createEvaluation(result: EnhancedGenerateResult, options: TextGenerationOptions): Promise<EvaluationData>;
    /**
     * Validate text generation options - delegated to Utilities
     */
    protected validateOptions(options: TextGenerationOptions): void;
    /**
     * Get provider information - delegated to Utilities
     */
    protected getProviderInfo(): {
        provider: string;
        model: string;
    };
    /**
     * Get timeout value in milliseconds - delegated to Utilities
     */
    getTimeout(options: TextGenerationOptions | StreamOptions): number;
    /**
     * Check if tool executions should be stored and handle storage
     */
    protected handleToolExecutionStorage(toolCalls: MemoryToolCallRecord[], toolResults: MemoryToolResultRecord[], options: TextGenerationOptions | StreamOptions, currentTime: Date): Promise<void>;
    /**
     * Utility method to chunk large prompts into smaller pieces
     * @param prompt The prompt to chunk
     * @param maxChunkSize Maximum size per chunk (default: 900,000 characters)
     * @param overlap Overlap between chunks to maintain context (default: 100 characters)
     * @returns Array of prompt chunks
     */
    static chunkPrompt(prompt: string, maxChunkSize?: number, overlap?: number): string[];
}
