"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestGenerationOrchestrator = void 0;
const async_retry_1 = __importDefault(require("async-retry"));
const mablApi_1 = require("../../mablApi");
const ApiError_1 = require("../../api/ApiError");
const toolExecutors_1 = require("./toolExecutors");
const boundingBoxUtils_1 = require("../sharedUtils/boundingBoxUtils");
const contentActions_1 = require("../background/capture/contentActions");
const generation_types_1 = require("../sharedUtils/generation-types");
const customFindUtils_1 = require("./customFindUtils");
const stepIndexUtils_1 = require("../sharedUtils/stepIndexUtils");
const messageConversion_1 = require("../sharedUtils/messageConversion");
const buildCliArtifacts_1 = require("../cli/buildCliArtifacts");
const DEFAULT_MAX_SCROLLABLE_ELEMENTS = 5;
const RETRY_COUNT = 3;
const RETRY_MIN_TIMEOUT_MS = 1_000;
const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);
const MAX_EMPTY_RESPONSE_RETRIES = 2;
class TestGenerationOrchestrator {
    constructor(stateProvider, eventHandler, logger, deps) {
        this.stateProvider = stateProvider;
        this.eventHandler = eventHandler;
        this.logger = logger;
        this.deps = deps;
    }
    updateDependencies(updates) {
        this.deps = { ...this.deps, ...updates };
    }
    updateProviders(stateProvider, eventHandler) {
        this.stateProvider = stateProvider;
        this.eventHandler = eventHandler;
    }
    async runGenerationLoop(session, shouldContinueCheck) {
        const { sessionId, trainingSessionId } = session;
        this.logger.info('Learn Next Step: Session ID:', sessionId);
        this.logger.info('Learn Next Step: Training Session ID:', trainingSessionId);
        const cleanup = () => {
            this.eventHandler.onCleanup();
        };
        const shouldContinue = () => {
            const status = this.stateProvider.getTestGenerationStatus();
            const activeSession = this.stateProvider.getActiveSession();
            return (status === generation_types_1.TestGenerationStatus.LearningSteps &&
                activeSession?.sessionId === sessionId);
        };
        if (shouldContinueCheck) {
            const checkResult = await shouldContinueCheck();
            if (!checkResult.continue) {
                this.logger.info(`Generation terminated early by shouldContinueCheck callback (save: ${checkResult.save})`);
                this.eventHandler.pauseStepLearning('External termination requested');
                return { cleanup, earlyTermination: { save: checkResult.save } };
            }
        }
        if (!shouldContinue()) {
            return { cleanup };
        }
        const workspaceId = this.deps.getWorkspaceId();
        if (!workspaceId) {
            this.logger.error('Unable to generate test because no workspace is set');
            this.eventHandler.failWithError('No workspace selected');
            return { cleanup, failureReason: 'No workspace selected' };
        }
        const apiClient = this.deps.getMablApiClient();
        const [screenshot, scrollableElements, tabs, pageSnapshot] = await this.getScreenshotScrollableElementsAndTabs();
        if (!shouldContinue()) {
            return { cleanup };
        }
        if (!screenshot) {
            this.logger.error('Unable to generate test due to failure to take screenshot');
            this.eventHandler.failWithError('Error taking screenshot');
            return { cleanup, failureReason: 'Error taking screenshot' };
        }
        const screenshotBase64 = screenshot.toString('base64');
        if (this.eventHandler.emitScreenshot) {
            this.eventHandler.emitScreenshot(screenshotBase64);
        }
        const artifactOptions = {
            state: this.stateProvider,
            screenshot: screenshotBase64,
            scrollableElements,
            tabs,
            pageSnapshot,
            isLocalTrainer: this.deps.isLocalTrainer,
            accessibilityChecksEnabled: this.deps.accessibilityChecksEnabled,
        };
        let artifacts;
        try {
            artifacts = (0, buildCliArtifacts_1.buildAllArtifacts)(artifactOptions);
            artifacts.forEach((artifact) => {
                if (artifact.artifact_type === 'inline') {
                    const inlineArtifact = artifact;
                    this.stateProvider.setArtifact(inlineArtifact.name, inlineArtifact);
                }
            });
        }
        catch (error) {
            this.logger.error('Error building artifacts:', error);
            this.eventHandler.failWithError('Error building context', {
                addToLog: true,
            });
            return { cleanup, failureReason: 'Error building context' };
        }
        if (!shouldContinue()) {
            return { cleanup };
        }
        const existingMessages = this.stateProvider.getAuthoringMessages();
        const authoringSessionId = this.stateProvider.getAuthoringSessionId();
        if (!authoringSessionId) {
            this.logger.error('No authoring session ID found');
            this.eventHandler.failWithError('No authoring session initialized');
            return { cleanup, failureReason: 'No authoring session initialized' };
        }
        const lastSentMessageCount = this.stateProvider.getLastSentMessageCount();
        const newMessages = existingMessages.slice(lastSentMessageCount);
        const request = {
            agent_session_id: authoringSessionId,
            new_messages: newMessages,
            new_artifacts: artifacts,
            desktop_app_version: this.deps.getAppVersion(),
            session_id: sessionId,
            training_session_id: trainingSessionId,
        };
        let authoringResponse;
        try {
            authoringResponse = await this.callAuthoringApiWithRetry(apiClient, workspaceId, request);
        }
        catch (e) {
            if (!shouldContinue()) {
                return { cleanup };
            }
            const error = e;
            this.logger.error('Error executing test authoring', error);
            this.eventHandler.failWithError(error.message, {
                code: error.statusCode,
                showInReplay: true,
                addToLog: true,
            });
            return { cleanup, failureReason: error.message };
        }
        if (!shouldContinue()) {
            return { cleanup };
        }
        this.stateProvider.addAuthoringMessages(authoringResponse.new_messages);
        const totalMessages = existingMessages.length + authoringResponse.new_messages.length;
        this.stateProvider.setLastSentMessageCount(totalMessages);
        authoringResponse.new_artifacts.forEach((artifact) => {
            if (artifact.artifact_type === 'inline') {
                const inlineArtifact = artifact;
                this.stateProvider.setArtifact(inlineArtifact.name, artifact);
            }
        });
        const testExecutionStateArtifact = artifacts.find((a) => a.name === 'test_execution_state');
        const formattedSteps = testExecutionStateArtifact?.artifact_data
            ?.value?.formatted_steps ?? [];
        const stepList = formattedSteps.map((entry) => {
            switch (entry.kind) {
                case 'task-start':
                    return `Start task: ${entry.text}`;
                case 'task-end':
                    return `End task: ${entry.text}`;
                case 'flow-start':
                    return `Start execute flow: ${entry.text}`;
                case 'flow-end':
                    return `End execute flow: ${entry.text}`;
                default:
                    return entry.text;
            }
        });
        this.eventHandler.addAgentLogEntry({
            type: generation_types_1.AgentLogEntryType.GenerateStepResponse,
            request: {
                screenshot: screenshot.toString('base64'),
                context: {
                    training_session_id: trainingSessionId,
                    active_url: this.stateProvider.getLastVisitUrl(),
                    step_list: stepList,
                    cursor_index: this.stateProvider.getNextStepIndex(),
                },
            },
            response: {
                tool_calls: (0, messageConversion_1.extractToolCallsFromMessages)(authoringResponse.new_messages, this.logger),
            },
        });
        if ((0, messageConversion_1.isAskingForHelp)(authoringResponse.new_messages)) {
            this.logger.info('Agent is requesting human interaction');
            const askForHelpToolCallPart = (0, messageConversion_1.getAskForHelpToolCallPart)(authoringResponse.new_messages);
            const agentMessageText = (0, messageConversion_1.getAskForHelpText)(authoringResponse.new_messages) ||
                'Can you give me a hint for how to proceed?';
            if (agentMessageText) {
                const agentMessage = {
                    role: 'model',
                    conversationId: session.conversationId ?? session.sessionId,
                    message: agentMessageText,
                    timestamp: Date.now(),
                    actionRequest: mablApi_1.AgentMessageActionRequestEnum.Action,
                };
                this.eventHandler.addAgentLogEntry({
                    type: generation_types_1.AgentLogEntryType.Message,
                    message: agentMessage,
                });
                const activeSession = this.stateProvider.getActiveSession();
                if (activeSession) {
                    try {
                        const userResponse = await this.eventHandler.requestHumanAction(agentMessage, activeSession.stopCondition);
                        this.eventHandler.addAgentLogEntry({
                            type: generation_types_1.AgentLogEntryType.Message,
                            message: userResponse,
                        });
                        const askForHelpToolOutputMessage = (0, messageConversion_1.createToolOutputMessage)([
                            (0, messageConversion_1.createToolOutputMessagePart)({
                                toolCallId: askForHelpToolCallPart?.id ?? '',
                                toolName: 'execute_ask_for_help',
                                result: {
                                    userResponse: userResponse.message,
                                },
                            }),
                        ]);
                        this.stateProvider.addAuthoringMessages([
                            askForHelpToolOutputMessage,
                        ]);
                        return this.runGenerationLoop(session, shouldContinueCheck);
                    }
                    catch (error) {
                        this.logger.error('Error handling human action request:', error);
                        this.eventHandler.pauseStepLearning('Agent requested help');
                        return {
                            cleanup,
                            earlyTermination: { save: true },
                            pendingAskForHelp: {
                                question: agentMessageText,
                                toolCallId: askForHelpToolCallPart?.id ?? '',
                            },
                        };
                    }
                }
            }
            const fallbackQuestion = agentMessageText || 'Can you give me a hint for how to proceed?';
            const fallbackToolCallId = askForHelpToolCallPart?.id ?? '';
            this.eventHandler.pauseStepLearning('Agent requested help');
            return {
                cleanup,
                earlyTermination: { save: true },
                pendingAskForHelp: {
                    question: fallbackQuestion,
                    toolCallId: fallbackToolCallId,
                },
            };
        }
        this.eventHandler.clearActiveConversation();
        const allToolCalls = (0, messageConversion_1.extractToolCallsFromMessages)(authoringResponse.new_messages, this.logger);
        const clientToolCalls = (0, messageConversion_1.filterClientSideToolCalls)(allToolCalls);
        const toolCallsToExecute = (0, messageConversion_1.filterToolCallsWithoutOutputs)(clientToolCalls, authoringResponse.new_messages);
        if (toolCallsToExecute.length === 0) {
            return this.runGenerationLoop(session, shouldContinueCheck);
        }
        this.eventHandler.beforeFinds();
        const executors = [];
        const skippedTools = [];
        for (const originalToolCall of toolCallsToExecute) {
            const executorResult = (0, toolExecutors_1.getExecutorByAuthoringName)(originalToolCall.name, originalToolCall.arguments || {}, this.stateProvider, this.eventHandler, this.logger);
            if (!executorResult) {
                this.logger.error(`Unable to find tool executor for authoring tool ${originalToolCall.name}`);
                skippedTools.push({
                    toolCallId: originalToolCall.id,
                    toolName: originalToolCall.name,
                    errorMessage: `No executor found for tool ${originalToolCall.name}`,
                });
                executors.push(undefined);
                continue;
            }
            const { executor: toolExecutor, toolCall } = executorResult;
            if (this.isStepMutatingTool(originalToolCall.name)) {
                const allSteps = this.stateProvider.getSteps();
                const cursorIndex = this.stateProvider.getNextStepIndex();
                if ((0, stepIndexUtils_1.isCursorInsideFlow)(allSteps, cursorIndex)) {
                    skippedTools.push({
                        toolCallId: originalToolCall.id,
                        toolName: originalToolCall.name,
                        errorMessage: 'Cannot modify steps inside a flow. Move the cursor outside the flow first.',
                    });
                    executors.push(undefined);
                    continue;
                }
            }
            const payload = {
                session,
                toolCall,
                screenshot,
                validateTest: this.deps.validateTest,
            };
            this.logger.info('[runGenerationLoop] Tool call information', JSON.stringify(toolCall, undefined, 2));
            if (toolExecutor.needsFind(toolCall)) {
                const customFindParams = (0, customFindUtils_1.extractCustomFindParams)(toolCall.toolParams);
                if (customFindParams) {
                    const findEvent = (0, customFindUtils_1.buildCustomFindEvent)(customFindParams, 'find_first', toolExecutor.getStepType(toolCall));
                    payload.find = findEvent;
                    findEvent.stepType = toolExecutor.getStepType(toolCall);
                    findEvent.synthetic = true;
                    payload.contextInfo = {
                        tabId: this.deps.trainingContext.getCurrentTab(),
                        frameId: this.deps.trainingContext.getCurrentFrame(),
                    };
                    executors.push({
                        payload: payload,
                        executor: toolExecutor,
                        toolCallId: originalToolCall.id || '',
                        toolName: originalToolCall.name,
                    });
                    continue;
                }
                const boundingBox = toolCall.toolParams?.elementBoundingBox
                    ? (0, boundingBoxUtils_1.convertToolLibraryBoundingBox)(toolCall.toolParams.elementBoundingBox)
                    : undefined;
                if (!boundingBox) {
                    this.logger.error(`Tool ${originalToolCall.name} requires element location but no bounding box was provided by the API`);
                    skippedTools.push({
                        toolCallId: originalToolCall.id,
                        toolName: originalToolCall.name,
                        errorMessage: 'Tool requires element location but no bounding box was provided',
                    });
                    executors.push(undefined);
                    continue;
                }
                const findEvent = await (0, contentActions_1.findByLocation)(this.deps.trainingContext, boundingBox, toolCall.toolParams?.elementTargetPoint, toolCall);
                try {
                    payload.find = findEvent;
                    if (findEvent) {
                        findEvent.stepType = toolExecutor.getStepType(toolCall);
                        findEvent.synthetic = true;
                        payload.contextInfo = findEvent.frameInfo ?? {
                            tabId: this.deps.trainingContext.getCurrentTab(),
                            frameId: this.deps.trainingContext.getCurrentFrame(),
                        };
                    }
                    payload.visualFindContext = {
                        screenshotBase64,
                        boundingBox,
                        getMablApiClient: this.deps.getMablApiClient,
                        workspaceId,
                    };
                    executors.push({
                        payload: payload,
                        executor: toolExecutor,
                        toolCallId: originalToolCall.id || '',
                        toolName: originalToolCall.name,
                    });
                }
                catch (e) {
                    this.logger.error('Error finding element for step generation', e);
                    skippedTools.push({
                        toolCallId: originalToolCall.id,
                        toolName: originalToolCall.name,
                        errorMessage: e instanceof Error ? e.message : 'Error finding element',
                    });
                    executors.push(undefined);
                }
            }
            else {
                executors.push({
                    payload: payload,
                    executor: toolExecutor,
                    toolCallId: originalToolCall.id || '',
                    toolName: originalToolCall.name,
                });
            }
        }
        const toolOutputMessageParts = [];
        for (const skippedTool of skippedTools) {
            const errorOutput = (0, messageConversion_1.createToolOutputMessagePart)({
                toolCallId: skippedTool.toolCallId,
                toolName: skippedTool.toolName,
                result: { errorMessage: skippedTool.errorMessage },
                isError: true,
            });
            toolOutputMessageParts.push(errorOutput);
        }
        for (const executorDescriptor of executors) {
            if (!shouldContinue()) {
                if (toolOutputMessageParts.length > 0) {
                    const toolOutputMessage = (0, messageConversion_1.createToolOutputMessage)(toolOutputMessageParts);
                    this.stateProvider.addAuthoringMessages([toolOutputMessage]);
                }
                return { cleanup };
            }
            if (!executorDescriptor?.payload) {
                continue;
            }
            const { needsTabInfo } = (0, toolExecutors_1.toolNeedsPostExecutionContext)(executorDescriptor.toolName, true);
            const tabsBefore = needsTabInfo
                ? await this.deps.trainingContext.getTrackedTabsInfo()
                : [];
            const toolResult = await executorDescriptor.executor.execute(executorDescriptor.payload, this.stateProvider, this.eventHandler, this.logger);
            if (!shouldContinue()) {
                if (toolOutputMessageParts.length > 0) {
                    const toolOutputMessage = (0, messageConversion_1.createToolOutputMessage)(toolOutputMessageParts);
                    this.stateProvider.addAuthoringMessages([toolOutputMessage]);
                }
                return { cleanup };
            }
            const outputContext = await this.captureToolOutputContext(toolResult.success, executorDescriptor.toolName, tabsBefore);
            const enrichedResult = this.enrichToolResultWithContext(toolResult, outputContext);
            const { imageContentItems, ...jsonResult } = enrichedResult;
            const toolOutputMessagePart = (0, messageConversion_1.createToolOutputMessagePart)({
                toolCallId: executorDescriptor.toolCallId,
                toolName: executorDescriptor.toolName,
                result: jsonResult,
                isError: !enrichedResult.success,
                screenshot: outputContext.screenshot,
                additionalImages: imageContentItems
                    ?.filter((img) => img.type === 'image-data' &&
                    typeof img.image_data === 'string' &&
                    typeof img.media_type === 'string')
                    .map((img) => ({
                    type: 'image-data',
                    image_data: img.image_data,
                    media_type: img.media_type,
                })),
            });
            toolOutputMessageParts.push(toolOutputMessagePart);
            const { endLearning, completeTaskEnded, failureReason } = this.handleToolResult({
                toolCall: executorDescriptor.payload.toolCall,
                toolResult: enrichedResult,
            });
            if (endLearning) {
                const toolOutputMessage = (0, messageConversion_1.createToolOutputMessage)(toolOutputMessageParts);
                this.stateProvider.addAuthoringMessages([toolOutputMessage]);
                const siblingToolOutputParts = toolOutputMessageParts.filter((part) => part.tool_call_id !== executorDescriptor.toolCallId);
                if (completeTaskEnded) {
                    return {
                        cleanup,
                        pendingCompleteTask: {
                            toolCallId: executorDescriptor.toolCallId,
                            siblingToolOutputParts,
                        },
                    };
                }
                return {
                    cleanup,
                    failureReason,
                    pendingFailedToolCall: {
                        toolCallId: executorDescriptor.toolCallId,
                        toolName: executorDescriptor.toolName,
                        siblingToolOutputParts,
                    },
                };
            }
        }
        this.eventHandler.afterFinds();
        const toolOutputMessage = (0, messageConversion_1.createToolOutputMessage)(toolOutputMessageParts);
        this.stateProvider.addAuthoringMessages([toolOutputMessage]);
        this.logger.info(`[runGenerationLoop] Completed tool execution cycle`);
        return this.runGenerationLoop(session, shouldContinueCheck);
    }
    callAuthoringApiWithRetry(apiClient, workspaceId, request) {
        let emptyResponseAttempts = 0;
        return (0, async_retry_1.default)(async (bail) => {
            let response;
            try {
                response = await apiClient.executeTestAuthoring(workspaceId, request);
            }
            catch (e) {
                const error = e;
                if (error.statusCode &&
                    RETRYABLE_STATUS_CODES.has(error.statusCode)) {
                    throw error;
                }
                bail(error);
                throw error;
            }
            const responseHasToolCalls = response.new_messages.some((m) => m.role === 'model' &&
                (m.parts ?? []).some((p) => p.type === 'tool_call'));
            if (!responseHasToolCalls &&
                !(0, messageConversion_1.isAskingForHelp)(response.new_messages) &&
                emptyResponseAttempts < MAX_EMPTY_RESPONSE_RETRIES) {
                emptyResponseAttempts++;
                this.logger.info(`Response contained no tool calls (possible early return), retrying API call (${emptyResponseAttempts}/${MAX_EMPTY_RESPONSE_RETRIES})`);
                throw new Error('Response contained no tool calls');
            }
            return response;
        }, {
            retries: RETRY_COUNT,
            minTimeout: RETRY_MIN_TIMEOUT_MS,
            onRetry: (error, attempt) => {
                if (error instanceof ApiError_1.ApiError) {
                    this.logger.info(`Retrying test authoring API call (attempt ${attempt}/${RETRY_COUNT}) after ${error.statusCode ?? 'unknown'} error: ${error.message}`);
                }
            },
        });
    }
    takeScreenshotWithRetry() {
        return (0, async_retry_1.default)(async (_bail, attemptNumber) => {
            if (attemptNumber > 1) {
                this.logger.info(`Retrying screenshot (attempt ${attemptNumber})...`);
            }
            const screenshot = await this.deps.takeScreenshot();
            if (!screenshot) {
                throw new Error('Failed to take screenshot');
            }
            return screenshot;
        }, { retries: RETRY_COUNT, minTimeout: RETRY_MIN_TIMEOUT_MS }).catch((error) => {
            this.logger.error('Screenshot failed after retries:', error);
            return undefined;
        });
    }
    async getScreenshotScrollableElementsAndTabs() {
        const [screenshot, scrollableElements, tabs, pageSnapshot] = await Promise.all([
            this.takeScreenshotWithRetry(),
            (0, contentActions_1.findScrollableElements)(this.deps.trainingContext, DEFAULT_MAX_SCROLLABLE_ELEMENTS),
            this.deps.trainingContext.getTrackedTabsInfo(),
            this.deps.getPageSnapshot(),
        ]);
        return [screenshot, scrollableElements, tabs, pageSnapshot];
    }
    handleToolResult({ toolCall, toolResult, }) {
        if (!toolCall.toolName) {
            throw new Error('Tool call name is required');
        }
        const toolType = toolCall.toolName;
        const isCompleteTaskTool = toolType === mablApi_1.ExecuteToolName.CompleteTask;
        const isEndTestTool = toolType === mablApi_1.ExecuteToolName.EndTest;
        if (isEndTestTool && toolResult.success) {
            this.eventHandler.stopLearningSuccess('completedTest');
            return {
                endLearning: true,
                increaseTaskIndex: false,
                completeTaskEnded: true,
            };
        }
        let increaseTaskIndex;
        if (isCompleteTaskTool && toolResult.success) {
            increaseTaskIndex = true;
            if (toolCall.toolParams?.endTest || toolResult.shouldEndTest) {
                this.eventHandler.stopLearningSuccess('completedTest');
                return { endLearning: true, increaseTaskIndex, completeTaskEnded: true };
            }
            this.eventHandler.completeTask();
            this.eventHandler.updateAgentWillPlan(true);
        }
        else {
            increaseTaskIndex = false;
            this.eventHandler.updateAgentWillPlan(false);
        }
        const activeSession = this.stateProvider.getActiveSession();
        const isEditMode = this.stateProvider.getTrainingAction() === 'edit';
        const logEntry = {
            type: generation_types_1.AgentLogEntryType.ToolExecuted,
            toolCall,
            toolResult,
            generatedSteps: toolResult.createdStepsInfo?.map((stepInfo) => stepInfo.humanizedStep),
        };
        try {
            this.eventHandler.addAgentLogEntry(logEntry);
        }
        catch (error) {
            this.logger.error('Failed to add agent log entry', error instanceof Error ? error : undefined);
        }
        if (this.shouldStopGeneration(toolResult, toolType, activeSession.stopCondition, isEditMode)) {
            this.eventHandler.failWithError('Step generation failed', {
                addToLog: true,
            });
            return {
                endLearning: true,
                increaseTaskIndex,
                completeTaskEnded: false,
                failureReason: 'Step generation failed',
            };
        }
        return {
            endLearning: false,
            increaseTaskIndex,
            completeTaskEnded: false,
        };
    }
    isStepMutatingTool(toolName) {
        const nonMutatingTools = new Set([
            mablApi_1.ExecuteToolName.MoveCursor,
            mablApi_1.ExecuteToolName.PlaySteps,
            mablApi_1.ExecuteToolName.EndTest,
            mablApi_1.ExecuteToolName.AskForHelp,
            mablApi_1.ExecuteToolName.ValidateTest,
        ]);
        return !nonMutatingTools.has(toolName);
    }
    shouldStopGeneration(toolResult, toolType, stopCondition, isEditMode) {
        if (stopCondition === 'generated-task' &&
            toolType === mablApi_1.ExecuteToolName.CompleteTask &&
            isEditMode) {
            return true;
        }
        if (toolResult.success) {
            return false;
        }
        return false;
    }
    async captureToolOutputContext(success, toolName, tabsBefore = []) {
        const { needsScreenshot, needsUrl, needsTabInfo } = (0, toolExecutors_1.toolNeedsPostExecutionContext)(toolName, success);
        const context = {};
        if (needsScreenshot) {
            try {
                const newScreenshot = await this.deps.takeScreenshot();
                if (newScreenshot) {
                    context.screenshot = newScreenshot.toString('base64');
                }
            }
            catch (error) {
                this.logger.warn('Failed to capture post-execution screenshot:', error);
            }
        }
        if (needsUrl) {
            context.currentUrl = await this.getPageUrl();
        }
        if (needsTabInfo && tabsBefore.length > 0) {
            try {
                const tabsAfter = await this.deps.trainingContext.getTrackedTabsInfo();
                const tabIdsBefore = new Set(tabsBefore.map((tab) => tab.tab_id));
                const createdTabs = tabsAfter
                    .filter((tab) => !tabIdsBefore.has(tab.tab_id))
                    .map((tab) => ({
                    tab_id: tab.tab_id,
                    url: tab.url,
                    title: tab.title,
                }));
                if (createdTabs.length > 0) {
                    context.createdTabs = createdTabs;
                }
            }
            catch (error) {
                this.logger.warn('Failed to capture created tabs info:', error);
            }
        }
        return context;
    }
    enrichToolResultWithContext(toolResult, outputContext) {
        const enrichedResult = {
            ...toolResult,
        };
        if (outputContext.currentUrl) {
            enrichedResult.current_url = outputContext.currentUrl;
        }
        if (outputContext.createdTabs && outputContext.createdTabs.length > 0) {
            enrichedResult.created_tabs = outputContext.createdTabs;
        }
        return enrichedResult;
    }
    async getPageUrl() {
        try {
            const page = await this.deps.trainingContext.getActivePage();
            const liveUrl = page?.url();
            if (liveUrl) {
                return liveUrl;
            }
        }
        catch (error) {
            this.logger.warn('Failed to read live URL from active page; falling back to cached last visit URL:', error);
        }
        return this.stateProvider.getLastVisitUrl();
    }
}
exports.TestGenerationOrchestrator = TestGenerationOrchestrator;
