repo_description: Chrome DevTools for coding agents
repo_topics: browser chrome chrome-devtools debugging devtools mcp mcp-server puppeteer
code: /**
   * @license
   * Copyright 2025 Google LLC
   * SPDX-License-Identifier: Apache-2.0
   */
  
  import {logger} from '../logger.js';
  import {DevTools} from '../third_party/index.js';
  
  const engine = DevTools.TraceEngine.TraceModel.Model.createWithAllHandlers();
  
  export interface TraceResult {
    parsedTrace: DevTools.TraceEngine.TraceModel.ParsedTrace;
    insights: DevTools.TraceEngine.Insights.Types.TraceInsightSets | null;
  }
  
  export function traceResultIsSuccess(
    x: TraceResult | TraceParseError,
  ): x is TraceResult {
    return 'parsedTrace' in x;
  }
  
  export interface TraceParseError {
    error: string;
  }
  
  export async function parseRawTraceBuffer(
    buffer: Uint8Array<ArrayBufferLike> | undefined,
  ): Promise<TraceResult | TraceParseError> {
    engine.resetProcessor();
    if (!buffer) {
      return {
        error: 'No buffer was provided.',
      };
    }
    const asString = new TextDecoder().decode(buffer);
    if (!asString) {
      return {
        error: 'Decoding the trace buffer returned an empty string.',
      };
    }
    try {
      const data = JSON.parse(asString) as
        | {
            traceEvents: DevTools.TraceEngine.Types.Events.Event[];
          }
        | DevTools.TraceEngine.Types.Events.Event[];
  
      const events = Array.isArray(data) ? data : data.traceEvents;
      await engine.parse(events);
      const parsedTrace = engine.parsedTrace();
      if (!parsedTrace) {
        return {
          error: 'No parsed trace was returned from the trace engine.',
        };
      }
  
      const insights = parsedTrace?.insights ?? null;
  
      return {
        parsedTrace,
        insights,
      };
    } catch (e) {
      const errorText = e instanceof Error ? e.message : JSON.stringify(e);
      logger(`Unexpected error parsing trace: ${errorText}`);
      return {
        error: errorText,
      };
    }
  }
  
  const extraFormatDescriptions = `Information on performance traces may contain main thread activity represented as call frames and network requests.
  
  ${DevTools.PerformanceTraceFormatter.callFrameDataFormatDescription}
  
  ${DevTools.PerformanceTraceFormatter.networkDataFormatDescription}`;
  
  export function getTraceSummary(result: TraceResult): string {
    const focus = DevTools.AgentFocus.fromParsedTrace(result.parsedTrace);
    const formatter = new DevTools.PerformanceTraceFormatter(focus);
    const summaryText = formatter.formatTraceSummary();
    return `## Summary of Performance trace findings:
  ${summaryText}
  
  ## Details on call tree & network request formats:
  ${extraFormatDescriptions}`;
  }
  
  export type InsightName =
    keyof DevTools.TraceEngine.Insights.Types.InsightModels;
  export type InsightOutput = {output: string} | {error: string};
  
  export function getInsightOutput(
    result: TraceResult,
    insightSetId: string,
    insightName: InsightName,
  ): InsightOutput {
    if (!result.insights) {
      return {
        error: 'No Performance insights are available for this trace.',
      };
    }
  
    const insightSet = result.insights.get(insightSetId);
    if (!insightSet) {
      return {
        error:
          'No Performance Insights for the given insight set id. Only use ids given in the "Available insight sets" list.',
      };
    }
  
    const matchingInsight =
      insightName in insightSet.model ? insightSet.model[insightName] : null;
    if (!matchingInsight) {
      return {
        error: `No Insight with the name ${insightName} found. Double check the name you provided is accurate and try again.`,
      };
    }
  
    const formatter = new DevTools.PerformanceInsightFormatter(
      DevTools.AgentFocus.fromParsedTrace(result.parsedTrace),
      matchingInsight,
    );
    return {output: formatter.formatInsight()};
  }
  