"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigurationError = exports.CacheError = exports.AIModelError = exports.ValidationError = exports.StepExecutionError = exports.PassmarkError = exports.assert = exports.generateEmail = exports.extractEmailContent = exports.emailsinkProvider = exports.configure = exports.executeWithAutoHealing = exports.runUserFlow = exports.runSteps = void 0;
const errors_1 = require("./errors");
const ai_1 = require("ai");
const ai_2 = require("axiom/ai");
const shortid_1 = __importDefault(require("shortid"));
const instrumentation_1 = require("./instrumentation");
// Only use withSpan when Axiom is configured, otherwise just execute the function directly
async function maybeWithSpan(meta, fn) {
    return (0, instrumentation_1.isAxiomEnabled)() ? (0, ai_2.withSpan)(meta, async () => fn()) : fn();
}
const zod_1 = require("zod");
const prompts_1 = require("./prompts");
const redis_1 = require("./redis");
const tools_1 = require("./tools");
const utils_1 = require("./utils");
const assertion_1 = require("./assertion");
const video_1 = require("./video");
const data_cache_1 = require("./data-cache");
const config_1 = require("./config");
const cua_1 = require("./cua");
const extract_1 = require("./extract");
const logger_1 = require("./logger");
const models_1 = require("./models");
const secure_script_runner_1 = require("./utils/secure-script-runner");
const tab_manager_1 = require("./utils/tab-manager");
const constants_1 = require("./constants");
/**
 * Executes a sequence of test steps using AI with intelligent caching.
 * Each step is described in natural language and executed via browser automation.
 * Successfully executed steps are cached in Redis for faster subsequent runs.
 *
 * @param options - Configuration including page, steps, assertions, and callbacks
 * @param options.page - The Playwright page instance
 * @param options.userFlow - Name of the user flow (used as cache key prefix)
 * @param options.steps - Array of steps to execute, each with a description and optional data
 * @param options.bypassCache - When true, skips cache and forces AI execution for all steps
 * @param options.assertions - Optional assertions to verify after step execution
 * @param options.executionId - Links multiple runSteps calls to share {{global.*}} placeholders
 * @param options.onStepStart - Callback fired when a step begins execution
 * @param options.onStepEnd - Callback fired when a step completes
 * @param options.onReasoning - Callback fired with AI reasoning for each tool call
 * @throws Rethrows step execution timeout errors
 *
 * @example
 * ```typescript
 * await runSteps({
 *   page,
 *   userFlow: "Checkout Flow",
 *   steps: [
 *     { description: "Add item to cart" },
 *     { description: "Fill in email", data: { value: "{{run.email}}" } },
 *   ],
 *   assertions: [{ assertion: "Order confirmation is displayed" }],
 *   expect,
 * });
 * ```
 */
const runSteps = async ({ page, test, expect, userFlow, steps, auth, bypassCache = false, onStepStart, onStepEnd, onReasoning, assertions, projectId, executionId, failAssertionsSilently, ai: callLevelAi, }) => {
    executionId = executionId || process.env.executionId;
    // Initialize Axiom telemetry now that any user `configure()` call has run.
    // Idempotent — re-entry is a no-op.
    (0, instrumentation_1.initTelemetry)();
    // Track all open tabs for this run. The active page is updated automatically
    // when a new tab opens, or explicitly via the `switchToTab` step field.
    const tabManager = (0, tab_manager_1.createTabManager)(page);
    const redis = (0, redis_1.getRedis)();
    if (!redis) {
        logger_1.logger.warn("Redis not configured. Step caching is disabled — all steps will use AI execution.");
        if (executionId) {
            logger_1.logger.warn("{{global.*}} placeholders will not persist across runSteps calls without Redis.");
        }
    }
    // Check if this is a Playwright retry - if so, bypass cache and use AI only
    const isPlaywrightRetry = test ? test.info().retry > 0 : false;
    if (isPlaywrightRetry) {
        logger_1.logger.debug(`Playwright retry detected (retry #${test.info().retry}). Bypassing cache and using AI only.`);
    }
    // Process dynamic placeholders before running steps
    const { processedSteps, processedAssertions, localValues, globalValues, projectDataValues } = await (0, data_cache_1.processPlaceholders)(steps, assertions, executionId, projectId);
    logger_1.logger.info(`Starting step-by-step execution of ${processedSteps.length} steps.`);
    // If any assertion opted into video evaluation, record a single screencast
    // spanning the full step run. One recording is shared across all video
    // assertions in this call; cleanup happens in a finally block below.
    const needsVideo = processedAssertions?.some((a) => a.video) ?? false;
    let videoRecorder;
    if (needsVideo) {
        videoRecorder = new video_1.VideoRecorder(tabManager.active());
        try {
            await videoRecorder.start();
        }
        catch (error) {
            logger_1.logger.warn({ err: error }, "Failed to start screencast — video assertions will fall back to screenshot/snapshot.");
            videoRecorder = undefined;
        }
    }
    let errorInStepExecution, stepThatFailed = "";
    for (let i = 0; i < processedSteps.length; i++) {
        // Resolve email placeholders lazily just before step execution
        // This ensures the email has arrived before we try to extract content
        // Use global email if available, otherwise fall back to run email, and then use the supplied email from regex
        // ~~~ This logic needs to be fixed as global email will always be present if executionId is provided ~~~
        const dynamicEmail = (0, data_cache_1.getDynamicEmail)(localValues, globalValues);
        // Re-process step data and waitUntil with current localValues to pick up extracted values from previous steps
        let currentStep = processedSteps[i];
        if (currentStep.data) {
            currentStep = {
                ...currentStep,
                data: Object.fromEntries(Object.entries(currentStep.data).map(([k, v]) => [
                    k,
                    (0, data_cache_1.replacePlaceholders)(v, localValues, globalValues, projectDataValues),
                ])),
            };
        }
        if (currentStep.waitUntil) {
            currentStep = {
                ...currentStep,
                waitUntil: (0, data_cache_1.replacePlaceholders)(currentStep.waitUntil, localValues, globalValues, projectDataValues),
            };
        }
        const step = await (0, data_cache_1.resolveEmailPlaceholders)(currentStep, dynamicEmail);
        const id = shortid_1.default.generate();
        // Resolve effective AI config for this step. Step > runSteps call > global.
        const effectiveAi = (0, config_1.resolveAI)(callLevelAi, step.ai);
        if (onStepStart) {
            onStepStart({ id, description: step.description });
        }
        // Switch tab before executing the step if requested.
        if (step.switchToTab !== undefined) {
            await tabManager.switchTo(step.switchToTab);
        }
        // Script mode: execute script directly, skip AI and cache
        if (step.isScript) {
            if (!step.script) {
                throw new errors_1.ValidationError(`Script step ${step.description} has no script content.`);
            }
            logger_1.logger.debug(`Executing Script Step: ${step.description}`);
            if (step.moduleId) {
                // moduleId is optional metadata used only for logging/debugging to identify the source module of this script step.
                logger_1.logger.debug(`Module ID: ${step.moduleId}`);
            }
            try {
                let pageScreenshotBeforeApplyingAction = "";
                if (step.waitUntil) {
                    pageScreenshotBeforeApplyingAction = (await tabManager.active().screenshot({ fullPage: false })).toString("base64");
                }
                if (onReasoning) {
                    onReasoning({
                        id,
                        reasoning: `Executing script for step: ${step.description}`,
                    });
                }
                // Execute script securely using AST-based validation
                // This prevents arbitrary code execution by only allowing safe Playwright method chains
                await (0, secure_script_runner_1.runSecureScript)({
                    page: tabManager,
                    script: step.script,
                    localValues: localValues,
                    globalValues: globalValues,
                    expect, // Pass expect for assertions like expect(locator).toContainText()
                });
                // Handle waitUntil if specified
                if (step.waitUntil) {
                    await (0, utils_1.waitForCondition)({
                        page: tabManager,
                        condition: step.waitUntil,
                        pageScreenshotBeforeApplyingAction,
                        previousSteps: processedSteps.slice(0, i),
                        currentStep: step,
                        nextStep: processedSteps[i + 1],
                    });
                }
                // Handle data extraction if specified
                // This is done post script execution
                if (step.extract) {
                    await (0, extract_1.applyExtraction)({
                        extract: step.extract,
                        tabManager,
                        localValues,
                        globalValues,
                        executionId,
                    });
                }
                if (onStepEnd) {
                    onStepEnd({ id, description: step.description });
                }
                continue; // Skip to next step
            }
            catch (error) {
                const message = error instanceof Error ? error.message : String(error);
                logger_1.logger.error(`Script execution failed: ${message}`);
                errorInStepExecution = message;
                stepThatFailed = step.description;
                break; // Stop execution on script failure
            }
        }
        // CUA mode: use OpenAI Responses API + built-in `computer` tool instead of
        // the ARIA-snapshot path. Coord-based actions aren't cacheable, so we skip
        // the redis cache lookup and the Vercel AI SDK step.
        if (effectiveAi.mode === "cua") {
            logger_1.logger.debug(`Executing Step (CUA): ${step.description}`);
            let pageScreenshotBeforeApplyingAction = "";
            if (step.waitUntil) {
                pageScreenshotBeforeApplyingAction = (await tabManager.active().screenshot({ fullPage: false })).toString("base64");
            }
            try {
                await maybeWithSpan({ capability: "step_execution", step: "cua_loop" }, () => (0, cua_1.runCUALoop)({
                    page: tabManager.active(),
                    instruction: (0, cua_1.buildRunStepsPromptCUA)({
                        auth,
                        steps: processedSteps,
                        step,
                        userFlow,
                        stepIndex: i,
                    }),
                    maxSteps: constants_1.STEP_EXECUTION_MAX_STEPS,
                    abortSignal: AbortSignal.timeout(constants_1.STEP_EXECUTION_TIMEOUT),
                    onReasoning: onReasoning
                        ? (reasoning) => onReasoning({ id, reasoning })
                        : undefined,
                    gateway: effectiveAi.gateway,
                }));
            }
            catch (error) {
                logger_1.logger.error({ err: error }, `CUA step execution failed: ${step.description}`);
                errorInStepExecution = error instanceof Error ? error.message : String(error);
                stepThatFailed = step.description;
                break;
            }
            if (step.waitUntil) {
                await (0, utils_1.waitForCondition)({
                    page: tabManager,
                    condition: step.waitUntil,
                    pageScreenshotBeforeApplyingAction,
                    previousSteps: processedSteps.slice(0, i),
                    currentStep: step,
                    nextStep: processedSteps[i + 1],
                });
            }
            if (step.extract) {
                await (0, extract_1.applyExtraction)({
                    extract: step.extract,
                    tabManager,
                    localValues,
                    globalValues,
                    executionId,
                });
            }
            if (onStepEnd) {
                onStepEnd({ id, description: step.description });
            }
            continue;
        }
        // First check if the step is cached on redis
        const cachedStep = redis ? await redis.hgetall(`step:${userFlow}:${step.description}`) : {};
        if (!bypassCache &&
            !isPlaywrightRetry &&
            !step.bypassCache &&
            cachedStep &&
            Object.keys(cachedStep).length > 0) {
            // Running cached step
            logger_1.logger.debug(`Executing Cached Step: ${step.description}`);
            const locator = cachedStep["locator"];
            const action = cachedStep["action"];
            const description = cachedStep["description"].replace(/'/g, "\\'");
            const value = cachedStep["value"];
            const input = step.data?.value || value;
            let code = "";
            switch (action) {
                case "click":
                case "dblclick":
                    code = `await page.${locator}.describe('${description}').${action}({ timeout: ${constants_1.CACHED_ACTION_TIMEOUT} });`;
                    break;
                case "fill":
                    code = `await page.${locator}.describe('${description}').fill("${input}", { timeout: ${constants_1.CACHED_ACTION_TIMEOUT} })`;
                    break;
                case "hover":
                    code = `await page.${locator}.describe('${description}').hover({ timeout: ${constants_1.CACHED_ACTION_TIMEOUT} })`;
                    break;
                case "select-option":
                    code = `await page.${locator}.describe('${description}').selectOption("${input}", { timeout: ${constants_1.CACHED_ACTION_TIMEOUT} })`;
                    break;
                case "waitForText":
                    code = `await page.getByText("${value}", { exact: true }).first().waitFor({ state: "visible" })`;
                    break;
            }
            logger_1.logger.debug(`Executing cached action:\n${code}`);
            try {
                let pageScreenshotBeforeApplyingAction = "";
                if (step.waitUntil) {
                    pageScreenshotBeforeApplyingAction = (await tabManager.active().screenshot({ fullPage: false })).toString("base64");
                }
                /**
                 *  Before executing the first cached step, ensure the DOM is stable to avoid
                 *  taking snapshot of a loading or transitioning state. Give it higher idle time because the page might
                 *  take a bit longer to stabilize right after navigation.
                 */
                const INITIAL_DOM_STABILIZATION_IDLE_TIME = constants_1.INITIAL_DOM_STABILIZATION_IDLE;
                if (i === 0) {
                    await (0, utils_1.waitForDOMStabilization)(tabManager, test, INITIAL_DOM_STABILIZATION_IDLE_TIME);
                }
                const pageSnapshotBeforeApplyingAction = await (0, utils_1.safeSnapshot)(tabManager);
                await (0, utils_1.runLocatorCode)(tabManager, code);
                /**
                 *  Verify that the action had the intended effect on the page. This is because sometimes cached pw action may silently fail.
                 *
                 *  Before verifying, this function will wait for the DOM to stabilize.
                 *  stabilization idle time is set to 500ms by default.
                 *
                 *  This means workflow is this: action performed -> wait for DOM stabilization -> check if action had effect -> next step
                 *
                 *  Auto healing will be triggered if the action did not have any effect on the page.
                 */
                await (0, utils_1.verifyActionEffect)(tabManager, action, pageSnapshotBeforeApplyingAction);
                if (step.waitUntil) {
                    await (0, utils_1.waitForCondition)({
                        page: tabManager,
                        condition: step.waitUntil,
                        pageScreenshotBeforeApplyingAction,
                        previousSteps: processedSteps.slice(0, i),
                        currentStep: step,
                        nextStep: processedSteps[i + 1],
                    });
                }
                // Handle data extraction if specified
                // This is done post cached step execution
                if (step.extract) {
                    await (0, extract_1.applyExtraction)({
                        extract: step.extract,
                        tabManager,
                        localValues,
                        globalValues,
                        executionId,
                    });
                }
                continue;
            }
            catch (error) {
                logger_1.logger.debug(`Error executing cached step, falling back to AI execution: ${error}`);
            }
        }
        const abortController = new AbortController();
        const { tools, getPendingCacheData, clearPendingCacheData } = (0, tools_1.getAItools)(tabManager.active(), {
            currentStep: step,
            abortController,
            test,
            tabManager,
        });
        logger_1.logger.debug(`Executing Step: ${step.description}`);
        let pageScreenshotBeforeApplyingAction = "";
        if (step.waitUntil) {
            pageScreenshotBeforeApplyingAction = (await tabManager.active().screenshot({ fullPage: false })).toString("base64");
        }
        const stepModelId = effectiveAi.getModelId("stepExecution");
        const model = (0, models_1.resolveModel)(stepModelId, effectiveAi.gateway);
        logger_1.logger.debug(`Using model: ${stepModelId} for step execution / gateway: ${effectiveAi.gateway}`);
        try {
            const result = await maybeWithSpan({ capability: "step_execution", step: "agentic_tool_calling" }, async () => (0, ai_1.generateText)({
                model,
                maxRetries: constants_1.MAX_RETRIES,
                temperature: 0,
                tools: tools,
                providerOptions: {
                    google: {
                        thinkingConfig: {
                            includeThoughts: false,
                            thinkingLevel: "medium",
                        },
                    },
                    openrouter: {
                        reasoning: {
                            effort: "medium",
                            exclude: true
                        },
                    },
                },
                onStepFinish: async ({ toolCalls }) => {
                    if (!onReasoning)
                        return;
                    // Append tool call reasoning to the response
                    toolCalls.forEach((toolCall) => {
                        const reasoning = `${(toolCall?.input).reasoning}\n\n`;
                        onReasoning({
                            id,
                            reasoning,
                        });
                    });
                },
                stopWhen: [(0, ai_1.stepCountIs)(constants_1.STEP_EXECUTION_MAX_STEPS), (0, ai_1.hasToolCall)("browser_stop")],
                abortSignal: AbortSignal.timeout(constants_1.STEP_EXECUTION_TIMEOUT),
                toolChoice: "auto",
                prompt: (0, prompts_1.buildRunStepsPrompt)({
                    auth,
                    steps: processedSteps,
                    step,
                    userFlow,
                    stepIndex: i,
                }),
            }));
            // Cache the step action only if it was a single tool call (simple, deterministic action).
            // Multi-step actions are not cached as they may be non-deterministic.
            const allToolCalls = result.steps
                .flatMap((s) => s.toolCalls)
                .filter((tool) => ["browser_snapshot", "browser_stop"].indexOf(tool.toolName) === -1);
            if (allToolCalls.length === 1 && redis) {
                const cacheData = getPendingCacheData();
                if (cacheData) {
                    await redis.hset(`step:${userFlow}:${step.description}`, cacheData);
                    logger_1.logger.debug(`Cached step action: ${step.description}`);
                }
            }
            clearPendingCacheData();
        }
        catch (error) {
            logger_1.logger.error({ err: error }, `Step execution failed: ${step.description}`);
            errorInStepExecution = error instanceof Error ? error.message : String(error);
            stepThatFailed = step.description;
            break;
        }
        if (step.waitUntil) {
            await (0, utils_1.waitForCondition)({
                page: tabManager,
                condition: step.waitUntil,
                pageScreenshotBeforeApplyingAction,
                previousSteps: processedSteps.slice(0, i),
                currentStep: step,
                nextStep: processedSteps[i + 1],
            });
        }
        // Handle data extraction if specified
        // This is done post AI step execution
        if (step.extract) {
            await (0, extract_1.applyExtraction)({
                extract: step.extract,
                tabManager,
                localValues,
                globalValues,
                executionId,
            });
        }
        if (onStepEnd) {
            onStepEnd({ id, description: step.description });
        }
    }
    if (errorInStepExecution) {
        logger_1.logger.warn(`Step execution encountered an error. Skipping assertions execution.`);
        const errorDescription = `\n${errorInStepExecution}\nStep: ${stepThatFailed}`;
        if (test) {
            test.info().annotations.push({
                type: "Error",
                description: errorDescription,
            });
        }
        // Stop & delete the recording before re-throwing so we don't leak a
        // running screencast or a temp file when steps fail mid-flow.
        if (videoRecorder) {
            await videoRecorder.stop();
            await videoRecorder.cleanup();
        }
        throw new errors_1.StepExecutionError(errorDescription, stepThatFailed);
    }
    // Stop recording (if any) before running assertions so the saved file is
    // ready to upload. Cleanup of the file happens in the finally below.
    if (videoRecorder) {
        await videoRecorder.stop();
    }
    try {
        if (processedAssertions && processedAssertions.length > 0 && expect) {
            for (const { assertion: preProcessedAssertion, effort, images, video, } of processedAssertions) {
                // Re-resolve placeholders against the latest localValues so extracted
                // values from steps (e.g. {{run.emailContent}}) get substituted in.
                const assertion = (0, data_cache_1.replacePlaceholders)(preProcessedAssertion, localValues, globalValues, projectDataValues);
                logger_1.logger.info(`Running assertion: ${assertion}`);
                const id = shortid_1.default.generate();
                if (onStepStart) {
                    onStepStart({
                        id,
                        description: "Starting assertion verification",
                    });
                }
                if (onReasoning) {
                    onReasoning({
                        id,
                        reasoning: `Verifying assertion: ${assertion}`,
                    });
                }
                const reasoning = await (0, assertion_1.assert)({
                    page: tabManager,
                    assertion,
                    test,
                    expect,
                    effort,
                    images,
                    failSilently: failAssertionsSilently,
                    maxRetries: 1,
                    onRetry: (retryCount, previousResult) => { },
                    video: video && Boolean(videoRecorder),
                    videoFilePath: videoRecorder?.filePath,
                });
                if (onReasoning) {
                    onReasoning({
                        id,
                        reasoning: `\n\n${reasoning}`,
                    });
                }
                if (onStepEnd) {
                    onStepEnd({ id, description: "Successfully verified assertion" });
                }
            }
        }
    }
    finally {
        if (videoRecorder) {
            await videoRecorder.cleanup();
        }
    }
};
exports.runSteps = runSteps;
/**
 * Runs a complete user flow as a single AI agent call.
 * Best for exploratory testing where exact steps are flexible.
 * The AI autonomously navigates, interacts, and verifies the flow.
 *
 * @param options - User flow configuration
 * @param options.page - The Playwright page instance
 * @param options.userFlow - Description of the user flow to execute
 * @param options.steps - Natural language description of steps to perform
 * @param options.effort - "low" uses a faster model, "high" uses a more capable model with deeper thinking
 * @param options.assertion - Optional assertion to verify after the flow completes
 * @returns The assertion result if an assertion was provided, the raw AI text response otherwise, or undefined on error
 *
 * @example
 * ```typescript
 * const result = await runUserFlow({
 *   page,
 *   userFlow: "Complete a purchase",
 *   steps: "Navigate to store, add an item, checkout",
 *   effort: "high",
 *   assertion: "Order confirmation is displayed",
 * });
 * ```
 */
const runUserFlow = async ({ page, userFlow, steps, assertion, effort = "low", thinkingBudget = constants_1.THINKING_BUDGET_DEFAULT, ai: callLevelAi, }) => {
    const abortController = new AbortController();
    const effectiveAi = (0, config_1.resolveAI)(callLevelAi);
    // CUA mode: skip the Vercel AI SDK path entirely. Run the Responses API loop,
    // then reuse the existing utility-model assertion parser on its final text.
    if (effectiveAi.mode === "cua") {
        try {
            const text = await maybeWithSpan({ capability: "user_flow_execution", step: "cua_loop" }, () => (0, cua_1.runCUALoop)({
                page,
                instruction: (0, cua_1.buildRunUserFlowPromptCUA)({ userFlow, steps, assertion }),
                maxSteps: constants_1.USER_FLOW_MAX_STEPS,
                abortSignal: abortController.signal,
                gateway: effectiveAi.gateway,
            }));
            if (assertion) {
                const { output } = await (0, ai_1.generateText)({
                    model: (0, models_1.resolveModel)(effectiveAi.getModelId("utility"), effectiveAi.gateway),
                    prompt: `Convert the following text output into a valid JSON object with the specified properties:\n\n${text}`,
                    output: ai_1.Output.object({
                        schema: zod_1.z.object({
                            assertionPassed: zod_1.z.boolean().describe("Indicates whether the assertion passed or not."),
                            confidenceScore: zod_1.z
                                .number()
                                .describe("Confidence score of the assertion, between 0 and 100."),
                            reasoning: zod_1.z
                                .string()
                                .describe("Brief explanation of the reasoning behind the assertion."),
                        }),
                    }),
                });
                return output;
            }
            return text;
        }
        catch (error) {
            logger_1.logger.error({ err: error }, "Error during CUA user flow execution");
            return;
        }
    }
    const model = effort === "low"
        ? (0, models_1.resolveModel)(effectiveAi.getModelId("userFlowLow"), effectiveAi.gateway)
        : (0, models_1.resolveModel)(effectiveAi.getModelId("userFlowHigh"), effectiveAi.gateway);
    const { tools } = (0, tools_1.getAItools)(page, {
        abortController,
    });
    try {
        const { text } = await maybeWithSpan({ capability: "user_flow_execution", step: "agentic_tool_calling" }, async () => {
            return (0, ai_1.generateText)({
                model,
                maxRetries: constants_1.MAX_RETRIES,
                temperature: 0,
                tools: tools,
                providerOptions: {
                    google: {
                        thinkingConfig: {
                            thinkingBudget,
                        },
                    },
                    openrouter: {
                        reasoning: {
                            max_tokens: thinkingBudget,
                        },
                    },
                },
                stopWhen: [(0, ai_1.stepCountIs)(constants_1.USER_FLOW_MAX_STEPS), (0, ai_1.hasToolCall)("browser_stop")],
                abortSignal: abortController.signal,
                prepareStep: async ({ messages }) => {
                    // Remove older messages to keep the context window small
                    if (messages.length > 11) {
                        const modifiedMessages = [messages[0], ...messages.slice(-10)];
                        return {
                            messages: modifiedMessages,
                        };
                    }
                    return {};
                },
                toolChoice: "auto",
                prompt: (0, prompts_1.buildRunUserFlowPrompt)({
                    steps,
                    userFlow,
                    assertion,
                }),
            });
        });
        if (assertion) {
            const { output } = await (0, ai_1.generateText)({
                model: (0, models_1.resolveModel)(effectiveAi.getModelId("utility"), effectiveAi.gateway),
                prompt: `Convert the following text output into a valid JSON object with the specified properties:\n\n${text}`,
                output: ai_1.Output.object({
                    schema: zod_1.z.object({
                        assertionPassed: zod_1.z.boolean().describe("Indicates whether the assertion passed or not."),
                        confidenceScore: zod_1.z
                            .number()
                            .describe("Confidence score of the assertion, between 0 and 100."),
                        reasoning: zod_1.z
                            .string()
                            .describe("Brief explanation of the reasoning behind the assertion."),
                    }),
                }),
            });
            return output;
        }
        return text;
    }
    catch (error) {
        logger_1.logger.error({ err: error }, "Error during user flow execution");
    }
};
exports.runUserFlow = runUserFlow;
/**
 * Wraps a cached Playwright flow with AI fallback for auto-healing.
 * Tries the cached flow first; if it fails (e.g., due to UI changes), falls back to AI execution.
 *
 * @param config - Configuration for cached and AI flow execution
 * @param config.cachedFlow - The cached Playwright flow to try first
 * @param config.aiFlow - The AI-powered fallback flow to run if cached flow fails
 * @param config.aiFlowTimeout - Optional timeout for the AI flow in milliseconds
 * @param config.test - Playwright test instance for retry detection and timeout management
 *
 * @example
 * ```typescript
 * await executeWithAutoHealing({
 *   cachedFlow: async () => { await page.getByRole("button").click(); },
 *   aiFlow: async () => { await runSteps({ page, userFlow: "Click submit", steps }); },
 *   test,
 * });
 * ```
 */
const executeWithAutoHealing = async (config) => {
    const { cachedFlow, aiFlow, test, aiFlowTimeout } = config;
    if (process.env.AI || test.info().retry > 0) {
        if (aiFlowTimeout) {
            test.setTimeout(aiFlowTimeout);
        }
        await aiFlow();
    }
    else {
        await cachedFlow();
    }
};
exports.executeWithAutoHealing = executeWithAutoHealing;
var config_2 = require("./config");
Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return config_2.configure; } });
var emailsink_1 = require("./providers/emailsink");
Object.defineProperty(exports, "emailsinkProvider", { enumerable: true, get: function () { return emailsink_1.emailsinkProvider; } });
var email_1 = require("./email");
Object.defineProperty(exports, "extractEmailContent", { enumerable: true, get: function () { return email_1.extractEmailContent; } });
Object.defineProperty(exports, "generateEmail", { enumerable: true, get: function () { return email_1.generateEmail; } });
var assertion_2 = require("./assertion");
Object.defineProperty(exports, "assert", { enumerable: true, get: function () { return assertion_2.assert; } });
var errors_2 = require("./errors");
Object.defineProperty(exports, "PassmarkError", { enumerable: true, get: function () { return errors_2.PassmarkError; } });
Object.defineProperty(exports, "StepExecutionError", { enumerable: true, get: function () { return errors_2.StepExecutionError; } });
Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return errors_2.ValidationError; } });
Object.defineProperty(exports, "AIModelError", { enumerable: true, get: function () { return errors_2.AIModelError; } });
Object.defineProperty(exports, "CacheError", { enumerable: true, get: function () { return errors_2.CacheError; } });
Object.defineProperty(exports, "ConfigurationError", { enumerable: true, get: function () { return errors_2.ConfigurationError; } });
