chrome-devtools-frontend 1.0.1519267 → 1.0.1520535

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 (94) hide show
  1. package/config/owner/COMMON_OWNERS +1 -2
  2. package/config/typescript/tsconfig.eslint.json +12 -1
  3. package/docs/ui_engineering.md +1011 -0
  4. package/front_end/core/host/GdpClient.ts +26 -5
  5. package/front_end/core/sdk/NetworkManager.ts +1 -0
  6. package/front_end/core/sdk/NetworkRequest.ts +10 -0
  7. package/front_end/entrypoints/main/MainImpl.ts +6 -1
  8. package/front_end/entrypoints/main/main-meta.ts +3 -3
  9. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +50 -48
  10. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
  11. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +128 -30
  12. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +98 -63
  13. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
  14. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +36 -21
  15. package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
  16. package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
  17. package/front_end/models/ai_assistance/performance/AIContext.ts +62 -7
  18. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -5
  19. package/front_end/models/badges/Badge.ts +6 -1
  20. package/front_end/models/badges/StarterBadge.ts +5 -0
  21. package/front_end/models/badges/UserBadges.ts +5 -4
  22. package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
  23. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -7
  24. package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
  25. package/front_end/panels/ai_assistance/components/ChatView.ts +45 -69
  26. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +47 -1
  27. package/front_end/panels/ai_assistance/components/chatView.css +13 -1
  28. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  29. package/front_end/panels/animation/animationTimeline.css +4 -0
  30. package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
  31. package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
  32. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
  33. package/front_end/panels/console/ConsolePrompt.ts +6 -0
  34. package/front_end/panels/console/ConsoleView.ts +4 -2
  35. package/front_end/panels/coverage/CoverageListView.ts +146 -198
  36. package/front_end/panels/coverage/CoverageView.ts +48 -18
  37. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
  38. package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
  39. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
  40. package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
  41. package/front_end/panels/search/SearchResultsPane.ts +48 -15
  42. package/front_end/panels/search/SearchView.ts +33 -30
  43. package/front_end/panels/search/searchView.css +0 -2
  44. package/front_end/panels/settings/components/SyncSection.ts +4 -4
  45. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +1 -4
  46. package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
  47. package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
  48. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
  49. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
  50. package/front_end/panels/timeline/TimelinePanel.ts +2 -0
  51. package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
  52. package/front_end/panels/timeline/components/ExportTraceOptions.ts +56 -4
  53. package/front_end/panels/timeline/components/exportTraceOptions.css +5 -0
  54. package/front_end/third_party/chromium/README.chromium +1 -1
  55. package/front_end/third_party/lighthouse/README.chromium +8 -1
  56. package/front_end/third_party/puppeteer/README.chromium +2 -2
  57. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  67. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  68. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  71. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
  77. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
  78. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  80. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  81. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +1 -1
  82. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
  83. package/front_end/third_party/puppeteer/package/package.json +3 -2
  84. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  85. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
  86. package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
  87. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  88. package/front_end/third_party/puppeteer/package/src/util/Function.ts +1 -1
  89. package/front_end/tsconfig.json +12 -1
  90. package/front_end/ui/legacy/InspectorView.ts +86 -13
  91. package/front_end/ui/legacy/TabbedPane.ts +2 -1
  92. package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
  93. package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
  94. package/package.json +1 -1
@@ -23,7 +23,7 @@ export enum SubscriptionTier {
23
23
  PRO_MONTHLY = 'SUBSCRIPTION_TIER_PRO_MONTHLY',
24
24
  }
25
25
 
26
- enum EligibilityStatus {
26
+ export enum EligibilityStatus {
27
27
  ELIGIBLE = 'ELIGIBLE',
28
28
  NOT_ELIGIBLE = 'NOT_ELIGIBLE',
29
29
  }
@@ -68,6 +68,11 @@ export interface Profile {
68
68
  };
69
69
  }
70
70
 
71
+ interface InitializeResult {
72
+ hasProfile: boolean;
73
+ isEligible: boolean;
74
+ }
75
+
71
76
  // The `batchGet` awards endpoint returns badge names with an
72
77
  // obfuscated user ID (e.g., `profiles/12345/awards/badge-name`).
73
78
  // This function normalizes them to use `me` instead of the ID
@@ -114,9 +119,20 @@ export class GdpClient {
114
119
  return gdpClientInstance;
115
120
  }
116
121
 
117
- async initialize(): Promise<void> {
118
- void this.getProfile();
119
- void this.checkEligibility();
122
+ async initialize(): Promise<InitializeResult> {
123
+ const profile = await this.getProfile();
124
+ if (profile) {
125
+ return {
126
+ hasProfile: true,
127
+ isEligible: true,
128
+ };
129
+ }
130
+
131
+ const isEligible = await this.isEligibleToCreateProfile();
132
+ return {
133
+ hasProfile: false,
134
+ isEligible,
135
+ };
120
136
  }
121
137
 
122
138
  async getProfile(): Promise<Profile|null> {
@@ -129,7 +145,12 @@ export class GdpClient {
129
145
  path: '/v1beta1/profile:get',
130
146
  method: 'GET',
131
147
  });
132
- return await this.#cachedProfilePromise;
148
+
149
+ const profile = await this.#cachedProfilePromise;
150
+ if (profile) {
151
+ this.#cachedEligibilityPromise = Promise.resolve({createProfile: EligibilityStatus.ELIGIBLE});
152
+ }
153
+ return profile;
133
154
  }
134
155
 
135
156
  async checkEligibility(): Promise<CheckElibigilityResponse|null> {
@@ -597,6 +597,7 @@ export class NetworkDispatcher implements ProtocolProxyApi.NetworkDispatcher {
597
597
  networkRequest.mixedContentType = request.mixedContentType || Protocol.Security.MixedContentType.None;
598
598
  networkRequest.setReferrerPolicy(request.referrerPolicy);
599
599
  networkRequest.setIsSameSite(request.isSameSite || false);
600
+ networkRequest.setIsAdRelated(request.isAdRelated || false);
600
601
  }
601
602
 
602
603
  private updateNetworkRequestWithResponse(networkRequest: NetworkRequest, response: Protocol.Network.Response): void {
@@ -320,6 +320,7 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
320
320
  directSocketInfo?: DirectSocketInfo;
321
321
  readonly #directSocketChunks: DirectSocketChunk[] = [];
322
322
  #isIpProtectionUsed: boolean;
323
+ #isAdRelated: boolean;
323
324
 
324
325
  constructor(
325
326
  requestId: string,
@@ -342,6 +343,7 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
342
343
  this.#initiator = initiator;
343
344
  this.#hasUserGesture = hasUserGesture;
344
345
  this.#isIpProtectionUsed = false;
346
+ this.#isAdRelated = false;
345
347
  }
346
348
 
347
349
  static create(
@@ -1850,6 +1852,14 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
1850
1852
  return this.#isIpProtectionUsed;
1851
1853
  }
1852
1854
 
1855
+ setIsAdRelated(isAdRelated: boolean): void {
1856
+ this.#isAdRelated = isAdRelated;
1857
+ }
1858
+
1859
+ isAdRelated(): boolean {
1860
+ return this.#isAdRelated;
1861
+ }
1862
+
1853
1863
  getAssociatedData(key: string): object|null {
1854
1864
  return this.#associatedData.get(key) || null;
1855
1865
  }
@@ -519,7 +519,12 @@ export class MainImpl {
519
519
 
520
520
  // Initialize `GDPClient` and `UserBadges` for Google Developer Program integration
521
521
  if (Host.GdpClient.isGdpProfilesAvailable()) {
522
- void Host.GdpClient.GdpClient.instance().initialize();
522
+ void Host.GdpClient.GdpClient.instance().initialize().then(({hasProfile, isEligible}) => {
523
+ const contextString = hasProfile ? 'has-profile' :
524
+ isEligible ? 'no-profile-and-eligible' :
525
+ 'no-profile-and-not-eligible';
526
+ void VisualLogging.logFunctionCall('gdp-client-initialize', contextString);
527
+ });
523
528
  void Badges.UserBadges.instance().initialize();
524
529
  Badges.UserBadges.instance().addEventListener(Badges.Events.BADGE_TRIGGERED, async ev => {
525
530
  loadedPanelCommonModule ??= await import('../../panels/common/common.js') as typeof PanelCommon;
@@ -188,9 +188,9 @@ const UIStrings = {
188
188
  browserLanguage: 'Browser UI language',
189
189
  /**
190
190
  * @description Label for a checkbox in the settings UI. Allows developers to opt-in/opt-out
191
- * of syncing DevTools settings via Chrome Sync.
191
+ * of saving settings to their Google account.
192
192
  */
193
- enableSync: 'Enable settings sync',
193
+ saveSettings: 'Save `DevTools` settings to your `Google` account',
194
194
  /**
195
195
  * @description Label for a checkbox in the settings UI. Allows developers to opt-in/opt-out
196
196
  * of receiving Google Developer Program (GDP) badges based on their activity in Chrome DevTools.
@@ -789,7 +789,7 @@ Common.Settings.registerSettingExtension({
789
789
  // This name must be kept in sync with DevToolsSettings::kSyncDevToolsPreferencesFrontendName.
790
790
  settingName: 'sync-preferences',
791
791
  settingType: Common.Settings.SettingType.BOOLEAN,
792
- title: i18nLazyString(UIStrings.enableSync),
792
+ title: i18nLazyString(UIStrings.saveSettings),
793
793
  defaultValue: false,
794
794
  reloadRequired: true,
795
795
  });
@@ -136,27 +136,13 @@ When referring to a trace event that has a corresponding \`eventKey\`, annotate
136
136
  When asking the user to make a choice between multiple options, output a list of choices at the end of your text response. The format is \`SUGGESTIONS: ["suggestion1", "suggestion2", "suggestion3"]\`. This MUST start on a newline, and be a single line.
137
137
  `;
138
138
 
139
- const callFrameDataFormatDescription = `Each call frame is presented in the following format:
139
+ const extraPreambleWhenFreshTrace = `Additional notes:
140
140
 
141
- 'id;name;duration;selfTime;urlIndex;childRange;[S]'
142
-
143
- Key definitions:
144
-
145
- * id: A unique numerical identifier for the call frame. Never mention this id in the output to the user.
146
- * name: A concise string describing the call frame (e.g., 'Evaluate Script', 'render', 'fetchData').
147
- * duration: The total execution time of the call frame, including its children.
148
- * selfTime: The time spent directly within the call frame, excluding its children's execution.
149
- * urlIndex: Index referencing the "All URLs" list. Empty if no specific script URL is associated.
150
- * 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.
151
- * S: _Optional_. The letter 'S' terminates the line if that call frame was selected by the user.
152
-
153
- Example Call Tree:
154
-
155
- 1;main;500;100;;
156
- 2;update;200;50;;3
157
- 3;animate;150;20;0;4-5;S
158
- 4;calculatePosition;80;80;;
159
- 5;applyStyles;50;50;;
141
+ When referring to an element for which you know the nodeId, annotate your output using markdown link syntax:
142
+ - For example, if nodeId is 23: [LCP element](#node-23)
143
+ - This link will reveal the element in the Elements panel
144
+ - Never mention node or nodeId when referring to the element, and especially not in the link text.
145
+ - When referring to the LCP, it's useful to also mention what the LCP element is via its nodeId. Use the markdown link syntax to do so.
160
146
  `;
161
147
 
162
148
  enum ScorePriority {
@@ -188,7 +174,7 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
188
174
  }
189
175
 
190
176
  override getOrigin(): string {
191
- const {min, max} = this.#focus.data.parsedTrace.data.Meta.traceBounds;
177
+ const {min, max} = this.#focus.parsedTrace.data.Meta.traceBounds;
192
178
  return `trace-${min}-${max}`;
193
179
  }
194
180
 
@@ -197,7 +183,7 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
197
183
  }
198
184
 
199
185
  override getTitle(): string {
200
- const focus = this.#focus.data;
186
+ const focus = this.#focus;
201
187
 
202
188
  let url = focus.insightSet?.url;
203
189
  if (!url) {
@@ -208,6 +194,9 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
208
194
  if (focus.insight) {
209
195
  parts.push(focus.insight.title);
210
196
  }
197
+ if (focus.event) {
198
+ parts.push(Trace.Name.forEntry(focus.event));
199
+ }
211
200
  if (focus.callTree) {
212
201
  const node = focus.callTree.selectedNode ?? focus.callTree.rootNode;
213
202
  parts.push(Trace.Name.forEntry(node.event));
@@ -220,9 +209,9 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
220
209
  * "Ask AI".
221
210
  */
222
211
  override async getSuggestions(): Promise<ConversationSuggestions|undefined> {
223
- const data = this.#focus.data;
212
+ const focus = this.#focus;
224
213
 
225
- if (data.callTree) {
214
+ if (focus.callTree) {
226
215
  return [
227
216
  {title: 'What\'s the purpose of this work?', jslogContext: 'performance-default'},
228
217
  {title: 'Where is time being spent?', jslogContext: 'performance-default'},
@@ -230,17 +219,17 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
230
219
  ];
231
220
  }
232
221
 
233
- if (data.insight) {
234
- return new PerformanceInsightFormatter(this.#focus, data.insight).getSuggestions();
222
+ if (focus.insight) {
223
+ return new PerformanceInsightFormatter(focus, focus.insight).getSuggestions();
235
224
  }
236
225
 
237
226
  const suggestions: ConversationSuggestions =
238
227
  [{title: 'What performance issues exist with my page?', jslogContext: 'performance-default'}];
239
228
 
240
- if (data.insightSet) {
241
- const lcp = data.insightSet ? Trace.Insights.Common.getLCP(data.insightSet) : null;
242
- const cls = data.insightSet ? Trace.Insights.Common.getCLS(data.insightSet) : null;
243
- const inp = data.insightSet ? Trace.Insights.Common.getINP(data.insightSet) : null;
229
+ if (focus.insightSet) {
230
+ const lcp = focus.insightSet ? Trace.Insights.Common.getLCP(focus.insightSet) : null;
231
+ const cls = focus.insightSet ? Trace.Insights.Common.getCLS(focus.insightSet) : null;
232
+ const inp = focus.insightSet ? Trace.Insights.Common.getINP(focus.insightSet) : null;
244
233
 
245
234
  const ModelHandlers = Trace.Handlers.ModelHandlers;
246
235
  const GOOD = Trace.Handlers.ModelHandlers.PageLoadMetrics.ScoreClassification.GOOD;
@@ -257,9 +246,9 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
257
246
 
258
247
  // Add up to 3 suggestions from the top failing insights.
259
248
  const top3FailingInsightSuggestions =
260
- Object.values(data.insightSet.model)
249
+ Object.values(focus.insightSet.model)
261
250
  .filter(model => model.state !== 'pass')
262
- .map(model => new PerformanceInsightFormatter(this.#focus, model).getSuggestions().at(-1))
251
+ .map(model => new PerformanceInsightFormatter(focus, model).getSuggestions().at(-1))
263
252
  .filter(suggestion => !!suggestion)
264
253
  .slice(0, 3);
265
254
  suggestions.push(...top3FailingInsightSuggestions);
@@ -278,6 +267,7 @@ const MAX_FUNCTION_RESULT_BYTE_LENGTH = 16384 * 4;
278
267
  */
279
268
  export class PerformanceAgent extends AiAgent<AgentFocus> {
280
269
  #formatter: PerformanceTraceFormatter|null = null;
270
+ #lastEventForEnhancedQuery: Trace.Types.Events.Event|undefined;
281
271
  #lastInsightForEnhancedQuery: Trace.Insights.Types.InsightModel|undefined;
282
272
  #hasShownAnalyzeTraceContext = false;
283
273
 
@@ -298,12 +288,16 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
298
288
  text: extraPreambleWhenNotExternal,
299
289
  metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
300
290
  };
291
+ #freshTraceExtraPreambleFact: Host.AidaClient.RequestFact = {
292
+ text: extraPreambleWhenFreshTrace,
293
+ metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
294
+ };
301
295
  #networkDataDescriptionFact: Host.AidaClient.RequestFact = {
302
296
  text: PerformanceTraceFormatter.networkDataFormatDescription,
303
297
  metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
304
298
  };
305
299
  #callFrameDataDescriptionFact: Host.AidaClient.RequestFact = {
306
- text: callFrameDataFormatDescription,
300
+ text: PerformanceTraceFormatter.callFrameDataFormatDescription,
307
301
  metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
308
302
  };
309
303
  #traceFacts: Host.AidaClient.RequestFact[] = [];
@@ -371,10 +365,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
371
365
  * 1. go to paulirish.com, record a trace
372
366
  * 2. say "What performance issues exist with my page?"
373
367
  * 3. then say "images"
374
- *
375
- * TODO(cjamcl): reduce the reliance on this by making sure all URL references
376
- * (such as the insight formatters) add the "eventKey" as a suffix, just like all
377
- * other events.
378
368
  */
379
369
  #parseForKnownUrls(response: string): string {
380
370
  const focus = this.context?.getItem();
@@ -403,8 +393,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
403
393
  return match;
404
394
  }
405
395
 
406
- const request =
407
- focus.data.parsedTrace.data.NetworkRequests.byTime.find(request => request.args.data.url === urlText);
396
+ const request = focus.parsedTrace.data.NetworkRequests.byTime.find(request => request.args.data.url === urlText);
408
397
  if (!request) {
409
398
  return match;
410
399
  }
@@ -451,13 +440,21 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
451
440
  const focus = context.getItem();
452
441
  const selected: string[] = [];
453
442
 
454
- if (focus.data.callTree) {
443
+ if (focus.event) {
444
+ const includeEventInfo = focus.event !== this.#lastEventForEnhancedQuery;
445
+ this.#lastEventForEnhancedQuery = focus.event;
446
+ if (includeEventInfo) {
447
+ selected.push(`User selected an event ${this.#formatter?.serializeEvent(focus.event)}.\n\n`);
448
+ }
449
+ }
450
+
451
+ if (focus.callTree) {
455
452
  // If this is a followup chat about the same call tree, don't include the call tree serialization again.
456
453
  // We don't need to repeat it and we'd rather have more the context window space.
457
454
  let contextString = '';
458
- if (!this.#callTreeContextSet.has(focus.data.callTree)) {
459
- contextString = focus.data.callTree.serialize();
460
- this.#callTreeContextSet.add(focus.data.callTree);
455
+ if (!this.#callTreeContextSet.has(focus.callTree)) {
456
+ contextString = focus.callTree.serialize();
457
+ this.#callTreeContextSet.add(focus.callTree);
461
458
  }
462
459
 
463
460
  if (contextString) {
@@ -465,17 +462,17 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
465
462
  }
466
463
  }
467
464
 
468
- if (focus.data.insight) {
465
+ if (focus.insight) {
469
466
  // We only need to add Insight info to a prompt when the context changes. For example:
470
467
  // User clicks Insight A. We need to send info on Insight A with the prompt.
471
468
  // User asks follow up question. We do not need to resend Insight A with the prompt.
472
469
  // User clicks Insight B. We now need to send info on Insight B with the prompt.
473
470
  // User clicks Insight A. We should resend the Insight info with the prompt.
474
- const includeInsightInfo = focus.data.insight !== this.#lastInsightForEnhancedQuery;
475
- this.#lastInsightForEnhancedQuery = focus.data.insight;
471
+ const includeInsightInfo = focus.insight !== this.#lastInsightForEnhancedQuery;
472
+ this.#lastInsightForEnhancedQuery = focus.insight;
476
473
 
477
474
  if (includeInsightInfo) {
478
- selected.push(`User selected the ${focus.data.insight.insightKey} insight.\n\n`);
475
+ selected.push(`User selected the ${focus.insight.insightKey} insight.\n\n`);
479
476
  }
480
477
  }
481
478
 
@@ -587,6 +584,11 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
587
584
  this.addFact(this.#notExternalExtraPreambleFact);
588
585
  }
589
586
 
587
+ const isFresh = Tracing.FreshRecording.Tracker.instance().recordingIsFresh(focus.parsedTrace);
588
+ if (isFresh) {
589
+ this.addFact(this.#freshTraceExtraPreambleFact);
590
+ }
591
+
590
592
  this.addFact(this.#callFrameDataDescriptionFact);
591
593
  this.addFact(this.#networkDataDescriptionFact);
592
594
 
@@ -623,7 +625,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
623
625
 
624
626
  #declareFunctions(context: PerformanceTraceContext): void {
625
627
  const focus = context.getItem();
626
- const {parsedTrace, insightSet} = focus.data;
628
+ const {parsedTrace, insightSet} = focus;
627
629
 
628
630
  this.declareFunction<{insightName: string}, {details: string}>('getInsightDetails', {
629
631
  description:
@@ -116,11 +116,11 @@ export class PerformanceAnnotationsAgent extends AiAgent<AgentFocus> {
116
116
  }
117
117
 
118
118
  const focus = context.getItem();
119
- if (!focus.data.callTree) {
119
+ if (!focus.callTree) {
120
120
  throw new Error('unexpected context');
121
121
  }
122
122
 
123
- const callTree = focus.data.callTree;
123
+ const callTree = focus.callTree;
124
124
 
125
125
  yield {
126
126
  type: ResponseType.CONTEXT,
@@ -140,11 +140,11 @@ export class PerformanceAnnotationsAgent extends AiAgent<AgentFocus> {
140
140
  }
141
141
 
142
142
  const focus = context.getItem();
143
- if (!focus.data.callTree) {
143
+ if (!focus.callTree) {
144
144
  throw new Error('unexpected context');
145
145
  }
146
146
 
147
- const callTree = focus.data.callTree;
147
+ const callTree = focus.callTree;
148
148
  const contextString = callTree.serialize();
149
149
  return `${contextString}\n\n# User request\n\n${query}`;
150
150
  }