chrome-devtools-frontend 1.0.1618066 → 1.0.1621064

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 (54) hide show
  1. package/docs/checklist/README.md +8 -7
  2. package/eslint.config.mjs +7 -0
  3. package/front_end/core/sdk/NetworkManager.ts +23 -2
  4. package/front_end/core/sdk/ServerSentEventsProtocol.ts +1 -1
  5. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +119 -13
  6. package/front_end/generated/InspectorBackendCommands.ts +6 -6
  7. package/front_end/generated/SupportedCSSProperties.js +100 -100
  8. package/front_end/generated/protocol-mapping.d.ts +1 -9
  9. package/front_end/generated/protocol-proxy-api.d.ts +1 -9
  10. package/front_end/generated/protocol.ts +5 -0
  11. package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +512 -0
  12. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +65 -85
  13. package/front_end/models/ai_assistance/agents/StylingAgent.ts +9 -16
  14. package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
  15. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +21 -4
  16. package/front_end/models/bindings/SymbolizedError.ts +69 -2
  17. package/front_end/models/javascript_metadata/NativeFunctions.js +2 -2
  18. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +11 -3
  19. package/front_end/models/stack_trace/ErrorStackParser.ts +18 -0
  20. package/front_end/panels/ai_assistance/components/ChatMessage.ts +84 -2
  21. package/front_end/panels/ai_assistance/components/WalkthroughUtils.ts +1 -1
  22. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +9 -2
  23. package/front_end/panels/application/WebMCPView.ts +212 -89
  24. package/front_end/panels/application/webMCPView.css +260 -199
  25. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +9 -5
  26. package/front_end/panels/changes/ChangesSidebar.ts +2 -2
  27. package/front_end/panels/changes/ChangesView.ts +4 -7
  28. package/front_end/panels/console/ConsoleViewMessage.ts +1 -19
  29. package/front_end/panels/coverage/CoverageView.ts +4 -5
  30. package/front_end/panels/elements/ElementsPanel.ts +9 -12
  31. package/front_end/panels/elements/StylePropertiesSection.ts +1 -1
  32. package/front_end/panels/elements/components/ElementsBreadcrumbs.ts +19 -20
  33. package/front_end/panels/elements/components/ElementsBreadcrumbsUtils.ts +29 -29
  34. package/front_end/panels/elements/components/QueryContainer.ts +5 -6
  35. package/front_end/panels/elements/components/components.ts +0 -2
  36. package/front_end/panels/emulation/MediaQueryInspector.ts +4 -7
  37. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +47 -66
  38. package/front_end/panels/network/RequestConditionsDrawer.ts +6 -4
  39. package/front_end/panels/performance_monitor/PerformanceMonitor.ts +6 -9
  40. package/front_end/panels/protocol_monitor/JSONEditor.ts +2 -2
  41. package/front_end/panels/search/SearchView.ts +4 -7
  42. package/front_end/panels/sources/DebuggerPausedMessage.ts +18 -22
  43. package/front_end/panels/sources/ThreadsSidebarPane.ts +2 -4
  44. package/front_end/panels/web_audio/WebAudioView.ts +5 -4
  45. package/front_end/third_party/chromium/README.chromium +1 -1
  46. package/front_end/ui/components/icon_button/iconButton.css +1 -0
  47. package/front_end/ui/components/lists/list.css +4 -0
  48. package/front_end/ui/legacy/ViewRegistration.ts +2 -2
  49. package/front_end/ui/legacy/Widget.ts +9 -2
  50. package/front_end/ui/legacy/components/utils/Linkifier.ts +32 -11
  51. package/front_end/ui/visual_logging/KnownContextValues.ts +17 -0
  52. package/package.json +1 -1
  53. package/front_end/Images/src/dots-circle.svg +0 -10
  54. package/front_end/panels/elements/components/Helper.ts +0 -35
@@ -8,7 +8,6 @@ import * as i18n from '../../../core/i18n/i18n.js';
8
8
  import * as Platform from '../../../core/platform/platform.js';
9
9
  import * as Root from '../../../core/root/root.js';
10
10
  import * as SDK from '../../../core/sdk/sdk.js';
11
- import type * as Protocol from '../../../generated/protocol.js';
12
11
  import * as Tracing from '../../../services/tracing/tracing.js';
13
12
  import * as Annotations from '../../annotations/annotations.js';
14
13
  import * as Logs from '../../logs/logs.js';
@@ -62,11 +61,10 @@ export type MainThreadSectionLabel = 'nav-to-lcp'|'lcp-ttfb'|'lcp-render-delay'|
62
61
  * chrome_preambles.gcl). Sync local changes with the server-side.
63
62
  */
64
63
 
65
- const greenDevAdditionalAnnotationsFunction = `
64
+ const GREEN_DEV_ANNOTATIONS_INSTRUCTIONS = `
66
65
  - CRITICAL: You also have access to functions called addElementAnnotation and addNeworkRequestAnnotation,
67
- which should be used to highlight elements and network requests (respectively).`;
66
+ which should be used to highlight elements and network requests (respectively).
68
67
 
69
- const greenDevAdditionalAnnotationsGuidelines = `
70
68
  - CRITICAL: Each time an element or a network request is mentioned, you MUST ALSO call the functions
71
69
  addElementAnnotation (for an element) or addNeworkRequestAnnotation (for a network request).
72
70
  - CRITICAL: Don't add more than one annotation per element or network request.
@@ -75,6 +73,24 @@ const greenDevAdditionalAnnotationsGuidelines = `
75
73
  - The annotationMessage should be descriptive and relevant to why the element or network request is being highlighted.
76
74
  `;
77
75
 
76
+ const GREEN_DEV_FRESH_TRACE_ANNOTATIONS_INSTRUCTIONS = `
77
+ When referring to an element for which you know the nodeId, always call the function addElementAnnotation, specifying
78
+ the id and an annotation reason.
79
+ When referring to a network request for which you know the eventKey for, always call the function
80
+ addNetworkRequestAnnotation, specifying the id and an annotation reason.
81
+ - CRITICAL: Each time you add an annotating link you MUST ALSO call the function addElementAnnotation.
82
+ - CRITICAL: Each time you describe an element or network request as being problematic you MUST call the function
83
+ addElementAnnotation and specify an annotation reason.
84
+ - CRITICAL: Each time you describe a network request as being problematic you MUST call the function
85
+ addNetworkRequestAnnotation and specify an annotation reason.
86
+ - CRITICAL: If you spot ANY of the following problems:
87
+ - Render-blocking elements/network requests.
88
+ - Significant long task (especially on main thread).
89
+ - Layout shifts (e.g. due to unsized images).
90
+ ... then you MUST call addNetworkRequestAnnotation for ALL network requests and addaddElementAnnotation for all
91
+ elements described in your conclusion.
92
+ `;
93
+
78
94
  /**
79
95
  * Preamble clocks in at ~1341 tokens.
80
96
  * The prose is around 4.5 chars per token.
@@ -82,9 +98,7 @@ const greenDevAdditionalAnnotationsGuidelines = `
82
98
  *
83
99
  * Check token length in https://aistudio.google.com/
84
100
  */
85
- const buildPreamble = (): string => {
86
- const annotationsEnabled = Annotations.AnnotationRepository.annotationsEnabled();
87
- return `You are an assistant, expert in web performance and highly skilled with Chrome DevTools.
101
+ const preamble = `You are an assistant, expert in web performance and highly skilled with Chrome DevTools.
88
102
 
89
103
  Your primary goal is to provide actionable advice to web developers about their web page by using the Chrome Performance Panel and analyzing a trace. You may need to diagnose problems yourself, or you may be given direction for what to focus on by the user.
90
104
 
@@ -94,8 +108,6 @@ Always call getInsightDetails to gather more data on an insight or the actual LC
94
108
 
95
109
  You have functions available to learn more about the trace. Use these to confirm hypotheses, or to further explore the trace when diagnosing performance issues.
96
110
 
97
- ${annotationsEnabled ? greenDevAdditionalAnnotationsFunction : ''}
98
-
99
111
  You will be given bounds representing a time range within the trace. Bounds include a min and a max time in microseconds. max is always bigger than min in a bounds.
100
112
 
101
113
  The 3 main performance metrics are:
@@ -167,8 +179,6 @@ Note: if the user asks a specific question about the trace (such as "What is my
167
179
  - Structure your response using markdown headings and bullet points for improved readability.
168
180
  - Be direct and to the point. Avoid unnecessary introductory phrases or filler content. Focus on delivering actionable advice efficiently.
169
181
 
170
- ${annotationsEnabled ? greenDevAdditionalAnnotationsGuidelines : ''}
171
-
172
182
  ## Strict Constraints
173
183
 
174
184
  Adhere to the following critical requirements:
@@ -186,7 +196,6 @@ Adhere to the following critical requirements:
186
196
  - Do not provide answers on non-web-development topics, such as legal, financial, medical, or personal advice.
187
197
  - Use the precision of Strunk & White, the brevity of Hemingway, and the simple clarity of Vonnegut. Don't add repeated information, and keep the whole answer short.
188
198
  `;
189
- };
190
199
 
191
200
  const extraPreambleWhenNotExternal = `Additional notes:
192
201
 
@@ -198,38 +207,14 @@ When referring to a trace event that has a corresponding \`eventKey\`, annotate
198
207
  When asking the user to make a choice between 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.
199
208
  `;
200
209
 
201
- const buildExtraPreambleWhenFreshTrace = (): string => {
202
- const annotationsEnabled = Annotations.AnnotationRepository.annotationsEnabled();
203
- const greenDevAdditionalGuidelineFreshTrace = `
204
- When referring to an element for which you know the nodeId, always call the function addElementAnnotation, specifying
205
- the id and an annotation reason.
206
- When referring to a network request for which you know the eventKey for, always call the function
207
- addNetworkRequestAnnotation, specifying the id and an annotation reason.
208
- - CRITICAL: Each time you add an annotating link you MUST ALSO call the function addElementAnnotation.
209
- - CRITICAL: Each time you describe an element or network request as being problematic you MUST call the function
210
- addElementAnnotation and specify an annotation reason.
211
- - CRITICAL: Each time you describe a network request as being problematic you MUST call the function
212
- addNetworkRequestAnnotation and specify an annotation reason.
213
- - CRITICAL: If you spot ANY of the following problems:
214
- - Render-blocking elements/network requests.
215
- - Significant long task (especially on main thread).
216
- - Layout shifts (e.g. due to unsized images).
217
- ... then you MUST call addNetworkRequestAnnotation for ALL network requests and addaddElementAnnotation for all
218
- elements described in your conclusion.
219
- `;
220
-
221
- const extraPreambleWhenFreshTrace = `Additional notes:
210
+ const freshTracePreamble = `Additional notes:
222
211
 
223
212
  When referring to an element for which you know the nodeId, annotate your output using markdown link syntax:
224
213
  - For example, if nodeId is 23: [LCP element](#node-23)
225
214
  - This link will reveal the element in the Elements panel
226
215
  - Never mention node or nodeId when referring to the element, and especially not in the link text.
227
216
  - 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.
228
-
229
- ${annotationsEnabled ? greenDevAdditionalGuidelineFreshTrace : ''}`;
230
-
231
- return extraPreambleWhenFreshTrace;
232
- };
217
+ `;
233
218
 
234
219
  enum ScorePriority {
235
220
  REQUIRED = 3,
@@ -406,6 +391,7 @@ export function getLabelName(label: MainThreadSectionLabel, focus: AgentFocus):
406
391
  * instance for a new conversation.
407
392
  */
408
393
  export class PerformanceAgent extends AiAgent<AgentFocus> {
394
+ readonly preamble = preamble;
409
395
  #formatter: PerformanceTraceFormatter|null = null;
410
396
  #lastEventForEnhancedQuery: Trace.Types.Events.Event|undefined;
411
397
  #lastInsightForEnhancedQuery: Trace.Insights.Types.InsightModel|undefined;
@@ -428,7 +414,15 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
428
414
  metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
429
415
  };
430
416
  #freshTraceExtraPreambleFact: Host.AidaClient.RequestFact = {
431
- text: buildExtraPreambleWhenFreshTrace(),
417
+ text: freshTracePreamble,
418
+ metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
419
+ };
420
+ #greenDevAnnotationsFact: Host.AidaClient.RequestFact = {
421
+ text: GREEN_DEV_ANNOTATIONS_INSTRUCTIONS,
422
+ metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
423
+ };
424
+ #greenDevFreshTraceAnnotationsFact: Host.AidaClient.RequestFact = {
425
+ text: GREEN_DEV_FRESH_TRACE_ANNOTATIONS_INSTRUCTIONS,
432
426
  metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
433
427
  };
434
428
  #networkDataDescriptionFact: Host.AidaClient.RequestFact = {
@@ -450,6 +444,8 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
450
444
  this.#networkDataDescriptionFact,
451
445
  this.#freshTraceExtraPreambleFact,
452
446
  this.#notExternalExtraPreambleFact,
447
+ this.#greenDevAnnotationsFact,
448
+ this.#greenDevFreshTraceAnnotationsFact,
453
449
  ]);
454
450
 
455
451
  /**
@@ -459,20 +455,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
459
455
  */
460
456
  #additionalSelectionsForQuery: string[] = [];
461
457
 
462
- /**
463
- * The CWV widget is shown when we analyze the trace summary, but we don't
464
- * want to show it on every single "Analyzing data..." pill, as we show one
465
- * after every prompt. So we make sure for a given Insight Set (which is based on navigation)
466
- * we only show it once.
467
- */
468
- #hasShownWidgetForInsightSet = new WeakSet<Trace.Insights.Types.InsightSet>();
469
- #hasShownWidgetForCallTree = new WeakSet<AICallTree>();
470
- #hasShownWidgetForInsight = new WeakSet<Trace.Insights.Types.InsightModel>();
471
-
472
- get preamble(): string {
473
- return buildPreamble();
474
- }
475
-
476
458
  get clientFeature(): Host.AidaClient.ClientFeature {
477
459
  return Host.AidaClient.ClientFeature.CHROME_PERFORMANCE_FULL_AGENT;
478
460
  }
@@ -531,35 +513,31 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
531
513
 
532
514
  // Case 1: Specific task (call tree) -> timeline summary & bottom up tree widgets
533
515
  if (focus.callTree) {
534
- if (!this.#hasShownWidgetForCallTree.has(focus.callTree)) {
535
- const event = focus.callTree.selectedNode?.event;
536
- if (event) {
537
- const {startTime, endTime} = Trace.Helpers.Timing.eventTimingsMicroSeconds(event);
538
- const bounds = Trace.Helpers.Timing.traceWindowFromMicroSeconds(startTime, endTime);
539
- widgets.push({
540
- name: 'TIMELINE_RANGE_SUMMARY',
541
- data: {
542
- bounds,
543
- parsedTrace: focus.parsedTrace,
544
- track: 'main',
545
- },
546
- });
547
- widgets.push({
548
- name: 'BOTTOM_UP_TREE',
549
- data: {
550
- bounds,
551
- parsedTrace: focus.parsedTrace,
552
- },
553
- });
554
- this.#hasShownWidgetForCallTree.add(focus.callTree);
555
- }
516
+ const event = focus.callTree.selectedNode?.event;
517
+ if (event) {
518
+ const {startTime, endTime} = Trace.Helpers.Timing.eventTimingsMicroSeconds(event);
519
+ const bounds = Trace.Helpers.Timing.traceWindowFromMicroSeconds(startTime, endTime);
520
+ widgets.push({
521
+ name: 'TIMELINE_RANGE_SUMMARY',
522
+ data: {
523
+ bounds,
524
+ parsedTrace: focus.parsedTrace,
525
+ track: 'main',
526
+ },
527
+ });
528
+ widgets.push({
529
+ name: 'BOTTOM_UP_TREE',
530
+ data: {
531
+ bounds,
532
+ parsedTrace: focus.parsedTrace,
533
+ },
534
+ });
556
535
  }
557
536
  return widgets;
558
537
  }
559
538
 
560
539
  // Case 2: LCP Insight -> LCP breakdown & CWV widgets
561
- if (focus.insight && Trace.Insights.Models.LCPBreakdown.isLCPBreakdownInsight(focus.insight) &&
562
- !this.#hasShownWidgetForInsight.has(focus.insight)) {
540
+ if (focus.insight && Trace.Insights.Models.LCPBreakdown.isLCPBreakdownInsight(focus.insight)) {
563
541
  widgets.push({
564
542
  name: 'PERF_INSIGHT',
565
543
  data: {
@@ -567,12 +545,11 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
567
545
  insightData: focus.insight,
568
546
  },
569
547
  });
570
- this.#hasShownWidgetForInsight.add(focus.insight);
571
548
  }
572
549
 
573
550
  // Case 3: Whole Trace or insight other than LCP -> CWV widget
574
551
  const primaryInsightSet = focus.primaryInsightSet;
575
- if (primaryInsightSet && !this.#hasShownWidgetForInsightSet.has(primaryInsightSet)) {
552
+ if (primaryInsightSet) {
576
553
  widgets.push({
577
554
  name: 'CORE_VITALS',
578
555
  data: {
@@ -580,7 +557,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
580
557
  insightSetKey: primaryInsightSet.id,
581
558
  },
582
559
  });
583
- this.#hasShownWidgetForInsightSet.add(primaryInsightSet);
584
560
  }
585
561
 
586
562
  return widgets;
@@ -833,9 +809,17 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
833
809
  this.addFact(this.#notExternalExtraPreambleFact);
834
810
  }
835
811
 
812
+ const annotationsEnabled = Annotations.AnnotationRepository.annotationsEnabled();
813
+ if (annotationsEnabled) {
814
+ this.addFact(this.#greenDevAnnotationsFact);
815
+ }
816
+
836
817
  const isFresh = Tracing.FreshRecording.Tracker.instance().recordingIsFresh(focus.parsedTrace);
837
818
  if (isFresh) {
838
819
  this.addFact(this.#freshTraceExtraPreambleFact);
820
+ if (annotationsEnabled) {
821
+ this.addFact(this.#greenDevFreshTraceAnnotationsFact);
822
+ }
839
823
  }
840
824
 
841
825
  this.addFact(this.#callFrameDataDescriptionFact);
@@ -935,7 +919,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
935
919
  #declareFunctions(context: PerformanceTraceContext): void {
936
920
  const focus = context.getItem();
937
921
  const {parsedTrace} = focus;
938
- const processedNodeIds = new Set<Protocol.DOM.BackendNodeId>();
939
922
 
940
923
  this.declareFunction<{insightSetId: string, insightName: string}, {details: string}>('getInsightDetails', {
941
924
  description:
@@ -992,9 +975,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
992
975
  const lcpEvent = lcpMetric?.event;
993
976
  if (lcpEvent && Trace.Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
994
977
  const nodeId = lcpEvent.args.data?.nodeId;
995
- // We want to show only one DOM tree widget per walkthrough per node.
996
- // We do want to show the widget for the same node again, if it's within a new walkthrough.
997
- if (nodeId && !processedNodeIds.has(nodeId)) {
978
+ if (nodeId) {
998
979
  const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
999
980
  const domModel = target?.model(SDK.DOMModel.DOMModel);
1000
981
  if (domModel) {
@@ -1021,7 +1002,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
1021
1002
  networkRequest,
1022
1003
  },
1023
1004
  });
1024
- processedNodeIds.add(nodeId);
1025
1005
  }
1026
1006
  }
1027
1007
  }
@@ -47,14 +47,7 @@ const UIStringsNotTranslate = {
47
47
 
48
48
  const lockedString = i18n.i18n.lockedString;
49
49
 
50
- function getPreamble(): string {
51
- /**
52
- * WARNING: preamble defined in code is only used when userTier is
53
- * TESTERS. Otherwise, a server-side preamble is used (see
54
- * chrome_preambles.gcl). Sync local changes with the server-side.
55
- */
56
- /* clang-format off */
57
- let preamble = `You are the most advanced CSS/DOM/HTML debugging assistant integrated into Chrome DevTools.
50
+ const preamble = `You are the most advanced CSS/DOM/HTML debugging assistant integrated into Chrome DevTools.
58
51
  You always suggest considering the best web development practices and the newest platform features such as view transitions.
59
52
  The user selected a DOM element in the browser's DevTools and sends a query about the page or the selected DOM element.
60
53
  First, examine the provided context, then use the functions to gather additional context and resolve the user request.
@@ -92,9 +85,7 @@ If the user asks a question that requires an investigation of a problem, use thi
92
85
  - [Suggestion 1]
93
86
  - [Suggestion 2]`;
94
87
 
95
- const greenDevEmulationEnabled = Greendev.Prototypes.instance().isEnabled('emulationCapabilities');
96
- if (greenDevEmulationEnabled) {
97
- preamble += `
88
+ const emulationInstructions = `
98
89
  # Emulation and Screenshots
99
90
 
100
91
  * If asked to verify whether the page is visually broken or if there are display problems with specific devices, use the \`activateDeviceEmulation\` tool. This tool will activate emulation for a specified device and capture a screenshot.
@@ -127,10 +118,6 @@ When referring to an element for which you know the nodeId, annotate your output
127
118
  - Always prefix the nodeId with the 'node-' prefix when using the markdown syntax.
128
119
  - This link will reveal the element in the Elements panel
129
120
  - Never mention node or nodeId when referring to the element, and especially not in the link text.`;
130
- }
131
-
132
- return preamble;
133
- }
134
121
 
135
122
  /* clang-format on */
136
123
 
@@ -242,7 +229,7 @@ export class NodeContext extends ConversationContext<SDK.DOMModel.DOMNode> {
242
229
  * instance for a new conversation.
243
230
  */
244
231
  export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
245
- preamble = getPreamble();
232
+ readonly preamble = preamble;
246
233
  readonly clientFeature = Host.AidaClient.ClientFeature.CHROME_STYLING_AGENT;
247
234
  get userTier(): string|undefined {
248
235
  const greenDevEmulationEnabled = Greendev.Prototypes.instance().isEnabled('emulationCapabilities');
@@ -278,6 +265,7 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
278
265
  #createExtensionScope: CreateExtensionScopeFunction;
279
266
  #greenDevEmulationScreenshot: string|null = null;
280
267
  #greenDevEmulationAxTree: string|null = null;
268
+ #hasAddedEmulationInstructions = false;
281
269
  #currentTurnId = 0;
282
270
 
283
271
  constructor(opts: ExecuteJsAgentOptions) {
@@ -821,6 +809,11 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
821
809
  this.#greenDevEmulationAxTree = null;
822
810
  }
823
811
 
812
+ if (Greendev.Prototypes.instance().isEnabled('emulationCapabilities') && !this.#hasAddedEmulationInstructions) {
813
+ multimodalInputEnhancementQuery = emulationInstructions + '\n' + multimodalInputEnhancementQuery;
814
+ this.#hasAddedEmulationInstructions = true;
815
+ }
816
+
824
817
  const elementEnchancementQuery = selectedElement ?
825
818
  `# Inspected element\n\n${
826
819
  await StylingAgent.describeElement(selectedElement.getItem())}\n\n# User request\n\n` :
@@ -9,6 +9,7 @@ import * as BreakpointDebuggerAgent from './agents/BreakpointDebuggerAgent.js';
9
9
  import * as ContextSelectionAgent from './agents/ContextSelectionAgent.js';
10
10
  import * as ConversationSummaryAgent from './agents/ConversationSummaryAgent.js';
11
11
  import * as FileAgent from './agents/FileAgent.js';
12
+ import * as GreenDevAgent from './agents/GreenDevAgent.js';
12
13
  import * as NetworkAgent from './agents/NetworkAgent.js';
13
14
  import * as PatchAgent from './agents/PatchAgent.js';
14
15
  import * as PerformanceAgent from './agents/PerformanceAgent.js';
@@ -53,6 +54,7 @@ export {
53
54
  ExtensionScope,
54
55
  FileAgent,
55
56
  FileFormatter,
57
+ GreenDevAgent,
56
58
  Injected,
57
59
  LighthouseFormatter,
58
60
  NetworkAgent,
@@ -7,7 +7,7 @@ import * as Platform from '../../core/platform/platform.js';
7
7
  import * as Root from '../../core/root/root.js';
8
8
  import * as SDK from '../../core/sdk/sdk.js';
9
9
  import * as Protocol from '../../generated/protocol.js';
10
- import type * as StackTrace from '../stack_trace/stack_trace.js';
10
+ import * as StackTrace from '../stack_trace/stack_trace.js';
11
11
  // eslint-disable-next-line @devtools/es-modules-import
12
12
  import * as StackTraceImpl from '../stack_trace/stack_trace_impl.js';
13
13
  import type * as TextUtils from '../text_utils/text_utils.js';
@@ -20,7 +20,12 @@ import {type LiveLocation, type LiveLocationPool, LiveLocationWithPool} from './
20
20
  import {NetworkProject} from './NetworkProject.js';
21
21
  import type {ResourceMapping} from './ResourceMapping.js';
22
22
  import {type ResourceScriptFile, ResourceScriptMapping} from './ResourceScriptMapping.js';
23
- import {SymbolizedError} from './SymbolizedError.js';
23
+ import {
24
+ type SymbolizedError,
25
+ SymbolizedErrorObject,
26
+ SymbolizedSyntaxError,
27
+ UnparsableError
28
+ } from './SymbolizedError.js';
24
29
 
25
30
  export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObserver<SDK.DebuggerModel.DebuggerModel> {
26
31
  readonly resourceMapping: ResourceMapping;
@@ -233,6 +238,13 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
233
238
  fetchedExceptionDetails = details;
234
239
  causeRemoteObject = causeRemote;
235
240
 
241
+ if (remoteObject.className === 'SyntaxError' && fetchedExceptionDetails) {
242
+ const syntaxError = await SymbolizedSyntaxError.fromExceptionDetails(
243
+ remoteObject.runtimeModel().target(), this, fetchedExceptionDetails);
244
+ if (syntaxError) {
245
+ return syntaxError;
246
+ }
247
+ }
236
248
  } else if (remoteObject.type === 'string') {
237
249
  errorStack = remoteObject.description || '';
238
250
  } else {
@@ -245,12 +257,17 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
245
257
  causeRemoteObject ? this.createSymbolizedError(causeRemoteObject) : Promise.resolve(null),
246
258
  ]);
247
259
 
260
+ const issueSummary = fetchedExceptionDetails?.exceptionMetaData?.issueSummary;
261
+ if (typeof issueSummary === 'string') {
262
+ errorStack = StackTrace.ErrorStackParser.concatErrorDescriptionAndIssueSummary(errorStack, issueSummary);
263
+ }
264
+
248
265
  if (!stackTrace) {
249
- return null;
266
+ return new UnparsableError(errorStack, cause);
250
267
  }
251
268
 
252
269
  const message = StackTraceImpl.DetailedErrorStackParser.parseMessage(errorStack);
253
- return new SymbolizedError(message, stackTrace, cause);
270
+ return new SymbolizedErrorObject(message, stackTrace, cause);
254
271
  }
255
272
 
256
273
  async createLiveLocation(
@@ -3,9 +3,28 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
+ import * as SDK from '../../core/sdk/sdk.js';
7
+ import type * as Protocol from '../../generated/protocol.js';
6
8
  import * as StackTrace from '../stack_trace/stack_trace.js';
9
+ import type * as Workspace from '../workspace/workspace.js';
7
10
 
8
- export class SymbolizedError extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
11
+ import type {DebuggerWorkspaceBinding} from './DebuggerWorkspaceBinding.js';
12
+ import {type LiveLocation, LiveLocationPool} from './LiveLocation.js';
13
+
14
+ export type SymbolizedError = SymbolizedErrorObject|SymbolizedSyntaxError|UnparsableError;
15
+
16
+ export class UnparsableError extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
17
+ readonly errorStack: string;
18
+ readonly cause: SymbolizedError|null;
19
+
20
+ constructor(errorStack: string, cause: SymbolizedError|null) {
21
+ super();
22
+ this.errorStack = errorStack;
23
+ this.cause = cause;
24
+ }
25
+ }
26
+
27
+ export class SymbolizedErrorObject extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
9
28
  readonly message: string;
10
29
  readonly stackTrace: StackTrace.StackTrace.ParsedErrorStackTrace;
11
30
  readonly cause: SymbolizedError|null;
@@ -23,7 +42,9 @@ export class SymbolizedError extends Common.ObjectWrapper.ObjectWrapper<EventTyp
23
42
  dispose(): void {
24
43
  this.stackTrace.removeEventListener(StackTrace.StackTrace.Events.UPDATED, this.#fireUpdated, this);
25
44
  this.cause?.removeEventListener(Events.UPDATED, this.#fireUpdated, this);
26
- this.cause?.dispose();
45
+ if (this.cause instanceof SymbolizedErrorObject) {
46
+ this.cause.dispose();
47
+ }
27
48
  }
28
49
 
29
50
  #fireUpdated(): void {
@@ -31,6 +52,52 @@ export class SymbolizedError extends Common.ObjectWrapper.ObjectWrapper<EventTyp
31
52
  }
32
53
  }
33
54
 
55
+ export class SymbolizedSyntaxError extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
56
+ readonly message: string;
57
+ #uiLocation: Workspace.UISourceCode.UILocation|null = null;
58
+
59
+ constructor(message: string) {
60
+ super();
61
+ this.message = message;
62
+ }
63
+
64
+ get uiLocation(): Workspace.UISourceCode.UILocation|null {
65
+ return this.#uiLocation;
66
+ }
67
+
68
+ static async fromExceptionDetails(
69
+ target: SDK.Target.Target, debuggerWorkspaceBinding: DebuggerWorkspaceBinding,
70
+ exceptionDetails: Protocol.Runtime.ExceptionDetails): Promise<SymbolizedSyntaxError|null> {
71
+ const {exception, scriptId, lineNumber, columnNumber} = exceptionDetails;
72
+ if (!exception || exception.subtype !== 'error' || exception.className !== 'SyntaxError') {
73
+ throw new Error('SymbolizedSyntaxError.fromExceptionDetails expects a SyntaxError');
74
+ }
75
+ if (!scriptId) {
76
+ return null;
77
+ }
78
+
79
+ const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
80
+ if (!debuggerModel) {
81
+ return null;
82
+ }
83
+
84
+ const rawLocation = debuggerModel.createRawLocationByScriptId(scriptId, lineNumber, columnNumber);
85
+ const symbolizedSyntaxError = new SymbolizedSyntaxError(exception.description || '');
86
+
87
+ // We don't implement dispose here. We won't create many of these so a couple
88
+ // LiveLocationPools and SymbolizedSyntaxError instances leaking is fine.
89
+ await debuggerWorkspaceBinding.createLiveLocation(
90
+ rawLocation, symbolizedSyntaxError.#update.bind(symbolizedSyntaxError), new LiveLocationPool());
91
+
92
+ return symbolizedSyntaxError;
93
+ }
94
+
95
+ async #update(liveLocation: LiveLocation): Promise<void> {
96
+ this.#uiLocation = await liveLocation.uiLocation();
97
+ this.dispatchEventToListeners(Events.UPDATED);
98
+ }
99
+ }
100
+
34
101
  export const enum Events {
35
102
  UPDATED = 'UPDATED',
36
103
  }
@@ -8339,7 +8339,7 @@ export const NativeFunctions = [
8339
8339
  },
8340
8340
  {
8341
8341
  name: "setConstraints",
8342
- signatures: [["constraints"]]
8342
+ signatures: [["?constraints"]]
8343
8343
  },
8344
8344
  {
8345
8345
  name: "createContext",
@@ -9353,7 +9353,7 @@ export const NativeFunctions = [
9353
9353
  },
9354
9354
  {
9355
9355
  name: "copyElementImageToTexture",
9356
- signatures: [["source","destination"],["source","width","height","destination"],["source","sx","sy","swidth","sheight","destination"]]
9356
+ signatures: [["source","destination"],["source","width","height","destination"],["source","sx","sy","swidth","sheight","destination"],["source","sx","sy","swidth","sheight","width","height","destination"]]
9357
9357
  },
9358
9358
  {
9359
9359
  name: "multiDrawIndirect",
@@ -181,9 +181,17 @@ export function parseMessage(stack: string): string {
181
181
  export function augmentRawFramesWithScriptIds(
182
182
  rawFrames: RawFrame[], protocolStackTrace: Protocol.Runtime.StackTrace): void {
183
183
  for (const rawFrame of rawFrames) {
184
- const protocolFrame = protocolStackTrace.callFrames.find(
185
- frame => rawFrame.url === frame.url && rawFrame.lineNumber === frame.lineNumber &&
186
- rawFrame.columnNumber === frame.columnNumber);
184
+ const isWasm = rawFrame.parsedFrameInfo?.isWasm;
185
+ const protocolFrame = protocolStackTrace.callFrames.find(frame => {
186
+ if (isWasm) {
187
+ // The parser parses Wasm offsets into the `columnNumber` field. The `lineNumber` is always -1.
188
+ // In the protocol trace, the `lineNumber` is 0 (for Wasm) and `columnNumber` is the bytecode offset.
189
+ return rawFrame.url === frame.url && rawFrame.columnNumber === frame.columnNumber;
190
+ }
191
+ return rawFrame.url === frame.url && rawFrame.lineNumber === frame.lineNumber &&
192
+ rawFrame.columnNumber === frame.columnNumber;
193
+ });
194
+
187
195
  if (protocolFrame) {
188
196
  // @ts-expect-error scriptId is a readonly property.
189
197
  rawFrame.scriptId = protocolFrame.scriptId;
@@ -21,6 +21,24 @@ export interface ParsedErrorFrame {
21
21
  };
22
22
  }
23
23
 
24
+ /**
25
+ * Combines the error description (essentially the `Error#stack` property value)
26
+ * with the `issueSummary`.
27
+ *
28
+ * @param description the `description` property of the `Error` remote object.
29
+ * @param issueSummary the optional `issueSummary` of the `exceptionMetaData`.
30
+ * @returns the enriched description.
31
+ * @see https://goo.gle/devtools-reduce-network-noise-design
32
+ */
33
+ export function concatErrorDescriptionAndIssueSummary(description: string, issueSummary: string): string {
34
+ // Insert the issue summary right after the error message.
35
+ const pos = description.indexOf('\n');
36
+ const prefix = pos === -1 ? description : description.substring(0, pos);
37
+ const suffix = pos === -1 ? '' : description.substring(pos);
38
+ description = `${prefix}. ${issueSummary}${suffix}`;
39
+ return description;
40
+ }
41
+
24
42
  /**
25
43
  * Takes a V8 Error#stack string and extracts source position information.
26
44
  *