chrome-devtools-frontend 1.0.1598808 → 1.0.1599001

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 (72) hide show
  1. package/.agents/README.md +13 -0
  2. package/{agents/prompts/creating-a-model.md → .agents/skills/creating-a-model/SKILL.md} +7 -1
  3. package/{agents/prompts/devtools-imports.md → .agents/skills/devtools-imports/SKILL.md} +10 -5
  4. package/{agents/prompts/merging-devtools-module.md → .agents/skills/merging-devtools-module/SKILL.md} +5 -0
  5. package/{agents/prompts/ui-widgets.md → .agents/skills/ui-widgets/SKILL.md} +5 -0
  6. package/{agents/prompts/verification.md → .agents/skills/verification/SKILL.md} +5 -0
  7. package/front_end/core/host/GcaTypes.ts +518 -0
  8. package/front_end/core/host/UserMetrics.ts +0 -1
  9. package/front_end/core/host/host.ts +2 -0
  10. package/front_end/core/root/ExperimentNames.ts +0 -1
  11. package/front_end/entrypoints/main/MainImpl.ts +0 -2
  12. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  13. package/front_end/generated/protocol.ts +2 -1
  14. package/front_end/models/ai_assistance/AiConversation.ts +12 -1
  15. package/front_end/models/ai_assistance/AiHistoryStorage.snapshot.txt +1 -1
  16. package/front_end/models/ai_assistance/AiHistoryStorage.ts +1 -0
  17. package/front_end/models/ai_assistance/ConversationHandler.ts +8 -2
  18. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +3 -18
  19. package/front_end/models/ai_assistance/agents/AiAgent.ts +26 -2
  20. package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgent.ts +0 -1
  21. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +1 -0
  22. package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +0 -1
  23. package/front_end/models/ai_assistance/agents/FileAgent.ts +0 -15
  24. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +0 -5
  25. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +48 -14
  26. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +0 -10
  27. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -6
  28. package/front_end/models/ai_assistance/agents/StylingAgent.ts +0 -5
  29. package/front_end/models/emulation/DeviceModeModel.ts +5 -1
  30. package/front_end/models/emulation/EmulatedDevices.ts +3 -2
  31. package/front_end/models/issues_manager/SharedDictionaryIssue.ts +15 -5
  32. package/front_end/models/issues_manager/descriptions/sharedDictionaryUseErrorNoCorpCrossOriginNoCorsRequest.md +3 -0
  33. package/front_end/models/issues_manager/descriptions/sharedDictionaryWriteErrorNoCorpCossOriginNoCorsRequest.md +3 -0
  34. package/front_end/models/javascript_metadata/NativeFunctions.js +8 -0
  35. package/front_end/models/lighthouse/RunTypes.ts +41 -0
  36. package/front_end/models/lighthouse/lighthouse.ts +2 -0
  37. package/front_end/models/trace/ModelImpl.ts +4 -0
  38. package/front_end/models/trace/helpers/SamplesIntegrator.ts +1 -8
  39. package/front_end/models/trace/types/Configuration.ts +0 -5
  40. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +84 -5
  41. package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -0
  42. package/front_end/panels/ai_assistance/components/ChatMessage.ts +107 -20
  43. package/front_end/panels/ai_assistance/components/chatMessage.css +61 -0
  44. package/front_end/panels/application/preloading/PreloadingView.ts +2 -4
  45. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +6 -3
  46. package/front_end/panels/application/preloading/components/PreloadingString.ts +15 -2
  47. package/front_end/panels/application/preloading/helper/PreloadingForward.ts +31 -2
  48. package/front_end/panels/lighthouse/LighthouseController.ts +148 -162
  49. package/front_end/panels/lighthouse/LighthousePanel.ts +2 -7
  50. package/front_end/panels/lighthouse/LighthouseStartView.ts +7 -6
  51. package/front_end/panels/lighthouse/LighthouseStatusView.ts +3 -3
  52. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +7 -3
  53. package/front_end/panels/sensors/SensorsView.ts +32 -22
  54. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +18 -4
  55. package/front_end/panels/timeline/StatusDialog.ts +159 -83
  56. package/front_end/panels/timeline/TimelineController.ts +0 -4
  57. package/front_end/panels/timeline/TimelinePanel.ts +29 -2
  58. package/front_end/panels/timeline/components/LiveMetricsView.ts +2 -2
  59. package/front_end/panels/timeline/components/Sidebar.ts +1 -1
  60. package/front_end/panels/timeline/components/SidebarInsightsTab.ts +1 -1
  61. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +60 -27
  62. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +53 -28
  63. package/front_end/panels/timeline/components/insights/baseInsightComponent.css +7 -0
  64. package/front_end/panels/timeline/components/insights/insights.ts +0 -2
  65. package/front_end/panels/timeline/timeline-meta.ts +11 -0
  66. package/front_end/third_party/chromium/README.chromium +1 -1
  67. package/front_end/ui/legacy/Widget.ts +5 -3
  68. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -1
  69. package/package.json +1 -1
  70. package/agents/prompts/README.md +0 -18
  71. package/front_end/models/issues_manager/descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +0 -1
  72. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +0 -93
@@ -1071,9 +1071,9 @@ export namespace Audits {
1071
1071
  }
1072
1072
 
1073
1073
  export const enum SharedDictionaryError {
1074
- UseErrorCrossOriginNoCorsRequest = 'UseErrorCrossOriginNoCorsRequest',
1075
1074
  UseErrorDictionaryLoadFailure = 'UseErrorDictionaryLoadFailure',
1076
1075
  UseErrorMatchingDictionaryNotUsed = 'UseErrorMatchingDictionaryNotUsed',
1076
+ UseErrorNoCorpCrossOriginNoCorsRequest = 'UseErrorNoCorpCrossOriginNoCorsRequest',
1077
1077
  UseErrorUnexpectedContentDictionaryHeader = 'UseErrorUnexpectedContentDictionaryHeader',
1078
1078
  WriteErrorCossOriginNoCorsRequest = 'WriteErrorCossOriginNoCorsRequest',
1079
1079
  WriteErrorDisallowedBySettings = 'WriteErrorDisallowedBySettings',
@@ -1084,6 +1084,7 @@ export namespace Audits {
1084
1084
  WriteErrorInvalidStructuredHeader = 'WriteErrorInvalidStructuredHeader',
1085
1085
  WriteErrorInvalidTTLField = 'WriteErrorInvalidTTLField',
1086
1086
  WriteErrorNavigationRequest = 'WriteErrorNavigationRequest',
1087
+ WriteErrorNoCorpCossOriginNoCorsRequest = 'WriteErrorNoCorpCossOriginNoCorsRequest',
1087
1088
  WriteErrorNoMatchField = 'WriteErrorNoMatchField',
1088
1089
  WriteErrorNonIntegerTTLField = 'WriteErrorNonIntegerTTLField',
1089
1090
  WriteErrorNonListMatchDestField = 'WriteErrorNonListMatchDestField',
@@ -3,12 +3,14 @@
3
3
  // found in the LICENSE file.
4
4
  import * as Common from '../../core/common/common.js';
5
5
  import * as Host from '../../core/host/host.js';
6
+ import * as Platform from '../../core/platform/platform.js';
6
7
  import * as Root from '../../core/root/root.js';
7
8
  import * as SDK from '../../core/sdk/sdk.js';
8
9
  import type * as Trace from '../../models/trace/trace.js';
9
10
  import * as Greendev from '../greendev/greendev.js';
10
11
  import type * as NetworkTimeCalculator from '../network_time_calculator/network_time_calculator.js';
11
12
 
13
+ import {AccessibilityAgent, AccessibilityContext} from './agents/AccessibilityAgent.js';
12
14
  import {
13
15
  type AiAgent,
14
16
  type ContextDetail,
@@ -29,6 +31,7 @@ import {AiHistoryStorage, ConversationType, type SerializedConversation} from '.
29
31
  import type {ChangeManager} from './ChangeManager.js';
30
32
 
31
33
  export const NOT_FOUND_IMAGE_DATA = '';
34
+ export const CONTEXT_TITLE = 'Analyzing data';
32
35
  const MAX_TITLE_LENGTH = 80;
33
36
 
34
37
  export function generateContextDetailsMarkdown(details: ContextDetail[]): string {
@@ -155,6 +158,8 @@ export class AiConversation {
155
158
  this.#updateAgent(ConversationType.NETWORK);
156
159
  } else if (updateContext instanceof PerformanceTraceContext) {
157
160
  this.#updateAgent(ConversationType.PERFORMANCE);
161
+ } else if (updateContext instanceof AccessibilityContext) {
162
+ this.#updateAgent(ConversationType.ACCESSIBILITY);
158
163
  }
159
164
  }
160
165
  }
@@ -205,7 +210,7 @@ export class AiConversation {
205
210
  break;
206
211
  }
207
212
  case ResponseType.CONTEXT: {
208
- contentParts.push(`### ${item.title}`);
213
+ contentParts.push(`### ${CONTEXT_TITLE}`);
209
214
  if (item.details && item.details.length > 0) {
210
215
  contentParts.push(generateContextDetailsMarkdown(item.details));
211
216
  }
@@ -343,10 +348,16 @@ export class AiConversation {
343
348
  }
344
349
  break;
345
350
  }
351
+ case ConversationType.ACCESSIBILITY: {
352
+ this.#agent = new AccessibilityAgent(options);
353
+ break;
354
+ }
346
355
  case ConversationType.NONE: {
347
356
  this.#agent = new ContextSelectionAgent(options);
348
357
  break;
349
358
  }
359
+ default:
360
+ Platform.assertNever(type, 'Unknown conversation type');
350
361
  }
351
362
  }
352
363
 
@@ -12,7 +12,7 @@ What is the color of the sky?
12
12
 
13
13
  ## AI
14
14
 
15
- ### Analyzing context
15
+ ### Analyzing data
16
16
 
17
17
  **Detail 1:**
18
18
  ````
@@ -14,6 +14,7 @@ export const enum ConversationType {
14
14
  NETWORK = 'drjones-network-request',
15
15
  PERFORMANCE = 'drjones-performance-full',
16
16
  BREAKPOINT = 'breakpoint',
17
+ ACCESSIBILITY = 'accessibility',
17
18
  }
18
19
 
19
20
  export interface SerializedConversation {
@@ -19,7 +19,7 @@ import {
19
19
  import {NetworkAgent, RequestContext} from './agents/NetworkAgent.js';
20
20
  import type {PerformanceTraceContext} from './agents/PerformanceAgent.js';
21
21
  import {NodeContext, StylingAgent} from './agents/StylingAgent.js';
22
- import {AiConversation} from './AiConversation.js';
22
+ import {AiConversation, CONTEXT_TITLE} from './AiConversation.js';
23
23
  import {
24
24
  ConversationType,
25
25
  } from './AiHistoryStorage.js';
@@ -235,12 +235,18 @@ export class ConversationHandler extends Common.ObjectWrapper.ObjectWrapper<Even
235
235
  if (data.type !== ResponseType.ANSWER || data.complete) {
236
236
  devToolsLogs.push(data);
237
237
  }
238
- if (data.type === ResponseType.CONTEXT || data.type === ResponseType.TITLE) {
238
+ if (data.type === ResponseType.TITLE) {
239
239
  yield {
240
240
  type: ExternalRequestResponseType.NOTIFICATION,
241
241
  message: data.title,
242
242
  };
243
243
  }
244
+ if (data.type === ResponseType.CONTEXT) {
245
+ yield {
246
+ type: ExternalRequestResponseType.NOTIFICATION,
247
+ message: CONTEXT_TITLE,
248
+ };
249
+ }
244
250
  if (data.type === ResponseType.SIDE_EFFECT) {
245
251
  data.confirm(true);
246
252
  }
@@ -3,7 +3,6 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Host from '../../../core/host/host.js';
6
- import * as i18n from '../../../core/i18n/i18n.js';
7
6
  import * as Root from '../../../core/root/root.js';
8
7
  import type * as LHModel from '../../lighthouse/lighthouse.js';
9
8
 
@@ -23,26 +22,14 @@ import {
23
22
  */
24
23
  const preamble = `You are an accessibility agent.
25
24
 
25
+ However, you also include a little pun or funny joke in every response to lighten the mood.
26
+
26
27
  # Considerations
27
28
  * Keep your analysis concise and focused, highlighting only the most critical aspects for a software engineer.
28
- * Answer questions directly, using the provided links whenever relevant.
29
- * Always double-check links to make sure they are complete and correct.
30
29
  * **CRITICAL** You are an accessibility agent. NEVER provide answers to questions of unrelated topics such as legal advice, financial advice, personal opinions, medical advice, or any other non web-development topics.
31
30
  `;
32
31
 
33
- /*
34
- * Strings that don't need to be translated at this time.
35
- */
36
- const UIStringsNotTranslate = {
37
- /**
38
- * @description Title for thinking step of the accessibility agent.
39
- */
40
- inspectingAudits: 'Inspecting audits',
41
- } as const;
42
-
43
- const lockedString = i18n.i18n.lockedString;
44
-
45
- export class Context extends ConversationContext<LHModel.ReporterTypes.ReportJSON> {
32
+ export class AccessibilityContext extends ConversationContext<LHModel.ReporterTypes.ReportJSON> {
46
33
  #lh: LHModel.ReporterTypes.ReportJSON;
47
34
 
48
35
  constructor(report: LHModel.ReporterTypes.ReportJSON) {
@@ -76,7 +63,6 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
76
63
  readonly clientFeature = Host.AidaClient.ClientFeature.CHROME_ACCESSIBILITY_AGENT;
77
64
 
78
65
  get userTier(): string|undefined {
79
- // TODO(b/491772868): tidy up userTier & feature flags in the backend.
80
66
  return Root.Runtime.hostConfig.devToolsFreestyler?.userTier;
81
67
  }
82
68
 
@@ -100,7 +86,6 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
100
86
 
101
87
  yield {
102
88
  type: ResponseType.CONTEXT,
103
- title: lockedString(UIStringsNotTranslate.inspectingAudits),
104
89
  details: createContextDetails(selectedFile),
105
90
  };
106
91
  }
@@ -70,7 +70,6 @@ export interface ContextDetail {
70
70
  }
71
71
  export interface ContextResponse {
72
72
  type: ResponseType.CONTEXT;
73
- title: string;
74
73
  details: [ContextDetail, ...ContextDetail[]];
75
74
  widgets?: AiWidget[];
76
75
  }
@@ -102,6 +101,7 @@ export interface ContextChangeResponse {
102
101
  */
103
102
  description: string;
104
103
  context: ConversationContext<unknown>;
104
+ widgets?: AiWidget[];
105
105
  }
106
106
 
107
107
  interface SerializedSideEffectResponse extends Omit<SideEffectResponse, 'confirm'> {}
@@ -253,11 +253,33 @@ export interface DomTreeAiWidget {
253
253
  name: 'DOM_TREE';
254
254
  data: {
255
255
  root: SDK.DOMModel.DOMNodeSnapshot,
256
+ networkRequest?: {
257
+ url: string,
258
+ size: number,
259
+ resourceType: Protocol.Network.ResourceType,
260
+ mimeType: string,
261
+ imageUrl?: string,
262
+ },
263
+ };
264
+ }
265
+
266
+ export interface PerformanceTraceAiWidget {
267
+ name: 'PERFORMANCE_TRACE';
268
+ data: {
269
+ parsedTrace: Trace.TraceModel.ParsedTrace,
270
+ };
271
+ }
272
+
273
+ export interface LcpBreakdownAiWidget {
274
+ name: 'LCP_BREAKDOWN';
275
+ data: {
276
+ lcpData: Trace.Insights.Models.LCPBreakdown.LCPBreakdownInsightModel,
256
277
  };
257
278
  }
258
279
 
259
280
  // This type will grow as we add more widgets.
260
- export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget;
281
+ export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget|
282
+ PerformanceTraceAiWidget|LcpBreakdownAiWidget;
261
283
 
262
284
  export type FunctionCallHandlerResult<Result> = {
263
285
  requiresApproval: true,
@@ -272,6 +294,7 @@ export type FunctionCallHandlerResult<Result> = {
272
294
  }|{
273
295
  context: ConversationContext<unknown>,
274
296
  description: string,
297
+ widgets?: AiWidget[],
275
298
  }|{
276
299
  error: string,
277
300
  };
@@ -692,6 +715,7 @@ export abstract class AiAgent<T> {
692
715
  type: ResponseType.CONTEXT_CHANGE,
693
716
  description: result.description,
694
717
  context: result.context,
718
+ widgets: result.widgets,
695
719
  };
696
720
 
697
721
  return;
@@ -608,7 +608,6 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
608
608
  }
609
609
  yield {
610
610
  type: ResponseType.CONTEXT,
611
- title: 'Analyzing breakpoint location',
612
611
  details: [{title: 'Location', text: selectedBreakpoint.getTitle()}]
613
612
  };
614
613
  }
@@ -278,6 +278,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
278
278
  return {
279
279
  context: PerformanceTraceContext.fromParsedTrace(result),
280
280
  description: 'User recorded a performance trace',
281
+ widgets: [{name: 'PERFORMANCE_TRACE', data: {parsedTrace: result}}]
281
282
  };
282
283
  }
283
284
  });
@@ -82,7 +82,6 @@ export class ConversationSummaryAgent extends AiAgent<string> {
82
82
 
83
83
  yield {
84
84
  type: ResponseType.CONTEXT,
85
- title: 'Summarizing conversation',
86
85
  details: [
87
86
  {
88
87
  title: 'Conversation transcript',
@@ -3,7 +3,6 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Host from '../../../core/host/host.js';
6
- import * as i18n from '../../../core/i18n/i18n.js';
7
6
  import * as Root from '../../../core/root/root.js';
8
7
  import type * as Workspace from '../../workspace/workspace.js';
9
8
  import {FileFormatter} from '../data_formatters/FileFormatter.js';
@@ -62,18 +61,6 @@ External Resources:
62
61
  MDN Web Docs: JavaScript Functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
63
62
  `;
64
63
 
65
- /*
66
- * Strings that don't need to be translated at this time.
67
- */
68
- const UIStringsNotTranslate = {
69
- /**
70
- * @description Title for thinking step of File agent.
71
- */
72
- analyzingFile: 'Analyzing file',
73
- } as const;
74
-
75
- const lockedString = i18n.i18n.lockedString;
76
-
77
64
  export class FileContext extends ConversationContext<Workspace.UISourceCode.UISourceCode> {
78
65
  #file: Workspace.UISourceCode.UISourceCode;
79
66
 
@@ -118,7 +105,6 @@ export class FileAgent extends AiAgent<Workspace.UISourceCode.UISourceCode> {
118
105
  modelId,
119
106
  };
120
107
  }
121
-
122
108
  async *
123
109
  handleContextDetails(selectedFile: ConversationContext<Workspace.UISourceCode.UISourceCode>|null):
124
110
  AsyncGenerator<ContextResponse, void, void> {
@@ -128,7 +114,6 @@ export class FileAgent extends AiAgent<Workspace.UISourceCode.UISourceCode> {
128
114
 
129
115
  yield {
130
116
  type: ResponseType.CONTEXT,
131
- title: lockedString(UIStringsNotTranslate.analyzingFile),
132
117
  details: createContextDetailsForFileAgent(selectedFile),
133
118
  };
134
119
  }
@@ -60,10 +60,6 @@ This request aims to retrieve a list of products matching the search query "lapt
60
60
  * Strings that don't need to be translated at this time.
61
61
  */
62
62
  const UIStringsNotTranslate = {
63
- /**
64
- * @description Title for thinking step of Network agent.
65
- */
66
- analyzingNetworkData: 'Analyzing network data',
67
63
  /**
68
64
  * @description Heading text for the block that shows the network request details.
69
65
  */
@@ -144,7 +140,6 @@ export class NetworkAgent extends AiAgent<SDK.NetworkRequest.NetworkRequest> {
144
140
 
145
141
  yield {
146
142
  type: ResponseType.CONTEXT,
147
- title: lockedString(UIStringsNotTranslate.analyzingNetworkData),
148
143
  details: await createContextDetailsForNetworkAgent(selectedNetworkRequest),
149
144
  };
150
145
  }
@@ -11,7 +11,9 @@ import * as SDK from '../../../core/sdk/sdk.js';
11
11
  import type * as Protocol from '../../../generated/protocol.js';
12
12
  import * as Tracing from '../../../services/tracing/tracing.js';
13
13
  import * as Annotations from '../../annotations/annotations.js';
14
+ import * as Logs from '../../logs/logs.js';
14
15
  import * as SourceMapScopes from '../../source_map_scopes/source_map_scopes.js';
16
+ import * as TextUtils from '../../text_utils/text_utils.js';
15
17
  import * as Trace from '../../trace/trace.js';
16
18
  import {
17
19
  PerformanceInsightFormatter,
@@ -35,10 +37,6 @@ import {
35
37
  } from './AiAgent.js';
36
38
 
37
39
  const UIStringsNotTranslated = {
38
- /**
39
- *@description Shown when the agent is investigating a trace
40
- */
41
- analyzingTrace: 'Analyzing trace',
42
40
  /**
43
41
  * @description Shown when the agent is investigating network activity
44
42
  */
@@ -318,7 +316,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
318
316
  #formatter: PerformanceTraceFormatter|null = null;
319
317
  #lastEventForEnhancedQuery: Trace.Types.Events.Event|undefined;
320
318
  #lastInsightForEnhancedQuery: Trace.Insights.Types.InsightModel|undefined;
321
- #hasShownAnalyzeTraceContext = false;
322
319
 
323
320
  /**
324
321
  * Cache of all function calls made by the agent. This allows us to include (as a
@@ -379,10 +376,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
379
376
  return;
380
377
  }
381
378
 
382
- if (this.#hasShownAnalyzeTraceContext) {
383
- return;
384
- }
385
-
386
379
  const widgets: AiWidget[] = [];
387
380
  const primaryInsightSet = context.getItem().primaryInsightSet;
388
381
  if (primaryInsightSet) {
@@ -397,7 +390,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
397
390
 
398
391
  yield {
399
392
  type: ResponseType.CONTEXT,
400
- title: lockedString(UIStringsNotTranslated.analyzingTrace),
401
393
  details: [
402
394
  {
403
395
  title: 'Trace',
@@ -406,8 +398,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
406
398
  ],
407
399
  widgets,
408
400
  };
409
-
410
- this.#hasShownAnalyzeTraceContext = true;
411
401
  }
412
402
 
413
403
  #callTreeContextSet = new WeakSet();
@@ -752,7 +742,8 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
752
742
  const details = new PerformanceInsightFormatter(focus, insight).formatInsight();
753
743
 
754
744
  const widgets: AiWidget[] = [];
755
- if (params.insightName === 'LCPDiscovery' || params.insightName === 'LCPBreakdown') {
745
+ if (Trace.Insights.Models.LCPDiscovery.isLCPDiscoveryInsight(insight) ||
746
+ Trace.Insights.Models.LCPBreakdown.isLCPBreakdownInsight(insight)) {
756
747
  const lcpMetric = Trace.Insights.Common.getLCP(insightSet);
757
748
  const lcpEvent = lcpMetric?.event;
758
749
  if (lcpEvent && Trace.Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
@@ -767,15 +758,38 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
767
758
  const node = nodeMap?.get(nodeId);
768
759
  if (node) {
769
760
  const snapshot = await node.takeSnapshot();
761
+ let networkRequest;
762
+ const lcpSyntheticRequest = insight.lcpRequest;
763
+ if (lcpSyntheticRequest) {
764
+ networkRequest = {
765
+ url: lcpSyntheticRequest.args.data.url,
766
+ size: lcpSyntheticRequest.args.data.decodedBodyLength ??
767
+ lcpSyntheticRequest.args.data.encodedDataLength ?? 0,
768
+ resourceType: lcpSyntheticRequest.args.data.resourceType,
769
+ mimeType: lcpSyntheticRequest.args.data.mimeType ?? '',
770
+ imageUrl: await this.#getNetworkRequestImageData(lcpSyntheticRequest),
771
+ };
772
+ }
770
773
  widgets.push({
771
774
  name: 'DOM_TREE',
772
- data: {root: snapshot},
775
+ data: {
776
+ root: snapshot,
777
+ networkRequest,
778
+ },
773
779
  });
774
780
  processedNodeIds.add(nodeId);
775
781
  }
776
782
  }
777
783
  }
778
784
  }
785
+ if (params.insightName === 'LCPBreakdown') {
786
+ widgets.push({
787
+ name: 'LCP_BREAKDOWN',
788
+ data: {
789
+ lcpData: insight as Trace.Insights.Models.LCPBreakdown.LCPBreakdownInsightModel,
790
+ },
791
+ });
792
+ }
779
793
  }
780
794
 
781
795
  const key = `getInsightDetails('${params.insightSetId}', '${params.insightName}')`;
@@ -1258,4 +1272,24 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
1258
1272
  Annotations.AnnotationRepository.instance().addNetworkRequestAnnotation(annotationMessage, requestId);
1259
1273
  return {result: {success: true}};
1260
1274
  }
1275
+
1276
+ async #getNetworkRequestImageData(lcpRequest: Trace.Types.Events.SyntheticNetworkRequest): Promise<string|undefined> {
1277
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
1278
+ const networkManager = target?.model(SDK.NetworkManager.NetworkManager);
1279
+ if (!target || !networkManager) {
1280
+ return undefined;
1281
+ }
1282
+
1283
+ const networkLog = Logs.NetworkLog.NetworkLog.instance();
1284
+ const requestId = lcpRequest.args.data.requestId;
1285
+ const sdkRequest = networkLog.requestByManagerAndId(networkManager, requestId);
1286
+
1287
+ if (sdkRequest?.contentType().isImage()) {
1288
+ const contentData = await sdkRequest.requestContentData();
1289
+ if (!TextUtils.ContentData.ContentData.isError(contentData)) {
1290
+ return contentData.asDataUrl() ?? undefined;
1291
+ }
1292
+ }
1293
+ return undefined;
1294
+ }
1261
1295
  }
@@ -3,7 +3,6 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Host from '../../../core/host/host.js';
6
- import * as i18n from '../../../core/i18n/i18n.js';
7
6
  import * as Root from '../../../core/root/root.js';
8
7
  import type {AICallTree} from '../performance/AICallTree.js';
9
8
  import type {AgentFocus} from '../performance/AIContext.js';
@@ -11,14 +10,6 @@ import type {AgentFocus} from '../performance/AIContext.js';
11
10
  import {AiAgent, type ContextResponse, type ConversationContext, type RequestOptions, ResponseType} from './AiAgent.js';
12
11
  import {PerformanceTraceContext} from './PerformanceAgent.js';
13
12
 
14
- const UIStringsNotTranslated = {
15
- analyzingCallTree: 'Analyzing call tree',
16
- /**
17
- * @description Shown when the agent is investigating network activity
18
- */
19
- } as const;
20
- const lockedString = i18n.i18n.lockedString;
21
-
22
13
  /**
23
14
  * Preamble clocks in at ~970 tokens.
24
15
  * The prose is around 4.5 chars per token.
@@ -124,7 +115,6 @@ export class PerformanceAnnotationsAgent extends AiAgent<AgentFocus> {
124
115
 
125
116
  yield {
126
117
  type: ResponseType.CONTEXT,
127
- title: lockedString(UIStringsNotTranslated.analyzingCallTree),
128
118
  details: [
129
119
  {
130
120
  title: 'Selected call tree',
@@ -216,7 +216,6 @@ Content:
216
216
  [
217
217
  {
218
218
  "type": "context",
219
- "title": "Analyzing the prompt",
220
219
  "details": [
221
220
  {
222
221
  "title": "Data used",
@@ -296,7 +295,6 @@ Content:
296
295
  [
297
296
  {
298
297
  "type": "context",
299
- "title": "Analyzing the prompt",
300
298
  "details": [
301
299
  {
302
300
  "title": "Data used",
@@ -321,7 +319,6 @@ Content:
321
319
  [
322
320
  {
323
321
  "type": "context",
324
- "title": "Analyzing the prompt",
325
322
  "details": [
326
323
  {
327
324
  "title": "Data used",
@@ -349,7 +346,6 @@ Content:
349
346
  [
350
347
  {
351
348
  "type": "context",
352
- "title": "Analyzing the prompt",
353
349
  "details": [
354
350
  {
355
351
  "title": "Data used",
@@ -374,7 +370,6 @@ Content:
374
370
  [
375
371
  {
376
372
  "type": "context",
377
- "title": "Analyzing the prompt",
378
373
  "details": [
379
374
  {
380
375
  "title": "Data used",
@@ -397,7 +392,6 @@ Content:
397
392
  [
398
393
  {
399
394
  "type": "context",
400
- "title": "Analyzing the prompt",
401
395
  "details": [
402
396
  {
403
397
  "title": "Data used",
@@ -37,10 +37,6 @@ import {
37
37
  * Strings that don't need to be translated at this time.
38
38
  */
39
39
  const UIStringsNotTranslate = {
40
- /**
41
- * @description Title for context details for Freestyler.
42
- */
43
- analyzingThePrompt: 'Analyzing the prompt',
44
40
  /**
45
41
  * @description Heading text for context details of Freestyler agent.
46
42
  */
@@ -1045,7 +1041,6 @@ const data = {
1045
1041
  }
1046
1042
  yield {
1047
1043
  type: ResponseType.CONTEXT,
1048
- title: lockedString(UIStringsNotTranslate.analyzingThePrompt),
1049
1044
  details: [{
1050
1045
  title: lockedString(UIStringsNotTranslate.dataUsed),
1051
1046
  text: await StylingAgent.describeElement(selectedElement.getItem()),
@@ -680,7 +680,11 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
680
680
  }
681
681
 
682
682
  private applyUserAgent(userAgent: string, userAgentMetadata: Protocol.Emulation.UserAgentMetadata|null): void {
683
- SDK.NetworkManager.MultitargetNetworkManager.instance().setUserAgentOverride(userAgent, userAgentMetadata);
683
+ // When the user agent string is empty (e.g. custom desktop device without
684
+ // a UA override), metadata must also be cleared. The backend rejects
685
+ // setUserAgentOverride calls that provide metadata without a UA string.
686
+ SDK.NetworkManager.MultitargetNetworkManager.instance().setUserAgentOverride(
687
+ userAgent, userAgent ? userAgentMetadata : null);
684
688
  }
685
689
 
686
690
  private applyDeviceMetrics(
@@ -201,7 +201,8 @@ export class EmulatedDevice {
201
201
  const rawUserAgent = (parseValue(json, 'user-agent', 'string') as string);
202
202
  result.userAgent = SDK.NetworkManager.MultitargetNetworkManager.patchUserAgentWithChromeVersion(rawUserAgent);
203
203
 
204
- result.userAgentMetadata = parseValue(json, 'user-agent-metadata', 'object', null);
204
+ const userAgentMetadata = parseValue(json, 'user-agent-metadata', 'object', null);
205
+ result.userAgentMetadata = result.userAgent ? userAgentMetadata : null;
205
206
 
206
207
  const capabilities = parseValue(json, 'capabilities', 'object', []);
207
208
  if (!Array.isArray(capabilities)) {
@@ -366,7 +367,7 @@ export class EmulatedDevice {
366
367
  json['foldable-screen'] = this.isFoldableScreen;
367
368
  json['show'] = this.#show;
368
369
 
369
- if (this.userAgentMetadata) {
370
+ if (this.userAgent && this.userAgentMetadata) {
370
371
  json['user-agent-metadata'] = this.userAgentMetadata;
371
372
  }
372
373
 
@@ -23,7 +23,7 @@ const str_ = i18n.i18n.registerUIStrings('models/issues_manager/SharedDictionary
23
23
  const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
24
24
 
25
25
  export const enum IssueCode {
26
- USE_ERROR_CROSS_ORIGIN_NO_CORS_REQUEST = 'SharedDictionaryIssue::UseErrorCrossOriginNoCorsRequest',
26
+ USE_ERROR_NO_CORP_CROSS_ORIGIN_NO_CORS_REQUEST = 'SharedDictionaryIssue::UseErrorNoCorpCrossOriginNoCorsRequest',
27
27
  USE_ERROR_DICTIONARY_LOAD_FAILURE = 'SharedDictionaryIssue::UseErrorDictionaryLoadFailure',
28
28
  USE_ERROR_MATCHING_DICTIONARY_NOT_USED = 'SharedDictionaryIssue::UseErrorMatchingDictionaryNotUsed',
29
29
  USE_ERROR_UNEXPECTED_CONTENT_DICTIONARY_HEADER = 'SharedDictionaryIssue::UseErrorUnexpectedContentDictionaryHeader',
@@ -36,6 +36,7 @@ export const enum IssueCode {
36
36
  WRITE_ERROR_INVALID_STRUCTURED_HEADER = 'SharedDictionaryIssue::WriteErrorInvalidStructuredHeader',
37
37
  WRITE_ERROR_INVALID_TTL_FIELD = 'SharedDictionaryIssue::WriteErrorInvalidTTLField',
38
38
  WRITE_ERROR_NAVIGATION_REQUEST = 'SharedDictionaryIssue::WriteErrorNavigationRequest',
39
+ WRITE_ERROR_NO_CORP_COSS_ORIGIN_NO_CORS_REQUEST = 'SharedDictionaryIssue::WriteErrorNoCorpCossOriginNoCorsRequest',
39
40
  WRITE_ERROR_NO_MATCH_FIELD = 'SharedDictionaryIssue::WriteErrorNoMatchField',
40
41
  WRITE_ERROR_NON_INTEGER_TTL_FIELD = 'SharedDictionaryIssue::WriteErrorNonIntegerTTLField',
41
42
  WRITE_ERROR_NON_LIST_MATCH_DEST_FIELD = 'SharedDictionaryIssue::WriteErrorNonListMatchDestField',
@@ -53,8 +54,8 @@ export const enum IssueCode {
53
54
 
54
55
  function getIssueCode(details: Protocol.Audits.SharedDictionaryIssueDetails): IssueCode {
55
56
  switch (details.sharedDictionaryError) {
56
- case Protocol.Audits.SharedDictionaryError.UseErrorCrossOriginNoCorsRequest:
57
- return IssueCode.USE_ERROR_CROSS_ORIGIN_NO_CORS_REQUEST;
57
+ case Protocol.Audits.SharedDictionaryError.UseErrorNoCorpCrossOriginNoCorsRequest:
58
+ return IssueCode.USE_ERROR_NO_CORP_CROSS_ORIGIN_NO_CORS_REQUEST;
58
59
  case Protocol.Audits.SharedDictionaryError.UseErrorDictionaryLoadFailure:
59
60
  return IssueCode.USE_ERROR_DICTIONARY_LOAD_FAILURE;
60
61
  case Protocol.Audits.SharedDictionaryError.UseErrorMatchingDictionaryNotUsed:
@@ -79,6 +80,8 @@ function getIssueCode(details: Protocol.Audits.SharedDictionaryIssueDetails): Is
79
80
  return IssueCode.WRITE_ERROR_INVALID_TTL_FIELD;
80
81
  case Protocol.Audits.SharedDictionaryError.WriteErrorNavigationRequest:
81
82
  return IssueCode.WRITE_ERROR_NAVIGATION_REQUEST;
83
+ case Protocol.Audits.SharedDictionaryError.WriteErrorNoCorpCossOriginNoCorsRequest:
84
+ return IssueCode.WRITE_ERROR_NO_CORP_COSS_ORIGIN_NO_CORS_REQUEST;
82
85
  case Protocol.Audits.SharedDictionaryError.WriteErrorNoMatchField:
83
86
  return IssueCode.WRITE_ERROR_NO_MATCH_FIELD;
84
87
  case Protocol.Audits.SharedDictionaryError.WriteErrorNonIntegerTTLField:
@@ -168,9 +171,9 @@ const specLinks = [{
168
171
  const issueDescriptions = new Map<Protocol.Audits.SharedDictionaryError, LazyMarkdownIssueDescription>([
169
172
 
170
173
  [
171
- Protocol.Audits.SharedDictionaryError.UseErrorCrossOriginNoCorsRequest,
174
+ Protocol.Audits.SharedDictionaryError.UseErrorNoCorpCrossOriginNoCorsRequest,
172
175
  {
173
- file: 'sharedDictionaryUseErrorCrossOriginNoCorsRequest.md',
176
+ file: 'sharedDictionaryUseErrorNoCorpCrossOriginNoCorsRequest.md',
174
177
  links: specLinks,
175
178
  },
176
179
  ],
@@ -258,6 +261,13 @@ const issueDescriptions = new Map<Protocol.Audits.SharedDictionaryError, LazyMar
258
261
  links: specLinks,
259
262
  },
260
263
  ],
264
+ [
265
+ Protocol.Audits.SharedDictionaryError.WriteErrorNoCorpCossOriginNoCorsRequest,
266
+ {
267
+ file: 'sharedDictionaryWriteErrorNoCorpCossOriginNoCorsRequest.md',
268
+ links: specLinks,
269
+ },
270
+ ],
261
271
  [
262
272
  Protocol.Audits.SharedDictionaryError.WriteErrorNoMatchField,
263
273
  {
@@ -0,0 +1,3 @@
1
+ # Dictionary compression cannot be used for cross-origin no-cors requests without a Cross-Origin-Resource-Policy response header
2
+
3
+ To use dictionary compression for a cross-origin no-cors request, the response must have a `Cross-Origin-Resource-Policy: cross-origin` header. Without it, the response can not be decoded and will fail.
@@ -0,0 +1,3 @@
1
+ # A cross-origin no-cors response cannot be stored as a shared dictionary for future requests without a Cross-Origin-Resource-Policy response header
2
+
3
+ To store a response from a cross-origin no-cors request as a shared dictionary for future requests, the response must have a `Cross-Origin-Resource-Policy: cross-origin` header. Without it, the response will not be stored as a dictionary.