chrome-devtools-frontend 1.0.1597448 → 1.0.1598808

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 (143) hide show
  1. package/docs/ui_engineering.md +6 -6
  2. package/front_end/core/host/AidaClient.ts +6 -0
  3. package/front_end/core/sdk/CPUThrottlingManager.ts +5 -1
  4. package/front_end/core/sdk/CSSMatchedStyles.ts +2 -0
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +28 -0
  6. package/front_end/models/ai_assistance/AiConversation.ts +40 -22
  7. package/front_end/models/ai_assistance/ChangeManager.ts +16 -0
  8. package/front_end/models/ai_assistance/ExtensionScope.ts +11 -3
  9. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +127 -0
  10. package/front_end/models/ai_assistance/agents/AiAgent.ts +35 -6
  11. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +1 -1
  12. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +11 -8
  13. package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +109 -0
  14. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +30 -1
  15. package/front_end/models/ai_assistance/agents/StylingAgent.ts +34 -4
  16. package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
  17. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +27 -0
  18. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +21 -0
  19. package/front_end/models/trace/Processor.ts +1 -0
  20. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -0
  21. package/front_end/models/trace/insights/CharacterSet.ts +172 -0
  22. package/front_end/models/trace/insights/Models.ts +1 -0
  23. package/front_end/models/trace/insights/types.ts +1 -0
  24. package/front_end/models/trace/types/TraceEvents.ts +17 -0
  25. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +22 -12
  26. package/front_end/panels/ai_assistance/PatchWidget.ts +1 -1
  27. package/front_end/panels/ai_assistance/components/ChatMessage.ts +131 -63
  28. package/front_end/panels/ai_assistance/components/ChatView.ts +1 -1
  29. package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +18 -9
  30. package/front_end/panels/ai_assistance/components/chatMessage.css +11 -0
  31. package/front_end/panels/application/AppManifestView.ts +4 -5
  32. package/front_end/panels/application/CookieItemsView.ts +9 -15
  33. package/front_end/panels/application/DeviceBoundSessionsView.ts +20 -26
  34. package/front_end/panels/application/FrameDetailsView.ts +10 -16
  35. package/front_end/panels/application/OriginTrialTreeView.ts +2 -3
  36. package/front_end/panels/application/ReportingApiView.ts +14 -18
  37. package/front_end/panels/application/components/BackForwardCacheView.ts +3 -3
  38. package/front_end/panels/application/preloading/PreloadingView.ts +18 -3
  39. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -3
  40. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +3 -2
  41. package/front_end/panels/changes/ChangesView.ts +13 -15
  42. package/front_end/panels/console/ConsolePinPane.ts +3 -3
  43. package/front_end/panels/coverage/CoverageListView.ts +1 -1
  44. package/front_end/panels/coverage/CoverageView.ts +4 -4
  45. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +3 -3
  46. package/front_end/panels/css_overview/CSSOverviewPanel.ts +11 -15
  47. package/front_end/panels/developer_resources/DeveloperResourcesView.ts +3 -5
  48. package/front_end/panels/elements/ElementsTreeOutline.ts +34 -8
  49. package/front_end/panels/elements/EventListenersWidget.ts +3 -2
  50. package/front_end/panels/elements/StandaloneStylesContainer.ts +21 -6
  51. package/front_end/panels/elements/StylePropertiesSection.ts +129 -1
  52. package/front_end/panels/elements/StylePropertyHighlighter.ts +3 -0
  53. package/front_end/panels/elements/StylePropertyTreeElement.ts +49 -4
  54. package/front_end/panels/elements/stylesSidebarPane.css +34 -0
  55. package/front_end/panels/issues/AffectedSelectivePermissionsInterventionView.ts +3 -5
  56. package/front_end/panels/layer_viewer/LayerDetailsView.ts +2 -1
  57. package/front_end/panels/layer_viewer/Layers3DView.ts +5 -4
  58. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +5 -6
  59. package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +6 -11
  60. package/front_end/panels/network/RequestConditionsDrawer.ts +4 -4
  61. package/front_end/panels/network/RequestCookiesView.ts +5 -6
  62. package/front_end/panels/network/RequestHeadersView.ts +4 -4
  63. package/front_end/panels/network/RequestInitiatorView.ts +7 -5
  64. package/front_end/panels/network/RequestResponseView.ts +10 -15
  65. package/front_end/panels/performance_monitor/PerformanceMonitor.ts +2 -2
  66. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +5 -6
  67. package/front_end/panels/recorder/components/RecordingView.ts +31 -36
  68. package/front_end/panels/recorder/components/StepEditor.ts +6 -7
  69. package/front_end/panels/recorder/components/StepView.ts +2 -1
  70. package/front_end/panels/search/SearchView.ts +3 -4
  71. package/front_end/panels/settings/WorkspaceSettingsTab.ts +2 -5
  72. package/front_end/panels/timeline/TimelineDetailsView.ts +8 -6
  73. package/front_end/panels/timeline/components/LiveMetricsView.ts +5 -8
  74. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +2 -7
  75. package/front_end/panels/timeline/components/insights/Cache.ts +8 -10
  76. package/front_end/panels/timeline/components/insights/CharacterSet.ts +34 -0
  77. package/front_end/panels/timeline/components/insights/DOMSize.ts +16 -20
  78. package/front_end/panels/timeline/components/insights/DocumentLatency.ts +2 -6
  79. package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +3 -4
  80. package/front_end/panels/timeline/components/insights/EventRef.ts +2 -2
  81. package/front_end/panels/timeline/components/insights/FontDisplay.ts +3 -4
  82. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +5 -7
  83. package/front_end/panels/timeline/components/insights/INPBreakdown.ts +3 -4
  84. package/front_end/panels/timeline/components/insights/ImageDelivery.ts +3 -4
  85. package/front_end/panels/timeline/components/insights/ImageRef.ts +2 -4
  86. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +2 -0
  87. package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +5 -7
  88. package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +2 -4
  89. package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +3 -4
  90. package/front_end/panels/timeline/components/insights/ModernHTTP.ts +3 -4
  91. package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +7 -11
  92. package/front_end/panels/timeline/components/insights/NodeLink.ts +2 -4
  93. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +3 -4
  94. package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +7 -10
  95. package/front_end/panels/timeline/components/insights/ThirdParties.ts +5 -7
  96. package/front_end/panels/timeline/components/insights/insights.ts +2 -0
  97. package/front_end/panels/web_audio/WebAudioView.ts +3 -4
  98. package/front_end/panels/webauthn/WebauthnPane.ts +2 -2
  99. package/front_end/third_party/chromium/README.chromium +1 -1
  100. package/front_end/third_party/puppeteer/README.chromium +2 -2
  101. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Realm.js +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Realm.js.map +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.d.ts +4 -0
  104. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.d.ts.map +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.js +13 -0
  106. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.js.map +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  108. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  109. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  110. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  111. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  112. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  113. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/cjs/third_party/mitt/mitt.js +2 -2
  115. package/front_end/third_party/puppeteer/package/lib/cjs/third_party/parsel-js/parsel-js.js +1 -1
  116. package/front_end/third_party/puppeteer/package/lib/cjs/third_party/rxjs/rxjs.js +446 -446
  117. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +166 -167
  118. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Realm.js +2 -2
  119. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Realm.js.map +1 -1
  120. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.d.ts +4 -0
  121. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.d.ts.map +1 -1
  122. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.js +12 -0
  123. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.js.map +1 -1
  124. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  125. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  126. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  127. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  128. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  129. package/front_end/third_party/puppeteer/package/lib/esm/third_party/mitt/mitt.js +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/esm/third_party/parsel-js/parsel-js.js +1 -1
  131. package/front_end/third_party/puppeteer/package/lib/esm/third_party/rxjs/rxjs.js +97 -97
  132. package/front_end/third_party/puppeteer/package/package.json +1 -1
  133. package/front_end/third_party/puppeteer/package/src/bidi/Realm.ts +2 -2
  134. package/front_end/third_party/puppeteer/package/src/bidi/util.ts +17 -0
  135. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  136. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  137. package/front_end/ui/components/settings/SettingCheckbox.ts +2 -0
  138. package/front_end/ui/legacy/Widget.ts +42 -6
  139. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +3 -3
  140. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +8 -8
  141. package/front_end/ui/visual_logging/Debugging.ts +0 -32
  142. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  143. package/package.json +1 -1
@@ -2,11 +2,10 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import * as Common from '../../../core/common/common.js';
6
5
  import * as Host from '../../../core/host/host.js';
7
6
  import * as i18n from '../../../core/i18n/i18n.js';
8
7
  import * as Root from '../../../core/root/root.js';
9
- import * as SDK from '../../../core/sdk/sdk.js';
8
+ import type * as SDK from '../../../core/sdk/sdk.js';
10
9
  import * as Logs from '../../logs/logs.js';
11
10
  import * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
12
11
  import type * as Trace from '../../trace/trace.js';
@@ -63,7 +62,7 @@ You aim to help developers of all levels, prioritizing teaching web concepts as
63
62
  */
64
63
  export class ContextSelectionAgent extends AiAgent<never> {
65
64
  readonly preamble = preamble;
66
- readonly clientFeature = Host.AidaClient.ClientFeature.CHROME_FILE_AGENT;
65
+ readonly clientFeature = Host.AidaClient.ClientFeature.CHROME_CONTEXT_SELECTION_AGENT;
67
66
  get userTier(): string|undefined {
68
67
  // TODO: Make this depend on variable.
69
68
  return Root.Runtime.hostConfig.devToolsFreestyler?.userTier;
@@ -81,6 +80,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
81
80
  readonly #performanceRecordAndReload?: () => Promise<Trace.TraceModel.ParsedTrace>;
82
81
  readonly #onInspectElement?: () => Promise<SDK.DOMModel.DOMNode|null>;
83
82
  readonly #networkTimeCalculator?: NetworkTimeCalculator.NetworkTransferTimeCalculator;
83
+ #allowedOrigin: () => string | undefined;
84
84
 
85
85
  constructor(opts: AgentOptions&{
86
86
  performanceRecordAndReload?: () => Promise<Trace.TraceModel.ParsedTrace>,
@@ -91,6 +91,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
91
91
  this.#performanceRecordAndReload = opts.performanceRecordAndReload;
92
92
  this.#onInspectElement = opts.onInspectElement;
93
93
  this.#networkTimeCalculator = opts.networkTimeCalculator;
94
+ this.#allowedOrigin = opts.allowedOrigin ?? (() => undefined);
94
95
 
95
96
  this.declareFunction<Record<string, never>>('listNetworkRequests', {
96
97
  description: `Gives a list of network requests including URL, status code, and duration.`,
@@ -109,12 +110,12 @@ export class ContextSelectionAgent extends AiAgent<never> {
109
110
  },
110
111
  handler: async () => {
111
112
  const requests = [];
112
- const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
113
- const inspectedURL = target?.inspectedURL();
114
- const mainSecurityOrigin = inspectedURL ? new Common.ParsedURL.ParsedURL(inspectedURL).securityOrigin() : null;
113
+ const origin = this.#allowedOrigin();
115
114
 
115
+ let hasCrossOriginRequest = false;
116
116
  for (const request of Logs.NetworkLog.NetworkLog.instance().requests()) {
117
- if (mainSecurityOrigin && request.securityOrigin() !== mainSecurityOrigin) {
117
+ if (origin && request.securityOrigin() !== origin) {
118
+ hasCrossOriginRequest = true;
118
119
  continue;
119
120
  }
120
121
 
@@ -129,7 +130,9 @@ export class ContextSelectionAgent extends AiAgent<never> {
129
130
 
130
131
  if (requests.length === 0) {
131
132
  return {
132
- error: 'No requests recorded by DevTools',
133
+ error: hasCrossOriginRequest ?
134
+ `No requests showing with origin ${origin}. Tell the user to start a new chat` :
135
+ 'No requests recorded by DevTools',
133
136
  };
134
137
  }
135
138
 
@@ -0,0 +1,109 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Host from '../../../core/host/host.js';
6
+ import * as Root from '../../../core/root/root.js';
7
+
8
+ import {AiAgent, type ContextResponse, ConversationContext, type RequestOptions, ResponseType} from './AiAgent.js';
9
+
10
+ const preamble =
11
+ `You are an expert technical assistant specializing in summarizing debugging conversations from Chrome DevTools.
12
+ You will receive a markdown-formatted transcript of a conversation between a user and a DevTools AI assistant.
13
+ Your goal is to produce a succinct, structured summary that a local AI agent in the user's IDE can use to apply code fixes.
14
+
15
+ Focus on extracting:
16
+ 1. **Core Issue:** The primary problem or question the user was investigating.
17
+ 2. **Diagnostic Findings:** Key technical details discovered (e.g., specific functions, bottlenecks, error messages, URLs, or CSS properties).
18
+ 3. **Proposed Solution:** The specific changes or optimizations recommended by the DevTools assistant.
19
+ 4. **Actionable Steps:** A clear, step-by-step list of instructions for the IDE agent to implement the fix.
20
+
21
+ Maintain a professional, technical, and extremely concise tone. Avoid conversational filler or introductory/concluding remarks.
22
+ The output must be structured markdown.`;
23
+
24
+ export class ConversationSummaryContext extends ConversationContext<string> {
25
+ #conversation: string;
26
+ constructor(conversation: string) {
27
+ super();
28
+ this.#conversation = conversation;
29
+ }
30
+
31
+ getOrigin(): string {
32
+ return 'devtools://ai-assistance';
33
+ }
34
+
35
+ getItem(): string {
36
+ return this.#conversation;
37
+ }
38
+
39
+ override getTitle(): string {
40
+ return 'Conversation';
41
+ }
42
+ }
43
+
44
+ /**
45
+ * An agent that takes a full conversation between a user and an agent in markdown
46
+ * format and produces a succinct summary of the conversation.
47
+ *
48
+ * This summary is designed to be read by a local agent in the user's IDE and it
49
+ * will be used to help apply fixes to the user's local codebase based on the
50
+ * debugging information the devtools agent found.
51
+ *
52
+ * This agent is not intended to be used directly by users in the AI Assistance
53
+ * panel when chatting with DevTools AI.
54
+ */
55
+ export class ConversationSummaryAgent extends AiAgent<string> {
56
+ override preamble = preamble;
57
+
58
+ get clientFeature(): Host.AidaClient.ClientFeature {
59
+ return Host.AidaClient.ClientFeature.CHROME_CONVERSATION_SUMMARY_AGENT;
60
+ }
61
+
62
+ get userTier(): string|undefined {
63
+ // TODO(b/491772868): tidy up userTier & feature flags in the backend.
64
+ return Root.Runtime.hostConfig.devToolsFreestyler?.userTier;
65
+ }
66
+
67
+ get options(): RequestOptions {
68
+ // TODO(b/491772868): tidy up userTier & feature flags in the backend.
69
+ const temperature = Root.Runtime.hostConfig.devToolsFreestyler?.temperature;
70
+ const modelId = Root.Runtime.hostConfig.devToolsFreestyler?.modelId;
71
+
72
+ return {
73
+ temperature,
74
+ modelId,
75
+ };
76
+ }
77
+
78
+ async * handleContextDetails(context: ConversationContext<string>|null): AsyncGenerator<ContextResponse, void, void> {
79
+ if (!context) {
80
+ return;
81
+ }
82
+
83
+ yield {
84
+ type: ResponseType.CONTEXT,
85
+ title: 'Summarizing conversation',
86
+ details: [
87
+ {
88
+ title: 'Conversation transcript',
89
+ text: context.getItem(),
90
+ },
91
+ ],
92
+ };
93
+ }
94
+
95
+ override async enhanceQuery(query: string, context: ConversationContext<string>|null): Promise<string> {
96
+ const conversation = context ? context.getItem() : query;
97
+ return `Summarize the following conversation:\n\n${conversation}`;
98
+ }
99
+
100
+ async summarizeConversation(conversation: string): Promise<string> {
101
+ const context = new ConversationSummaryContext(conversation);
102
+ const response = await Array.fromAsync(this.run('', {selected: context}));
103
+ const lastResponse = response.at(-1);
104
+ if (lastResponse && lastResponse.type === ResponseType.ANSWER && lastResponse.complete === true) {
105
+ return lastResponse.text.trim();
106
+ }
107
+ throw new Error('Failed to summarize conversation');
108
+ }
109
+ }
@@ -8,6 +8,7 @@ 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';
11
12
  import * as Tracing from '../../../services/tracing/tracing.js';
12
13
  import * as Annotations from '../../annotations/annotations.js';
13
14
  import * as SourceMapScopes from '../../source_map_scopes/source_map_scopes.js';
@@ -700,6 +701,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
700
701
  #declareFunctions(context: PerformanceTraceContext): void {
701
702
  const focus = context.getItem();
702
703
  const {parsedTrace} = focus;
704
+ const processedNodeIds = new Set<Protocol.DOM.BackendNodeId>();
703
705
 
704
706
  this.declareFunction<{insightSetId: string, insightName: string}, {details: string}>('getInsightDetails', {
705
707
  description:
@@ -749,9 +751,36 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
749
751
 
750
752
  const details = new PerformanceInsightFormatter(focus, insight).formatInsight();
751
753
 
754
+ const widgets: AiWidget[] = [];
755
+ if (params.insightName === 'LCPDiscovery' || params.insightName === 'LCPBreakdown') {
756
+ const lcpMetric = Trace.Insights.Common.getLCP(insightSet);
757
+ const lcpEvent = lcpMetric?.event;
758
+ if (lcpEvent && Trace.Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
759
+ const nodeId = lcpEvent.args.data?.nodeId;
760
+ // We want to show only one DOM tree widget per walkthrough per node.
761
+ // We do want to show the widget for the same node again, if it's within a new walkthrough.
762
+ if (nodeId && !processedNodeIds.has(nodeId)) {
763
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
764
+ const domModel = target?.model(SDK.DOMModel.DOMModel);
765
+ if (domModel) {
766
+ const nodeMap = await domModel.pushNodesByBackendIdsToFrontend(new Set([nodeId]));
767
+ const node = nodeMap?.get(nodeId);
768
+ if (node) {
769
+ const snapshot = await node.takeSnapshot();
770
+ widgets.push({
771
+ name: 'DOM_TREE',
772
+ data: {root: snapshot},
773
+ });
774
+ processedNodeIds.add(nodeId);
775
+ }
776
+ }
777
+ }
778
+ }
779
+ }
780
+
752
781
  const key = `getInsightDetails('${params.insightSetId}', '${params.insightName}')`;
753
782
  this.#cacheFunctionResult(focus, key, details);
754
- return {result: {details}};
783
+ return {result: {details}, widgets};
755
784
  },
756
785
  });
757
786
 
@@ -15,11 +15,12 @@ import {ChangeManager} from '../ChangeManager.js';
15
15
  import {debugLog} from '../debug.js';
16
16
  import {EvaluateAction, formatError, SideEffectError} from '../EvaluateAction.js';
17
17
  import {ExtensionScope} from '../ExtensionScope.js';
18
- import {FREESTYLER_WORLD_NAME} from '../injected.js';
18
+ import {AI_ASSISTANCE_CSS_CLASS_NAME, FREESTYLER_WORLD_NAME} from '../injected.js';
19
19
 
20
20
  import {
21
21
  type AgentOptions as BaseAgentOptions,
22
22
  AiAgent,
23
+ type AnswerResponse,
23
24
  type ComputedStyleAiWidget,
24
25
  type ContextResponse,
25
26
  ConversationContext,
@@ -194,6 +195,8 @@ async function executeJsCode(
194
195
  const MAX_OBSERVATION_BYTE_LENGTH = 25_000;
195
196
  const OBSERVATION_TIMEOUT = 5_000;
196
197
 
198
+ export const AI_ASSISTANCE_FILTER_REGEX = `\\.${AI_ASSISTANCE_CSS_CLASS_NAME}-.*&`;
199
+
197
200
  type CreateExtensionScopeFunction = (changes: ChangeManager) => {
198
201
  install(): Promise<void>, uninstall(): Promise<void>,
199
202
  };
@@ -317,15 +320,17 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
317
320
  #createExtensionScope: CreateExtensionScopeFunction;
318
321
  #greenDevEmulationScreenshot: string|null = null;
319
322
  #greenDevEmulationAxTree: string|null = null;
323
+ #currentTurnId = 0;
320
324
 
321
325
  constructor(opts: AgentOptions) {
322
326
  super(opts);
323
327
 
324
328
  this.#changes = opts.changeManager || new ChangeManager();
325
329
  this.#execJs = opts.execJs ?? executeJsCode;
326
- this.#createExtensionScope = opts.createExtensionScope ?? ((changes: ChangeManager) => {
327
- return new ExtensionScope(changes, this.sessionId, this.context?.getItem() ?? null);
328
- });
330
+ this.#createExtensionScope =
331
+ opts.createExtensionScope ?? ((changes: ChangeManager) => {
332
+ return new ExtensionScope(changes, this.sessionId, this.context?.getItem() ?? null, this.#currentTurnId);
333
+ });
329
334
 
330
335
  this.declareFunction<{
331
336
  elements: number[],
@@ -1048,6 +1053,31 @@ const data = {
1048
1053
  };
1049
1054
  }
1050
1055
 
1056
+ protected override async preRun(): Promise<void> {
1057
+ this.#currentTurnId++;
1058
+ }
1059
+
1060
+ protected override async finalizeAnswer(answer: AnswerResponse): Promise<AnswerResponse> {
1061
+ if (!Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled) {
1062
+ return answer;
1063
+ }
1064
+
1065
+ const changedNodeIds = this.#changes.getChangedNodesForGroupId(this.sessionId, this.#currentTurnId);
1066
+ if (changedNodeIds.length === 0) {
1067
+ return answer;
1068
+ }
1069
+ answer.widgets = [
1070
+ ...(answer.widgets ?? []),
1071
+ ...changedNodeIds.map(id => ({
1072
+ name: 'STYLE_PROPERTIES' as const,
1073
+ data: {
1074
+ backendNodeId: id,
1075
+ selector: AI_ASSISTANCE_FILTER_REGEX,
1076
+ },
1077
+ })),
1078
+ ];
1079
+ return answer;
1080
+ }
1051
1081
  override async enhanceQuery(
1052
1082
  query: string, selectedElement: ConversationContext<SDK.DOMModel.DOMNode>|null,
1053
1083
  multimodalInputType?: MultimodalInputType): Promise<string> {
@@ -3,9 +3,11 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as AgentProject from './AgentProject.js';
6
+ import * as AccessibilityAgent from './agents/AccessibilityAgent.js';
6
7
  import * as AiAgent from './agents/AiAgent.js';
7
8
  import * as BreakpointDebuggerAgent from './agents/BreakpointDebuggerAgent.js';
8
9
  import * as ContextSelectionAgent from './agents/ContextSelectionAgent.js';
10
+ import * as ConversationSummaryAgent from './agents/ConversationSummaryAgent.js';
9
11
  import * as FileAgent from './agents/FileAgent.js';
10
12
  import * as NetworkAgent from './agents/NetworkAgent.js';
11
13
  import * as PatchAgent from './agents/PatchAgent.js';
@@ -32,6 +34,7 @@ import * as AIContext from './performance/AIContext.js';
32
34
  import * as AIQueries from './performance/AIQueries.js';
33
35
 
34
36
  export {
37
+ AccessibilityAgent,
35
38
  AgentProject,
36
39
  AiAgent,
37
40
  AICallTree,
@@ -45,6 +48,7 @@ export {
45
48
  ChangeManager,
46
49
  ContextSelectionAgent,
47
50
  ConversationHandler,
51
+ ConversationSummaryAgent,
48
52
  Debug,
49
53
  EvaluateAction,
50
54
  ExtensionScope,
@@ -203,6 +203,10 @@ export class PerformanceInsightFormatter {
203
203
  {title: 'Is my site polyfilling modern JavaScript features?'},
204
204
  {title: 'How can I reduce the amount of legacy JavaScript on my page?'},
205
205
  ];
206
+ case 'CharacterSet':
207
+ return [
208
+ {title: 'How do I declare a character encoding for my page?'},
209
+ ];
206
210
  default:
207
211
  throw new Error(`Unknown insight key '${this.#insight.insightKey}'`);
208
212
  }
@@ -881,6 +885,20 @@ ${requestSummary}`;
881
885
  * @param insight The Network Dependency Tree Insight Model to query.
882
886
  * @returns a string formatted for sending to Ask AI.
883
887
  */
888
+ formatCharacterSetInsight(insight: Trace.Insights.Models.CharacterSet.CharacterSetInsightModel): string {
889
+ let output = '';
890
+ if (insight.data) {
891
+ output += 'HTTP Content-Type header charset: ' + (insight.data.hasHttpCharset ? 'present' : 'missing') + '.\n';
892
+ output += 'HTML meta charset disposition: ' + (insight.data.metaCharsetDisposition ?? 'unknown') + '.\n';
893
+
894
+ if (!insight.data.hasHttpCharset && insight.data.metaCharsetDisposition !== 'found-in-first-1024-bytes') {
895
+ output +=
896
+ '\nThe page does not declare character encoding via HTTP header or a meta charset tag in the first 1024 bytes.\n';
897
+ }
898
+ }
899
+ return output;
900
+ }
901
+
884
902
  formatViewportInsight(insight: Trace.Insights.Models.Viewport.ViewportInsightModel): string {
885
903
  let output = '';
886
904
 
@@ -995,6 +1013,10 @@ ${this.#links()}`;
995
1013
  return this.formatViewportInsight(this.#insight);
996
1014
  }
997
1015
 
1016
+ if (Trace.Insights.Models.CharacterSet.isCharacterSetInsight(this.#insight)) {
1017
+ return this.formatCharacterSetInsight(this.#insight);
1018
+ }
1019
+
998
1020
  return '';
999
1021
  }
1000
1022
 
@@ -1074,6 +1096,9 @@ ${this.#links()}`;
1074
1096
  links.push('https://web.dev/articles/baseline-and-polyfills');
1075
1097
  links.push('https://philipwalton.com/articles/the-state-of-es5-on-the-web/');
1076
1098
  break;
1099
+ case 'CharacterSet':
1100
+ links.push('https://developer.chrome.com/docs/insights/charset/');
1101
+ break;
1077
1102
  }
1078
1103
 
1079
1104
  return links.map(link => '- ' + link).join('\n');
@@ -1158,6 +1183,8 @@ To pass this insight, ensure your server supports and prioritizes a modern HTTP
1158
1183
  return `This insight identified legacy JavaScript in your application's modules that may be creating unnecessary code.
1159
1184
 
1160
1185
  Polyfills and transforms enable older browsers to use new JavaScript features. However, many are not necessary for modern browsers. Consider modifying your JavaScript build process to not transpile Baseline features, unless you know you must support older browsers.`;
1186
+ case 'CharacterSet':
1187
+ return `This insight checks that the page declares a character encoding, ideally via the Content-Type HTTP response header. A missing or late charset declaration can force the browser to re-parse the document once it finally determines the encoding, delaying first contentful paint. Best practice: include charset=utf-8 in the Content-Type header and add <meta charset="utf-8"> as the very first element inside <head>.`;
1161
1188
  }
1162
1189
  }
1163
1190
  }
@@ -467,6 +467,7 @@ Here are all the insights that contain some related request from the given range
467
467
  - DocumentLatency: news.yahoo.com/ (news.yahoo.com) (eventKey: s-2116, ts: 157423489126)
468
468
  - ThirdParties: cmp.js (consent.cmp.oath.com) (eventKey: s-3382, ts: 157423742399), gpt.js (securepubads.g.doubleclick.net) (eventKey: s-6244, ts: 157423760529), loader.js (cdn.taboola.com) (eventKey: s-6352, ts: 157423761978)
469
469
  - Cache: prebid-2.0.js (s.yimg.com) (eventKey: s-6279, ts: 157423760925), cmp.js (consent.cmp.oath.com) (eventKey: s-3382, ts: 157423742399), d1irmdsmbztlvx.js (s.yimg.com) (eventKey: s-6185, ts: 157423759200), consent.js (s.yimg.com) (eventKey: s-3384, ts: 157423742450), wnsrvbjmeprtfrnfx.js (s.yimg.com) (eventKey: s-6273, ts: 157423760794)
470
+ - CharacterSet: news.yahoo.com/ (news.yahoo.com) (eventKey: s-2116, ts: 157423489126)
470
471
  - LegacyJavaScript: benji-2.2.99.js (s.yimg.com) (eventKey: s-3387, ts: 157423742567), 25fa214.caas-news_web.min.js (s.yimg.com) (eventKey: s-3412, ts: 157423743431), wnsrvbjmeprtfrnfx.js (s.yimg.com) (eventKey: s-6273, ts: 157423760794), consent.js (s.yimg.com) (eventKey: s-3384, ts: 157423742450), news.yahoo.com/ (news.yahoo.com) (eventKey: s-2116, ts: 157423489126)
471
472
  === end content
472
473
 
@@ -510,6 +511,10 @@ Available insights:
510
511
  description: 3rd party code can significantly impact load performance. [Reduce and defer loading of 3rd party code](https://developer.chrome.com/docs/performance/insights/third-parties) to prioritize your page's content.
511
512
  relevant trace bounds: {min: 1020034871372, max: 1020035171789}
512
513
  example question: Which third parties are having the largest impact on my page performance?
514
+ - insight name: CharacterSet
515
+ description: A character encoding declaration is required. It can be done with a meta charset tag in the first 1024 bytes of the HTML or in the Content-Type HTTP response header. [Learn more about declaring the character encoding](https://developer.chrome.com/docs/insights/charset/).
516
+ relevant trace bounds: {min: 1020034836013, max: 1020034892491}
517
+ example question: How do I declare a character encoding for my page?
513
518
  === end content
514
519
 
515
520
  Title: PerformanceTraceFormatter formatTraceSummary yahoo-news.json.gz
@@ -617,6 +622,10 @@ Available insights:
617
622
  relevant trace bounds: {min: 171608170438, max: 171608877165}
618
623
  example question: Show me the most impactful render-blocking requests that I should focus on
619
624
  example question: How can I reduce the number of render-blocking requests?
625
+ - insight name: CharacterSet
626
+ description: A character encoding declaration is required. It can be done with a meta charset tag in the first 1024 bytes of the HTML or in the Content-Type HTTP response header. [Learn more about declaring the character encoding](https://developer.chrome.com/docs/insights/charset/).
627
+ relevant trace bounds: {min: 171607584346, max: 171608171143}
628
+ example question: How do I declare a character encoding for my page?
620
629
 
621
630
  ## insight set id: NAVIGATION_1
622
631
 
@@ -641,6 +650,10 @@ Available insights:
641
650
  relevant trace bounds: {min: 171614330544, max: 171615043224}
642
651
  example question: Show me the most impactful render-blocking requests that I should focus on
643
652
  example question: How can I reduce the number of render-blocking requests?
653
+ - insight name: CharacterSet
654
+ description: A character encoding declaration is required. It can be done with a meta charset tag in the first 1024 bytes of the HTML or in the Content-Type HTTP response header. [Learn more about declaring the character encoding](https://developer.chrome.com/docs/insights/charset/).
655
+ relevant trace bounds: {min: 171613750986, max: 171614330870}
656
+ example question: How do I declare a character encoding for my page?
644
657
  === end content
645
658
 
646
659
  Title: PerformanceTraceFormatter formatTraceSummary deals with CrUX manager errors
@@ -714,6 +727,10 @@ Available insights:
714
727
  description: 3rd party code can significantly impact load performance. [Reduce and defer loading of 3rd party code](https://developer.chrome.com/docs/performance/insights/third-parties) to prioritize your page's content.
715
728
  relevant trace bounds: {min: 59728701403, max: 59729465969}
716
729
  example question: Which third parties are having the largest impact on my page performance?
730
+ - insight name: CharacterSet
731
+ description: A character encoding declaration is required. It can be done with a meta charset tag in the first 1024 bytes of the HTML or in the Content-Type HTTP response header. [Learn more about declaring the character encoding](https://developer.chrome.com/docs/insights/charset/).
732
+ relevant trace bounds: {min: 59728651057, max: 59728790724}
733
+ example question: How do I declare a character encoding for my page?
717
734
  === end content
718
735
 
719
736
  Title: PerformanceTraceFormatter formatTraceSummary image-delivery.json.gz
@@ -794,6 +811,10 @@ Available insights:
794
811
  description: 3rd party code can significantly impact load performance. [Reduce and defer loading of 3rd party code](https://developer.chrome.com/docs/performance/insights/third-parties) to prioritize your page's content.
795
812
  relevant trace bounds: {min: 59728701403, max: 59729465969}
796
813
  example question: Which third parties are having the largest impact on my page performance?
814
+ - insight name: CharacterSet
815
+ description: A character encoding declaration is required. It can be done with a meta charset tag in the first 1024 bytes of the HTML or in the Content-Type HTTP response header. [Learn more about declaring the character encoding](https://developer.chrome.com/docs/insights/charset/).
816
+ relevant trace bounds: {min: 59728651057, max: 59728790724}
817
+ example question: How do I declare a character encoding for my page?
797
818
  === end content
798
819
 
799
820
  Title: PerformanceTraceFormatter formatTraceSummary includes INP insight when there is no navigation
@@ -352,6 +352,7 @@ export class TraceProcessor extends EventTarget {
352
352
  SlowCSSSelector: null,
353
353
  ForcedReflow: null,
354
354
  Cache: null,
355
+ CharacterSet: null,
355
356
  ModernHTTP: null,
356
357
  LegacyJavaScript: null,
357
358
  };
@@ -40,11 +40,17 @@ let metricScoresByFrameId = new Map<FrameId, Map<AnyNavigationStart, Map<MetricN
40
40
  */
41
41
  let allMarkerEvents: Types.Events.PageLoadEvent[] = [];
42
42
 
43
+ // Grouped by navigation to make it easier for insights to scope checks.
44
+ let metaCharsetCheckEventsByNavigation = new Map<AnyNavigationStart, Types.Events.MetaCharsetCheck[]>();
45
+ let metaCharsetCheckEventsArray: Types.Events.MetaCharsetCheck[] = [];
46
+
43
47
  export function reset(): void {
44
48
  metricScoresByFrameId = new Map();
45
49
  pageLoadEventsArray = [];
46
50
  allMarkerEvents = [];
47
51
  selectedLCPCandidateEvents = new Set();
52
+ metaCharsetCheckEventsByNavigation = new Map();
53
+ metaCharsetCheckEventsArray = [];
48
54
  }
49
55
 
50
56
  let pageLoadEventsArray: Types.Events.PageLoadEvent[] = [];
@@ -60,6 +66,11 @@ let pageLoadEventsArray: Types.Events.PageLoadEvent[] = [];
60
66
  let selectedLCPCandidateEvents = new Set<Types.Events.AnyLargestContentfulPaintCandidate>();
61
67
 
62
68
  export function handleEvent(event: Types.Events.Event): void {
69
+ if (Types.Events.isMetaCharsetCheck(event)) {
70
+ metaCharsetCheckEventsArray.push(event);
71
+ return;
72
+ }
73
+
63
74
  if (!Types.Events.eventIsPageLoadEvent(event)) {
64
75
  return;
65
76
  }
@@ -393,6 +404,23 @@ export async function finalize(): Promise<void> {
393
404
  storePageLoadMetricAgainstNavigationId(navigation, pageLoadEvent);
394
405
  }
395
406
  }
407
+
408
+ const {navigationsByFrameId} = metaHandlerData();
409
+ metaCharsetCheckEventsArray.sort((a, b) => a.ts - b.ts);
410
+ for (const metaCharsetCheckEvent of metaCharsetCheckEventsArray) {
411
+ const frameId = metaCharsetCheckEvent.args.data?.frame;
412
+ if (!frameId) {
413
+ continue;
414
+ }
415
+ const navigation = Helpers.Trace.getNavigationForTraceEvent(metaCharsetCheckEvent, frameId, navigationsByFrameId);
416
+ if (!navigation) {
417
+ continue;
418
+ }
419
+ const eventsForNavigation =
420
+ Platform.MapUtilities.getWithDefault(metaCharsetCheckEventsByNavigation, navigation, () => []);
421
+ eventsForNavigation.push(metaCharsetCheckEvent);
422
+ }
423
+
396
424
  // NOTE: if you are looking for the TBT calculation, it has temporarily been
397
425
  // removed. See crbug.com/1424335 for details.
398
426
  const allFinalLCPEvents = gatherFinalLCPEvents();
@@ -421,12 +449,17 @@ export interface PageLoadMetricsData {
421
449
  * main frame.
422
450
  */
423
451
  allMarkerEvents: Types.Events.PageLoadEvent[];
452
+ /**
453
+ * MetaCharsetCheck events grouped by navigation.
454
+ */
455
+ metaCharsetCheckEventsByNavigation: Map<AnyNavigationStart, Types.Events.MetaCharsetCheck[]>;
424
456
  }
425
457
 
426
458
  export function data(): PageLoadMetricsData {
427
459
  return {
428
460
  metricScoresByFrameId,
429
461
  allMarkerEvents,
462
+ metaCharsetCheckEventsByNavigation,
430
463
  };
431
464
  }
432
465