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.
- package/docs/checklist/README.md +8 -7
- package/eslint.config.mjs +7 -0
- package/front_end/core/sdk/NetworkManager.ts +23 -2
- package/front_end/core/sdk/ServerSentEventsProtocol.ts +1 -1
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +119 -13
- package/front_end/generated/InspectorBackendCommands.ts +6 -6
- package/front_end/generated/SupportedCSSProperties.js +100 -100
- package/front_end/generated/protocol-mapping.d.ts +1 -9
- package/front_end/generated/protocol-proxy-api.d.ts +1 -9
- package/front_end/generated/protocol.ts +5 -0
- package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +512 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +65 -85
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +9 -16
- package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +21 -4
- package/front_end/models/bindings/SymbolizedError.ts +69 -2
- package/front_end/models/javascript_metadata/NativeFunctions.js +2 -2
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +11 -3
- package/front_end/models/stack_trace/ErrorStackParser.ts +18 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +84 -2
- package/front_end/panels/ai_assistance/components/WalkthroughUtils.ts +1 -1
- package/front_end/panels/ai_assistance/components/WalkthroughView.ts +9 -2
- package/front_end/panels/application/WebMCPView.ts +212 -89
- package/front_end/panels/application/webMCPView.css +260 -199
- package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +9 -5
- package/front_end/panels/changes/ChangesSidebar.ts +2 -2
- package/front_end/panels/changes/ChangesView.ts +4 -7
- package/front_end/panels/console/ConsoleViewMessage.ts +1 -19
- package/front_end/panels/coverage/CoverageView.ts +4 -5
- package/front_end/panels/elements/ElementsPanel.ts +9 -12
- package/front_end/panels/elements/StylePropertiesSection.ts +1 -1
- package/front_end/panels/elements/components/ElementsBreadcrumbs.ts +19 -20
- package/front_end/panels/elements/components/ElementsBreadcrumbsUtils.ts +29 -29
- package/front_end/panels/elements/components/QueryContainer.ts +5 -6
- package/front_end/panels/elements/components/components.ts +0 -2
- package/front_end/panels/emulation/MediaQueryInspector.ts +4 -7
- package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +47 -66
- package/front_end/panels/network/RequestConditionsDrawer.ts +6 -4
- package/front_end/panels/performance_monitor/PerformanceMonitor.ts +6 -9
- package/front_end/panels/protocol_monitor/JSONEditor.ts +2 -2
- package/front_end/panels/search/SearchView.ts +4 -7
- package/front_end/panels/sources/DebuggerPausedMessage.ts +18 -22
- package/front_end/panels/sources/ThreadsSidebarPane.ts +2 -4
- package/front_end/panels/web_audio/WebAudioView.ts +5 -4
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/icon_button/iconButton.css +1 -0
- package/front_end/ui/components/lists/list.css +4 -0
- package/front_end/ui/legacy/ViewRegistration.ts +2 -2
- package/front_end/ui/legacy/Widget.ts +9 -2
- package/front_end/ui/legacy/components/utils/Linkifier.ts +32 -11
- package/front_end/ui/visual_logging/KnownContextValues.ts +17 -0
- package/package.json +1 -1
- package/front_end/Images/src/dots-circle.svg +0 -10
- 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
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 {
|
|
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
|
|
266
|
+
return new UnparsableError(errorStack, cause);
|
|
250
267
|
}
|
|
251
268
|
|
|
252
269
|
const message = StackTraceImpl.DetailedErrorStackParser.parseMessage(errorStack);
|
|
253
|
-
return new
|
|
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
|
-
|
|
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
|
|
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
|
|
185
|
-
|
|
186
|
-
|
|
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
|
*
|