chrome-devtools-frontend 1.0.1642899 → 1.0.1643099

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 (93) hide show
  1. package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
  2. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +4 -5
  3. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  4. package/front_end/generated/protocol.ts +7 -0
  5. package/front_end/models/ai_assistance/AiAgent2.ts +23 -5
  6. package/front_end/models/ai_assistance/AiConversation.ts +15 -12
  7. package/front_end/models/ai_assistance/AiUtils.ts +71 -0
  8. package/front_end/models/ai_assistance/ChangeManager.ts +2 -5
  9. package/front_end/models/ai_assistance/{agents/ConversationSummaryAgent.ts → ConversationSummary.ts} +29 -66
  10. package/front_end/models/ai_assistance/ExtensionScope.ts +1 -4
  11. package/front_end/models/ai_assistance/{agents/PerformanceAnnotationsAgent.ts → PerformanceAnnotations.ts} +47 -89
  12. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +31 -21
  13. package/front_end/models/ai_assistance/agents/AiAgent.ts +21 -6
  14. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +11 -0
  15. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +53 -3
  16. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +2 -0
  17. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +72 -79
  18. package/front_end/models/ai_assistance/agents/StorageAgent.ts +47 -38
  19. package/front_end/models/ai_assistance/agents/StylingAgent.ts +22 -21
  20. package/front_end/models/ai_assistance/ai_assistance.ts +6 -4
  21. package/front_end/models/ai_assistance/skills/styling.md +12 -4
  22. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +140 -0
  23. package/front_end/models/ai_assistance/tools/GetStyles.ts +6 -2
  24. package/front_end/models/ai_assistance/tools/Tool.ts +10 -1
  25. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +2 -0
  26. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +5 -7
  27. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +6 -7
  28. package/front_end/panels/ai_assistance/components/ChatMessage.ts +96 -4
  29. package/front_end/panels/ai_assistance/components/chatMessage.css +6 -0
  30. package/front_end/panels/application/components/AdsView.ts +219 -0
  31. package/front_end/panels/application/components/adsView.css +54 -0
  32. package/front_end/panels/application/components/components.ts +2 -0
  33. package/front_end/panels/console/SymbolizedErrorWidget.ts +73 -22
  34. package/front_end/panels/network/NetworkLogView.ts +5 -1
  35. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +5 -4
  36. package/front_end/third_party/chromium/README.chromium +1 -1
  37. package/front_end/third_party/lighthouse/README.chromium +2 -2
  38. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1607 -5733
  39. package/front_end/third_party/lighthouse/locales/ar-XB.json +290 -65
  40. package/front_end/third_party/lighthouse/locales/ar.json +290 -65
  41. package/front_end/third_party/lighthouse/locales/bg.json +290 -65
  42. package/front_end/third_party/lighthouse/locales/ca.json +295 -70
  43. package/front_end/third_party/lighthouse/locales/cs.json +290 -65
  44. package/front_end/third_party/lighthouse/locales/da.json +294 -69
  45. package/front_end/third_party/lighthouse/locales/de.json +295 -70
  46. package/front_end/third_party/lighthouse/locales/el.json +290 -65
  47. package/front_end/third_party/lighthouse/locales/en-GB.json +290 -65
  48. package/front_end/third_party/lighthouse/locales/en-US.json +79 -67
  49. package/front_end/third_party/lighthouse/locales/en-XA.json +253 -64
  50. package/front_end/third_party/lighthouse/locales/en-XL.json +79 -67
  51. package/front_end/third_party/lighthouse/locales/es-419.json +290 -65
  52. package/front_end/third_party/lighthouse/locales/es.json +298 -73
  53. package/front_end/third_party/lighthouse/locales/fi.json +290 -65
  54. package/front_end/third_party/lighthouse/locales/fil.json +290 -65
  55. package/front_end/third_party/lighthouse/locales/fr.json +294 -69
  56. package/front_end/third_party/lighthouse/locales/he.json +293 -68
  57. package/front_end/third_party/lighthouse/locales/hi.json +291 -66
  58. package/front_end/third_party/lighthouse/locales/hr.json +290 -65
  59. package/front_end/third_party/lighthouse/locales/hu.json +290 -65
  60. package/front_end/third_party/lighthouse/locales/id.json +290 -65
  61. package/front_end/third_party/lighthouse/locales/it.json +294 -69
  62. package/front_end/third_party/lighthouse/locales/ja.json +290 -65
  63. package/front_end/third_party/lighthouse/locales/ko.json +290 -65
  64. package/front_end/third_party/lighthouse/locales/lt.json +290 -65
  65. package/front_end/third_party/lighthouse/locales/lv.json +290 -65
  66. package/front_end/third_party/lighthouse/locales/nl.json +290 -65
  67. package/front_end/third_party/lighthouse/locales/no.json +290 -65
  68. package/front_end/third_party/lighthouse/locales/pl.json +290 -65
  69. package/front_end/third_party/lighthouse/locales/pt-PT.json +291 -66
  70. package/front_end/third_party/lighthouse/locales/pt.json +290 -65
  71. package/front_end/third_party/lighthouse/locales/ro.json +290 -65
  72. package/front_end/third_party/lighthouse/locales/ru.json +301 -76
  73. package/front_end/third_party/lighthouse/locales/sk.json +291 -66
  74. package/front_end/third_party/lighthouse/locales/sl.json +290 -65
  75. package/front_end/third_party/lighthouse/locales/sr-Latn.json +290 -65
  76. package/front_end/third_party/lighthouse/locales/sr.json +290 -65
  77. package/front_end/third_party/lighthouse/locales/sv.json +297 -72
  78. package/front_end/third_party/lighthouse/locales/ta.json +291 -66
  79. package/front_end/third_party/lighthouse/locales/te.json +293 -68
  80. package/front_end/third_party/lighthouse/locales/th.json +291 -66
  81. package/front_end/third_party/lighthouse/locales/tr.json +290 -65
  82. package/front_end/third_party/lighthouse/locales/uk.json +290 -65
  83. package/front_end/third_party/lighthouse/locales/vi.json +291 -66
  84. package/front_end/third_party/lighthouse/locales/zh-HK.json +292 -67
  85. package/front_end/third_party/lighthouse/locales/zh-TW.json +291 -66
  86. package/front_end/third_party/lighthouse/locales/zh.json +291 -66
  87. package/front_end/third_party/lighthouse/report/bundle.d.ts +6 -6
  88. package/front_end/third_party/lighthouse/report/bundle.js +4 -7
  89. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  90. package/front_end/ui/legacy/Widget.ts +32 -8
  91. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  92. package/mcp/mcp.ts +1 -0
  93. package/package.json +1 -1
@@ -27,10 +27,8 @@ import {
27
27
  } from './AiAgent.js';
28
28
  import {
29
29
  type CreateExtensionScopeFunction,
30
- executeJavaScriptFunction,
31
30
  type ExecuteJsAgentOptions,
32
31
  executeJsCode,
33
- JavascriptExecutor
34
32
  } from './ExecuteJavascript.js';
35
33
 
36
34
  const preamble = `You are the most advanced CSS/DOM/HTML debugging assistant integrated into Chrome DevTools.
@@ -169,32 +167,21 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
169
167
  }
170
168
 
171
169
  #execJs: typeof executeJsCode;
172
- #javascriptExecutor: JavascriptExecutor;
173
170
 
174
171
  #changes: ChangeManager;
175
172
  #createExtensionScope: CreateExtensionScopeFunction;
176
173
  #greenDevEmulationScreenshot: string|null = null;
177
174
  #greenDevEmulationAxTree: string|null = null;
178
175
  #hasAddedEmulationInstructions = false;
179
- #currentTurnId = 0;
180
176
 
181
177
  constructor(opts: ExecuteJsAgentOptions) {
182
178
  super(opts);
183
179
 
184
180
  this.#changes = opts.changeManager || new ChangeManager();
185
181
  this.#execJs = opts.execJs ?? executeJsCode;
186
- this.#createExtensionScope =
187
- opts.createExtensionScope ?? ((changes: ChangeManager) => {
188
- return new ExtensionScope(changes, this.sessionId, this.context?.getItem() ?? null, this.#currentTurnId);
189
- });
190
- this.#javascriptExecutor = new JavascriptExecutor(
191
- {
192
- executionMode: this.executionMode,
193
- getContextNode: () => this.#getSelectedNode(),
194
- createExtensionScope: this.#createExtensionScope.bind(this),
195
- changes: this.#changes,
196
- },
197
- this.#execJs);
182
+ this.#createExtensionScope = opts.createExtensionScope ?? ((changes: ChangeManager) => {
183
+ return new ExtensionScope(changes, this.sessionId, this.context?.getItem() ?? null);
184
+ });
198
185
 
199
186
  const getStylesTool = ToolRegistry.get(ToolName.GET_STYLES);
200
187
  if (!getStylesTool) {
@@ -209,7 +196,25 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
209
196
  }),
210
197
  });
211
198
 
212
- this.declareFunction('executeJavaScript', executeJavaScriptFunction(this.#javascriptExecutor));
199
+ const executeJsTool = ToolRegistry.get(ToolName.EXECUTE_JAVASCRIPT);
200
+ if (!executeJsTool) {
201
+ throw new Error('Required tool "executeJavaScript" not found');
202
+ }
203
+ this.declareFunction(ToolName.EXECUTE_JAVASCRIPT, {
204
+ description: executeJsTool.description,
205
+ parameters: executeJsTool.parameters,
206
+ displayInfoFromArgs: executeJsTool.displayInfoFromArgs,
207
+ handler: (args, options) => executeJsTool.handler(
208
+ args,
209
+ {
210
+ conversationContext: this.context ?? null,
211
+ changeManager: this.#changes,
212
+ createExtensionScope: this.#createExtensionScope.bind(this),
213
+ execJs: this.#execJs,
214
+ },
215
+ options,
216
+ ),
217
+ });
213
218
 
214
219
  if (Annotations.AnnotationRepository.annotationsEnabled()) {
215
220
  this.declareFunction<{
@@ -515,10 +520,6 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
515
520
  }
516
521
  }
517
522
 
518
- protected override async preRun(): Promise<void> {
519
- this.#currentTurnId++;
520
- }
521
-
522
523
  override async enhanceQuery(
523
524
  query: string, selectedElement: ConversationContext<SDK.DOMModel.DOMNode>|null,
524
525
  multimodalInputType?: MultimodalInputType): Promise<string> {
@@ -6,7 +6,6 @@ import * as AgentProject from './AgentProject.js';
6
6
  import * as AccessibilityAgent from './agents/AccessibilityAgent.js';
7
7
  import * as AiAgent from './agents/AiAgent.js';
8
8
  import * as ContextSelectionAgent from './agents/ContextSelectionAgent.js';
9
- import * as ConversationSummaryAgent from './agents/ConversationSummaryAgent.js';
10
9
  import * as FileAgent from './agents/FileAgent.js';
11
10
  import * as GreenDevAgent from './agents/GreenDevAgent.js';
12
11
  import * as GreenDevAgentAntigravityCliSocketClient from './agents/GreenDevAgentAntigravityCliSocketClient.js';
@@ -14,7 +13,6 @@ import * as GreenDevAgentGeminiCliSocketClient from './agents/GreenDevAgentGemin
14
13
  import * as NetworkAgent from './agents/NetworkAgent.js';
15
14
  import * as PatchAgent from './agents/PatchAgent.js';
16
15
  import * as PerformanceAgent from './agents/PerformanceAgent.js';
17
- import * as PerformanceAnnotationsAgent from './agents/PerformanceAnnotationsAgent.js';
18
16
  import * as StorageAgent from './agents/StorageAgent.js';
19
17
  import * as StylingAgent from './agents/StylingAgent.js';
20
18
  import * as AiAgent2 from './AiAgent2.js';
@@ -25,6 +23,7 @@ import * as AiUtils from './AiUtils.js';
25
23
  import * as BuiltInAi from './BuiltInAi.js';
26
24
  import * as ChangeManager from './ChangeManager.js';
27
25
  import * as DOMNodeContext from './contexts/DOMNodeContext.js';
26
+ import * as ConversationSummary from './ConversationSummary.js';
28
27
  import * as FileFormatter from './data_formatters/FileFormatter.js';
29
28
  import * as LighthouseFormatter from './data_formatters/LighthouseFormatter.js';
30
29
  import * as NetworkRequestFormatter from './data_formatters/NetworkRequestFormatter.js';
@@ -38,7 +37,9 @@ import * as Injected from './injected.js';
38
37
  import * as AICallTree from './performance/AICallTree.js';
39
38
  import * as AIContext from './performance/AIContext.js';
40
39
  import * as AIQueries from './performance/AIQueries.js';
40
+ import * as PerformanceAnnotations from './PerformanceAnnotations.js';
41
41
  import * as StorageItem from './StorageItem.js';
42
+ import * as ExecuteJavaScript from './tools/ExecuteJavaScript.js';
42
43
  import * as GetStyles from './tools/GetStyles.js';
43
44
  import * as Tool from './tools/Tool.js';
44
45
  import * as ToolRegistry from './tools/ToolRegistry.js';
@@ -58,10 +59,11 @@ export {
58
59
  BuiltInAi,
59
60
  ChangeManager,
60
61
  ContextSelectionAgent,
61
- ConversationSummaryAgent,
62
+ ConversationSummary,
62
63
  Debug,
63
64
  DOMNodeContext,
64
65
  EvaluateAction,
66
+ ExecuteJavaScript,
65
67
  ExtensionScope,
66
68
  FileAgent,
67
69
  FileFormatter,
@@ -75,7 +77,7 @@ export {
75
77
  NetworkRequestFormatter,
76
78
  PatchAgent,
77
79
  PerformanceAgent,
78
- PerformanceAnnotationsAgent,
80
+ PerformanceAnnotations,
79
81
  PerformanceInsightFormatter,
80
82
  PerformanceTraceFormatter,
81
83
  StorageAgent,
@@ -2,12 +2,13 @@
2
2
  name: styling
3
3
  description: Helping with CSS and styling
4
4
  allowed-tools:
5
+ - executeJavaScript
5
6
  - getStyles
6
7
  ---
7
- You are a CSS/DOM/HTML debugging assistant.
8
+ You are the most advanced CSS/DOM/HTML debugging assistant integrated into Chrome DevTools.
8
9
  You always suggest considering the best web development practices and the newest platform features such as view transitions.
9
10
  The user selected a DOM element in the browser's DevTools and sends a query about the page or the selected DOM element.
10
- First, examine the provided context, then use the getStyles function to gather additional context and resolve the user request.
11
+ First, examine the provided context, then use the getStyles and executeJavaScript functions to gather additional context and resolve the user request.
11
12
 
12
13
  # Considerations
13
14
 
@@ -19,10 +20,17 @@ First, examine the provided context, then use the getStyles function to gather a
19
20
  * Please answer only if you are sure about the answer. Otherwise, explain why you're not able to answer.
20
21
  * When answering, always consider MULTIPLE possible solutions.
21
22
  * When answering, remember to consider CSS concepts such as the CSS cascade, explicit and implicit stacking contexts and various CSS layout types.
22
- * Use the getStyles function available to you to investigate and fulfill the user request.
23
- * ALWAYS OUTPUT a list of follow-up queries at the end of your text response. The format is SUGGESTIONS: ["suggestion1", "suggestion2", "suggestion3"]. Make sure that the array and the `SUGGESTIONS: ` text is in the same line.
23
+ * Use the functions available to you to investigate and fulfill the user request.
24
+ * After applying a fix, please ask the user to confirm if the fix worked or not.
25
+ * ALWAYS OUTPUT a list of follow-up queries at the end of your text response. The format is SUGGESTIONS: ["suggestion1", "suggestion2", "suggestion3"]. Make sure that the array and the `SUGGESTIONS: ` text is in the same line. You're also capable of executing the fix for the issue user mentioned. Reflect this in your suggestions.
24
26
  * 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.
27
+ * **CRITICAL** NEVER output text before a function call. Always do a function call first.
25
28
  * **CRITICAL** When answering questions about positioning or layout, ALWAYS inspect `position`, `display` and all other related properties. You MUST provide a specific list of CSS property names when calling getStyles. Do not use generic values like "all" or "*".
29
+ * **CRITICAL** When writing JavaScript via the `executeJavaScript` tool:
30
+ - To return data, define a top-level `data` variable and populate it with a JSON-serializable object.
31
+ - If you modify styles on an element, ALWAYS call the pre-defined global `async setElementStyles(el: Element, styles: object)` function. This function is an internal mechanism and should never be presented to the user.
32
+ - Never assume a selector for the elements unless you verified your knowledge.
33
+ - Consider that `data` variables from previous function calls are not available in a new function call.
26
34
  * **CRITICAL** You are a CSS/DOM/HTML debugging assistant. NEVER provide answers to questions of unrelated topics such as legal advice, financial advice, personal opinions, medical advice, religion, race, politics, sexuality, gender, or any other non web-development topics. Answer "Sorry, I can't answer that. I'm best at questions about debugging web pages." to such questions.
27
35
 
28
36
  ## Response Structure
@@ -0,0 +1,140 @@
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
+ import type {FunctionCallHandlerResult, FunctionHandlerOptions,} from '../agents/AiAgent.js';
8
+ import {JavascriptExecutor} from '../agents/ExecuteJavascript.js';
9
+ import {DOMNodeContext} from '../contexts/DOMNodeContext.js';
10
+
11
+ import {
12
+ type Tool,
13
+ type ToolArgs,
14
+ type ToolContext,
15
+ ToolName,
16
+ } from './Tool.js';
17
+
18
+ export interface ExecuteJavaScriptArgs extends ToolArgs {
19
+ code: string;
20
+ explanation: string;
21
+ title: string;
22
+ }
23
+
24
+ export class ExecuteJavaScriptTool implements Tool<ExecuteJavaScriptArgs, unknown> {
25
+ readonly name = ToolName.EXECUTE_JAVASCRIPT;
26
+
27
+ readonly description =
28
+ 'This function allows you to run JavaScript code on the inspected page to access the element styles and page content.\nCall this function to gather additional information or modify the page state. Call this function enough times to investigate the user request.';
29
+
30
+ readonly parameters: Host.AidaClient.FunctionObjectParam<keyof ExecuteJavaScriptArgs> = {
31
+ type: Host.AidaClient.ParametersTypes.OBJECT,
32
+ description: '',
33
+ nullable: false,
34
+ properties: {
35
+ code: {
36
+ type: Host.AidaClient.ParametersTypes.STRING,
37
+ description:
38
+ `JavaScript code snippet to run on the inspected page. Make sure the code is formatted for readability.
39
+
40
+ # Instructions
41
+
42
+ * To return data, define a top-level \`data\` variable and populate it with data you want to get. Only JSON-serializable objects can be assigned to \`data\`.
43
+ * If you modify styles on an element, ALWAYS call the pre-defined global \`async setElementStyles(el: Element, styles: object)\` function. This function is an internal mechanism for you and should never be presented as a command/advice to the user.
44
+ * **CRITICAL** Only get styles that might be relevant to the user request.
45
+ * **CRITICAL** Never assume a selector for the elements unless you verified your knowledge.
46
+ * **CRITICAL** Consider that \`data\` variable from the previous function calls are not available in a new function call.
47
+
48
+ For example, the code to change element styles:
49
+
50
+ \`\`\`
51
+ await setElementStyles($0, {
52
+ color: 'blue',
53
+ });
54
+ \`\`\`
55
+
56
+ For example, the code to get overlapping elements:
57
+
58
+ \`\`\`
59
+ const data = {
60
+ overlappingElements: Array.from(document.querySelectorAll('*'))
61
+ .filter(el => {
62
+ const rect = el.getBoundingClientRect();
63
+ const popupRect = $0.getBoundingClientRect();
64
+ return (
65
+ el !== $0 &&
66
+ rect.left < popupRect.right &&
67
+ rect.right > popupRect.left &&
68
+ rect.top < popupRect.bottom &&
69
+ rect.bottom > popupRect.top
70
+ );
71
+ })
72
+ .map(el => ({
73
+ tagName: el.tagName,
74
+ id: el.id,
75
+ className: el.className,
76
+ zIndex: window.getComputedStyle(el)['z-index']
77
+ }))
78
+ };
79
+ \`\`\`
80
+ `,
81
+ },
82
+ explanation: {
83
+ type: Host.AidaClient.ParametersTypes.STRING,
84
+ description: 'Explain why you want to run this code',
85
+ },
86
+ title: {
87
+ type: Host.AidaClient.ParametersTypes.STRING,
88
+ description: 'Provide a summary of what the code does. For example, "Checking related element styles".',
89
+ },
90
+ },
91
+ required: ['code', 'explanation', 'title'],
92
+ };
93
+
94
+ displayInfoFromArgs(params: ExecuteJavaScriptArgs): {
95
+ title: string,
96
+ thought: string,
97
+ action: string,
98
+ } {
99
+ return {
100
+ title: params.title,
101
+ thought: params.explanation,
102
+ action: params.code,
103
+ };
104
+ }
105
+
106
+ async handler(
107
+ params: ExecuteJavaScriptArgs,
108
+ context: ToolContext,
109
+ options?: FunctionHandlerOptions,
110
+ ): Promise<FunctionCallHandlerResult<unknown>> {
111
+ const activeContext = context.conversationContext;
112
+ if (!activeContext || !(activeContext instanceof DOMNodeContext)) {
113
+ return {error: 'Error: Could not find the currently selected element.'};
114
+ }
115
+
116
+ const selectedNode = activeContext.getItem();
117
+ if (!selectedNode) {
118
+ return {error: 'Error: Could not find the currently selected element.'};
119
+ }
120
+
121
+ const executionMode = Root.Runtime.hostConfig.devToolsFreestyler?.executionMode ??
122
+ Root.Runtime.HostConfigFreestylerExecutionMode.ALL_SCRIPTS;
123
+
124
+ const changes = context.changeManager;
125
+ const createExtensionScope = context.createExtensionScope;
126
+ if (!changes || !createExtensionScope) {
127
+ return {error: 'Internal Error: Required change manager or extension scope creator is missing.'};
128
+ }
129
+
130
+ const executor = new JavascriptExecutor({
131
+ executionMode,
132
+ getContextNode: () => selectedNode,
133
+ createExtensionScope,
134
+ changes,
135
+ },
136
+ context.execJs);
137
+
138
+ return await executor.executeAction(params.code, options);
139
+ }
140
+ }
@@ -5,7 +5,7 @@
5
5
  import * as Host from '../../../core/host/host.js';
6
6
  import * as SDK from '../../../core/sdk/sdk.js';
7
7
  import type * as Protocol from '../../../generated/protocol.js';
8
- import type {ComputedStyleAiWidget, FunctionCallHandlerResult} from '../agents/AiAgent.js';
8
+ import type {ComputedStyleAiWidget, FunctionCallHandlerResult, FunctionHandlerOptions} from '../agents/AiAgent.js';
9
9
  import {DOMNodeContext} from '../contexts/DOMNodeContext.js';
10
10
  import {debugLog} from '../debug.js';
11
11
 
@@ -69,7 +69,11 @@ export class GetStylesTool implements Tool<GetStylesArgs, unknown> {
69
69
  };
70
70
  }
71
71
 
72
- async handler(params: GetStylesArgs, context: ToolContext): Promise<FunctionCallHandlerResult<unknown>> {
72
+ async handler(
73
+ params: GetStylesArgs,
74
+ context: ToolContext,
75
+ _options?: FunctionHandlerOptions,
76
+ ): Promise<FunctionCallHandlerResult<unknown>> {
73
77
  const widgets: ComputedStyleAiWidget[] = [];
74
78
  const result:
75
79
  Record<string, {computed: Record<string, string|undefined>, authored: Record<string, string|undefined>}> = {};
@@ -3,13 +3,20 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import type * as Host from '../../../core/host/host.js';
6
- import type {ConversationContext, FunctionCallHandlerResult,} from '../agents/AiAgent.js';
6
+ import type {ConversationContext, FunctionCallHandlerResult, FunctionHandlerOptions} from '../agents/AiAgent.js';
7
+ import type {executeJsCode} from '../agents/ExecuteJavascript.js';
8
+ import type {ChangeManager} from '../ChangeManager.js';
7
9
 
8
10
  /**
9
11
  * Context provided to the tool's handler execution.
10
12
  */
11
13
  export interface ToolContext {
12
14
  conversationContext: ConversationContext<unknown>|null;
15
+ changeManager?: ChangeManager;
16
+ createExtensionScope?: (changes: ChangeManager) => {
17
+ install(): Promise<void>, uninstall(): Promise<void>,
18
+ };
19
+ execJs?: typeof executeJsCode;
13
20
  }
14
21
 
15
22
  /**
@@ -18,6 +25,7 @@ export interface ToolContext {
18
25
  export type ToolArgs = Record<string, unknown>;
19
26
 
20
27
  export const enum ToolName {
28
+ EXECUTE_JAVASCRIPT = 'executeJavaScript',
21
29
  GET_STYLES = 'getStyles',
22
30
  }
23
31
 
@@ -51,5 +59,6 @@ export interface Tool<Args extends ToolArgs = ToolArgs, ReturnType = unknown, >
51
59
  handler(
52
60
  args: Args,
53
61
  context: ToolContext,
62
+ options?: FunctionHandlerOptions,
54
63
  ): Promise<FunctionCallHandlerResult<ReturnType>>;
55
64
  }
@@ -2,6 +2,7 @@
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 {ExecuteJavaScriptTool} from './ExecuteJavaScript.js';
5
6
  import {GetStylesTool} from './GetStyles.js';
6
7
  import {type Tool, ToolName} from './Tool.js';
7
8
 
@@ -10,6 +11,7 @@ import {type Tool, ToolName} from './Tool.js';
10
11
  * Keep this type concrete (no type-erasure) to preserve exact tool types.
11
12
  */
12
13
  export const TOOLS = {
14
+ [ToolName.EXECUTE_JAVASCRIPT]: new ExecuteJavaScriptTool(),
13
15
  [ToolName.GET_STYLES]: new GetStylesTool(),
14
16
  };
15
17
 
@@ -244,8 +244,6 @@ export class HeapSnapshotLoaderProxy extends HeapSnapshotProxyObject implements
244
244
  const snapshotProxy = await this.callFactoryMethodPromise('buildSnapshot', HeapSnapshotProxy, [channel.port1]);
245
245
  secondWorker.dispose();
246
246
  this.dispose();
247
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
248
- // @ts-expect-error
249
247
  snapshotProxy.setProfileUid(this.profileUid);
250
248
  await snapshotProxy.updateStaticData();
251
249
  this.snapshotReceivedCallback(snapshotProxy);
@@ -254,7 +252,7 @@ export class HeapSnapshotLoaderProxy extends HeapSnapshotProxyObject implements
254
252
 
255
253
  export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
256
254
  staticData: HeapSnapshotModel.StaticData|null;
257
- profileUid?: string;
255
+ profileUid?: number;
258
256
 
259
257
  constructor(worker: HeapSnapshotWorkerProxy, objectId: number) {
260
258
  super(worker, objectId);
@@ -279,7 +277,7 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
279
277
  }
280
278
 
281
279
  calculateSnapshotDiff(
282
- baseSnapshotId: string,
280
+ baseSnapshotId: number,
283
281
  baseSnapshotAggregates: Record<string, HeapSnapshotModel.AggregateForDiff>,
284
282
  ): Promise<Record<string, HeapSnapshotModel.Diff>> {
285
283
  return this.callMethodPromise('calculateSnapshotDiff', baseSnapshotId, baseSnapshotAggregates);
@@ -301,7 +299,7 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
301
299
  return this.callFactoryMethod('createRetainingEdgesProvider', HeapSnapshotProviderProxy, nodeIndex);
302
300
  }
303
301
 
304
- createAddedNodesProvider(baseSnapshotId: string, classKey: string): HeapSnapshotProviderProxy {
302
+ createAddedNodesProvider(baseSnapshotId: number, classKey: string): HeapSnapshotProviderProxy {
305
303
  return this.callFactoryMethod('createAddedNodesProvider', HeapSnapshotProviderProxy, baseSnapshotId, classKey);
306
304
  }
307
305
 
@@ -391,11 +389,11 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
391
389
  return this.staticData.totalSize;
392
390
  }
393
391
 
394
- get uid(): string|undefined {
392
+ get uid(): number|undefined {
395
393
  return this.profileUid;
396
394
  }
397
395
 
398
- setProfileUid(profileUid: string): void {
396
+ setProfileUid(profileUid: number): void {
399
397
  this.profileUid = profileUid;
400
398
  }
401
399
 
@@ -657,7 +657,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
657
657
  // NodeJS debugging does not have Elements panel, thus this action might not exist.
658
658
  #toggleSearchElementAction?: UI.ActionRegistration.Action;
659
659
  #aidaClient: Host.AidaClient.AidaClient;
660
- #conversationSummaryAgent?: AiAssistanceModel.ConversationSummaryAgent.ConversationSummaryAgent;
660
+ #conversationSummary?: AiAssistanceModel.ConversationSummary.ConversationSummary;
661
661
  #viewOutput: PanelViewOutput = {};
662
662
  #serverSideLoggingEnabled = isAiAssistanceServerSideLoggingEnabled();
663
663
  #aiAssistanceEnabledSetting: Common.Settings.Setting<boolean>|undefined;
@@ -784,17 +784,16 @@ export class AiAssistancePanel extends UI.Panel.Panel {
784
784
  markdownRenderer,
785
785
  conversationMarkdown: this.#conversation.getConversationMarkdown(),
786
786
  generateConversationSummary: async (markdown: string) => {
787
- if (!this.#conversationSummaryAgent) {
788
- this.#conversationSummaryAgent = new AiAssistanceModel.ConversationSummaryAgent.ConversationSummaryAgent({
787
+ if (!this.#conversationSummary) {
788
+ this.#conversationSummary = new AiAssistanceModel.ConversationSummary.ConversationSummary({
789
789
  aidaClient: this.#aidaClient,
790
790
  serverSideLoggingEnabled: this.#serverSideLoggingEnabled,
791
791
  });
792
792
  }
793
- return await this.#conversationSummaryAgent.summarizeConversation(markdown);
793
+ return await this.#conversationSummary.summarizeConversation(markdown);
794
794
  },
795
- onTextSubmit: async (
796
- text: string, imageInput?: Host.AidaClient.Part,
797
- multimodalInputType?: AiAssistanceModel.AiAgent.MultimodalInputType) => {
795
+ onTextSubmit: async (text: string, imageInput?: Host.AidaClient.Part,
796
+ multimodalInputType?: AiAssistanceModel.AiAgent.MultimodalInputType) => {
798
797
  const submit = (): void => {
799
798
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceQuerySubmitted);
800
799
  void this.#startConversation(text, imageInput, multimodalInputType);
@@ -14,9 +14,9 @@ import * as SDK from '../../../core/sdk/sdk.js';
14
14
  import type * as Protocol from '../../../generated/protocol.js';
15
15
  import type {
16
16
  AiWidget, BottomUpTreeAiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget, DomTreeAiWidget, LighthouseReportAiWidget,
17
- NetworkRequestGeneralHeadersAiWidget, PerfInsightAiWidget, PerformanceTraceAiWidget, SourceCodeAiWidget,
18
- SourceFileAiWidget, SourceFilesListAiWidget, StylePropertiesAiWidget, TimelineEventSummaryAiWidget,
19
- TimelineRangeSummaryAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
17
+ NetworkRequestGeneralHeadersAiWidget, NetworkRequestsListAiWidget, PerfInsightAiWidget, PerformanceTraceAiWidget,
18
+ SourceCodeAiWidget, SourceFileAiWidget, SourceFilesListAiWidget, StylePropertiesAiWidget,
19
+ TimelineEventSummaryAiWidget, TimelineRangeSummaryAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
20
20
  import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
21
21
  import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
22
22
  import * as Formatter from '../../../models/formatter/formatter.js';
@@ -30,6 +30,7 @@ import * as Buttons from '../../../ui/components/buttons/buttons.js';
30
30
  import * as Input from '../../../ui/components/input/input.js';
31
31
  import type * as MarkdownView from '../../../ui/components/markdown_view/markdown_view.js';
32
32
  import type {MarkdownLitRenderer} from '../../../ui/components/markdown_view/MarkdownView.js';
33
+ import * as Snackbars from '../../../ui/components/snackbars/snackbars.js';
33
34
  import * as UIHelpers from '../../../ui/helpers/helpers.js';
34
35
  import * as UI from '../../../ui/legacy/legacy.js';
35
36
  import * as Lit from '../../../ui/lit/lit.js';
@@ -414,6 +415,14 @@ const UIStringsNotTranslate = {
414
415
  * @description Title for the character set declaration widget.
415
416
  */
416
417
  characterSet: 'Character set declaration',
418
+ /**
419
+ * @description Title for the network requests list widget.
420
+ */
421
+ networkRequests: 'Network requests',
422
+ /**
423
+ * @description Accessible label for the reveal button in the network requests list widget.
424
+ */
425
+ revealFirstNetworkRequest: 'Reveal first network request in Network panel',
417
426
  /**
418
427
  * @description Title for the source files list widget.
419
428
  */
@@ -1300,7 +1309,12 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
1300
1309
  if (response === null) {
1301
1310
  return;
1302
1311
  }
1303
- void Common.Revealer.reveal(response?.revealable);
1312
+ Common.Revealer.reveal(response?.revealable).catch((error: Error) => {
1313
+ if (!error.message) {
1314
+ return;
1315
+ }
1316
+ Snackbars.Snackbar.Snackbar.show({message: error.message});
1317
+ });
1304
1318
  }
1305
1319
 
1306
1320
  const classes = Lit.Directives.classMap({
@@ -1466,6 +1480,79 @@ async function makeSourceFilesListWidget(widgetData: SourceFilesListAiWidget): P
1466
1480
  };
1467
1481
  }
1468
1482
 
1483
+ const expandedNetworkRequestsWidgets = new WeakSet<NetworkRequestsListAiWidget>();
1484
+
1485
+ // A widget with a table of the list of network requests sent to the agent.
1486
+ // Only show 15 requests maximum in collapsed version. The rest of the requests
1487
+ // will be hidden unless the user clicks "Show all".
1488
+ async function makeNetworkRequestsListWidget(widgetData: NetworkRequestsListAiWidget):
1489
+ Promise<WidgetMakerResponse|null> {
1490
+ const requests = widgetData.data.requests;
1491
+ if (requests.length === 0) {
1492
+ return null;
1493
+ }
1494
+
1495
+ const isExpanded = expandedNetworkRequestsWidgets.has(widgetData);
1496
+ // We only want just expanded widget to be expanded, if the user closed and reopened the walkthrought, the widget should be collapsed again.
1497
+ // Therefore, after rendering the widget, we remove the widget from the set of expanded widgets so that it is collapsed on next render.
1498
+ if (isExpanded) {
1499
+ expandedNetworkRequestsWidgets.delete(widgetData);
1500
+ }
1501
+ const displayedRequests = isExpanded ? requests : requests.slice(0, 15);
1502
+
1503
+ // The table contains same fields as the ones sent to the agent.
1504
+ // clang-format off
1505
+ const renderedWidget = html`
1506
+ <div class="network-requests-widget">
1507
+ <devtools-data-grid striped inline>
1508
+ <table>
1509
+ <tr>
1510
+ <th id="name" weight="4">${i18n.i18n.lockedString('Name')}</th>
1511
+ <th id="status" weight="1">${i18n.i18n.lockedString('Status')}</th>
1512
+ <th id="size" weight="1">${i18n.i18n.lockedString('Size')}</th>
1513
+ <th id="time" weight="1">${i18n.i18n.lockedString('Time')}</th>
1514
+ </tr>
1515
+ ${displayedRequests.map(request => html`
1516
+ <tr>
1517
+ <td>${request.name()}</td>
1518
+ <td>${request.statusCode}</td>
1519
+ <td>${i18n.ByteUtilities.formatBytesToKb(request.transferSize)}</td>
1520
+ <td>${i18n.TimeUtilities.secondsToString(request.duration)}</td>
1521
+ </tr>
1522
+ `)}
1523
+ </table>
1524
+ </devtools-data-grid>
1525
+ ${!isExpanded && requests.length > 15 ? html`
1526
+ <div class="show-all-container">
1527
+ <button class="show-all-widget-requests-button text-button"
1528
+ jslog=${VisualLogging.action('show-all-widget-requests-button').track({click: true})}
1529
+ @click=${(e: Event) => {
1530
+ expandedNetworkRequestsWidgets.add(widgetData);
1531
+ const widgetEl = (e.target as HTMLElement).closest('.widget');
1532
+ if (widgetEl) {
1533
+ const widget = UI.Widget.Widget.get(widgetEl) as ChatMessage;
1534
+ if (widget && widget.performUpdate) {
1535
+ void widget.performUpdate();
1536
+ }
1537
+ }
1538
+ }}>
1539
+ ${i18n.i18n.lockedString(`Show all ${requests.length} network requests`)}
1540
+ </button>
1541
+ </div>
1542
+ ` : Lit.nothing}
1543
+ </div>
1544
+ `;
1545
+ // clang-format on
1546
+
1547
+ return {
1548
+ renderedWidget,
1549
+ title: lockedString(UIStringsNotTranslate.networkRequests),
1550
+ revealable: requests[0],
1551
+ accessibleRevealLabel: lockedString(UIStringsNotTranslate.revealFirstNetworkRequest),
1552
+ jslogContext: 'network-requests-list-widget',
1553
+ };
1554
+ }
1555
+
1469
1556
  function renderNetworkRequestPreview(networkRequest: NonNullable<DomTreeAiWidget['data']['networkRequest']>):
1470
1557
  Lit.TemplateResult {
1471
1558
  const filename = networkRequest.url.split('/').pop() || networkRequest.url;
@@ -1583,6 +1670,8 @@ export function getWidgetSignature(widget: AiWidget): string {
1583
1670
  return `${widget.name}:${widget.data.request.requestId()}`;
1584
1671
  case 'SOURCE_CODE':
1585
1672
  return `${widget.name}:${widget.data.url}:${widget.data.line ?? ''}:${widget.data.column ?? ''}`;
1673
+ case 'NETWORK_REQUESTS_LIST':
1674
+ return `${widget.name}:${widget.data.requests.map(r => r.requestId()).join(',')}`;
1586
1675
  default:
1587
1676
  Platform.assertNever(widget, 'Unknown AiWidget name');
1588
1677
  }
@@ -1672,6 +1761,9 @@ async function renderWidgets(
1672
1761
  case 'SOURCE_FILES_LIST':
1673
1762
  response = await makeSourceFilesListWidget(widgetData);
1674
1763
  break;
1764
+ case 'NETWORK_REQUESTS_LIST':
1765
+ response = await makeNetworkRequestsListWidget(widgetData);
1766
+ break;
1675
1767
  case 'LIGHTHOUSE_REPORT':
1676
1768
  response = await makeLighthouseReportWidget(widgetData);
1677
1769
  break;
@@ -624,4 +624,10 @@
624
624
  display: none;
625
625
  }
626
626
  }
627
+
628
+ .network-requests-widget {
629
+ display: flex;
630
+ flex-direction: column;
631
+ gap: var(--sys-size-4);
632
+ }
627
633
  }