/**
 * Vercel AI SDK adapter for StreamTextProvider.
 *
 * This adapter bridges our high-level StreamTextProvider interface
 * to the Vercel AI SDK's streamText function. It handles:
 * - Mapping our options to SDK options
 * - Adding onFinally support via stream wrapping
 * - Translating SDK results to our interface
 *
 * This is the only place that depends on Vercel AI SDK internals.
 * All other code (middlewares, etc.) depends only on our interface.
 */
import { streamText } from "ai";
import { getErrorMeta, getLogger } from "../../../../util/logger.js";
import { withStreamLifecycle } from "../stream-lifecycle.js";
const passThroughDownload = async (requestedDownloads) => requestedDownloads.map(() => null);
/**
 * Adapter that implements StreamTextProvider using Vercel AI SDK.
 *
 * This class wraps Vercel's streamText function and adds our custom
 * lifecycle management (particularly onFinally support).
 *
 * @example
 * ```typescript
 * const provider = new VercelStreamTextAdapter();
 * const result = await provider.streamText({
 *   model: anthropic('claude-3-5-sonnet-20241022'),
 *   messages: [{ role: 'user', content: 'Hello!' }],
 *   onFinally: async () => {
 *     console.log('Stream consumed!');
 *   }
 * });
 *
 * for await (const text of result.textStream) {
 *   console.log(text);
 * }
 * // onFinally runs here
 * ```
 */
export class VercelStreamTextAdapter {
    /**
     * Streams text using Vercel AI SDK with lifecycle management.
     *
     * @param options - Stream options including onFinally hook
     * @returns Promise resolving to stream result
     */
    async streamText(options) {
        // Extract our custom options that SDK doesn't support
        const { onFinally, user, ...rest } = options;
        const normalizeError = (error) => error instanceof Error ? error : new Error(String(error));
        const handleOnError = async (error) => {
            if (!rest.onError)
                return;
            await rest.onError(normalizeError(error));
        };
        // Validate that messages are provided (either directly or via user)
        if (!rest.messages && !user) {
            throw new Error("Either messages or user must be provided to streamText");
        }
        // Capture the messages for debugging purposes
        const messagesForLLM = rest.messages || (user ? [user] : []);
        // Call Vercel SDK's streamText with proper generic types
        // TOOLS: The tools type parameter from our method signature
        // any, any: OUTPUT and PARTIAL_OUTPUT (we don't use structured outputs)
        const rawResult = streamText({
            model: rest.model,
            messages: messagesForLLM,
            system: rest.system,
            tools: rest.tools,
            ...(rest.maxSteps !== undefined && { maxSteps: rest.maxSteps }),
            ...(rest.maxOutputTokens !== undefined && {
                maxOutputTokens: rest.maxOutputTokens,
            }),
            abortSignal: rest.abortSignal,
            headers: rest.headers,
            providerOptions: rest.providerOptions,
            stopWhen: rest.stopWhen,
            experimental_transform: rest.experimental_transform,
            // Always pass URL parts through untouched. The downstream Clark/server
            // pipeline is responsible for lease refresh and media materialization.
            experimental_download: passThroughDownload,
            // Surface low-level provider events (Anthropic `ping` keep-alives, Bedrock
            // message metadata, etc.) as `{type:"raw"}` chunks. During Claude extended
            // thinking the provider can go 30-90s without emitting a reasoning-delta,
            // and without these ticks our IdleMonitor would stall the stream mid-
            // thinking and retry. Raw chunks flow through managed-stream.ts → reset
            // the idle timer. Observers that don't care about raw chunks ignore them.
            // See APPS-3460.
            includeRawChunks: true,
            prepareStep: rest.prepareStep,
            onChunk: rest.onChunk,
            onStepFinish: rest.onStepFinish,
            onFinish: rest.onFinish,
            onError: rest.onError ? (event) => handleOnError(event.error) : undefined,
            onAbort: rest.onAbort,
        });
        // Suppress unhandled rejections - prevents SIGABRT when tools throw errors
        // When a tool execution fails, the Vercel AI SDK rejects these promises.
        // The onError callback at line 94 already handles error reporting, but we need
        // to suppress the rejections here to prevent Node.js from triggering SIGABRT.
        // Attach catch handlers to suppress unhandled rejections
        // These handlers prevent SIGABRT but don't change the promise behavior
        const observeRejection = (promiseLike, label) => {
            Promise.resolve(promiseLike).catch((error) => {
                getLogger().debug(label, getErrorMeta(error));
            });
        };
        const stepsPromise = Promise.resolve(rawResult.steps);
        observeRejection(stepsPromise, "Stream steps promise rejected");
        observeRejection(rawResult.files, "Stream files promise rejected");
        observeRejection(rawResult.sources, "Stream sources promise rejected");
        observeRejection(rawResult.toolCalls, "Stream toolCalls promise rejected");
        observeRejection(rawResult.staticToolCalls, "Stream staticToolCalls promise rejected");
        observeRejection(rawResult.dynamicToolCalls, "Stream dynamicToolCalls promise rejected");
        observeRejection(rawResult.staticToolResults, "Stream staticToolResults promise rejected");
        observeRejection(rawResult.dynamicToolResults, "Stream dynamicToolResults promise rejected");
        observeRejection(rawResult.toolResults, "Stream toolResults promise rejected");
        observeRejection(rawResult.text, "Stream text promise rejected");
        observeRejection(rawResult.finishReason, "Stream finishReason promise rejected");
        observeRejection(rawResult.rawFinishReason, "Stream rawFinishReason promise rejected");
        observeRejection(rawResult.usage, "Stream usage promise rejected");
        observeRejection(rawResult.totalUsage, "Stream totalUsage promise rejected");
        observeRejection(rawResult.warnings, "Stream warnings promise rejected");
        observeRejection(rawResult.request, "Stream request promise rejected");
        observeRejection(rawResult.providerMetadata, "Stream providerMetadata promise rejected");
        // Create our StreamTextResult (extracts only what we need) FIRST
        // Return the original promises - rejections are now handled by the catch handlers above
        const result = {
            textStream: rawResult.textStream,
            fullStream: rawResult.fullStream,
            steps: stepsPromise, // Use a real Promise - rejection is now suppressed
        };
        // Then apply onFinally lifecycle hook if provided
        return onFinally ? withStreamLifecycle(result, onFinally) : result;
    }
}
/**
 * Creates a StreamTextProvider using Vercel AI SDK.
 *
 * Convenience function for creating the adapter.
 *
 * @returns A new Vercel SDK adapter instance
 *
 * @example
 * ```typescript
 * const provider = createVercelProvider();
 *
 * // Use with middlewares
 * const wrapped = applyMiddleware(
 *   provider,
 *   contextMiddleware,
 *   loggingMiddleware
 * );
 * ```
 */
export function createVercelProvider() {
    return new VercelStreamTextAdapter();
}
//# sourceMappingURL=vercel.js.map