"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const actHandler_js_1 = require("../../lib/v3/handlers/actHandler.js");
const extractHandler_js_1 = require("../../lib/v3/handlers/extractHandler.js");
const observeHandler_js_1 = require("../../lib/v3/handlers/observeHandler.js");
const timeoutGuard_js_1 = require("../../lib/v3/handlers/handlerUtils/timeoutGuard.js");
const actHandlerUtils_js_1 = require("../../lib/v3/handlers/handlerUtils/actHandlerUtils.js");
const index_js_1 = require("../../lib/v3/understudy/a11y/snapshot/index.js");
const sdkErrors_js_1 = require("../../lib/v3/types/public/sdkErrors.js");
const inference_js_1 = require("../../lib/inference.js");
const methods_js_1 = require("../../lib/v3/types/public/methods.js");
vitest_1.vi.mock("../../lib/v3/handlers/handlerUtils/timeoutGuard", () => ({
    createTimeoutGuard: vitest_1.vi.fn(),
}));
vitest_1.vi.mock("../../lib/v3/handlers/handlerUtils/actHandlerUtils", () => ({
    waitForDomNetworkQuiet: vitest_1.vi.fn(),
    performUnderstudyMethod: vitest_1.vi.fn(),
}));
vitest_1.vi.mock("../../lib/v3/understudy/a11y/snapshot", () => ({
    captureHybridSnapshot: vitest_1.vi.fn(),
    diffCombinedTrees: vitest_1.vi.fn(),
}));
vitest_1.vi.mock("../../lib/inference", () => ({
    act: vitest_1.vi.fn(),
    extract: vitest_1.vi.fn(),
    observe: vitest_1.vi.fn(),
}));
(0, vitest_1.describe)("ActHandler timeout guard", () => {
    (0, vitest_1.beforeEach)(() => {
        vitest_1.vi.clearAllMocks();
    });
    (0, vitest_1.it)("throws ActTimeoutError when timeout expires before snapshot", async () => {
        const waitForDomNetworkQuietMock = vitest_1.vi.mocked(actHandlerUtils_js_1.waitForDomNetworkQuiet);
        waitForDomNetworkQuietMock.mockResolvedValue(undefined);
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "",
            combinedXpathMap: {},
            combinedUrlMap: {},
        });
        // Make createTimeoutGuard return a guard that throws on call #2
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            let calls = 0;
            return vitest_1.vi.fn(() => {
                calls += 1;
                if (calls >= 2) {
                    throw errorFactory
                        ? errorFactory(timeoutMs)
                        : new sdkErrors_js_1.ActTimeoutError(timeoutMs);
                }
            });
        });
        const handler = buildActHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.act({
            instruction: "do something",
            page: fakePage,
            timeout: 5,
        })).rejects.toThrow(sdkErrors_js_1.ActTimeoutError);
        // Verify pre-timeout helper ran
        (0, vitest_1.expect)(waitForDomNetworkQuietMock).toHaveBeenCalledTimes(1);
        // Verify snapshot was NOT called (timeout fired before it)
        (0, vitest_1.expect)(captureHybridSnapshotMock).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)("throws ActTimeoutError when timeout expires before LLM call", async () => {
        const waitForDomNetworkQuietMock = vitest_1.vi.mocked(actHandlerUtils_js_1.waitForDomNetworkQuiet);
        waitForDomNetworkQuietMock.mockResolvedValue(undefined);
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: {},
            combinedUrlMap: {},
        });
        const actInferenceMock = vitest_1.vi.mocked(inference_js_1.act);
        // Throw on call #3 (after snapshot but before LLM)
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            let calls = 0;
            return vitest_1.vi.fn(() => {
                calls += 1;
                if (calls >= 3) {
                    throw errorFactory
                        ? errorFactory(timeoutMs)
                        : new sdkErrors_js_1.ActTimeoutError(timeoutMs);
                }
            });
        });
        const handler = buildActHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.act({
            instruction: "do something",
            page: fakePage,
            timeout: 5,
        })).rejects.toThrow(sdkErrors_js_1.ActTimeoutError);
        // Snapshot should have been called
        (0, vitest_1.expect)(captureHybridSnapshotMock).toHaveBeenCalledTimes(1);
        // LLM inference should NOT have been called
        (0, vitest_1.expect)(actInferenceMock).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)("throws ActTimeoutError with correct message format", async () => {
        const waitForDomNetworkQuietMock = vitest_1.vi.mocked(actHandlerUtils_js_1.waitForDomNetworkQuiet);
        waitForDomNetworkQuietMock.mockResolvedValue(undefined);
        const timeoutMs = 100;
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((ms, errorFactory) => {
            return vitest_1.vi.fn(() => {
                throw errorFactory ? errorFactory(ms) : new sdkErrors_js_1.ActTimeoutError(ms);
            });
        });
        const handler = buildActHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        try {
            await handler.act({
                instruction: "do something",
                page: fakePage,
                timeout: timeoutMs,
            });
            throw new Error("Expected ActTimeoutError to be thrown");
        }
        catch (error) {
            (0, vitest_1.expect)(error).toBeInstanceOf(sdkErrors_js_1.ActTimeoutError);
            (0, vitest_1.expect)(error.message).toContain("act()");
            (0, vitest_1.expect)(error.message).toContain(`${timeoutMs}ms`);
            (0, vitest_1.expect)(error.name).toBe("ActTimeoutError");
        }
    });
});
(0, vitest_1.describe)("ActHandler two-step timeout", () => {
    (0, vitest_1.beforeEach)(() => {
        vitest_1.vi.clearAllMocks();
    });
    (0, vitest_1.it)("throws ActTimeoutError during step 2; step 2 action does not run", async () => {
        const waitForDomNetworkQuietMock = vitest_1.vi.mocked(actHandlerUtils_js_1.waitForDomNetworkQuiet);
        waitForDomNetworkQuietMock.mockResolvedValue(undefined);
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: { "1-0": "/html/body/button" },
            combinedUrlMap: {},
        });
        const { performUnderstudyMethod } = await Promise.resolve().then(() => __importStar(require("../../lib/v3/handlers/handlerUtils/actHandlerUtils.js")));
        const performUnderstudyMethodMock = vitest_1.vi.mocked(performUnderstudyMethod);
        performUnderstudyMethodMock.mockResolvedValue(undefined);
        const actInferenceMock = vitest_1.vi.mocked(inference_js_1.act);
        // First call returns a two-step action
        actInferenceMock.mockResolvedValueOnce({
            element: {
                elementId: "1-0",
                description: "click button",
                method: "click",
                arguments: [],
            },
            twoStep: true,
            prompt_tokens: 100,
            completion_tokens: 50,
            inference_time_ms: 500,
        });
        const diffCombinedTreesMock = vitest_1.vi.mocked((await Promise.resolve().then(() => __importStar(require("../../lib/v3/understudy/a11y/snapshot/index.js"))))
            .diffCombinedTrees);
        diffCombinedTreesMock.mockReturnValue("diff tree");
        // Timeout fires after step 1 completes, during step 2 snapshot
        // ensureTimeRemaining calls: 1=before wait, 2=after wait/before snap1, 3=before LLM1,
        // 4=before action1, 5=inside takeDeterministicAction, 6=performUnderstudy,
        // 7=before snap2 (this one should throw)
        let callCount = 0;
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            return vitest_1.vi.fn(() => {
                callCount += 1;
                if (callCount >= 7) {
                    throw errorFactory
                        ? errorFactory(timeoutMs)
                        : new sdkErrors_js_1.ActTimeoutError(timeoutMs);
                }
            });
        });
        const handler = buildActHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.act({
            instruction: "click then type",
            page: fakePage,
            timeout: 50,
        })).rejects.toThrow(sdkErrors_js_1.ActTimeoutError);
        // Step 1 action should have been executed
        (0, vitest_1.expect)(performUnderstudyMethodMock).toHaveBeenCalledTimes(1);
        // Step 2 LLM call should NOT have happened
        (0, vitest_1.expect)(actInferenceMock).toHaveBeenCalledTimes(1);
    });
});
(0, vitest_1.describe)("ActHandler self-heal timeout", () => {
    (0, vitest_1.beforeEach)(() => {
        vitest_1.vi.clearAllMocks();
    });
    (0, vitest_1.it)("throws ActTimeoutError during self-heal snapshot; no retry action executes", async () => {
        const waitForDomNetworkQuietMock = vitest_1.vi.mocked(actHandlerUtils_js_1.waitForDomNetworkQuiet);
        waitForDomNetworkQuietMock.mockResolvedValue(undefined);
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: { "1-0": "/html/body/button" },
            combinedUrlMap: {},
        });
        const { performUnderstudyMethod } = await Promise.resolve().then(() => __importStar(require("../../lib/v3/handlers/handlerUtils/actHandlerUtils.js")));
        const performUnderstudyMethodMock = vitest_1.vi.mocked(performUnderstudyMethod);
        // First call fails, triggering self-heal
        performUnderstudyMethodMock.mockRejectedValueOnce(new Error("Element not found"));
        const actInferenceMock = vitest_1.vi.mocked(inference_js_1.act);
        actInferenceMock.mockResolvedValue({
            element: {
                elementId: "1-0",
                description: "click button",
                method: "click",
                arguments: [],
            },
            twoStep: false,
            prompt_tokens: 100,
            completion_tokens: 50,
            inference_time_ms: 500,
        });
        // Timeout during self-heal snapshot (call 7 or later)
        let callCount = 0;
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            return vitest_1.vi.fn(() => {
                callCount += 1;
                // Timeout during self-heal snapshot call
                if (callCount >= 7) {
                    throw errorFactory
                        ? errorFactory(timeoutMs)
                        : new sdkErrors_js_1.ActTimeoutError(timeoutMs);
                }
            });
        });
        const handler = buildActHandler({ selfHeal: true });
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.act({
            instruction: "click button",
            page: fakePage,
            timeout: 50,
        })).rejects.toThrow(sdkErrors_js_1.ActTimeoutError);
        // First action attempt should have been tried
        (0, vitest_1.expect)(performUnderstudyMethodMock).toHaveBeenCalledTimes(1);
        // First LLM call should have happened
        (0, vitest_1.expect)(actInferenceMock).toHaveBeenCalledTimes(1);
        // Self-heal snapshot should have been started (call happened)
        (0, vitest_1.expect)(captureHybridSnapshotMock).toHaveBeenCalled();
    });
    (0, vitest_1.it)("throws ActTimeoutError during self-heal LLM inference; no retry action executes", async () => {
        const waitForDomNetworkQuietMock = vitest_1.vi.mocked(actHandlerUtils_js_1.waitForDomNetworkQuiet);
        waitForDomNetworkQuietMock.mockResolvedValue(undefined);
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: { "1-0": "/html/body/button" },
            combinedUrlMap: {},
        });
        const { performUnderstudyMethod } = await Promise.resolve().then(() => __importStar(require("../../lib/v3/handlers/handlerUtils/actHandlerUtils.js")));
        const performUnderstudyMethodMock = vitest_1.vi.mocked(performUnderstudyMethod);
        // First call fails, triggering self-heal
        performUnderstudyMethodMock.mockRejectedValueOnce(new Error("Element not found"));
        const actInferenceMock = vitest_1.vi.mocked(inference_js_1.act);
        actInferenceMock.mockResolvedValueOnce({
            element: {
                elementId: "1-0",
                description: "click button",
                method: "click",
                arguments: [],
            },
            twoStep: false,
            prompt_tokens: 100,
            completion_tokens: 50,
            inference_time_ms: 500,
        });
        // Timeout during self-heal LLM inference (call 8)
        let callCount = 0;
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            return vitest_1.vi.fn(() => {
                callCount += 1;
                // Timeout during self-heal LLM call
                if (callCount >= 8) {
                    throw errorFactory
                        ? errorFactory(timeoutMs)
                        : new sdkErrors_js_1.ActTimeoutError(timeoutMs);
                }
            });
        });
        const handler = buildActHandler({ selfHeal: true });
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.act({
            instruction: "click button",
            page: fakePage,
            timeout: 50,
        })).rejects.toThrow(sdkErrors_js_1.ActTimeoutError);
        // Self-heal snapshot was captured
        (0, vitest_1.expect)(captureHybridSnapshotMock).toHaveBeenCalledTimes(2);
        // Only one LLM inference (the retry inference was aborted by timeout)
        (0, vitest_1.expect)(actInferenceMock).toHaveBeenCalledTimes(1);
    });
});
(0, vitest_1.describe)("ExtractHandler timeout guard", () => {
    (0, vitest_1.beforeEach)(() => {
        vitest_1.vi.clearAllMocks();
    });
    (0, vitest_1.it)("throws ExtractTimeoutError when timeout expires before snapshot", async () => {
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: {},
            combinedUrlMap: {},
        });
        const extractInferenceMock = vitest_1.vi.mocked(inference_js_1.extract);
        // Throw immediately on first call
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            return vitest_1.vi.fn(() => {
                throw errorFactory
                    ? errorFactory(timeoutMs)
                    : new sdkErrors_js_1.ExtractTimeoutError(timeoutMs);
            });
        });
        const handler = buildExtractHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.extract({
            instruction: "extract title",
            page: fakePage,
            timeout: 5,
        })).rejects.toThrow(sdkErrors_js_1.ExtractTimeoutError);
        // Snapshot should NOT have been called
        (0, vitest_1.expect)(captureHybridSnapshotMock).not.toHaveBeenCalled();
        // LLM inference should NOT have been called
        (0, vitest_1.expect)(extractInferenceMock).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)("throws ExtractTimeoutError when timeout expires before LLM call", async () => {
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: {},
            combinedUrlMap: {},
        });
        const extractInferenceMock = vitest_1.vi.mocked(inference_js_1.extract);
        // Throw on call #2 (after snapshot but before LLM)
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            let calls = 0;
            return vitest_1.vi.fn(() => {
                calls += 1;
                if (calls >= 2) {
                    throw errorFactory
                        ? errorFactory(timeoutMs)
                        : new sdkErrors_js_1.ExtractTimeoutError(timeoutMs);
                }
            });
        });
        const handler = buildExtractHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.extract({
            instruction: "extract title",
            page: fakePage,
            timeout: 5,
        })).rejects.toThrow(sdkErrors_js_1.ExtractTimeoutError);
        // Snapshot should have been called
        (0, vitest_1.expect)(captureHybridSnapshotMock).toHaveBeenCalledTimes(1);
        // LLM inference should NOT have been called
        (0, vitest_1.expect)(extractInferenceMock).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)("throws ExtractTimeoutError with correct message format", async () => {
        const timeoutMs = 200;
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((ms, errorFactory) => {
            return vitest_1.vi.fn(() => {
                throw errorFactory ? errorFactory(ms) : new sdkErrors_js_1.ExtractTimeoutError(ms);
            });
        });
        const handler = buildExtractHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        try {
            await handler.extract({
                instruction: "extract title",
                page: fakePage,
                timeout: timeoutMs,
            });
            throw new Error("Expected ExtractTimeoutError to be thrown");
        }
        catch (error) {
            (0, vitest_1.expect)(error).toBeInstanceOf(sdkErrors_js_1.ExtractTimeoutError);
            (0, vitest_1.expect)(error.message).toContain("extract()");
            (0, vitest_1.expect)(error.message).toContain(`${timeoutMs}ms`);
            (0, vitest_1.expect)(error.name).toBe("ExtractTimeoutError");
        }
    });
    (0, vitest_1.it)("stops LLM and post-processing when timeout expires", async () => {
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: {},
            combinedUrlMap: { "1-0": "https://example.com" },
        });
        const extractInferenceMock = vitest_1.vi.mocked(inference_js_1.extract);
        // Allow snapshot but timeout before LLM
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            let calls = 0;
            return vitest_1.vi.fn(() => {
                calls += 1;
                if (calls >= 2) {
                    throw errorFactory
                        ? errorFactory(timeoutMs)
                        : new sdkErrors_js_1.ExtractTimeoutError(timeoutMs);
                }
            });
        });
        const handler = buildExtractHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.extract({
            instruction: "extract links",
            page: fakePage,
            timeout: 5,
        })).rejects.toThrow(sdkErrors_js_1.ExtractTimeoutError);
        // Post-processing (URL injection) never runs because LLM was never called
        (0, vitest_1.expect)(extractInferenceMock).not.toHaveBeenCalled();
    });
});
(0, vitest_1.describe)("ObserveHandler timeout guard", () => {
    (0, vitest_1.beforeEach)(() => {
        vitest_1.vi.clearAllMocks();
    });
    (0, vitest_1.it)("throws ObserveTimeoutError when timeout expires before snapshot", async () => {
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: {},
            combinedUrlMap: {},
        });
        const observeInferenceMock = vitest_1.vi.mocked(inference_js_1.observe);
        // Throw immediately on first call
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            return vitest_1.vi.fn(() => {
                throw errorFactory
                    ? errorFactory(timeoutMs)
                    : new sdkErrors_js_1.ObserveTimeoutError(timeoutMs);
            });
        });
        const handler = buildObserveHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.observe({
            instruction: "find buttons",
            page: fakePage,
            timeout: 5,
        })).rejects.toThrow(sdkErrors_js_1.ObserveTimeoutError);
        // Snapshot should NOT have been called
        (0, vitest_1.expect)(captureHybridSnapshotMock).not.toHaveBeenCalled();
        // LLM inference should NOT have been called
        (0, vitest_1.expect)(observeInferenceMock).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)("throws ObserveTimeoutError when timeout expires before LLM call", async () => {
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: {},
            combinedUrlMap: {},
        });
        const observeInferenceMock = vitest_1.vi.mocked(inference_js_1.observe);
        // Throw on call #2 (after snapshot but before LLM)
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            let calls = 0;
            return vitest_1.vi.fn(() => {
                calls += 1;
                if (calls >= 2) {
                    throw errorFactory
                        ? errorFactory(timeoutMs)
                        : new sdkErrors_js_1.ObserveTimeoutError(timeoutMs);
                }
            });
        });
        const handler = buildObserveHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.observe({
            instruction: "find buttons",
            page: fakePage,
            timeout: 5,
        })).rejects.toThrow(sdkErrors_js_1.ObserveTimeoutError);
        // Snapshot should have been called
        (0, vitest_1.expect)(captureHybridSnapshotMock).toHaveBeenCalledTimes(1);
        // LLM inference should NOT have been called
        (0, vitest_1.expect)(observeInferenceMock).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)("throws ObserveTimeoutError with correct message format", async () => {
        const timeoutMs = 150;
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((ms, errorFactory) => {
            return vitest_1.vi.fn(() => {
                throw errorFactory ? errorFactory(ms) : new sdkErrors_js_1.ObserveTimeoutError(ms);
            });
        });
        const handler = buildObserveHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        try {
            await handler.observe({
                instruction: "find buttons",
                page: fakePage,
                timeout: timeoutMs,
            });
            throw new Error("Expected ObserveTimeoutError to be thrown");
        }
        catch (error) {
            (0, vitest_1.expect)(error).toBeInstanceOf(sdkErrors_js_1.ObserveTimeoutError);
            (0, vitest_1.expect)(error.message).toContain("observe()");
            (0, vitest_1.expect)(error.message).toContain(`${timeoutMs}ms`);
            (0, vitest_1.expect)(error.name).toBe("ObserveTimeoutError");
        }
    });
    (0, vitest_1.it)("aborts result processing when timeout expires", async () => {
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: { "1-0": "/html/body/button" },
            combinedUrlMap: {},
        });
        const observeInferenceMock = vitest_1.vi.mocked(inference_js_1.observe);
        // Timeout before LLM call
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs, errorFactory) => {
            let calls = 0;
            return vitest_1.vi.fn(() => {
                calls += 1;
                if (calls >= 2) {
                    throw errorFactory
                        ? errorFactory(timeoutMs)
                        : new sdkErrors_js_1.ObserveTimeoutError(timeoutMs);
                }
            });
        });
        const handler = buildObserveHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        await (0, vitest_1.expect)(handler.observe({
            instruction: "find all interactive elements",
            page: fakePage,
            timeout: 5,
        })).rejects.toThrow(sdkErrors_js_1.ObserveTimeoutError);
        // Result mapping/processing never happens
        (0, vitest_1.expect)(observeInferenceMock).not.toHaveBeenCalled();
    });
});
(0, vitest_1.describe)("No-timeout success paths", () => {
    (0, vitest_1.beforeEach)(() => {
        vitest_1.vi.clearAllMocks();
    });
    (0, vitest_1.it)("act() completes successfully without timeout and records metrics", async () => {
        const waitForDomNetworkQuietMock = vitest_1.vi.mocked(actHandlerUtils_js_1.waitForDomNetworkQuiet);
        waitForDomNetworkQuietMock.mockResolvedValue(undefined);
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: { "1-0": "/html/body/button" },
            combinedUrlMap: {},
        });
        const { performUnderstudyMethod } = await Promise.resolve().then(() => __importStar(require("../../lib/v3/handlers/handlerUtils/actHandlerUtils.js")));
        const performUnderstudyMethodMock = vitest_1.vi.mocked(performUnderstudyMethod);
        performUnderstudyMethodMock.mockResolvedValue(undefined);
        const actInferenceMock = vitest_1.vi.mocked(inference_js_1.act);
        actInferenceMock.mockResolvedValue({
            element: {
                elementId: "1-0",
                description: "click button",
                method: "click",
                arguments: [],
            },
            twoStep: false,
            prompt_tokens: 100,
            completion_tokens: 50,
            reasoning_tokens: 10,
            cached_input_tokens: 5,
            inference_time_ms: 500,
        });
        // No timeout - guard never throws
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation(() => {
            return vitest_1.vi.fn(() => {
                // No-op - never throws
            });
        });
        const metricsCallback = vitest_1.vi.fn();
        const handler = buildActHandler({ onMetrics: metricsCallback });
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        const result = await handler.act({
            instruction: "click button",
            page: fakePage,
            // No timeout specified
        });
        (0, vitest_1.expect)(result.success).toBe(true);
        (0, vitest_1.expect)(metricsCallback).toHaveBeenCalledWith(methods_js_1.V3FunctionName.ACT, 100, 50, 10, 5, 500);
    });
    (0, vitest_1.it)("extract() completes successfully without timeout and records metrics", async () => {
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: {},
            combinedUrlMap: {},
        });
        const extractInferenceMock = vitest_1.vi.mocked(inference_js_1.extract);
        extractInferenceMock.mockResolvedValue({
            title: "Test Title",
            metadata: { completed: true, progress: "100%" },
            prompt_tokens: 200,
            completion_tokens: 100,
            reasoning_tokens: 20,
            cached_input_tokens: 10,
            inference_time_ms: 800,
        });
        // No timeout - guard never throws
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation(() => {
            return vitest_1.vi.fn(() => {
                // No-op - never throws
            });
        });
        const metricsCallback = vitest_1.vi.fn();
        const handler = buildExtractHandler({ onMetrics: metricsCallback });
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        const result = await handler.extract({
            instruction: "extract title",
            page: fakePage,
            // No timeout specified
        });
        (0, vitest_1.expect)(result).toHaveProperty("title", "Test Title");
        (0, vitest_1.expect)(metricsCallback).toHaveBeenCalledWith(methods_js_1.V3FunctionName.EXTRACT, 200, 100, 20, 10, 800);
    });
    (0, vitest_1.it)("observe() completes successfully without timeout and records metrics", async () => {
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: { "1-0": "/html/body/button" },
            combinedUrlMap: {},
        });
        const observeInferenceMock = vitest_1.vi.mocked(inference_js_1.observe);
        observeInferenceMock.mockResolvedValue({
            elements: [
                {
                    elementId: "1-0",
                    description: "Submit button",
                },
            ],
            prompt_tokens: 150,
            completion_tokens: 75,
            reasoning_tokens: 15,
            cached_input_tokens: 8,
            inference_time_ms: 600,
        });
        // No timeout - guard never throws
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation(() => {
            return vitest_1.vi.fn(() => {
                // No-op - never throws
            });
        });
        const metricsCallback = vitest_1.vi.fn();
        const handler = buildObserveHandler({ onMetrics: metricsCallback });
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        const result = await handler.observe({
            instruction: "find buttons",
            page: fakePage,
            // No timeout specified
        });
        (0, vitest_1.expect)(result).toHaveLength(1);
        (0, vitest_1.expect)(result[0]).toHaveProperty("description", "Submit button");
        (0, vitest_1.expect)(metricsCallback).toHaveBeenCalledWith(methods_js_1.V3FunctionName.OBSERVE, 150, 75, 15, 8, 600);
    });
    (0, vitest_1.it)("act() with zero timeout behaves as no timeout", async () => {
        const waitForDomNetworkQuietMock = vitest_1.vi.mocked(actHandlerUtils_js_1.waitForDomNetworkQuiet);
        waitForDomNetworkQuietMock.mockResolvedValue(undefined);
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: { "1-0": "/html/body/button" },
            combinedUrlMap: {},
        });
        const { performUnderstudyMethod } = await Promise.resolve().then(() => __importStar(require("../../lib/v3/handlers/handlerUtils/actHandlerUtils.js")));
        const performUnderstudyMethodMock = vitest_1.vi.mocked(performUnderstudyMethod);
        performUnderstudyMethodMock.mockResolvedValue(undefined);
        const actInferenceMock = vitest_1.vi.mocked(inference_js_1.act);
        actInferenceMock.mockResolvedValue({
            element: {
                elementId: "1-0",
                description: "click button",
                method: "click",
                arguments: [],
            },
            twoStep: false,
            prompt_tokens: 100,
            completion_tokens: 50,
            inference_time_ms: 500,
        });
        // When timeout is 0 or negative, createTimeoutGuard returns a no-op
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs) => {
            if (!timeoutMs || timeoutMs <= 0) {
                return vitest_1.vi.fn(() => {
                    // No-op
                });
            }
            return vitest_1.vi.fn(() => {
                throw new sdkErrors_js_1.ActTimeoutError(timeoutMs);
            });
        });
        const handler = buildActHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        const result = await handler.act({
            instruction: "click button",
            page: fakePage,
            timeout: 0, // Zero timeout should be treated as "no timeout"
        });
        (0, vitest_1.expect)(result.success).toBe(true);
    });
    (0, vitest_1.it)("act() with negative timeout behaves as no timeout", async () => {
        const waitForDomNetworkQuietMock = vitest_1.vi.mocked(actHandlerUtils_js_1.waitForDomNetworkQuiet);
        waitForDomNetworkQuietMock.mockResolvedValue(undefined);
        const captureHybridSnapshotMock = vitest_1.vi.mocked(index_js_1.captureHybridSnapshot);
        captureHybridSnapshotMock.mockResolvedValue({
            combinedTree: "tree content",
            combinedXpathMap: { "1-0": "/html/body/button" },
            combinedUrlMap: {},
        });
        const { performUnderstudyMethod } = await Promise.resolve().then(() => __importStar(require("../../lib/v3/handlers/handlerUtils/actHandlerUtils.js")));
        const performUnderstudyMethodMock = vitest_1.vi.mocked(performUnderstudyMethod);
        performUnderstudyMethodMock.mockResolvedValue(undefined);
        const actInferenceMock = vitest_1.vi.mocked(inference_js_1.act);
        actInferenceMock.mockResolvedValue({
            element: {
                elementId: "1-0",
                description: "click button",
                method: "click",
                arguments: [],
            },
            twoStep: false,
            prompt_tokens: 100,
            completion_tokens: 50,
            inference_time_ms: 500,
        });
        vitest_1.vi.mocked(timeoutGuard_js_1.createTimeoutGuard).mockImplementation((timeoutMs) => {
            if (!timeoutMs || timeoutMs <= 0) {
                return vitest_1.vi.fn(() => {
                    // No-op
                });
            }
            return vitest_1.vi.fn(() => {
                throw new sdkErrors_js_1.ActTimeoutError(timeoutMs);
            });
        });
        const handler = buildActHandler();
        const fakePage = {
            mainFrame: vitest_1.vi.fn().mockReturnValue({}),
        };
        const result = await handler.act({
            instruction: "click button",
            page: fakePage,
            timeout: -100, // Negative timeout should be treated as "no timeout"
        });
        (0, vitest_1.expect)(result.success).toBe(true);
    });
});
function buildActHandler(options = {}) {
    const defaultClientOptions = {};
    const fakeClient = {
        type: "openai",
        modelName: "gpt-4o",
        clientOptions: defaultClientOptions,
    };
    const resolveLlmClient = vitest_1.vi.fn().mockReturnValue(fakeClient);
    return new actHandler_js_1.ActHandler(fakeClient, "gpt-4o", defaultClientOptions, resolveLlmClient, undefined, false, options.selfHeal ?? false, options.onMetrics, undefined);
}
function buildExtractHandler(options = {}) {
    const defaultClientOptions = {};
    const fakeClient = {
        type: "openai",
        modelName: "gpt-4o",
        clientOptions: defaultClientOptions,
    };
    const resolveLlmClient = vitest_1.vi.fn().mockReturnValue(fakeClient);
    return new extractHandler_js_1.ExtractHandler(fakeClient, "gpt-4o", defaultClientOptions, resolveLlmClient, undefined, false, false, options.onMetrics);
}
function buildObserveHandler(options = {}) {
    const defaultClientOptions = {};
    const fakeClient = {
        type: "openai",
        modelName: "gpt-4o",
        clientOptions: defaultClientOptions,
    };
    const resolveLlmClient = vitest_1.vi.fn().mockReturnValue(fakeClient);
    return new observeHandler_js_1.ObserveHandler(fakeClient, "gpt-4o", defaultClientOptions, resolveLlmClient, undefined, false, false, options.onMetrics);
}
//# sourceMappingURL=timeout-handlers.test.js.map