chrome-devtools-frontend 1.0.1642845 → 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 (128) hide show
  1. package/SECURITY.md +1 -0
  2. package/front_end/core/host/UserMetrics.ts +2 -1
  3. package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
  4. package/front_end/core/sdk/CSSMatchedStyles.ts +55 -26
  5. package/front_end/core/sdk/CSSRule.ts +1 -0
  6. package/front_end/core/sdk/DebuggerModel.ts +5 -0
  7. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +4 -3
  8. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +4 -3
  9. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +4 -5
  10. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  11. package/front_end/generated/protocol.ts +7 -0
  12. package/front_end/models/ai_assistance/AiAgent2.ts +100 -18
  13. package/front_end/models/ai_assistance/AiConversation.ts +18 -14
  14. package/front_end/models/ai_assistance/AiUtils.ts +71 -0
  15. package/front_end/models/ai_assistance/ChangeManager.ts +2 -5
  16. package/front_end/models/ai_assistance/{agents/ConversationSummaryAgent.ts → ConversationSummary.ts} +29 -66
  17. package/front_end/models/ai_assistance/ExtensionScope.ts +1 -4
  18. package/front_end/models/ai_assistance/{agents/PerformanceAnnotationsAgent.ts → PerformanceAnnotations.ts} +47 -89
  19. package/front_end/models/ai_assistance/README.md +8 -0
  20. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +65 -40
  21. package/front_end/models/ai_assistance/agents/AiAgent.ts +37 -6
  22. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +11 -0
  23. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +55 -5
  24. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +2 -0
  25. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +119 -78
  26. package/front_end/models/ai_assistance/agents/StorageAgent.ts +47 -38
  27. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -25
  28. package/front_end/models/ai_assistance/agents/StylingAgent.ts +46 -326
  29. package/front_end/models/ai_assistance/ai_assistance.ts +14 -4
  30. package/front_end/models/ai_assistance/contexts/DOMNodeContext.snapshot.txt +51 -0
  31. package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +200 -0
  32. package/front_end/models/ai_assistance/skills/styling.md +44 -2
  33. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +140 -0
  34. package/front_end/models/ai_assistance/tools/GetStyles.ts +141 -0
  35. package/front_end/models/ai_assistance/tools/Tool.ts +64 -0
  36. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +36 -0
  37. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +5 -7
  38. package/front_end/models/lighthouse/LighthouseReporterTypes.ts +5 -0
  39. package/front_end/models/live-metrics/LiveMetrics.ts +24 -13
  40. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +2 -2
  41. package/front_end/models/stack_trace/StackTrace.ts +4 -1
  42. package/front_end/models/stack_trace/StackTraceImpl.ts +9 -2
  43. package/front_end/models/stack_trace/StackTraceModel.ts +17 -4
  44. package/front_end/models/stack_trace/Trie.ts +1 -1
  45. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +25 -22
  46. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
  47. package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
  48. package/front_end/panels/ai_assistance/components/ChatMessage.ts +96 -4
  49. package/front_end/panels/ai_assistance/components/chatMessage.css +6 -0
  50. package/front_end/panels/application/DOMStorageItemsView.ts +4 -0
  51. package/front_end/panels/application/KeyValueStorageItemsView.ts +39 -7
  52. package/front_end/panels/application/components/AdsView.ts +219 -0
  53. package/front_end/panels/application/components/adsView.css +54 -0
  54. package/front_end/panels/application/components/components.ts +2 -0
  55. package/front_end/panels/common/ExtensionServer.ts +26 -15
  56. package/front_end/panels/console/SymbolizedErrorWidget.ts +73 -22
  57. package/front_end/panels/elements/StandaloneStylesContainer.ts +1 -1
  58. package/front_end/panels/elements/StylePropertiesSection.ts +8 -0
  59. package/front_end/panels/elements/StylePropertyHighlighter.ts +4 -2
  60. package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -5
  61. package/front_end/panels/elements/StylesContainer.ts +1 -1
  62. package/front_end/panels/elements/StylesSidebarPane.ts +4 -4
  63. package/front_end/panels/layer_viewer/PaintProfilerView.ts +106 -132
  64. package/front_end/panels/lighthouse/LighthousePanel.ts +4 -3
  65. package/front_end/panels/network/NetworkLogView.ts +8 -1
  66. package/front_end/panels/network/networkLogView.css +0 -15
  67. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +5 -4
  68. package/front_end/third_party/chromium/README.chromium +1 -1
  69. package/front_end/third_party/lighthouse/README.chromium +2 -2
  70. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1607 -5733
  71. package/front_end/third_party/lighthouse/locales/ar-XB.json +290 -65
  72. package/front_end/third_party/lighthouse/locales/ar.json +290 -65
  73. package/front_end/third_party/lighthouse/locales/bg.json +290 -65
  74. package/front_end/third_party/lighthouse/locales/ca.json +295 -70
  75. package/front_end/third_party/lighthouse/locales/cs.json +290 -65
  76. package/front_end/third_party/lighthouse/locales/da.json +294 -69
  77. package/front_end/third_party/lighthouse/locales/de.json +295 -70
  78. package/front_end/third_party/lighthouse/locales/el.json +290 -65
  79. package/front_end/third_party/lighthouse/locales/en-GB.json +290 -65
  80. package/front_end/third_party/lighthouse/locales/en-US.json +79 -67
  81. package/front_end/third_party/lighthouse/locales/en-XA.json +253 -64
  82. package/front_end/third_party/lighthouse/locales/en-XL.json +79 -67
  83. package/front_end/third_party/lighthouse/locales/es-419.json +290 -65
  84. package/front_end/third_party/lighthouse/locales/es.json +298 -73
  85. package/front_end/third_party/lighthouse/locales/fi.json +290 -65
  86. package/front_end/third_party/lighthouse/locales/fil.json +290 -65
  87. package/front_end/third_party/lighthouse/locales/fr.json +294 -69
  88. package/front_end/third_party/lighthouse/locales/he.json +293 -68
  89. package/front_end/third_party/lighthouse/locales/hi.json +291 -66
  90. package/front_end/third_party/lighthouse/locales/hr.json +290 -65
  91. package/front_end/third_party/lighthouse/locales/hu.json +290 -65
  92. package/front_end/third_party/lighthouse/locales/id.json +290 -65
  93. package/front_end/third_party/lighthouse/locales/it.json +294 -69
  94. package/front_end/third_party/lighthouse/locales/ja.json +290 -65
  95. package/front_end/third_party/lighthouse/locales/ko.json +290 -65
  96. package/front_end/third_party/lighthouse/locales/lt.json +290 -65
  97. package/front_end/third_party/lighthouse/locales/lv.json +290 -65
  98. package/front_end/third_party/lighthouse/locales/nl.json +290 -65
  99. package/front_end/third_party/lighthouse/locales/no.json +290 -65
  100. package/front_end/third_party/lighthouse/locales/pl.json +290 -65
  101. package/front_end/third_party/lighthouse/locales/pt-PT.json +291 -66
  102. package/front_end/third_party/lighthouse/locales/pt.json +290 -65
  103. package/front_end/third_party/lighthouse/locales/ro.json +290 -65
  104. package/front_end/third_party/lighthouse/locales/ru.json +301 -76
  105. package/front_end/third_party/lighthouse/locales/sk.json +291 -66
  106. package/front_end/third_party/lighthouse/locales/sl.json +290 -65
  107. package/front_end/third_party/lighthouse/locales/sr-Latn.json +290 -65
  108. package/front_end/third_party/lighthouse/locales/sr.json +290 -65
  109. package/front_end/third_party/lighthouse/locales/sv.json +297 -72
  110. package/front_end/third_party/lighthouse/locales/ta.json +291 -66
  111. package/front_end/third_party/lighthouse/locales/te.json +293 -68
  112. package/front_end/third_party/lighthouse/locales/th.json +291 -66
  113. package/front_end/third_party/lighthouse/locales/tr.json +290 -65
  114. package/front_end/third_party/lighthouse/locales/uk.json +290 -65
  115. package/front_end/third_party/lighthouse/locales/vi.json +291 -66
  116. package/front_end/third_party/lighthouse/locales/zh-HK.json +292 -67
  117. package/front_end/third_party/lighthouse/locales/zh-TW.json +291 -66
  118. package/front_end/third_party/lighthouse/locales/zh.json +291 -66
  119. package/front_end/third_party/lighthouse/report/bundle.d.ts +6 -6
  120. package/front_end/third_party/lighthouse/report/bundle.js +4 -7
  121. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  122. package/front_end/ui/legacy/Widget.ts +32 -8
  123. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +36 -3
  124. package/front_end/ui/legacy/components/data_grid/dataGridAiButton.css +20 -0
  125. package/front_end/ui/legacy/components/utils/Linkifier.ts +19 -4
  126. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  127. package/mcp/mcp.ts +1 -0
  128. package/package.json +1 -1
@@ -0,0 +1,200 @@
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 i18n from '../../../core/i18n/i18n.js';
6
+ import type * as SDK from '../../../core/sdk/sdk.js';
7
+ import {
8
+ type ContextDetail,
9
+ ConversationContext,
10
+ type ConversationSuggestions,
11
+ } from '../agents/AiAgent.js';
12
+
13
+ const UIStringsNotTranslate = {
14
+ /**
15
+ * @description Heading text for context details of DevTools AI Agent.
16
+ */
17
+ dataUsed: 'Data used',
18
+ } as const;
19
+
20
+ const lockedString = i18n.i18n.lockedString;
21
+
22
+ export class DOMNodeContext extends ConversationContext<SDK.DOMModel.DOMNode> {
23
+ #node: SDK.DOMModel.DOMNode;
24
+
25
+ constructor(node: SDK.DOMModel.DOMNode) {
26
+ super();
27
+ this.#node = node;
28
+ }
29
+
30
+ override getURL(): string {
31
+ const ownerDocument = this.#node.ownerDocument;
32
+ if (!ownerDocument) {
33
+ // The node is detached from a document.
34
+ return 'detached';
35
+ }
36
+ return ownerDocument.documentURL;
37
+ }
38
+
39
+ getItem(): SDK.DOMModel.DOMNode {
40
+ return this.#node;
41
+ }
42
+
43
+ override getTitle(): string {
44
+ throw new Error('Not implemented');
45
+ }
46
+
47
+ override async getSuggestions(): Promise<ConversationSuggestions|undefined> {
48
+ const layoutProps = await this.#node.domModel().cssModel().getLayoutPropertiesFromComputedStyle(this.#node.id);
49
+
50
+ if (!layoutProps) {
51
+ return;
52
+ }
53
+
54
+ if (layoutProps.isFlex) {
55
+ return [
56
+ {title: 'How can I make flex items wrap?', jslogContext: 'flex-wrap'},
57
+ {title: 'How do I distribute flex items evenly?', jslogContext: 'flex-distribute'},
58
+ {title: 'What is flexbox?', jslogContext: 'flex-what'},
59
+ ];
60
+ }
61
+ if (layoutProps.isSubgrid) {
62
+ return [
63
+ {title: 'Where is this grid defined?', jslogContext: 'subgrid-where'},
64
+ {title: 'How to overwrite parent grid properties?', jslogContext: 'subgrid-override'},
65
+ {title: 'How do subgrids work? ', jslogContext: 'subgrid-how'},
66
+ ];
67
+ }
68
+ if (layoutProps.isGrid) {
69
+ return [
70
+ {title: 'How do I align items in a grid?', jslogContext: 'grid-align'},
71
+ {title: 'How to add spacing between grid items?', jslogContext: 'grid-gap'},
72
+ {title: 'How does grid layout work?', jslogContext: 'grid-how'},
73
+ ];
74
+ }
75
+ if (layoutProps.hasScroll) {
76
+ return [
77
+ {title: 'How do I remove scrollbars for this element?', jslogContext: 'scroll-remove'},
78
+ {title: 'How can I style a scrollbar?', jslogContext: 'scroll-style'},
79
+ {title: 'Why does this element scroll?', jslogContext: 'scroll-why'},
80
+ ];
81
+ }
82
+ if (layoutProps.containerType) {
83
+ return [
84
+ {title: 'What are container queries?', jslogContext: 'container-what'},
85
+ {title: 'How do I use container-type?', jslogContext: 'container-how'},
86
+ {title: 'What\'s the container context for this element?', jslogContext: 'container-context'},
87
+ ];
88
+ }
89
+
90
+ return;
91
+ }
92
+
93
+ override async getPromptDetails(): Promise<string|null> {
94
+ return `# Inspected element
95
+
96
+ ${await this.describe()}`;
97
+ }
98
+
99
+ override async getUserFacingDetails(): Promise<[ContextDetail, ...ContextDetail[]]|null> {
100
+ return [
101
+ {
102
+ title: lockedString(UIStringsNotTranslate.dataUsed),
103
+ text: await this.describe(),
104
+ },
105
+ ];
106
+ }
107
+
108
+ async describe(): Promise<string> {
109
+ const element = this.#node;
110
+ let output = `* Element's uid is ${element.backendNodeId()}.
111
+ * Its selector is \`${element.simpleSelector()}\``;
112
+ const childNodes = await element.getChildNodesPromise();
113
+ if (childNodes) {
114
+ const textChildNodes = childNodes.filter(childNode => childNode.nodeType() === Node.TEXT_NODE);
115
+ const elementChildNodes = childNodes.filter(childNode => childNode.nodeType() === Node.ELEMENT_NODE);
116
+ switch (elementChildNodes.length) {
117
+ case 0:
118
+ output += '\n* It doesn\'t have any child element nodes';
119
+ break;
120
+ case 1:
121
+ output += `\n* It only has 1 child element node: \`${elementChildNodes[0].simpleSelector()}\``;
122
+ break;
123
+ default:
124
+ output += `\n* It has ${elementChildNodes.length} child element nodes: ${
125
+ elementChildNodes.map(node => `\`${node.simpleSelector()}\` (uid=${node.backendNodeId()})`).join(', ')}`;
126
+ }
127
+
128
+ switch (textChildNodes.length) {
129
+ case 0:
130
+ output += '\n* It doesn\'t have any child text nodes';
131
+ break;
132
+ case 1:
133
+ output += '\n* It only has 1 child text node';
134
+ break;
135
+ default:
136
+ output += `\n* It has ${textChildNodes.length} child text nodes`;
137
+ }
138
+ }
139
+
140
+ if (element.nextSibling) {
141
+ const elementOrNodeElementNodeText = element.nextSibling.nodeType() === Node.ELEMENT_NODE ?
142
+ `an element (uid=${element.nextSibling.backendNodeId()})` :
143
+ 'a non element';
144
+ output += `\n* It has a next sibling and it is ${elementOrNodeElementNodeText} node`;
145
+ }
146
+
147
+ if (element.previousSibling) {
148
+ const elementOrNodeElementNodeText = element.previousSibling.nodeType() === Node.ELEMENT_NODE ?
149
+ `an element (uid=${element.previousSibling.backendNodeId()})` :
150
+ 'a non element';
151
+ output += `\n* It has a previous sibling and it is ${elementOrNodeElementNodeText} node`;
152
+ }
153
+
154
+ if (element.isInShadowTree()) {
155
+ output += '\n* It is in a shadow DOM tree.';
156
+ }
157
+
158
+ const parentNode = element.parentNode;
159
+ if (parentNode) {
160
+ const parentChildrenNodes = await parentNode.getChildNodesPromise();
161
+ output += `\n* Its parent's selector is \`${parentNode.simpleSelector()}\` (uid=${parentNode.backendNodeId()})`;
162
+ const elementOrNodeElementNodeText = parentNode.nodeType() === Node.ELEMENT_NODE ? 'an element' : 'a non element';
163
+ output += `\n* Its parent is ${elementOrNodeElementNodeText} node`;
164
+ if (parentNode.isShadowRoot()) {
165
+ output += '\n* Its parent is a shadow root.';
166
+ }
167
+ if (parentChildrenNodes) {
168
+ const childElementNodes =
169
+ parentChildrenNodes.filter(siblingNode => siblingNode.nodeType() === Node.ELEMENT_NODE);
170
+ switch (childElementNodes.length) {
171
+ case 0:
172
+ break;
173
+ case 1:
174
+ output += '\n* Its parent has only 1 child element node';
175
+ break;
176
+ default:
177
+ output += `\n* Its parent has ${childElementNodes.length} child element nodes: ${
178
+ childElementNodes.map(node => `\`${node.simpleSelector()}\` (uid=${node.backendNodeId()})`)
179
+ .join(', ')}`;
180
+ break;
181
+ }
182
+
183
+ const siblingTextNodes = parentChildrenNodes.filter(siblingNode => siblingNode.nodeType() === Node.TEXT_NODE);
184
+ switch (siblingTextNodes.length) {
185
+ case 0:
186
+ break;
187
+ case 1:
188
+ output += '\n* Its parent has only 1 child text node';
189
+ break;
190
+ default:
191
+ output += `\n* Its parent has ${siblingTextNodes.length} child text nodes: ${
192
+ siblingTextNodes.map(node => `\`${node.simpleSelector()}\``).join(', ')}`;
193
+ break;
194
+ }
195
+ }
196
+ }
197
+
198
+ return output.trim();
199
+ }
200
+ }
@@ -1,6 +1,48 @@
1
1
  ---
2
2
  name: styling
3
3
  description: Helping with CSS and styling
4
- allowed-tools: []
4
+ allowed-tools:
5
+ - executeJavaScript
6
+ - getStyles
5
7
  ---
6
- You are a CSS expert helping the user style elements.
8
+ You are the most advanced CSS/DOM/HTML debugging assistant integrated into Chrome DevTools.
9
+ You always suggest considering the best web development practices and the newest platform features such as view transitions.
10
+ The user selected a DOM element in the browser's DevTools and sends a query about the page or the selected DOM element.
11
+ First, examine the provided context, then use the getStyles and executeJavaScript functions to gather additional context and resolve the user request.
12
+
13
+ # Considerations
14
+
15
+ * Meticulously investigate all potential causes for the observed behavior before moving on. Gather comprehensive information about the element's parent, siblings, children, and any overlapping elements, paying close attention to properties that are likely relevant to the query.
16
+ * Be aware of the different node types (element, text, comment, document fragment, etc.) and their properties. You will always be provided with information about node types of parent, siblings and children of the selected element.
17
+ * Avoid making assumptions without sufficient evidence, and always seek further clarification if needed.
18
+ * Always explore multiple possible explanations for the observed behavior before settling on a conclusion.
19
+ * When presenting solutions, clearly distinguish between the primary cause and contributing factors.
20
+ * Please answer only if you are sure about the answer. Otherwise, explain why you're not able to answer.
21
+ * When answering, always consider MULTIPLE possible solutions.
22
+ * When answering, remember to consider CSS concepts such as the CSS cascade, explicit and implicit stacking contexts and various CSS layout types.
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.
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.
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.
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.
35
+
36
+ ## Response Structure
37
+
38
+ If the user asks a question that requires an investigation of a problem, use this structure:
39
+ - If available, point out the root cause(s) of the problem.
40
+ - Example: "**Root Cause**: The page is slow because of [reason]."
41
+ - Example: "**Root Causes**:"
42
+ - [Reason 1]
43
+ - [Reason 2]
44
+ - if applicable, list actionable solution suggestion(s) in order of impact:
45
+ - Example: "**Suggestion**: [Suggestion 1]
46
+ - Example: "**Suggestions**:"
47
+ - [Suggestion 1]
48
+ - [Suggestion 2]
@@ -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
+ }
@@ -0,0 +1,141 @@
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 SDK from '../../../core/sdk/sdk.js';
7
+ import type * as Protocol from '../../../generated/protocol.js';
8
+ import type {ComputedStyleAiWidget, FunctionCallHandlerResult, FunctionHandlerOptions} from '../agents/AiAgent.js';
9
+ import {DOMNodeContext} from '../contexts/DOMNodeContext.js';
10
+ import {debugLog} from '../debug.js';
11
+
12
+ import {type Tool, type ToolArgs, type ToolContext, ToolName} from './Tool.js';
13
+
14
+ export interface GetStylesArgs extends ToolArgs {
15
+ elements: number[];
16
+ styleProperties: string[];
17
+ explanation: string;
18
+ }
19
+
20
+ export class GetStylesTool implements Tool<GetStylesArgs, unknown> {
21
+ readonly name = ToolName.GET_STYLES;
22
+ readonly description =
23
+ `Get computed and source styles for one or multiple elements on the inspected page for multiple elements at once by uid.
24
+
25
+ **CRITICAL** An element uid is a number, not a selector.
26
+ **CRITICAL** Use selectors to refer to elements in the text output. Do not use uids.
27
+ **CRITICAL** Always provide the explanation argument to explain what and why you query.
28
+ **CRITICAL** You MUST provide a specific list of CSS property names. Do not use generic values like "all" or "*".`;
29
+
30
+ readonly parameters: Host.AidaClient.FunctionObjectParam<keyof GetStylesArgs> = {
31
+ type: Host.AidaClient.ParametersTypes.OBJECT,
32
+ description: '',
33
+ nullable: false,
34
+ properties: {
35
+ explanation: {
36
+ type: Host.AidaClient.ParametersTypes.STRING,
37
+ description: 'Explain why you want to get styles',
38
+ nullable: false,
39
+ },
40
+ elements: {
41
+ type: Host.AidaClient.ParametersTypes.ARRAY,
42
+ description: 'A list of element uids to get data for. These are numbers, not selectors.',
43
+ items: {type: Host.AidaClient.ParametersTypes.INTEGER, description: 'An element uid.'},
44
+ nullable: false,
45
+ },
46
+ styleProperties: {
47
+ type: Host.AidaClient.ParametersTypes.ARRAY,
48
+ description:
49
+ 'One or more specific CSS style property names to fetch. Generic values like "all" or "*" are not supported.',
50
+ nullable: false,
51
+ items: {
52
+ type: Host.AidaClient.ParametersTypes.STRING,
53
+ description: 'A CSS style property name to retrieve. For example, \'background-color\'.'
54
+ }
55
+ },
56
+ },
57
+ required: ['explanation', 'elements', 'styleProperties']
58
+ };
59
+
60
+ displayInfoFromArgs(params: GetStylesArgs): {
61
+ title: string,
62
+ thought: string,
63
+ action: string,
64
+ } {
65
+ return {
66
+ title: 'Reading computed and source styles',
67
+ thought: params.explanation,
68
+ action: `getStyles(${JSON.stringify(params.elements)}, ${JSON.stringify(params.styleProperties)})`,
69
+ };
70
+ }
71
+
72
+ async handler(
73
+ params: GetStylesArgs,
74
+ context: ToolContext,
75
+ _options?: FunctionHandlerOptions,
76
+ ): Promise<FunctionCallHandlerResult<unknown>> {
77
+ const widgets: ComputedStyleAiWidget[] = [];
78
+ const result:
79
+ Record<string, {computed: Record<string, string|undefined>, authored: Record<string, string|undefined>}> = {};
80
+
81
+ const activeContext = context.conversationContext;
82
+ if (!activeContext || !(activeContext instanceof DOMNodeContext)) {
83
+ return {error: 'Error: Could not find the currently selected element.'};
84
+ }
85
+
86
+ const selectedNode = activeContext.getItem();
87
+ if (!selectedNode) {
88
+ return {error: 'Error: Could not find the currently selected element.'};
89
+ }
90
+
91
+ for (const uid of params.elements) {
92
+ result[uid] = {computed: {}, authored: {}};
93
+ debugLog(`Action to execute: uid=${uid}`);
94
+ const node =
95
+ new SDK.DOMModel.DeferredDOMNode(selectedNode.domModel().target(), uid as Protocol.DOM.BackendNodeId);
96
+ const resolved = await node.resolvePromise();
97
+ if (!resolved) {
98
+ return {error: 'Error: Could not find the element with uid=' + uid};
99
+ }
100
+ const newContext = new DOMNodeContext(resolved);
101
+ if (activeContext.getOrigin() !== newContext.getOrigin()) {
102
+ return {error: 'Error: Node does not belong to the current origin.'};
103
+ }
104
+ const styles = await resolved.domModel().cssModel().getComputedStyle(resolved.id);
105
+ if (!styles) {
106
+ return {error: 'Error: Could not get computed styles.'};
107
+ }
108
+ const matchedStyles = await resolved.domModel().cssModel().getMatchedStyles(resolved.id);
109
+ if (!matchedStyles) {
110
+ return {error: 'Error: Could not get authored styles.'};
111
+ }
112
+ widgets.push({
113
+ name: 'COMPUTED_STYLES',
114
+ data: {
115
+ computedStyles: styles,
116
+ backendNodeId: node.backendNodeId(),
117
+ matchedCascade: matchedStyles,
118
+ properties: params.styleProperties,
119
+ }
120
+ });
121
+ for (const prop of params.styleProperties) {
122
+ result[uid].computed[prop] = styles.get(prop);
123
+ }
124
+ for (const style of matchedStyles.nodeStyles()) {
125
+ for (const property of style.allProperties()) {
126
+ if (!params.styleProperties.includes(property.name)) {
127
+ continue;
128
+ }
129
+ const state = matchedStyles.propertyState(property);
130
+ if (state === SDK.CSSMatchedStyles.PropertyState.ACTIVE) {
131
+ result[uid].authored[property.name] = property.value;
132
+ }
133
+ }
134
+ }
135
+ }
136
+ return {
137
+ result: JSON.stringify(result, null, 2),
138
+ widgets,
139
+ };
140
+ }
141
+ }
@@ -0,0 +1,64 @@
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 type * as Host from '../../../core/host/host.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';
9
+
10
+ /**
11
+ * Context provided to the tool's handler execution.
12
+ */
13
+ export interface ToolContext {
14
+ conversationContext: ConversationContext<unknown>|null;
15
+ changeManager?: ChangeManager;
16
+ createExtensionScope?: (changes: ChangeManager) => {
17
+ install(): Promise<void>, uninstall(): Promise<void>,
18
+ };
19
+ execJs?: typeof executeJsCode;
20
+ }
21
+
22
+ /**
23
+ * Base argument type for AI Tools.
24
+ */
25
+ export type ToolArgs = Record<string, unknown>;
26
+
27
+ export const enum ToolName {
28
+ EXECUTE_JAVASCRIPT = 'executeJavaScript',
29
+ GET_STYLES = 'getStyles',
30
+ }
31
+
32
+ /**
33
+ * Non-generic metadata interface for a Tool.
34
+ * Used for storing and retrieving tools generically without type-erasure concerns.
35
+ */
36
+ export interface BaseTool {
37
+ readonly name: ToolName;
38
+ readonly description: string;
39
+ /**
40
+ * JSON schema representing the parameters this tool accepts.
41
+ */
42
+ readonly parameters: Host.AidaClient.FunctionObjectParam<string|number|symbol>;
43
+ }
44
+
45
+ /**
46
+ * Main generic interface for defining a Tool.
47
+ * Binds the parameter schema properties and the handler implementation to a strict `Args` contract.
48
+ *
49
+ * @template Args - The expected object type for tool arguments. Must be an object type.
50
+ * @template ReturnType - The type of data returned by the handler function.
51
+ */
52
+ export interface Tool<Args extends ToolArgs = ToolArgs, ReturnType = unknown, > extends BaseTool {
53
+ readonly parameters: Host.AidaClient.FunctionObjectParam<keyof Args>;
54
+ readonly displayInfoFromArgs?: (
55
+ args: Args,
56
+ ) => {
57
+ title?: string, thought?: string, action?: string, suggestions?: [string, ...string[]],
58
+ };
59
+ handler(
60
+ args: Args,
61
+ context: ToolContext,
62
+ options?: FunctionHandlerOptions,
63
+ ): Promise<FunctionCallHandlerResult<ReturnType>>;
64
+ }
@@ -0,0 +1,36 @@
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 {ExecuteJavaScriptTool} from './ExecuteJavaScript.js';
6
+ import {GetStylesTool} from './GetStyles.js';
7
+ import {type Tool, ToolName} from './Tool.js';
8
+
9
+ /**
10
+ * Plain object registry containing concrete instantiated tools.
11
+ * Keep this type concrete (no type-erasure) to preserve exact tool types.
12
+ */
13
+ export const TOOLS = {
14
+ [ToolName.EXECUTE_JAVASCRIPT]: new ExecuteJavaScriptTool(),
15
+ [ToolName.GET_STYLES]: new GetStylesTool(),
16
+ };
17
+
18
+ /**
19
+ * Registry class for registering and querying AI Assistance Tools.
20
+ */
21
+ export class ToolRegistry {
22
+ /**
23
+ * Retrieves a tool by its literal name with 100% type safety.
24
+ *
25
+ * @template K - A key from the `TOOLS` registry.
26
+ * @returns The concrete class type of the requested tool.
27
+ */
28
+ static get<K extends keyof typeof TOOLS>(name: K): typeof TOOLS[K];
29
+ /**
30
+ * Fallback retrieval signature for general or runtime string lookups.
31
+ */
32
+ static get(name: string): Tool|undefined;
33
+ static get(name: string): Tool|undefined {
34
+ return Object.prototype.hasOwnProperty.call(TOOLS, name) ? TOOLS[name as keyof typeof TOOLS] as Tool : undefined;
35
+ }
36
+ }
@@ -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