/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { ConsoleFormatter } from './formatters/ConsoleFormatter.js';
import { HeapSnapshotFormatter } from './formatters/HeapSnapshotFormatter.js';
import { isEdgeLike, isNodeLike } from './formatters/HeapSnapshotFormatter.js';
import { IssueFormatter } from './formatters/IssueFormatter.js';
import { NetworkFormatter } from './formatters/NetworkFormatter.js';
import { SnapshotFormatter } from './formatters/SnapshotFormatter.js';
import { UncaughtError } from './PageCollector.js';
import { TextSnapshot } from './TextSnapshot.js';
import { DevTools, toonEncode } from './third_party/index.js';
import { handleDialog } from './tools/pages.js';
import { getInsightOutput, getTraceSummary } from './trace-processing/parse.js';
import { paginate } from './utils/pagination.js';
export function replaceHtmlElementsWithUids(schema) {
    if (typeof schema === 'boolean') {
        return;
    }
    let isHtmlElement = false;
    for (const [key, value] of Object.entries(schema)) {
        if (key === 'x-mcp-type' && value === 'HTMLElement') {
            isHtmlElement = true;
            break;
        }
    }
    if (isHtmlElement) {
        schema.properties = { uid: { type: 'string' } };
        schema.required = ['uid'];
    }
    if (schema.properties) {
        for (const key of Object.keys(schema.properties)) {
            replaceHtmlElementsWithUids(schema.properties[key]);
        }
    }
    if (schema.items) {
        if (Array.isArray(schema.items)) {
            for (const item of schema.items) {
                replaceHtmlElementsWithUids(item);
            }
        }
        else {
            replaceHtmlElementsWithUids(schema.items);
        }
    }
    if (schema.anyOf) {
        for (const s of schema.anyOf) {
            replaceHtmlElementsWithUids(s);
        }
    }
    if (schema.allOf) {
        for (const s of schema.allOf) {
            replaceHtmlElementsWithUids(s);
        }
    }
    if (schema.oneOf) {
        for (const s of schema.oneOf) {
            replaceHtmlElementsWithUids(s);
        }
    }
}
async function getToolGroups(page) {
    // Check if there is a `devtoolstooldiscovery` event listener
    const windowHandle = await page.pptrPage.evaluateHandle(() => window);
    // @ts-expect-error internal API
    const client = page.pptrPage._client();
    const { listeners } = await client.send('DOMDebugger.getEventListeners', {
        objectId: windowHandle.remoteObject().objectId,
    });
    if (listeners.find(l => l.type === 'devtoolstooldiscovery') === undefined) {
        return [];
    }
    const toolGroups = await page.pptrPage.evaluate(() => {
        if (window.__dtmcp) {
            window.__dtmcp.toolGroups = [];
        }
        return new Promise(resolve => {
            const event = new CustomEvent('devtoolstooldiscovery');
            const groups = [];
            // @ts-expect-error Adding custom property
            event.respondWith = toolGroup => {
                if (!window.__dtmcp) {
                    window.__dtmcp = {};
                }
                if (!window.__dtmcp.toolGroups) {
                    window.__dtmcp.toolGroups = [];
                }
                if (typeof toolGroup.name !== 'string' ||
                    (toolGroup.description &&
                        typeof toolGroup.description !== 'string') ||
                    !Array.isArray(toolGroup.tools)) {
                    console.error('Invalid toolGroup:', toolGroup);
                    return;
                }
                for (const tool of toolGroup.tools) {
                    if (typeof tool.name !== 'string' ||
                        typeof tool.description !== 'string' ||
                        typeof tool.inputSchema !== 'object' ||
                        typeof tool.execute !== 'function') {
                        console.error('Invalid tool:', tool);
                        return;
                    }
                }
                window.__dtmcp.toolGroups.push(toolGroup);
                // When receiving a toolGroup for the first time, expose a simple execution helper
                if (!window.__dtmcp.executeTool) {
                    window.__dtmcp.executeTool = async (toolName, args) => {
                        if (!window.__dtmcp?.toolGroups ||
                            window.__dtmcp.toolGroups.length === 0) {
                            throw new Error('No tools found on the page');
                        }
                        for (const group of window.__dtmcp.toolGroups) {
                            const tool = group.tools?.find(t => t.name === toolName);
                            if (tool) {
                                return await tool.execute(args);
                            }
                        }
                        throw new Error(`Tool ${toolName} not found`);
                    };
                }
                groups.push(toolGroup);
            };
            window.dispatchEvent(event);
            // If at least one toolGroup was added synchronously, resolve with the array.
            // Otherwise, use setTimeout to allow for any microtask/asynchronous respondWith calls, or resolve with an empty array.
            if (groups.length > 0) {
                resolve(groups);
            }
            else {
                setTimeout(() => {
                    if (groups.length > 0) {
                        resolve(groups);
                    }
                    else {
                        resolve([]);
                    }
                }, 0);
            }
        });
    });
    for (const group of toolGroups) {
        for (const tool of group.tools ?? []) {
            replaceHtmlElementsWithUids(tool.inputSchema);
        }
    }
    return toolGroups;
}
export class McpResponse {
    #includePages = false;
    #includeExtensionServiceWorkers = false;
    #includeExtensionPages = false;
    #snapshotParams;
    #attachedNetworkRequestId;
    #attachedNetworkRequestOptions;
    #attachedConsoleMessageId;
    #attachedTraceSummary;
    #attachedTraceInsight;
    #attachedLighthouseResult;
    #textResponseLines = [];
    #images = [];
    #heapSnapshotOptions;
    #networkRequestsOptions;
    #consoleDataOptions;
    #listExtensions;
    #listThirdPartyDeveloperTools;
    #listWebMcpTools;
    #devToolsData;
    #tabId;
    #args;
    #page;
    #redactNetworkHeaders = true;
    #error;
    #attachedWaitForResult;
    get #deviceScope() {
        return this.#page?.viewport?.isMobile ? 'PHONE' : 'DESKTOP';
    }
    constructor(args) {
        this.#args = args;
    }
    setPage(page) {
        this.#page = page;
    }
    setRedactNetworkHeaders(value) {
        this.#redactNetworkHeaders = value;
    }
    attachDevToolsData(data) {
        this.#devToolsData = data;
    }
    setTabId(tabId) {
        this.#tabId = tabId;
    }
    setIncludePages(value) {
        this.#includePages = value;
        if (this.#args.categoryExtensions) {
            this.#includeExtensionServiceWorkers = value;
            this.#includeExtensionPages = value;
        }
    }
    includeSnapshot(params) {
        this.#snapshotParams = params ?? {
            verbose: false,
        };
    }
    setListExtensions() {
        this.#listExtensions = true;
    }
    setListThirdPartyDeveloperTools() {
        this.#listThirdPartyDeveloperTools = true;
    }
    setListWebMcpTools() {
        this.#listWebMcpTools = true;
    }
    setIncludeNetworkRequests(value, options) {
        if (!value) {
            this.#networkRequestsOptions = undefined;
            return;
        }
        this.#networkRequestsOptions = {
            include: value,
            pagination: options?.pageSize || options?.pageIdx
                ? {
                    pageSize: options.pageSize,
                    pageIdx: options.pageIdx,
                }
                : undefined,
            resourceTypes: options?.resourceTypes,
            includePreservedRequests: options?.includePreservedRequests,
            networkRequestIdInDevToolsUI: options?.networkRequestIdInDevToolsUI,
        };
    }
    setIncludeConsoleData(value, options) {
        if (!value) {
            this.#consoleDataOptions = undefined;
            return;
        }
        this.#consoleDataOptions = {
            include: value,
            pagination: options?.pageSize || options?.pageIdx
                ? {
                    pageSize: options.pageSize,
                    pageIdx: options.pageIdx,
                }
                : undefined,
            types: options?.types,
            includePreservedMessages: options?.includePreservedMessages,
            serviceWorkerId: options?.serviceWorkerId,
        };
    }
    setError(error) {
        this.#error = error;
    }
    attachNetworkRequest(reqId, options) {
        this.#attachedNetworkRequestId = reqId;
        this.#attachedNetworkRequestOptions = options;
    }
    attachConsoleMessage(msgid) {
        this.#attachedConsoleMessageId = msgid;
    }
    attachTraceSummary(result) {
        this.#attachedTraceSummary = result;
    }
    attachTraceInsight(trace, insightSetId, insightName) {
        this.#attachedTraceInsight = {
            trace,
            insightSetId,
            insightName,
        };
    }
    attachLighthouseResult(result) {
        this.#attachedLighthouseResult = result;
    }
    get includePages() {
        return this.#includePages;
    }
    get attachedTraceSummary() {
        return this.#attachedTraceSummary;
    }
    get attachedTracedInsight() {
        return this.#attachedTraceInsight;
    }
    get attachedLighthouseResult() {
        return this.#attachedLighthouseResult;
    }
    get includeNetworkRequests() {
        return this.#networkRequestsOptions?.include ?? false;
    }
    get includeConsoleData() {
        return this.#consoleDataOptions?.include ?? false;
    }
    get attachedNetworkRequestId() {
        return this.#attachedNetworkRequestId;
    }
    get networkRequestsPageIdx() {
        return this.#networkRequestsOptions?.pagination?.pageIdx;
    }
    get consoleMessagesPageIdx() {
        return this.#consoleDataOptions?.pagination?.pageIdx;
    }
    get consoleMessagesTypes() {
        return this.#consoleDataOptions?.types;
    }
    get error() {
        return this.#error;
    }
    appendResponseLine(value) {
        this.#textResponseLines.push(value);
    }
    attachWaitForResult(result) {
        this.#attachedWaitForResult = result;
    }
    setHeapSnapshotAggregates(aggregates, options) {
        this.#heapSnapshotOptions = {
            ...this.#heapSnapshotOptions,
            include: true,
            aggregates,
            pagination: options,
        };
    }
    setHeapSnapshotStats(stats, staticData) {
        this.#heapSnapshotOptions = {
            ...this.#heapSnapshotOptions,
            include: true,
            stats,
            staticData,
        };
    }
    setHeapSnapshotNodes(nodes, options) {
        this.#heapSnapshotOptions = {
            ...this.#heapSnapshotOptions,
            include: true,
            nodes,
            pagination: options,
        };
    }
    setHeapSnapshotRetainingPaths(retainingPaths) {
        this.#heapSnapshotOptions = {
            ...this.#heapSnapshotOptions,
            include: true,
            retainingPaths,
        };
    }
    setHeapSnapshotDominators(dominators) {
        this.#heapSnapshotOptions = {
            ...this.#heapSnapshotOptions,
            include: true,
            dominators,
        };
    }
    attachImage(value) {
        this.#images.push(value);
    }
    get responseLines() {
        return this.#textResponseLines;
    }
    get images() {
        return this.#images;
    }
    get snapshotParams() {
        return this.#snapshotParams;
    }
    get listWebMcpTools() {
        return this.#listWebMcpTools;
    }
    async handle(toolName, context, useToon = false) {
        if (this.#includePages) {
            await context.createPagesSnapshot();
        }
        if (this.#includeExtensionServiceWorkers) {
            await context.createExtensionServiceWorkersSnapshot();
        }
        let snapshot;
        if (this.#snapshotParams) {
            if (!this.#page) {
                throw new Error('Response must have a page');
            }
            this.#page.textSnapshot = await TextSnapshot.create(this.#page, {
                verbose: this.#snapshotParams.verbose,
                devtoolsData: this.#devToolsData,
            });
            const textSnapshot = this.#page.textSnapshot;
            if (textSnapshot) {
                const formatter = new SnapshotFormatter(textSnapshot);
                if (this.#snapshotParams.filePath) {
                    const result = await context.saveFile(new TextEncoder().encode(formatter.toString()), this.#snapshotParams.filePath, '.txt');
                    snapshot = result.filename;
                }
                else {
                    snapshot = formatter;
                }
            }
        }
        let detailedNetworkRequest;
        if (this.#attachedNetworkRequestId) {
            if (!this.#page) {
                throw new Error(`Response must have an McpPage`);
            }
            const request = context.getNetworkRequestById(this.#page, this.#attachedNetworkRequestId);
            const formatter = await NetworkFormatter.from(request, {
                requestId: this.#attachedNetworkRequestId,
                requestIdResolver: req => context.getNetworkRequestStableId(req),
                fetchData: true,
                requestFilePath: this.#attachedNetworkRequestOptions?.requestFilePath,
                responseFilePath: this.#attachedNetworkRequestOptions?.responseFilePath,
                saveFile: (data, filename, extension) => context.saveFile(data, filename, extension),
                redactNetworkHeaders: this.#redactNetworkHeaders,
            });
            detailedNetworkRequest = formatter;
        }
        let detailedConsoleMessage;
        if (this.#attachedConsoleMessageId) {
            if (!this.#page) {
                throw new Error(`Response must have an McpPage`);
            }
            const message = context.getConsoleMessageById(this.#page, this.#attachedConsoleMessageId);
            const consoleMessageStableId = this.#attachedConsoleMessageId;
            if ('args' in message || message instanceof UncaughtError) {
                const consoleMessage = message;
                const devTools = context.getDevToolsUniverse(this.#page);
                detailedConsoleMessage = await ConsoleFormatter.from(consoleMessage, {
                    id: consoleMessageStableId,
                    fetchDetailedData: true,
                    devTools: devTools ?? undefined,
                });
            }
            else if (message instanceof DevTools.AggregatedIssue) {
                const formatter = new IssueFormatter(message, {
                    id: consoleMessageStableId,
                    requestIdResolver: context.resolveCdpRequestId.bind(context, this.#page),
                    elementIdResolver: this.#page.resolveCdpElementId.bind(this.#page),
                });
                if (!formatter.isValid()) {
                    throw new Error("Can't provide details for the msgid " + consoleMessageStableId);
                }
                detailedConsoleMessage = formatter;
            }
        }
        let extensions;
        if (this.#listExtensions) {
            extensions = await context.listExtensions();
        }
        let thirdPartyDeveloperTools = [];
        if (this.#args.categoryExperimentalThirdParty &&
            this.#listThirdPartyDeveloperTools &&
            this.#page) {
            thirdPartyDeveloperTools = await getToolGroups(this.#page);
            if (thirdPartyDeveloperTools) {
                this.#page.thirdPartyDeveloperTools = thirdPartyDeveloperTools;
            }
        }
        let webmcpTools;
        if (this.#args.categoryExperimentalWebmcp &&
            this.#listWebMcpTools &&
            this.#page) {
            webmcpTools = this.#page.getWebMcpTools();
        }
        let consoleMessages;
        if (this.#consoleDataOptions?.include) {
            let messages;
            let page;
            if (this.#consoleDataOptions.serviceWorkerId) {
                messages = context.getServiceWorkerConsoleData(this.#consoleDataOptions.serviceWorkerId);
            }
            else {
                page = this.#page;
                if (!page) {
                    throw new Error(`Response must have an McpPage`);
                }
                messages = context.getConsoleData(page, this.#consoleDataOptions.includePreservedMessages);
            }
            if (this.#consoleDataOptions.types?.length) {
                const normalizedTypes = new Set(this.#consoleDataOptions.types);
                messages = messages.filter(message => {
                    if ('type' in message) {
                        return normalizedTypes.has(message.type());
                    }
                    if (message instanceof DevTools.AggregatedIssue) {
                        return normalizedTypes.has('issue');
                    }
                    return normalizedTypes.has('error');
                });
            }
            consoleMessages = (await Promise.all(messages.map(async (item) => {
                const consoleMessageStableId = context.getConsoleMessageStableId(item);
                if ('args' in item || item instanceof UncaughtError) {
                    const consoleMessage = item;
                    const devTools = page
                        ? context.getDevToolsUniverse(page)
                        : null;
                    return await ConsoleFormatter.from(consoleMessage, {
                        id: consoleMessageStableId,
                        fetchDetailedData: false,
                        devTools: devTools ?? undefined,
                    });
                }
                if (item instanceof DevTools.AggregatedIssue) {
                    const formatter = new IssueFormatter(item, {
                        id: consoleMessageStableId,
                    });
                    if (!formatter.isValid()) {
                        return null;
                    }
                    return formatter;
                }
                return null;
            }))).filter(item => item !== null);
        }
        let networkRequests;
        if (this.#networkRequestsOptions?.include) {
            if (!this.#page) {
                throw new Error(`Response must have an McpPage`);
            }
            let requests = context.getNetworkRequests(this.#page, this.#networkRequestsOptions?.includePreservedRequests);
            // Apply resource type filtering if specified
            if (this.#networkRequestsOptions.resourceTypes?.length) {
                const normalizedTypes = new Set(this.#networkRequestsOptions.resourceTypes);
                requests = requests.filter(request => {
                    const type = request.resourceType();
                    return normalizedTypes.has(type);
                });
            }
            if (requests.length) {
                networkRequests = await Promise.all(requests.map(request => NetworkFormatter.from(request, {
                    requestId: context.getNetworkRequestStableId(request),
                    selectedInDevToolsUI: context.getNetworkRequestStableId(request) ===
                        this.#networkRequestsOptions?.networkRequestIdInDevToolsUI,
                    fetchData: false,
                    saveFile: (data, filename, extension) => context.saveFile(data, filename, extension),
                    redactNetworkHeaders: this.#redactNetworkHeaders,
                })));
            }
        }
        return this.format(toolName, context, {
            detailedConsoleMessage,
            consoleMessages,
            snapshot,
            detailedNetworkRequest,
            networkRequests,
            traceInsight: this.#attachedTraceInsight,
            traceSummary: this.#attachedTraceSummary,
            extensions,
            lighthouseResult: this.#attachedLighthouseResult,
            thirdPartyDeveloperTools,
            webmcpTools,
            errorMessage: this.#error?.message,
        }, useToon);
    }
    async format(toolName, context, data, useToon) {
        const structuredContent = {};
        const response = [];
        if (this.#textResponseLines.length) {
            structuredContent.message = this.#textResponseLines.join('\n');
            response.push(...this.#textResponseLines);
        }
        if (this.#attachedWaitForResult) {
            if (this.#attachedWaitForResult.navigatedToUrl) {
                response.push(`Page navigated to ${this.#attachedWaitForResult.navigatedToUrl}.`);
                structuredContent.navigatedToUrl =
                    this.#attachedWaitForResult.navigatedToUrl;
            }
        }
        const networkConditions = this.#page?.networkConditions;
        if (networkConditions) {
            const timeout = this.#page.pptrPage.getDefaultNavigationTimeout();
            response.push(`Emulating network conditions: ${networkConditions}`);
            response.push(`Default navigation timeout set to ${timeout} ms`);
            structuredContent.networkConditions = networkConditions;
            structuredContent.navigationTimeout = timeout;
        }
        const geolocation = this.#page?.geolocation;
        if (geolocation) {
            response.push(`Emulating geolocation: latitude=${geolocation.latitude}, longitude=${geolocation.longitude}`);
            structuredContent.geolocation = geolocation;
        }
        const viewport = this.#page?.viewport;
        if (viewport) {
            response.push(`Emulating viewport: ${JSON.stringify(viewport)}`);
            structuredContent.viewport = viewport;
        }
        const userAgent = this.#page?.userAgent;
        if (userAgent) {
            response.push(`Emulating user agent: ${userAgent}`);
            structuredContent.userAgent = userAgent;
        }
        const cpuThrottlingRate = this.#page?.cpuThrottlingRate ?? 1;
        if (cpuThrottlingRate > 1) {
            response.push(`Emulating CPU throttling: ${cpuThrottlingRate}x slowdown`);
            structuredContent.cpuThrottlingRate = cpuThrottlingRate;
        }
        const colorScheme = this.#page?.colorScheme;
        if (colorScheme) {
            response.push(`Emulating color scheme: ${colorScheme}`);
            structuredContent.colorScheme = colorScheme;
        }
        const dialog = this.#page?.getDialog();
        if (dialog) {
            const defaultValueIfNeeded = dialog.type() === 'prompt'
                ? ` (default value: "${dialog.defaultValue()}")`
                : '';
            response.push(`# Open dialog
${dialog.type()}: ${dialog.message()}${defaultValueIfNeeded}.
Call ${handleDialog.name} to handle it before continuing.`);
            structuredContent.dialog = {
                type: dialog.type(),
                message: dialog.message(),
                defaultValue: dialog.defaultValue(),
            };
        }
        if (this.#includePages) {
            const allPages = context.getPages();
            const { regularPages, extensionPages } = allPages.reduce((acc, page) => {
                if (page.url().startsWith('chrome-extension://')) {
                    acc.extensionPages.push(page);
                }
                else {
                    acc.regularPages.push(page);
                }
                return acc;
            }, { regularPages: [], extensionPages: [] });
            if (regularPages.length) {
                const parts = [`## Pages`];
                const structuredPages = [];
                for (const page of regularPages) {
                    const isolatedContextName = context.getIsolatedContextName(page);
                    const contextLabel = isolatedContextName
                        ? ` isolatedContext=${isolatedContextName}`
                        : '';
                    const title = await fetchPageTitle(page);
                    const pageLabel = title
                        ? `${truncateTitle(title)} (${page.url()})`
                        : page.url();
                    parts.push(`${context.getPageId(page)}: ${pageLabel}${context.isPageSelected(page) ? ' [selected]' : ''}${contextLabel}`);
                    structuredPages.push(createStructuredPage(page, context, title));
                }
                response.push(...parts);
                structuredContent.pages = structuredPages;
            }
            if (this.#includeExtensionPages) {
                if (extensionPages.length) {
                    response.push(`## Extension Pages`);
                    const structuredExtensionPages = [];
                    for (const page of extensionPages) {
                        const isolatedContextName = context.getIsolatedContextName(page);
                        const contextLabel = isolatedContextName
                            ? ` isolatedContext=${isolatedContextName}`
                            : '';
                        const title = await fetchPageTitle(page);
                        const pageLabel = title
                            ? `${truncateTitle(title)} (${page.url()})`
                            : page.url();
                        response.push(`${context.getPageId(page)}: ${pageLabel}${context.isPageSelected(page) ? ' [selected]' : ''}${contextLabel}`);
                        structuredExtensionPages.push(createStructuredPage(page, context, title));
                    }
                    structuredContent.extensionPages = structuredExtensionPages;
                }
            }
        }
        if (this.#includeExtensionServiceWorkers) {
            if (context.getExtensionServiceWorkers().length) {
                response.push(`## Extension Service Workers`);
            }
            for (const extensionServiceWorker of context.getExtensionServiceWorkers()) {
                response.push(`${extensionServiceWorker.id}: ${extensionServiceWorker.url}`);
            }
            structuredContent.extensionServiceWorkers = context
                .getExtensionServiceWorkers()
                .map(extensionServiceWorker => {
                return {
                    id: extensionServiceWorker.id,
                    url: extensionServiceWorker.url,
                };
            });
        }
        if (this.#tabId) {
            structuredContent.tabId = this.#tabId;
        }
        if (data.traceSummary) {
            const summary = getTraceSummary(data.traceSummary, this.#deviceScope);
            response.push(summary);
            structuredContent.traceSummary = summary;
            structuredContent.traceInsights = [];
            for (const insightSet of data.traceSummary.insights?.values() ?? []) {
                for (const [insightName, model] of Object.entries(insightSet.model)) {
                    structuredContent.traceInsights.push({
                        insightName,
                        insightKey: model.insightKey,
                    });
                }
            }
        }
        if (data.traceInsight) {
            const insightOutput = getInsightOutput(data.traceInsight.trace, data.traceInsight.insightSetId, data.traceInsight.insightName, this.#deviceScope);
            if ('error' in insightOutput) {
                response.push(insightOutput.error);
            }
            else {
                response.push(insightOutput.output);
            }
        }
        if (data.lighthouseResult) {
            structuredContent.lighthouseResult = data.lighthouseResult;
            const { summary, reports } = data.lighthouseResult;
            response.push('## Lighthouse Audit Results');
            response.push(`Mode: ${summary.mode}`);
            response.push(`Device: ${summary.device}`);
            response.push(`URL: ${summary.url}`);
            response.push('### Category Scores');
            for (const score of summary.scores) {
                response.push(`- ${score.title}: ${(score.score ?? 0) * 100} (${score.id})`);
            }
            response.push('### Audit Summary');
            response.push(`Passed: ${summary.audits.passed}`);
            response.push(`Failed: ${summary.audits.failed}`);
            response.push(`Total Timing: ${summary.timing.total}ms`);
            response.push('### Reports');
            for (const report of reports) {
                response.push(`- ${report}`);
            }
        }
        if (data.snapshot) {
            if (typeof data.snapshot === 'string') {
                response.push(`Saved snapshot to ${data.snapshot}.`);
                structuredContent.snapshotFilePath = data.snapshot;
            }
            else {
                structuredContent.snapshot = data.snapshot.toJSON();
                response.push('## Latest page snapshot');
                response.push(useToon
                    ? toonEncode(structuredContent.snapshot)
                    : data.snapshot.toString());
            }
        }
        if (this.#heapSnapshotOptions?.include) {
            response.push('## Heap Snapshot Data');
            const stats = this.#heapSnapshotOptions.stats;
            const staticData = this.#heapSnapshotOptions.staticData;
            if (stats) {
                response.push(`Statistics: ${JSON.stringify(stats, null, 2)}`);
                structuredContent.heapSnapshot = structuredContent.heapSnapshot || {};
                structuredContent.heapSnapshot.stats = stats;
            }
            if (staticData) {
                response.push(`Static Data: ${JSON.stringify(staticData, null, 2)}`);
                structuredContent.heapSnapshot = structuredContent.heapSnapshot || {};
                structuredContent.heapSnapshot.staticData = staticData;
            }
            const aggregates = this.#heapSnapshotOptions.aggregates;
            if (aggregates) {
                const sortedEntries = HeapSnapshotFormatter.sort(aggregates);
                const paginationData = this.#dataWithPagination(sortedEntries, this.#heapSnapshotOptions.pagination);
                structuredContent.pagination = paginationData.pagination;
                response.push(...paginationData.info);
                const paginatedRecord = Object.fromEntries(paginationData.items);
                const formatter = new HeapSnapshotFormatter(paginatedRecord);
                structuredContent.heapSnapshotData = formatter.toJSON();
                response.push(useToon
                    ? toonEncode(structuredContent.heapSnapshotData)
                    : formatter.toString());
            }
            const nodes = this.#heapSnapshotOptions.nodes;
            if (nodes) {
                let items = Array.from(nodes.items);
                const firstItem = nodes.items[0];
                if (firstItem) {
                    if (isNodeLike(firstItem)) {
                        items = items
                            .filter(isNodeLike)
                            .sort((a, b) => b.retainedSize - a.retainedSize);
                    }
                    else if (isEdgeLike(firstItem)) {
                        items = items.filter(isEdgeLike);
                    }
                }
                const paginationData = this.#dataWithPagination(items, this.#heapSnapshotOptions.pagination);
                response.push(HeapSnapshotFormatter.formatNodes(paginationData.items));
                structuredContent.pagination = paginationData.pagination;
                response.push(...paginationData.info);
                structuredContent.heapSnapshotNodes = paginationData.items;
            }
            const retainingPaths = this.#heapSnapshotOptions.retainingPaths;
            if (retainingPaths) {
                response.push('### Retaining Paths');
                const { paths, limitsReached } = retainingPaths;
                if (paths.length === 0) {
                    response.push('No retaining paths found.');
                }
                else {
                    response.push(HeapSnapshotFormatter.formatRetainingPaths(paths));
                }
                const reached = Object.entries(limitsReached)
                    .filter(([, hit]) => hit)
                    .map(([limit]) => limit);
                if (reached.length > 0) {
                    response.push(`Note: results are truncated, the following limits were reached: ${reached.join(', ')}.`);
                }
                structuredContent.heapSnapshotRetainingPaths =
                    retainingPaths;
            }
            const dominators = this.#heapSnapshotOptions.dominators;
            if (dominators) {
                response.push('### Dominator Chain');
                if (dominators.length === 0) {
                    response.push('No dominators found.');
                }
                else {
                    response.push(HeapSnapshotFormatter.formatDominators(dominators));
                }
                structuredContent.heapSnapshotDominators = dominators;
            }
        }
        if (data.detailedNetworkRequest) {
            response.push(data.detailedNetworkRequest.toStringDetailed());
            structuredContent.networkRequest =
                data.detailedNetworkRequest.toJSONDetailed();
        }
        if (data.detailedConsoleMessage) {
            response.push(data.detailedConsoleMessage.toStringDetailed());
            structuredContent.consoleMessage =
                data.detailedConsoleMessage.toJSONDetailed();
        }
        if (data.extensions) {
            const extensionArray = Array.from(data.extensions.values());
            structuredContent.extensions = extensionArray;
            response.push('## Extensions');
            if (extensionArray.length === 0) {
                response.push('No extensions installed.');
            }
            else {
                const extensionsMessage = extensionArray
                    .map(extension => {
                    return `id=${extension.id} "${extension.name}" v${extension.version} ${extension.enabled ? 'Enabled' : 'Disabled'}`;
                })
                    .join('\n');
                response.push(extensionsMessage);
            }
        }
        if (data.thirdPartyDeveloperTools.length) {
            structuredContent.thirdPartyDeveloperTools =
                data.thirdPartyDeveloperTools;
            response.push('## Third-party developer tools');
            for (const toolGroup of data.thirdPartyDeveloperTools) {
                response.push(`${toolGroup.name}: ${toolGroup.description}`);
                response.push('Available tools:');
                const toolDefinitionsMessage = toolGroup.tools
                    .map(tool => {
                    return `name="${tool.name}", description="${tool.description}", inputSchema=${JSON.stringify(tool.inputSchema)}`;
                })
                    .join('\n');
                response.push(toolDefinitionsMessage);
            }
        }
        if (this.#listWebMcpTools && data.webmcpTools) {
            structuredContent.webmcpTools = data.webmcpTools.map(({ name, description, inputSchema, annotations }) => ({
                name,
                description,
                inputSchema,
                annotations,
            }));
            response.push('## WebMCP tools');
            if (data.webmcpTools.length === 0) {
                response.push('No WebMCP tools available.');
            }
            else {
                const webmcpToolsMessage = data.webmcpTools
                    .map(tool => {
                    return `name="${tool.name}", description="${tool.description}", inputSchema=${JSON.stringify(tool.inputSchema)}, annotations=${JSON.stringify(tool.annotations)}`;
                })
                    .join('\n');
                response.push(webmcpToolsMessage);
            }
        }
        if (this.#networkRequestsOptions?.include && data.networkRequests) {
            const requests = data.networkRequests;
            response.push('## Network requests');
            if (requests.length) {
                const paginationData = this.#dataWithPagination(requests, this.#networkRequestsOptions.pagination);
                structuredContent.pagination = paginationData.pagination;
                response.push(...paginationData.info);
                if (data.networkRequests) {
                    structuredContent.networkRequests = paginationData.items.map(i => i.toJSON());
                    response.push(...(useToon
                        ? [toonEncode(structuredContent.networkRequests)]
                        : paginationData.items.map(i => i.toString())));
                }
            }
            else {
                response.push('No requests found.');
            }
        }
        if (this.#consoleDataOptions?.include) {
            const messages = data.consoleMessages ?? [];
            response.push('## Console messages');
            if (messages.length) {
                const grouped = ConsoleFormatter.groupConsecutive(messages);
                const paginationData = this.#dataWithPagination(grouped, this.#consoleDataOptions.pagination);
                structuredContent.pagination = paginationData.pagination;
                structuredContent.consoleMessages = paginationData.items.map(item => item.toJSON());
                response.push(...paginationData.info);
                if (useToon) {
                    response.push(toonEncode(structuredContent.consoleMessages));
                }
                else {
                    response.push(...paginationData.items.map(item => item.toString()));
                }
            }
            else {
                response.push('<no console messages found>');
            }
        }
        if (data.errorMessage) {
            response.push(`Error: ${data.errorMessage}`);
            structuredContent.errorMessage = data.errorMessage;
        }
        const text = {
            type: 'text',
            text: response.join('\n'),
        };
        const images = this.#images.map(imageData => {
            return {
                type: 'image',
                ...imageData,
            };
        });
        return {
            content: [text, ...images],
            structuredContent,
        };
    }
    #dataWithPagination(data, pagination) {
        const response = [];
        const paginationResult = paginate(data, pagination);
        if (paginationResult.invalidPage) {
            response.push('Invalid page number provided. Showing first page.');
        }
        const { startIndex, endIndex, currentPage, totalPages } = paginationResult;
        response.push(`Showing ${startIndex + 1}-${endIndex} of ${data.length} (Page ${currentPage + 1} of ${totalPages}).`);
        if (pagination) {
            if (paginationResult.hasNextPage) {
                response.push(`Next page: ${currentPage + 1}`);
            }
            if (paginationResult.hasPreviousPage) {
                response.push(`Previous page: ${currentPage - 1}`);
            }
        }
        return {
            info: response,
            items: paginationResult.items,
            pagination: {
                currentPage: paginationResult.currentPage,
                totalPages: paginationResult.totalPages,
                hasNextPage: paginationResult.hasNextPage,
                hasPreviousPage: paginationResult.hasPreviousPage,
                startIndex: paginationResult.startIndex,
                endIndex: paginationResult.endIndex,
                invalidPage: paginationResult.invalidPage,
            },
        };
    }
    resetResponseLineForTesting() {
        this.#textResponseLines = [];
    }
}
function truncateTitle(title, maxLength = 50) {
    if (title.length <= maxLength) {
        return title;
    }
    return title.slice(0, maxLength - 3) + '...';
}
async function fetchPageTitle(page) {
    return Promise.race([
        page.title().catch(() => ''),
        new Promise(resolve => setTimeout(() => resolve(''), 1000)),
    ]);
}
function createStructuredPage(page, context, rawTitle) {
    const isolatedContextName = context.getIsolatedContextName(page);
    const title = truncateTitle(rawTitle);
    const entry = {
        id: context.getPageId(page),
        url: page.url(),
        title,
        selected: context.isPageSelected(page),
    };
    if (isolatedContextName) {
        entry.isolatedContext = isolatedContextName;
    }
    return entry;
}
//# sourceMappingURL=McpResponse.js.map