chrome-devtools-mcp-for-extension 0.19.2 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Color.js +13 -9
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ColorConverter.js +9 -7
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Console.js +1 -8
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Debouncer.js +2 -2
  5. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +9 -7
  6. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/MapWithDefault.js +5 -3
  7. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +10 -20
  8. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +0 -11
  9. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ReturnToPanel.js +6 -4
  10. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Revealer.js +5 -0
  11. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/SegmentedRange.js +1 -2
  12. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +110 -99
  13. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/StringOutputStream.js +1 -4
  14. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Throttler.js +2 -2
  15. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/common.js +1 -4
  16. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/AidaClient.js +21 -2
  17. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/DispatchHttpRequestClient.js +66 -0
  18. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/GdpClient.js +102 -86
  19. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHost.js +32 -450
  20. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostAPI.js +32 -29
  21. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +430 -0
  22. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/Platform.js +5 -3
  23. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +10 -22
  24. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +26 -15
  25. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/host.js +2 -1
  26. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +2 -2
  27. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/HostRuntime.js +19 -0
  28. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +34 -32
  29. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/api/HostRuntime.js +4 -0
  30. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/api/api.js +5 -0
  31. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/browser/HostRuntime.js +63 -0
  32. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/browser/browser.js +5 -0
  33. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/node/HostRuntime.js +72 -0
  34. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/node/node.js +5 -0
  35. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/platform.js +2 -2
  36. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +17 -0
  37. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/ConnectionTransport.js +12 -0
  38. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/DevToolsCDPConnection.js +138 -0
  39. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +49 -312
  40. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/protocol_client.js +4 -9
  41. package/build/node_modules/chrome-devtools-frontend/front_end/core/root/DevToolsContext.js +54 -0
  42. package/build/node_modules/chrome-devtools-frontend/front_end/core/root/root.js +2 -1
  43. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/AnimationModel.js +2 -3
  44. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CPUThrottlingManager.js +0 -3
  45. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +51 -17
  46. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +21 -7
  47. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +11 -5
  48. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSProperty.js +4 -7
  49. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParser.js +11 -9
  50. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +34 -24
  51. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSRule.js +52 -10
  52. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStartingStyle.js +21 -0
  53. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStyleDeclaration.js +4 -4
  54. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +39 -35
  55. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Connections.js +10 -47
  56. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ConsoleModel.js +1 -1
  57. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Cookie.js +1 -1
  58. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +29 -4
  59. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +5 -20
  60. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EnhancedTracesParser.js +17 -3
  61. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EventBreakpointsModel.js +4 -2
  62. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/HttpReasonPhraseStrings.js +4 -2
  63. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/IOModel.js +1 -4
  64. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +428 -121
  65. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +8 -22
  66. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PageResourceLoader.js +44 -34
  67. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PreloadingModel.js +64 -19
  68. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +139 -12
  69. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RemoteObject.js +5 -2
  70. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +3 -2
  71. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ScopeTreeCache.js +36 -0
  72. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ScreenCaptureModel.js +20 -18
  73. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Script.js +26 -1
  74. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ServerSentEventsProtocol.js +3 -0
  75. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +54 -14
  76. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapCache.js +16 -0
  77. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +19 -7
  78. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +240 -8
  79. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +12 -17
  80. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TargetManager.js +38 -29
  81. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TraceObject.js +11 -8
  82. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk-meta.js +80 -31
  83. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk.js +4 -2
  84. package/build/node_modules/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1301 -174
  85. package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +67 -22
  86. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +62 -91
  87. package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +188 -100
  88. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +30 -3
  89. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +50 -34
  90. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +152 -101
  91. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +11 -6
  92. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +25 -26
  93. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIQueries.js +46 -2
  94. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CSSWorkspaceBinding.js +10 -8
  95. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +82 -3
  96. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +33 -15
  97. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceMapping.js +105 -14
  98. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceScriptMapping.js +38 -0
  99. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/SASSSourceMapping.js +5 -4
  100. package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/CPUProfileDataModel.js +9 -7
  101. package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/ProfileTreeModel.js +6 -7
  102. package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/CrUXManager.js +6 -4
  103. package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +16 -2
  104. package/build/node_modules/chrome-devtools-frontend/front_end/models/network_time_calculator/RequestTimeRanges.js +6 -4
  105. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/FunctionCodeResolver.js +192 -0
  106. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +12 -16
  107. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/source_map_scopes.js +2 -2
  108. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +1 -1
  109. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/Trie.js +8 -0
  110. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +1 -0
  111. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/ModelImpl.js +6 -3
  112. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Processor.js +4 -4
  113. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Styles.js +1 -1
  114. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +11 -4
  115. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/FramesHandler.js +7 -5
  116. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +8 -4
  117. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +4 -1
  118. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +29 -3
  119. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/SamplesHandler.js +59 -6
  120. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/ScriptsHandler.js +26 -1
  121. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/UserTimingsHandler.js +1 -1
  122. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/helpers.js +1 -1
  123. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +4 -2
  124. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Trace.js +18 -4
  125. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/CLSCulprits.js +2 -1
  126. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Cache.js +2 -1
  127. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DOMSize.js +2 -1
  128. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DocumentLatency.js +12 -11
  129. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DuplicatedJavaScript.js +2 -1
  130. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/FontDisplay.js +2 -1
  131. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ForcedReflow.js +3 -2
  132. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/INPBreakdown.js +14 -2
  133. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ImageDelivery.js +2 -1
  134. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +13 -2
  135. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -1
  136. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LegacyJavaScript.js +4 -2
  137. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ModernHTTP.js +2 -1
  138. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +4 -3
  139. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +2 -1
  140. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/SlowCSSSelector.js +2 -1
  141. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ThirdParties.js +2 -1
  142. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Viewport.js +2 -1
  143. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +9 -3
  144. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +25 -2
  145. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/IgnoreListManager.js +41 -48
  146. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/UISourceCode.js +38 -0
  147. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/WorkspaceImpl.js +5 -8
  148. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/workspace-meta.js +34 -0
  149. package/build/src/McpContext.js +8 -1
  150. package/build/src/tools/bookmarks.js +0 -1
  151. package/build/src/tools/gemini-web.js +17 -25
  152. package/build/src/tools/snapshot.js +9 -2
  153. package/package.json +3 -3
@@ -239,7 +239,9 @@ export class PerformanceInsightFormatter {
239
239
  const rootCauseText = potentialRootCauses.length ? `- Potential root causes:\n ${potentialRootCauses.join('\n')}` :
240
240
  '- No potential root causes identified';
241
241
  const startTime = Trace.Helpers.Timing.microToMilli(Trace.Types.Timing.Micro(shift.ts - baseTime));
242
- return `### Layout shift ${index + 1}:
242
+ const impactedNodeNames = shift.rawSourceEvent.args.data?.impacted_nodes?.map(n => n.debug_name).filter(name => name !== undefined) ?? [];
243
+ const impactedNodeText = impactedNodeNames.length ? `\n- Impacted elements:\n - ${impactedNodeNames.join('\n - ')}\n` : '';
244
+ return `### Layout shift ${index + 1}:${impactedNodeText}
243
245
  - Start time: ${millis(startTime)}
244
246
  - Score: ${shift.args.data?.weighted_score_delta.toFixed(4)}
245
247
  ${rootCauseText}`;
@@ -432,7 +434,7 @@ Duplication grouped by Node modules: ${filesFormatted}`;
432
434
  output += 'No top-level functions causing forced reflows were identified.\n';
433
435
  }
434
436
  if (insight.aggregatedBottomUpData.length > 0) {
435
- output += '\n' + Trace.Insights.Models.ForcedReflow.UIStrings.relatedStackTrace + ' (including total time):\n';
437
+ output += '\n' + Trace.Insights.Models.ForcedReflow.UIStrings.reflowCallFrames + ' (including total time):\n';
436
438
  for (const data of insight.aggregatedBottomUpData) {
437
439
  output += `\n - ${this.#formatMicro(data.totalTime)} in ${callFrameToString(data.bottomUpData)}`;
438
440
  }
@@ -845,55 +847,69 @@ ${this.#links()}`;
845
847
  .join(', ');
846
848
  }
847
849
  #links() {
850
+ const links = [];
851
+ if (this.#insight.docs) {
852
+ links.push(this.#insight.docs);
853
+ }
848
854
  switch (this.#insight.insightKey) {
849
855
  case 'CLSCulprits':
850
- return `- https://web.dev/articles/cls
851
- - https://web.dev/articles/optimize-cls`;
856
+ links.push('https://web.dev/articles/cls');
857
+ links.push('https://web.dev/articles/optimize-cls');
858
+ break;
852
859
  case 'DocumentLatency':
853
- return '- https://web.dev/articles/optimize-ttfb';
860
+ links.push('https://web.dev/articles/optimize-ttfb');
861
+ break;
854
862
  case 'DOMSize':
855
- return '- https://developer.chrome.com/docs/lighthouse/performance/dom-size/';
856
- case 'DuplicatedJavaScript':
857
- return '';
863
+ links.push('https://developer.chrome.com/docs/lighthouse/performance/dom-size/');
864
+ break;
858
865
  case 'FontDisplay':
859
- return `- https://web.dev/articles/preload-optional-fonts
860
- - https://fonts.google.com/knowledge/glossary/foit
861
- - https://developer.chrome.com/blog/font-fallbacks`;
866
+ links.push('https://web.dev/articles/preload-optional-fonts');
867
+ links.push('https://fonts.google.com/knowledge/glossary/foit');
868
+ links.push('https://developer.chrome.com/blog/font-fallbacks');
869
+ break;
862
870
  case 'ForcedReflow':
863
- return '- https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing#avoid-forced-synchronous-layouts';
871
+ links.push('https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing#avoid-forced-synchronous-layouts');
872
+ break;
864
873
  case 'ImageDelivery':
865
- return '- https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/';
874
+ links.push('https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/');
875
+ break;
866
876
  case 'INPBreakdown':
867
- return `- https://web.dev/articles/inp
868
- - https://web.dev/explore/how-to-optimize-inp
869
- - https://web.dev/articles/optimize-long-tasks
870
- - https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing`;
871
- case 'LCPDiscovery':
872
- return `- https://web.dev/articles/lcp
873
- - https://web.dev/articles/optimize-lcp`;
877
+ links.push('https://web.dev/articles/inp');
878
+ links.push('https://web.dev/explore/how-to-optimize-inp');
879
+ links.push('https://web.dev/articles/optimize-long-tasks');
880
+ links.push('https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing');
881
+ break;
874
882
  case 'LCPBreakdown':
875
- return `- https://web.dev/articles/lcp
876
- - https://web.dev/articles/optimize-lcp`;
877
- case 'NetworkDependencyTree':
878
- return `- https://web.dev/learn/performance/understanding-the-critical-path
879
- - https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preconnect/`;
883
+ case 'LCPDiscovery':
880
884
  case 'RenderBlocking':
881
- return `- https://web.dev/articles/lcp
882
- - https://web.dev/articles/optimize-lcp`;
885
+ links.push('https://web.dev/articles/lcp');
886
+ links.push('https://web.dev/articles/optimize-lcp');
887
+ break;
888
+ case 'NetworkDependencyTree':
889
+ links.push('https://web.dev/learn/performance/understanding-the-critical-path');
890
+ links.push('https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preconnect/');
891
+ break;
883
892
  case 'SlowCSSSelector':
884
- return '- https://developer.chrome.com/docs/devtools/performance/selector-stats';
893
+ links.push('https://developer.chrome.com/docs/devtools/performance/selector-stats');
894
+ break;
885
895
  case 'ThirdParties':
886
- return '- https://web.dev/articles/optimizing-content-efficiency-loading-third-party-javascript/';
896
+ links.push('https://web.dev/articles/optimizing-content-efficiency-loading-third-party-javascript/');
897
+ break;
887
898
  case 'Viewport':
888
- return '- https://developer.chrome.com/blog/300ms-tap-delay-gone-away/';
899
+ links.push('https://developer.chrome.com/blog/300ms-tap-delay-gone-away/');
900
+ break;
889
901
  case 'Cache':
890
- return '- https://web.dev/uses-long-cache-ttl/';
902
+ links.push('https://web.dev/uses-long-cache-ttl/');
903
+ break;
891
904
  case 'ModernHTTP':
892
- return '- https://developer.chrome.com/docs/lighthouse/best-practices/uses-http2';
905
+ links.push('https://developer.chrome.com/docs/lighthouse/best-practices/uses-http2');
906
+ break;
893
907
  case 'LegacyJavaScript':
894
- return `- https://web.dev/articles/baseline-and-polyfills
895
- - https://philipwalton.com/articles/the-state-of-es5-on-the-web/`;
908
+ links.push('https://web.dev/articles/baseline-and-polyfills');
909
+ links.push('https://philipwalton.com/articles/the-state-of-es5-on-the-web/');
910
+ break;
896
911
  }
912
+ return links.map(link => '- ' + link).join('\n');
897
913
  }
898
914
  #description() {
899
915
  switch (this.#insight.insightKey) {
@@ -15,7 +15,7 @@ export class PerformanceTraceFormatter {
15
15
  constructor(focus) {
16
16
  this.#focus = focus;
17
17
  this.#parsedTrace = focus.parsedTrace;
18
- this.#insightSet = focus.insightSet;
18
+ this.#insightSet = focus.primaryInsightSet;
19
19
  this.#eventsSerializer = focus.eventsSerializer;
20
20
  }
21
21
  serializeEvent(event) {
@@ -91,58 +91,62 @@ export class PerformanceTraceFormatter {
91
91
  }
92
92
  formatTraceSummary() {
93
93
  const parsedTrace = this.#parsedTrace;
94
- const insightSet = this.#insightSet;
95
94
  const traceMetadata = this.#parsedTrace.metadata;
96
95
  const data = parsedTrace.data;
97
96
  const parts = [];
98
- const lcp = insightSet ? Trace.Insights.Common.getLCP(insightSet) : null;
99
- const cls = insightSet ? Trace.Insights.Common.getCLS(insightSet) : null;
100
- const inp = insightSet ? Trace.Insights.Common.getINP(insightSet) : null;
101
97
  parts.push(`URL: ${data.Meta.mainFrameURL}`);
102
- parts.push(`Bounds: ${this.serializeBounds(data.Meta.traceBounds)}`);
98
+ parts.push(`Trace bounds: ${this.serializeBounds(data.Meta.traceBounds)}`);
103
99
  parts.push('CPU throttling: ' + (traceMetadata.cpuThrottling ? `${traceMetadata.cpuThrottling}x` : 'none'));
104
100
  parts.push(`Network throttling: ${traceMetadata.networkThrottling ?? 'none'}`);
105
- if (lcp || cls || inp) {
106
- parts.push('Metrics (lab / observed):');
107
- if (lcp) {
108
- const nodeId = insightSet?.model.LCPBreakdown.lcpEvent?.args.data?.nodeId;
109
- const nodeIdText = nodeId !== undefined ? `, nodeId: ${nodeId}` : '';
110
- parts.push(` - LCP: ${Math.round(lcp.value / 1000)} ms, event: ${this.serializeEvent(lcp.event)}${nodeIdText}`);
111
- const subparts = insightSet?.model.LCPBreakdown.subparts;
112
- if (subparts) {
113
- const serializeSubpart = (subpart) => {
114
- return `${micros(subpart.range)}, bounds: ${this.serializeBounds(subpart)}`;
115
- };
116
- parts.push(' - LCP breakdown:');
117
- parts.push(` - TTFB: ${serializeSubpart(subparts.ttfb)}`);
118
- if (subparts.loadDelay !== undefined) {
119
- parts.push(` - Load delay: ${serializeSubpart(subparts.loadDelay)}`);
120
- }
121
- if (subparts.loadDuration !== undefined) {
122
- parts.push(` - Load duration: ${serializeSubpart(subparts.loadDuration)}`);
101
+ parts.push('\n# Available insight sets\n');
102
+ parts.push('The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.');
103
+ for (const insightSet of parsedTrace.insights?.values() ?? []) {
104
+ const lcp = insightSet ? Trace.Insights.Common.getLCP(insightSet) : null;
105
+ const cls = insightSet ? Trace.Insights.Common.getCLS(insightSet) : null;
106
+ const inp = insightSet ? Trace.Insights.Common.getINP(insightSet) : null;
107
+ parts.push(`\n## insight set id: ${insightSet.id}\n`);
108
+ parts.push(`URL: ${insightSet.url}`);
109
+ parts.push(`Bounds: ${this.serializeBounds(insightSet.bounds)}`);
110
+ if (lcp || cls || inp) {
111
+ parts.push('Metrics (lab / observed):');
112
+ if (lcp) {
113
+ const nodeId = insightSet?.model.LCPBreakdown.lcpEvent?.args.data?.nodeId;
114
+ const nodeIdText = nodeId !== undefined ? `, nodeId: ${nodeId}` : '';
115
+ parts.push(` - LCP: ${Math.round(lcp.value / 1000)} ms, event: ${this.serializeEvent(lcp.event)}${nodeIdText}`);
116
+ const subparts = insightSet?.model.LCPBreakdown.subparts;
117
+ if (subparts) {
118
+ const serializeSubpart = (subpart) => {
119
+ return `${micros(subpart.range)}, bounds: ${this.serializeBounds(subpart)}`;
120
+ };
121
+ parts.push(' - LCP breakdown:');
122
+ parts.push(` - TTFB: ${serializeSubpart(subparts.ttfb)}`);
123
+ if (subparts.loadDelay !== undefined) {
124
+ parts.push(` - Load delay: ${serializeSubpart(subparts.loadDelay)}`);
125
+ }
126
+ if (subparts.loadDuration !== undefined) {
127
+ parts.push(` - Load duration: ${serializeSubpart(subparts.loadDuration)}`);
128
+ }
129
+ parts.push(` - Render delay: ${serializeSubpart(subparts.renderDelay)}`);
123
130
  }
124
- parts.push(` - Render delay: ${serializeSubpart(subparts.renderDelay)}`);
131
+ }
132
+ if (inp) {
133
+ parts.push(` - INP: ${Math.round(inp.value / 1000)} ms, event: ${this.serializeEvent(inp.event)}`);
134
+ }
135
+ if (cls) {
136
+ const eventText = cls.worstClusterEvent ? `, event: ${this.serializeEvent(cls.worstClusterEvent)}` : '';
137
+ parts.push(` - CLS: ${cls.value.toFixed(2)}${eventText}`);
125
138
  }
126
139
  }
127
- if (inp) {
128
- parts.push(` - INP: ${Math.round(inp.value / 1000)} ms, event: ${this.serializeEvent(inp.event)}`);
140
+ else {
141
+ parts.push('Metrics (lab / observed): n/a');
129
142
  }
130
- if (cls) {
131
- const eventText = cls.worstClusterEvent ? `, event: ${this.serializeEvent(cls.worstClusterEvent)}` : '';
132
- parts.push(` - CLS: ${cls.value.toFixed(2)}${eventText}`);
143
+ const cruxParts = insightSet && this.#getCruxTraceSummary(insightSet);
144
+ if (cruxParts?.length) {
145
+ parts.push(...cruxParts);
146
+ }
147
+ else {
148
+ parts.push('Metrics (field / real users): n/a – no data for this page in CrUX');
133
149
  }
134
- }
135
- else {
136
- parts.push('Metrics (lab / observed): n/a');
137
- }
138
- const cruxParts = insightSet && this.#getCruxTraceSummary(insightSet);
139
- if (cruxParts?.length) {
140
- parts.push(...cruxParts);
141
- }
142
- else {
143
- parts.push('Metrics (field / real users): n/a – no data for this page in CrUX');
144
- }
145
- if (insightSet) {
146
150
  parts.push('Available insights:');
147
151
  for (const [insightName, model] of Object.entries(insightSet.model)) {
148
152
  if (model.state === 'pass') {
@@ -172,23 +176,44 @@ export class PerformanceTraceFormatter {
172
176
  parts.push(` - ${insightPartsText}`);
173
177
  }
174
178
  }
179
+ return parts.join('\n');
180
+ }
181
+ #formatFactByInsightSet(options) {
182
+ const { insights, title, description, empty, cb } = options;
183
+ const lines = [`# ${title}\n`];
184
+ if (description) {
185
+ lines.push(`${description}\n`);
186
+ }
187
+ if (insights?.size) {
188
+ const multipleInsightSets = insights.size > 1;
189
+ for (const insightSet of insights.values()) {
190
+ if (multipleInsightSets) {
191
+ lines.push(`## insight set id: ${insightSet.id}\n`);
192
+ }
193
+ lines.push((cb(insightSet) ?? empty) + '\n');
194
+ }
195
+ }
175
196
  else {
176
- parts.push('Available insights: none');
197
+ lines.push(empty + '\n');
177
198
  }
178
- return parts.join('\n');
199
+ return lines.join('\n');
179
200
  }
180
201
  formatCriticalRequests() {
181
- const insightSet = this.#insightSet;
182
- const criticalRequests = [];
183
- const walkRequest = (node) => {
184
- criticalRequests.push(node.request);
185
- node.children.forEach(walkRequest);
186
- };
187
- insightSet?.model.NetworkDependencyTree.rootNodes.forEach(walkRequest);
188
- if (!criticalRequests.length) {
189
- return '';
190
- }
191
- return 'Critical network requests:\n' + this.formatNetworkRequests(criticalRequests, { verbose: false });
202
+ const parsedTrace = this.#parsedTrace;
203
+ return this.#formatFactByInsightSet({
204
+ insights: parsedTrace.insights,
205
+ title: 'Critical network requests',
206
+ empty: 'none',
207
+ cb: insightSet => {
208
+ const criticalRequests = [];
209
+ const walkRequest = (node) => {
210
+ criticalRequests.push(node.request);
211
+ node.children.forEach(walkRequest);
212
+ };
213
+ insightSet.model.NetworkDependencyTree.rootNodes.forEach(walkRequest);
214
+ return criticalRequests.length ? this.formatNetworkRequests(criticalRequests, { verbose: false }) : null;
215
+ },
216
+ });
192
217
  }
193
218
  #serializeBottomUpRootNode(rootNode, limit) {
194
219
  // Sorted by selfTime.
@@ -220,19 +245,24 @@ export class PerformanceTraceFormatter {
220
245
  }
221
246
  return `- self: ${millis(node.selfTime)}, total: ${millis(node.totalTime)}, source: ${source}`;
222
247
  }
223
- const listText = topNodes.map(node => nodeToText.call(this, node)).join('\n');
224
- const format = `This is the bottom-up summary for the entire trace. Only the top ${limit} activities (sorted by self time) are shown. An activity is all the aggregated time spent on the same type of work. For example, it can be all the time spent in a specific JavaScript function, or all the time spent in a specific browser rendering stage (like layout, v8 compile, parsing html). "Self time" represents the aggregated time spent directly in an activity, across all occurrences. "Total time" represents the aggregated time spent in an activity or any of its children.`;
225
- return `${format}\n\n${listText}`;
248
+ return topNodes.map(node => nodeToText.call(this, node)).join('\n');
249
+ }
250
+ #getSerializeBottomUpRootNodeFormat(limit) {
251
+ return `This is the bottom-up summary for the entire trace. Only the top ${limit} activities (sorted by self time) are shown. An activity is all the aggregated time spent on the same type of work. For example, it can be all the time spent in a specific JavaScript function, or all the time spent in a specific browser rendering stage (like layout, v8 compile, parsing html). "Self time" represents the aggregated time spent directly in an activity, across all occurrences. "Total time" represents the aggregated time spent in an activity or any of its children.`;
226
252
  }
227
253
  formatMainThreadBottomUpSummary() {
228
254
  const parsedTrace = this.#parsedTrace;
229
- const insightSet = this.#insightSet;
230
- const bounds = parsedTrace.data.Meta.traceBounds;
231
- const rootNode = AIQueries.mainThreadActivityBottomUp(insightSet?.navigation?.args.data?.navigationId, bounds, parsedTrace);
232
- if (!rootNode) {
233
- return '';
234
- }
235
- return this.#serializeBottomUpRootNode(rootNode, 10);
255
+ const limit = 10;
256
+ return this.#formatFactByInsightSet({
257
+ insights: parsedTrace.insights,
258
+ title: 'Main thread bottom-up summary',
259
+ description: this.#getSerializeBottomUpRootNodeFormat(limit),
260
+ empty: 'no activity',
261
+ cb: insightSet => {
262
+ const rootNode = AIQueries.mainThreadActivityBottomUpSingleNavigation(insightSet.navigation?.args.data?.navigationId, insightSet.bounds, parsedTrace);
263
+ return rootNode ? this.#serializeBottomUpRootNode(rootNode, limit) : null;
264
+ },
265
+ });
236
266
  }
237
267
  #formatThirdPartyEntitySummaries(summaries) {
238
268
  const topMainThreadTimeEntries = summaries.toSorted((a, b) => b.mainThreadTime - a.mainThreadTime).slice(0, 5);
@@ -248,36 +278,36 @@ export class PerformanceTraceFormatter {
248
278
  return listText;
249
279
  }
250
280
  formatThirdPartySummary() {
251
- const insightSet = this.#insightSet;
252
- if (!insightSet) {
253
- return '';
254
- }
255
- const thirdParties = insightSet.model.ThirdParties;
256
- let summaries = thirdParties.entitySummaries ?? [];
257
- if (thirdParties.firstPartyEntity) {
258
- summaries = summaries.filter(s => s.entity !== thirdParties?.firstPartyEntity || null);
259
- }
260
- const listText = this.#formatThirdPartyEntitySummaries(summaries);
261
- if (!listText) {
262
- return '';
263
- }
264
- return `Third party summary:\n${listText}`;
281
+ const parsedTrace = this.#parsedTrace;
282
+ return this.#formatFactByInsightSet({
283
+ insights: parsedTrace.insights,
284
+ title: '3rd party summary',
285
+ empty: 'no 3rd parties',
286
+ cb: insightSet => {
287
+ const thirdPartySummaries = Trace.Extras.ThirdParties.summarizeByThirdParty(parsedTrace.data, insightSet.bounds);
288
+ return thirdPartySummaries.length ? this.#formatThirdPartyEntitySummaries(thirdPartySummaries) : null;
289
+ },
290
+ });
265
291
  }
266
292
  formatLongestTasks() {
267
293
  const parsedTrace = this.#parsedTrace;
268
- const insightSet = this.#insightSet;
269
- const bounds = parsedTrace.data.Meta.traceBounds;
270
- const longestTaskTrees = AIQueries.longestTasks(insightSet?.navigation?.args.data?.navigationId, bounds, parsedTrace, 3);
271
- if (!longestTaskTrees || longestTaskTrees.length === 0) {
272
- return 'Longest tasks: none';
273
- }
274
- const listText = longestTaskTrees
275
- .map(tree => {
276
- const time = millis(tree.rootNode.totalTime);
277
- return `- total time: ${time}, event: ${this.serializeEvent(tree.rootNode.event)}`;
278
- })
279
- .join('\n');
280
- return `Longest ${longestTaskTrees.length} tasks:\n${listText}`;
294
+ return this.#formatFactByInsightSet({
295
+ insights: parsedTrace.insights,
296
+ title: 'Longest tasks',
297
+ empty: 'none',
298
+ cb: insightSet => {
299
+ const longestTaskTrees = AIQueries.longestTasks(insightSet.navigation?.args.data?.navigationId, insightSet.bounds, parsedTrace, 3);
300
+ if (!longestTaskTrees?.length) {
301
+ return null;
302
+ }
303
+ return longestTaskTrees
304
+ .map(tree => {
305
+ const time = millis(tree.rootNode.totalTime);
306
+ return `- total time: ${time}, event: ${this.serializeEvent(tree.rootNode.event)}`;
307
+ })
308
+ .join('\n');
309
+ },
310
+ });
281
311
  }
282
312
  #serializeRelatedInsightsForEvents(events) {
283
313
  if (!events.length) {
@@ -313,16 +343,22 @@ export class PerformanceTraceFormatter {
313
343
  return results.join('\n');
314
344
  }
315
345
  formatMainThreadTrackSummary(bounds) {
346
+ if (!this.#parsedTrace.insights) {
347
+ return 'No main thread activity found';
348
+ }
316
349
  const results = [];
317
- const topDownTree = AIQueries.mainThreadActivityTopDown(this.#insightSet?.navigation?.args.data?.navigationId, bounds, this.#parsedTrace);
350
+ const insightSet = this.#parsedTrace.insights?.values().find(insightSet => Trace.Helpers.Timing.boundsIncludeTimeRange({ bounds, timeRange: insightSet.bounds }));
351
+ const topDownTree = AIQueries.mainThreadActivityTopDown(insightSet?.navigation?.args.data?.navigationId, bounds, this.#parsedTrace);
318
352
  if (topDownTree) {
319
353
  results.push('# Top-down main thread summary');
320
354
  results.push(this.formatCallTree(topDownTree, 2 /* headerLevel */));
321
355
  }
322
- const bottomUpRootNode = AIQueries.mainThreadActivityBottomUp(this.#insightSet?.navigation?.args.data?.navigationId, bounds, this.#parsedTrace);
356
+ const bottomUpRootNode = AIQueries.mainThreadActivityBottomUp(bounds, this.#parsedTrace);
323
357
  if (bottomUpRootNode) {
324
358
  results.push('# Bottom-up main thread summary');
325
- results.push(this.#serializeBottomUpRootNode(bottomUpRootNode, 20));
359
+ const limit = 20;
360
+ results.push(this.#getSerializeBottomUpRootNodeFormat(limit));
361
+ results.push(this.#serializeBottomUpRootNode(bottomUpRootNode, limit));
326
362
  }
327
363
  const thirdPartySummaries = Trace.Extras.ThirdParties.summarizeByThirdParty(this.#parsedTrace.data, bounds);
328
364
  if (thirdPartySummaries.length) {
@@ -496,7 +532,7 @@ Network requests data:
496
532
  }
497
533
  static callFrameDataFormatDescription = `Each call frame is presented in the following format:
498
534
 
499
- 'id;eventKey;name;duration;selfTime;urlIndex;childRange;[S]'
535
+ 'id;eventKey;name;duration;selfTime;urlIndex;childRange;[line];[column];[S]'
500
536
 
501
537
  Key definitions:
502
538
 
@@ -507,15 +543,17 @@ Key definitions:
507
543
  * selfTime: The time spent directly within the call frame, excluding its children's execution.
508
544
  * urlIndex: Index referencing the "All URLs" list. Empty if no specific script URL is associated.
509
545
  * childRange: Specifies the direct children of this node using their IDs. If empty ('' or 'S' at the end), the node has no children. If a single number (e.g., '4'), the node has one child with that ID. If in the format 'firstId-lastId' (e.g., '4-5'), it indicates a consecutive range of child IDs from 'firstId' to 'lastId', inclusive.
546
+ * line: An optional field for a call frame's line number. This is where the function is defined.
547
+ * column: An optional field for a call frame's column number. This is where the function is defined.
510
548
  * S: _Optional_. The letter 'S' terminates the line if that call frame was selected by the user.
511
549
 
512
550
  Example Call Tree:
513
551
 
514
- 1;r-123;main;500;100;;
515
- 2;r-124;update;200;50;;3
516
- 3;p-49575-15428179-2834-374;animate;150;20;0;4-5;S
517
- 4;p-49575-15428179-3505-1162;calculatePosition;80;80;;
518
- 5;p-49575-15428179-5391-2767;applyStyles;50;50;;
552
+ 1;r-123;main;500;100;0;1;;
553
+ 2;r-124;update;200;50;;3;0;1;
554
+ 3;p-49575-15428179-2834-374;animate;150;20;0;4-5;0;1;S
555
+ 4;p-49575-15428179-3505-1162;calculatePosition;80;80;0;1;;
556
+ 5;p-49575-15428179-5391-2767;applyStyles;50;50;0;1;;
519
557
  `;
520
558
  /**
521
559
  * Network requests format description that is sent to the model as a fact.
@@ -610,4 +648,17 @@ The order of headers corresponds to an internal fixed list. If a header is not p
610
648
  ];
611
649
  return parts.join(';');
612
650
  }
651
+ formatFunctionCode(code) {
652
+ const { startLine, startColumn } = code.range;
653
+ const { startLine: contextStartLine, startColumn: contextStartColumn, endLine: contextEndLine, endColumn: contextEndColumn } = code.rangeWithContext;
654
+ const name = code.functionBounds.name;
655
+ const url = code.functionBounds.uiSourceCode.url();
656
+ const parts = [];
657
+ parts.push(`${name} @ ${url}:${startLine}:${startColumn}. With added context, chunk is from ${contextStartLine}:${contextStartColumn} to ${contextEndLine}:${contextEndColumn}`);
658
+ parts.push('\nThe following is a markdown block of JavaScript. <FUNCTION_START> and <FUNCTION_END> marks the exact function declaration, and everything outside that is provided for additional context. Comments at the end of each line indicate the runtime performance cost of that code. Do not show the user the function markers or the additional context.\n');
659
+ parts.push('```');
660
+ parts.push(code.codeWithContext);
661
+ parts.push('```');
662
+ return parts.join('\n');
663
+ }
613
664
  }
@@ -1,7 +1,6 @@
1
1
  // Copyright 2024 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
- import * as Root from '../../../core/root/root.js';
5
4
  import * as Trace from '../../../models/trace/trace.js';
6
5
  import * as SourceMapsResolver from '../../../models/trace_source_maps_resolver/trace_source_maps_resolver.js';
7
6
  /** Iterates from a node down through its descendents. If the callback returns true, the loop stops. */
@@ -122,7 +121,7 @@ export class AICallTree {
122
121
  if (!data.Renderer.entryToNode.has(selectedEvent) && !data.Samples.entryToNode.has(selectedEvent)) {
123
122
  return null;
124
123
  }
125
- const allEventsEnabled = Root.Runtime.experiments.isEnabled('timeline-show-all-events');
124
+ const showAllEvents = parsedTrace.data.Meta.config.showAllEvents;
126
125
  const { startTime, endTime } = Trace.Helpers.Timing.eventTimingsMilliSeconds(selectedEvent);
127
126
  const selectedEventBounds = Trace.Helpers.Timing.traceWindowFromMicroSeconds(Trace.Helpers.Timing.milliToMicro(startTime), Trace.Helpers.Timing.milliToMicro(endTime));
128
127
  let threadEvents = data.Renderer.processes.get(selectedEvent.pid)?.threads.get(selectedEvent.tid)?.entries;
@@ -139,7 +138,7 @@ export class AICallTree {
139
138
  // If the "Show all events" experiment is on, we don't filter out any
140
139
  // events here, otherwise the generated call tree will not match what the
141
140
  // user is seeing.
142
- if (!allEventsEnabled) {
141
+ if (!showAllEvents) {
143
142
  filters.push(new Trace.Extras.TraceFilter.VisibleEventsFilter(Trace.Styles.visibleTypes()));
144
143
  }
145
144
  // Build a tree bounded by the selected event's timestamps, and our other filters applied
@@ -245,10 +244,12 @@ export class AICallTree {
245
244
  * 5. `urlIndex`: An index referencing a URL in the `allUrls` array. If no URL is present, this is an empty string.
246
245
  * 6. `childRange`: A string indicating the range of IDs for the node's children. Children should always have consecutive IDs.
247
246
  * If there is only one child, it's a single ID.
248
- * 7. `[S]`: An optional marker indicating that this node is the selected node.
247
+ * 7. `[line]`: An optional field for a call frame's line number.
248
+ * 8. `[column]`: An optional field for a call frame's column number.
249
+ * 9. `[S]`: An optional marker indicating that this node is the selected node.
249
250
  *
250
251
  * Example:
251
- * `1;Parse HTML;2.5;0.3;0;2-5;S`
252
+ * `1;Parse HTML;2.5;0.3;0;2-5;10;11;S`
252
253
  * This represents:
253
254
  * - Node ID 1
254
255
  * - Name "Parse HTML"
@@ -256,6 +257,7 @@ export class AICallTree {
256
257
  * - Self time of 0.3ms
257
258
  * - URL index 0 (meaning the URL is the first one in the `allUrls` array)
258
259
  * - Child range of IDs 2 to 5
260
+ * - Line, column is 10:11
259
261
  * - This node is the selected node (S marker)
260
262
  */
261
263
  stringifyNode(node, nodeId, parsedTrace, selectedNode, allUrls, childStartingNodeIndex) {
@@ -281,7 +283,8 @@ export class AICallTree {
281
283
  // 5. Self Time
282
284
  const selfTimeStr = roundToTenths(node.selfTime);
283
285
  // 6. URL Index
284
- const url = SourceMapsResolver.SourceMapsResolver.resolvedURLForEntry(parsedTrace, event);
286
+ const location = SourceMapsResolver.SourceMapsResolver.codeLocationForEntry(parsedTrace, event);
287
+ const url = location?.url;
285
288
  let urlIndexStr = '';
286
289
  if (url) {
287
290
  const existingIndex = allUrls.indexOf(url);
@@ -309,6 +312,8 @@ export class AICallTree {
309
312
  line += ';' + selfTimeStr;
310
313
  line += ';' + urlIndexStr;
311
314
  line += ';' + childRangeStr;
315
+ line += ';' + (location?.line ?? '');
316
+ line += ';' + (location?.column ?? '');
312
317
  if (selectedMarker) {
313
318
  line += ';' + selectedMarker;
314
319
  }
@@ -3,21 +3,30 @@
3
3
  // found in the LICENSE file.
4
4
  import * as Trace from '../../../models/trace/trace.js';
5
5
  import { AICallTree } from './AICallTree.js';
6
- function getFirstInsightSet(insights) {
7
- // Currently only support a single insight set. Pick the first one with a navigation.
8
- // TODO(cjamcl): we should just give the agent the entire insight set, and give
9
- // summary detail about all of them + the ability to query each.
10
- return [...insights.values()].filter(insightSet => insightSet.navigation).at(0) ?? null;
6
+ /**
7
+ * Gets the first, most relevant InsightSet to use, following the logic of:
8
+ * 1. If there is only one InsightSet, use that.
9
+ * 2. If there are more, prefer the first we find that has a navigation associated with it.
10
+ * 3. If none with a navigation are found, fallback to the first one.
11
+ * 4. Otherwise, return null.
12
+ */
13
+ function getPrimaryInsightSet(insights) {
14
+ const insightSets = Array.from(insights.values());
15
+ if (insightSets.length === 0) {
16
+ return null;
17
+ }
18
+ if (insightSets.length === 1) {
19
+ return insightSets[0];
20
+ }
21
+ return insightSets.filter(set => set.navigation).at(0) ?? insightSets.at(0) ?? null;
11
22
  }
12
23
  export class AgentFocus {
13
24
  static fromParsedTrace(parsedTrace) {
14
25
  if (!parsedTrace.insights) {
15
26
  throw new Error('missing insights');
16
27
  }
17
- const insightSet = getFirstInsightSet(parsedTrace.insights);
18
28
  return new AgentFocus({
19
29
  parsedTrace,
20
- insightSet,
21
30
  event: null,
22
31
  callTree: null,
23
32
  insight: null,
@@ -27,10 +36,8 @@ export class AgentFocus {
27
36
  if (!parsedTrace.insights) {
28
37
  throw new Error('missing insights');
29
38
  }
30
- const insightSet = getFirstInsightSet(parsedTrace.insights);
31
39
  return new AgentFocus({
32
40
  parsedTrace,
33
- insightSet,
34
41
  event: null,
35
42
  callTree: null,
36
43
  insight,
@@ -40,35 +47,27 @@ export class AgentFocus {
40
47
  if (!parsedTrace.insights) {
41
48
  throw new Error('missing insights');
42
49
  }
43
- const insightSet = getFirstInsightSet(parsedTrace.insights);
44
50
  const result = AgentFocus.#getCallTreeOrEvent(parsedTrace, event);
45
- return new AgentFocus({ parsedTrace, insightSet, event: result.event, callTree: result.callTree, insight: null });
51
+ return new AgentFocus({ parsedTrace, event: result.event, callTree: result.callTree, insight: null });
46
52
  }
47
53
  static fromCallTree(callTree) {
48
- const insights = callTree.parsedTrace.insights;
49
- // Select the insight set containing the call tree.
50
- // If for some reason that fails, fallback to the first one.
51
- let insightSet = null;
52
- if (insights) {
53
- const callTreeTimeRange = Trace.Helpers.Timing.traceWindowFromEvent(callTree.rootNode.event);
54
- insightSet = insights.values().find(set => Trace.Helpers.Timing.boundsIncludeTimeRange({
55
- timeRange: callTreeTimeRange,
56
- bounds: set.bounds,
57
- })) ??
58
- getFirstInsightSet(insights);
59
- }
60
- return new AgentFocus({ parsedTrace: callTree.parsedTrace, insightSet, event: null, callTree, insight: null });
54
+ return new AgentFocus({ parsedTrace: callTree.parsedTrace, event: null, callTree, insight: null });
61
55
  }
62
56
  #data;
57
+ #primaryInsightSet;
63
58
  eventsSerializer = new Trace.EventsSerializer.EventsSerializer();
64
59
  constructor(data) {
60
+ if (!data.parsedTrace.insights) {
61
+ throw new Error('missing insights');
62
+ }
65
63
  this.#data = data;
64
+ this.#primaryInsightSet = getPrimaryInsightSet(data.parsedTrace.insights);
66
65
  }
67
66
  get parsedTrace() {
68
67
  return this.#data.parsedTrace;
69
68
  }
70
- get insightSet() {
71
- return this.#data.insightSet;
69
+ get primaryInsightSet() {
70
+ return this.#primaryInsightSet;
72
71
  }
73
72
  /** Note: at most one of event or callTree is non-null. */
74
73
  get event() {