/**
 * 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";
/**
 * 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,
            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
        rawResult.steps.catch((error) => {
            getLogger().debug("Stream steps promise rejected", getErrorMeta(error));
        });
        rawResult.response?.catch((error) => {
            getLogger().debug("Stream response promise rejected", getErrorMeta(error));
        });
        // Also catch text promise - this rejects with AI_NoOutputGeneratedError
        // when tools fail and no output is generated
        rawResult.text?.catch((error) => {
            getLogger().debug("Stream text promise rejected", getErrorMeta(error));
        });
        // Catch other promises that might reject
        rawResult.finishReason?.catch((error) => {
            getLogger().debug("Stream finishReason promise rejected", getErrorMeta(error));
        });
        rawResult.usage?.catch((error) => {
            getLogger().debug("Stream usage promise rejected", getErrorMeta(error));
        });
        // 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: rawResult.steps, // Use original 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