chrome-devtools-frontend 1.0.1515796 → 1.0.1516909

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 (163) hide show
  1. package/docs/contributing/infrastructure.md +131 -82
  2. package/front_end/Tests.js +3 -29
  3. package/front_end/core/common/Progress.ts +73 -55
  4. package/front_end/core/host/GdpClient.ts +1 -1
  5. package/front_end/core/host/UserMetrics.ts +5 -2
  6. package/front_end/core/protocol_client/InspectorBackend.ts +2 -0
  7. package/front_end/core/root/Runtime.ts +0 -1
  8. package/front_end/core/sdk/CSSMatchedStyles.ts +12 -10
  9. package/front_end/core/sdk/CSSModel.ts +1 -31
  10. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +27 -7
  11. package/front_end/core/sdk/DebuggerModel.ts +1 -31
  12. package/front_end/core/sdk/EnhancedTracesParser.ts +81 -50
  13. package/front_end/core/sdk/NetworkManager.ts +1 -31
  14. package/front_end/core/sdk/NetworkRequest.ts +1 -31
  15. package/front_end/core/sdk/RehydratingConnection.snapshot.txt +1003 -0
  16. package/front_end/core/sdk/RehydratingConnection.ts +13 -18
  17. package/front_end/core/sdk/RehydratingObject.ts +8 -31
  18. package/front_end/core/sdk/RemoteObject.ts +1 -31
  19. package/front_end/core/sdk/ResourceTreeModel.ts +1 -31
  20. package/front_end/core/sdk/RuntimeModel.ts +1 -31
  21. package/front_end/core/sdk/ServiceWorkerManager.ts +1 -31
  22. package/front_end/core/sdk/SourceMap.ts +1 -31
  23. package/front_end/core/sdk/TraceObject.ts +8 -3
  24. package/front_end/entrypoints/main/MainImpl.ts +1 -3
  25. package/front_end/models/ai_assistance/AiHistoryStorage.ts +1 -3
  26. package/front_end/models/ai_assistance/ConversationHandler.ts +4 -6
  27. package/front_end/models/ai_assistance/agents/AiAgent.ts +4 -1
  28. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +110 -76
  29. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +2 -2
  30. package/front_end/models/ai_assistance/agents/StylingAgent.ts +2 -2
  31. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +178 -85
  32. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +308 -218
  33. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +100 -100
  34. package/front_end/models/ai_assistance/data_formatters/UnitFormatters.ts +10 -1
  35. package/front_end/models/ai_assistance/performance/AIContext.ts +19 -21
  36. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +24 -8
  37. package/front_end/models/badges/UserBadges.ts +38 -3
  38. package/front_end/models/bindings/ContentProviderBasedProject.ts +6 -4
  39. package/front_end/models/breakpoints/BreakpointManager.ts +3 -3
  40. package/front_end/models/formatter/FormatterWorkerPool.ts +3 -3
  41. package/front_end/models/har/Writer.ts +11 -11
  42. package/front_end/models/persistence/FileSystemWorkspaceBinding.ts +3 -3
  43. package/front_end/models/persistence/IsolatedFileSystem.ts +4 -4
  44. package/front_end/models/persistence/IsolatedFileSystemManager.ts +7 -7
  45. package/front_end/models/persistence/PersistenceImpl.ts +8 -8
  46. package/front_end/models/persistence/PlatformFileSystem.ts +1 -1
  47. package/front_end/models/trace/ModelImpl.ts +2 -16
  48. package/front_end/models/trace/Processor.ts +15 -9
  49. package/front_end/models/trace/handlers/AuctionWorkletsHandler.ts +4 -4
  50. package/front_end/models/trace/handlers/FramesHandler.ts +2 -2
  51. package/front_end/models/trace/handlers/LayoutShiftsHandler.ts +7 -10
  52. package/front_end/models/trace/handlers/MetaHandler.ts +11 -9
  53. package/front_end/models/trace/handlers/ScreenshotsHandler.ts +1 -1
  54. package/front_end/models/trace/handlers/ScriptsHandler.ts +5 -5
  55. package/front_end/models/trace/handlers/UserInteractionsHandler.ts +2 -14
  56. package/front_end/models/trace/handlers/UserTimingsHandler.ts +3 -4
  57. package/front_end/models/trace/insights/CLSCulprits.ts +1 -1
  58. package/front_end/models/trace/insights/DocumentLatency.ts +3 -4
  59. package/front_end/models/trace/insights/DuplicatedJavaScript.ts +1 -1
  60. package/front_end/models/trace/insights/INPBreakdown.ts +1 -1
  61. package/front_end/models/trace/insights/ImageDelivery.ts +1 -1
  62. package/front_end/models/trace/insights/LCPBreakdown.ts +1 -1
  63. package/front_end/models/trace/insights/LCPDiscovery.ts +1 -1
  64. package/front_end/models/trace/insights/ModernHTTP.ts +1 -1
  65. package/front_end/models/trace/insights/NetworkDependencyTree.ts +1 -1
  66. package/front_end/models/trace/insights/RenderBlocking.ts +1 -1
  67. package/front_end/models/trace/insights/types.ts +2 -0
  68. package/front_end/models/trace/types/TraceEvents.ts +41 -64
  69. package/front_end/models/trace_source_maps_resolver/trace_source_maps_resolver.ts +1 -1
  70. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +21 -99
  71. package/front_end/panels/application/ServiceWorkersView.ts +0 -1
  72. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +2 -3
  73. package/front_end/panels/common/BadgeNotification.ts +46 -10
  74. package/front_end/panels/common/GdpSignUpDialog.ts +6 -3
  75. package/front_end/panels/console/ConsoleView.ts +23 -28
  76. package/front_end/panels/console/ConsoleViewport.ts +2 -2
  77. package/front_end/panels/console/consoleView.css +11 -1
  78. package/front_end/panels/coverage/CoverageView.ts +2 -2
  79. package/front_end/panels/elements/ComputedStyleWidget.ts +1 -2
  80. package/front_end/panels/elements/ElementsTreeOutline.ts +2 -2
  81. package/front_end/panels/elements/LayoutPane.ts +1 -1
  82. package/front_end/panels/elements/StyleEditorWidget.ts +8 -19
  83. package/front_end/panels/elements/StylePropertyTreeElement.ts +39 -25
  84. package/front_end/panels/elements/StylesSidebarPane.ts +2 -2
  85. package/front_end/panels/elements/stylePropertiesTreeOutline.css +4 -3
  86. package/front_end/panels/layer_viewer/Layers3DView.ts +2 -2
  87. package/front_end/panels/layers/LayerTreeModel.ts +3 -3
  88. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +4 -4
  89. package/front_end/panels/network/NetworkLogView.ts +6 -2
  90. package/front_end/panels/network/NetworkLogViewColumns.ts +3 -3
  91. package/front_end/panels/network/NetworkSearchScope.ts +6 -6
  92. package/front_end/panels/search/SearchResultsPane.ts +32 -47
  93. package/front_end/panels/search/SearchView.ts +58 -80
  94. package/front_end/panels/settings/components/SyncSection.ts +7 -2
  95. package/front_end/panels/sources/OutlineQuickOpen.ts +3 -1
  96. package/front_end/panels/sources/SourcesSearchScope.ts +4 -4
  97. package/front_end/panels/sources/TabbedEditorContainer.ts +5 -5
  98. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +10 -5
  99. package/front_end/panels/timeline/TimelineFlameChartView.ts +18 -15
  100. package/front_end/panels/timeline/TimelinePanel.ts +41 -22
  101. package/front_end/panels/timeline/TimelineUIUtils.ts +13 -8
  102. package/front_end/panels/timeline/TracingLayerTree.ts +4 -5
  103. package/front_end/panels/timeline/components/ExportTraceOptions.ts +37 -22
  104. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +17 -7
  105. package/front_end/third_party/axe-core/README.chromium +1 -0
  106. package/front_end/third_party/codemirror/README.chromium +1 -0
  107. package/front_end/third_party/codemirror.next/README.chromium +1 -0
  108. package/front_end/third_party/csp_evaluator/README.chromium +1 -0
  109. package/front_end/third_party/diff/README.chromium +1 -0
  110. package/front_end/third_party/i18n/README.chromium +1 -0
  111. package/front_end/third_party/intl-messageformat/README.chromium +1 -0
  112. package/front_end/third_party/json5/README.chromium +1 -0
  113. package/front_end/third_party/legacy-javascript/README.chromium +1 -0
  114. package/front_end/third_party/lighthouse/README.chromium +1 -0
  115. package/front_end/third_party/lit/README.chromium +1 -0
  116. package/front_end/third_party/marked/README.chromium +1 -0
  117. package/front_end/third_party/puppeteer/README.chromium +2 -2
  118. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  119. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js +0 -20
  120. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js.map +1 -1
  121. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  122. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  123. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  124. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +1 -1
  125. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +1 -1
  126. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  127. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  128. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +2 -23
  129. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js +0 -20
  130. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js.map +1 -1
  131. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  132. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  133. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +1 -1
  134. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +1 -1
  135. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  136. package/front_end/third_party/puppeteer/package/package.json +1 -1
  137. package/front_end/third_party/puppeteer/package/src/cdp/Accessibility.ts +1 -21
  138. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  139. package/front_end/third_party/puppeteer/package/src/revisions.ts +1 -1
  140. package/front_end/third_party/puppeteer-replay/README.chromium +1 -0
  141. package/front_end/third_party/third-party-web/README.chromium +1 -0
  142. package/front_end/third_party/vscode.web-custom-data/README.chromium +1 -0
  143. package/front_end/third_party/wasmparser/README.chromium +1 -0
  144. package/front_end/third_party/web-vitals/README.chromium +1 -0
  145. package/front_end/ui/components/text_editor/config.ts +30 -1
  146. package/front_end/ui/components/tooltips/Tooltip.ts +18 -4
  147. package/front_end/ui/legacy/ContextMenu.ts +2 -2
  148. package/front_end/ui/legacy/GlassPane.ts +7 -3
  149. package/front_end/ui/legacy/ProgressIndicator.ts +29 -16
  150. package/front_end/ui/legacy/TabbedPane.ts +2 -2
  151. package/front_end/ui/legacy/Treeoutline.ts +10 -5
  152. package/front_end/ui/legacy/UIUtils.ts +42 -10
  153. package/front_end/ui/legacy/components/color_picker/Spectrum.ts +14 -14
  154. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +6 -6
  155. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +3 -29
  156. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +14 -14
  157. package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
  158. package/inspector_overlay/highlight_common.ts +1 -27
  159. package/inspector_overlay/highlight_grid_common.ts +1 -27
  160. package/inspector_overlay/tool_highlight.ts +1 -27
  161. package/inspector_overlay/tool_persistent.ts +1 -27
  162. package/inspector_overlay/tool_source_order.ts +1 -27
  163. package/package.json +1 -1
@@ -4,6 +4,7 @@
4
4
 
5
5
  import '../../../ui/components/icon_button/icon_button.js';
6
6
 
7
+ import * as Common from '../../../core/common/common.js';
7
8
  import * as Host from '../../../core/host/host.js';
8
9
  import * as i18n from '../../../core/i18n/i18n.js';
9
10
  import * as Platform from '../../../core/platform/platform.js';
@@ -11,7 +12,7 @@ import * as Root from '../../../core/root/root.js';
11
12
  import * as SDK from '../../../core/sdk/sdk.js';
12
13
  import * as Tracing from '../../../services/tracing/tracing.js';
13
14
  import * as Trace from '../../trace/trace.js';
14
- import type {ConversationType} from '../AiHistoryStorage.js';
15
+ import {ConversationType} from '../AiHistoryStorage.js';
15
16
  import {
16
17
  PerformanceInsightFormatter,
17
18
  TraceEventFormatter,
@@ -22,11 +23,10 @@ import {AICallTree} from '../performance/AICallTree.js';
22
23
  import {AgentFocus} from '../performance/AIContext.js';
23
24
 
24
25
  import {
25
- type AgentOptions,
26
26
  AiAgent,
27
27
  type ContextResponse,
28
28
  ConversationContext,
29
- type ConversationSuggestion,
29
+ type ConversationSuggestions,
30
30
  type ParsedResponse,
31
31
  type RequestOptions,
32
32
  type ResponseData,
@@ -38,7 +38,6 @@ const UIStringsNotTranslated = {
38
38
  *@description Shown when the agent is investigating a trace
39
39
  */
40
40
  analyzingTrace: 'Analyzing trace',
41
- analyzingCallTree: 'Analyzing call tree',
42
41
  /**
43
42
  * @description Shown when the agent is investigating network activity
44
43
  */
@@ -82,6 +81,7 @@ The 3 main performance metrics are:
82
81
 
83
82
  Trace events referenced in the information given to you will be marked with an \`eventKey\`. For example: \`LCP element: <img src="..."> (eventKey: r-123, ts: 123456)\`
84
83
  You can use this key with \`getEventByKey\` to get more information about that trace event. For example: \`getEventByKey('r-123')\`
84
+ You can also use this key with \`selectEventByKey\` to show the user a specific event
85
85
 
86
86
  ## Step-by-step instructions for debugging performance issues
87
87
 
@@ -173,6 +173,7 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
173
173
  }
174
174
 
175
175
  #focus: AgentFocus;
176
+ external = false;
176
177
 
177
178
  constructor(focus: AgentFocus) {
178
179
  super();
@@ -196,50 +197,74 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
196
197
  url = new URL(focus.parsedTrace.data.Meta.mainFrameURL);
197
198
  }
198
199
 
199
- return `Trace: ${url.hostname}`;
200
+ const parts = [`Trace: ${url.hostname}`];
201
+ if (focus.insight) {
202
+ parts.push(focus.insight.title);
203
+ }
204
+ if (focus.callTree) {
205
+ const node = focus.callTree.selectedNode ?? focus.callTree.rootNode;
206
+ parts.push(Trace.Name.forEntry(node.event));
207
+ }
208
+ return parts.join(' – ');
200
209
  }
201
210
 
202
211
  /**
203
212
  * Presents the default suggestions that are shown when the user first clicks
204
213
  * "Ask AI".
205
214
  */
206
- override async getSuggestions(): Promise<[ConversationSuggestion, ...ConversationSuggestion[]]|undefined> {
207
- const focus = this.#focus.data;
215
+ override async getSuggestions(): Promise<ConversationSuggestions|undefined> {
216
+ const data = this.#focus.data;
217
+
218
+ if (data.callTree) {
219
+ return [
220
+ {title: 'What\'s the purpose of this work?', jslogContext: 'performance-default'},
221
+ {title: 'Where is time being spent?', jslogContext: 'performance-default'},
222
+ {title: 'How can I optimize this?', jslogContext: 'performance-default'},
223
+ ];
224
+ }
208
225
 
209
- if (focus.type !== 'insight') {
210
- return;
226
+ if (data.insight) {
227
+ return new PerformanceInsightFormatter(data.parsedTrace, data.insight).getSuggestions();
228
+ }
229
+
230
+ const suggestions: ConversationSuggestions =
231
+ [{title: 'What performance issues exist with my page?', jslogContext: 'performance-default'}];
232
+
233
+ if (data.insightSet) {
234
+ const lcp = data.insightSet ? Trace.Insights.Common.getLCP(data.insightSet) : null;
235
+ const cls = data.insightSet ? Trace.Insights.Common.getCLS(data.insightSet) : null;
236
+ const inp = data.insightSet ? Trace.Insights.Common.getINP(data.insightSet) : null;
237
+
238
+ const ModelHandlers = Trace.Handlers.ModelHandlers;
239
+ const GOOD = Trace.Handlers.ModelHandlers.PageLoadMetrics.ScoreClassification.GOOD;
240
+
241
+ if (lcp && ModelHandlers.PageLoadMetrics.scoreClassificationForLargestContentfulPaint(lcp.value) !== GOOD) {
242
+ suggestions.push({title: 'How can I improve LCP?', jslogContext: 'performance-default'});
243
+ }
244
+ if (inp && ModelHandlers.UserInteractions.scoreClassificationForInteractionToNextPaint(inp.value) !== GOOD) {
245
+ suggestions.push({title: 'How can I improve INP?', jslogContext: 'performance-default'});
246
+ }
247
+ if (cls && ModelHandlers.LayoutShifts.scoreClassificationForLayoutShift(cls.value) !== GOOD) {
248
+ suggestions.push({title: 'How can I improve CLS?', jslogContext: 'performance-default'});
249
+ }
211
250
  }
212
251
 
213
- return new PerformanceInsightFormatter(focus.parsedTrace, focus.insight).getSuggestions();
252
+ return suggestions;
214
253
  }
215
254
  }
216
255
 
217
256
  // 16k Tokens * ~4 char per token.
218
257
  const MAX_FUNCTION_RESULT_BYTE_LENGTH = 16384 * 4;
219
258
 
220
- /**
221
- * Union of all the performance conversation types, which are all implemented by this file.
222
- * This temporary until all Performance Panel AI features use the "Full" type. go/chrome-devtools:more-powerful-performance-agent-design
223
- */
224
- type PerformanceConversationType =
225
- ConversationType.PERFORMANCE_FULL|ConversationType.PERFORMANCE_CALL_TREE|ConversationType.PERFORMANCE_INSIGHT;
226
-
227
259
  /**
228
260
  * One agent instance handles one conversation. Create a new agent
229
261
  * instance for a new conversation.
230
262
  */
231
263
  export class PerformanceAgent extends AiAgent<AgentFocus> {
232
- // TODO: would make more sense on AgentOptions
233
- #conversationType: PerformanceConversationType;
234
264
  #formatter: PerformanceTraceFormatter|null = null;
235
265
  #lastInsightForEnhancedQuery: Trace.Insights.Types.InsightModel|undefined;
236
266
  #eventsSerializer = new Trace.EventsSerializer.EventsSerializer();
237
- #lastFocusHandledForContextDetails: AgentFocus|null = null;
238
-
239
- constructor(opts: AgentOptions, conversationType: PerformanceConversationType) {
240
- super(opts);
241
- this.#conversationType = conversationType;
242
- }
267
+ #hasShownAnalyzeTraceContext = false;
243
268
 
244
269
  /**
245
270
  * Cache of all function calls made by the agent. This allows us to include (as a
@@ -285,7 +310,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
285
310
  }
286
311
 
287
312
  getConversationType(): ConversationType {
288
- return this.#conversationType;
313
+ return ConversationType.PERFORMANCE;
289
314
  }
290
315
 
291
316
  #lookupEvent(key: Trace.Types.File.SerializableKey): Trace.Types.Events.Event|null {
@@ -311,38 +336,22 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
311
336
  return;
312
337
  }
313
338
 
314
- const focus = context.getItem();
315
- if (this.#lastFocusHandledForContextDetails === focus) {
339
+ if (this.#hasShownAnalyzeTraceContext) {
316
340
  return;
317
341
  }
318
342
 
319
- this.#lastFocusHandledForContextDetails = focus;
343
+ yield {
344
+ type: ResponseType.CONTEXT,
345
+ title: lockedString(UIStringsNotTranslated.analyzingTrace),
346
+ details: [
347
+ {
348
+ title: 'Trace',
349
+ text: this.#formatter?.formatTraceSummary() ?? '',
350
+ },
351
+ ],
352
+ };
320
353
 
321
- if (focus.data.type === 'full' || focus.data.type === 'insight') {
322
- yield {
323
- type: ResponseType.CONTEXT,
324
- title: lockedString(UIStringsNotTranslated.analyzingTrace),
325
- details: [
326
- {
327
- title: 'Trace',
328
- text: this.#formatter?.formatTraceSummary() ?? '',
329
- },
330
- ],
331
- };
332
- } else if (focus.data.type === 'call-tree') {
333
- yield {
334
- type: ResponseType.CONTEXT,
335
- title: lockedString(UIStringsNotTranslated.analyzingCallTree),
336
- details: [
337
- {
338
- title: 'Selected call tree',
339
- text: focus.data.callTree.serialize(),
340
- },
341
- ],
342
- };
343
- } else {
344
- Platform.assertNever(focus.data, 'Unknown agent focus');
345
- }
354
+ this.#hasShownAnalyzeTraceContext = true;
346
355
  }
347
356
 
348
357
  #callTreeContextSet = new WeakSet();
@@ -368,7 +377,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
368
377
  return super.parseTextResponse(response);
369
378
  }
370
379
 
371
- override async enhanceQuery(query: string, context: ConversationContext<AgentFocus>|null): Promise<string> {
380
+ override async enhanceQuery(query: string, context: PerformanceTraceContext|null): Promise<string> {
372
381
  if (!context) {
373
382
  this.clearDeclaredFunctions();
374
383
  return query;
@@ -378,12 +387,9 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
378
387
  this.#declareFunctions(context);
379
388
 
380
389
  const focus = context.getItem();
390
+ const selected: string[] = [];
381
391
 
382
- if (focus.data.type === 'full') {
383
- return query;
384
- }
385
-
386
- if (focus.data.type === 'call-tree') {
392
+ if (focus.data.callTree) {
387
393
  // If this is a followup chat about the same call tree, don't include the call tree serialization again.
388
394
  // We don't need to repeat it and we'd rather have more the context window space.
389
395
  let contextString = '';
@@ -392,17 +398,12 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
392
398
  this.#callTreeContextSet.add(focus.data.callTree);
393
399
  }
394
400
 
395
- if (!contextString) {
396
- return query;
401
+ if (contextString) {
402
+ selected.push(`User selected the following call tree:\n\n${contextString}\n\n`);
397
403
  }
398
-
399
- let enhancedQuery = '';
400
- enhancedQuery += `User selected the following call tree:\n\n${contextString}\n\n`;
401
- enhancedQuery += `# User query\n\n${query}`;
402
- return enhancedQuery;
403
404
  }
404
405
 
405
- if (focus.data.type === 'insight') {
406
+ if (focus.data.insight) {
406
407
  // We only need to add Insight info to a prompt when the context changes. For example:
407
408
  // User clicks Insight A. We need to send info on Insight A with the prompt.
408
409
  // User asks follow up question. We do not need to resend Insight A with the prompt.
@@ -411,17 +412,17 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
411
412
  const includeInsightInfo = focus.data.insight !== this.#lastInsightForEnhancedQuery;
412
413
  this.#lastInsightForEnhancedQuery = focus.data.insight;
413
414
 
414
- if (!includeInsightInfo) {
415
- return query;
415
+ if (includeInsightInfo) {
416
+ selected.push(`User selected the ${focus.data.insight.insightKey} insight.\n\n`);
416
417
  }
418
+ }
417
419
 
418
- let enhancedQuery = '';
419
- enhancedQuery += `User selected the ${focus.data.insight.insightKey} insight.\n\n`;
420
- enhancedQuery += `# User query\n\n${query}`;
421
- return enhancedQuery;
420
+ if (!selected.length) {
421
+ return query;
422
422
  }
423
423
 
424
- Platform.assertNever(focus.data, 'Unknown agent focus');
424
+ selected.push(`# User query\n\n${query}`);
425
+ return selected.join('');
425
426
  }
426
427
 
427
428
  override async * run(initialQuery: string, options: {
@@ -552,7 +553,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
552
553
  this.#functionCallCacheForFocus.set(focus, cache);
553
554
  }
554
555
 
555
- #declareFunctions(context: ConversationContext<AgentFocus>): void {
556
+ #declareFunctions(context: PerformanceTraceContext): void {
556
557
  const focus = context.getItem();
557
558
  const {parsedTrace, insightSet} = focus.data;
558
559
 
@@ -840,5 +841,38 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
840
841
 
841
842
  });
842
843
  }
844
+
845
+ if (!context.external) {
846
+ this.declareFunction<{eventKey: string}, {success: boolean}>('selectEventByKey', {
847
+ description:
848
+ 'Selects the event in the flamechart for the user. If the user asks to show them something, it\'s likely a good idea to call this function.',
849
+ parameters: {
850
+ type: Host.AidaClient.ParametersTypes.OBJECT,
851
+ description: '',
852
+ nullable: false,
853
+ properties: {
854
+ eventKey: {
855
+ type: Host.AidaClient.ParametersTypes.STRING,
856
+ description: 'The key for the event.',
857
+ nullable: false,
858
+ }
859
+ },
860
+ },
861
+ displayInfoFromArgs: params => {
862
+ return {title: lockedString('Selecting event…'), action: `selectEventByKey('${params.eventKey}')`};
863
+ },
864
+ handler: async params => {
865
+ debugLog('Function call: selectEventByKey', params);
866
+ const event = this.#lookupEvent(params.eventKey as Trace.Types.File.SerializableKey);
867
+ if (!event) {
868
+ return {error: 'Invalid eventKey'};
869
+ }
870
+
871
+ const revealable = new SDK.TraceObject.RevealableEvent(event);
872
+ await Common.Revealer.reveal(revealable);
873
+ return {result: {success: true}};
874
+ },
875
+ });
876
+ }
843
877
  }
844
878
  }
@@ -116,7 +116,7 @@ export class PerformanceAnnotationsAgent extends AiAgent<AgentFocus> {
116
116
  }
117
117
 
118
118
  const focus = context.getItem();
119
- if (focus.data.type !== 'call-tree') {
119
+ if (!focus.data.callTree) {
120
120
  throw new Error('unexpected context');
121
121
  }
122
122
 
@@ -140,7 +140,7 @@ export class PerformanceAnnotationsAgent extends AiAgent<AgentFocus> {
140
140
  }
141
141
 
142
142
  const focus = context.getItem();
143
- if (focus.data.type !== 'call-tree') {
143
+ if (!focus.data.callTree) {
144
144
  throw new Error('unexpected context');
145
145
  }
146
146
 
@@ -18,7 +18,7 @@ import {
18
18
  AiAgent,
19
19
  type ContextResponse,
20
20
  ConversationContext,
21
- type ConversationSuggestion,
21
+ type ConversationSuggestions,
22
22
  type FunctionCallHandlerResult,
23
23
  MultimodalInputType,
24
24
  type ParsedResponse,
@@ -182,7 +182,7 @@ export class NodeContext extends ConversationContext<SDK.DOMModel.DOMNode> {
182
182
  throw new Error('Not implemented');
183
183
  }
184
184
 
185
- override async getSuggestions(): Promise<[ConversationSuggestion, ...ConversationSuggestion[]]|undefined> {
185
+ override async getSuggestions(): Promise<ConversationSuggestions|undefined> {
186
186
  const layoutProps = await this.#node.domModel().cssModel().getLayoutPropertiesFromComputedStyle(this.#node.id);
187
187
 
188
188
  if (!layoutProps) {