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
@@ -9,8 +9,6 @@ import type * as Protocol from '../../generated/protocol.js';
9
9
 
10
10
  export interface Change {
11
11
  groupId: string;
12
- // Optional turn ID to group changes from the same turn.
13
- turnId?: number;
14
12
  // Optional about where in the source the selector was defined.
15
13
  sourceLocation?: string;
16
14
  // Selector used by the page or a simple selector as the fallback.
@@ -104,7 +102,6 @@ export class ChangeManager {
104
102
  // it currently causes crashes in the Styles tab when duplicate selectors exist (crbug.com/393515428).
105
103
  // This workaround avoids that crash.
106
104
  existingChange.groupId = change.groupId;
107
- existingChange.turnId = change.turnId;
108
105
  } else {
109
106
  changes.push({
110
107
  ...change,
@@ -126,11 +123,11 @@ export class ChangeManager {
126
123
  .join('\n\n');
127
124
  }
128
125
 
129
- getChangedNodesForGroupId(groupId: string, turnId?: number): Protocol.DOM.BackendNodeId[] {
126
+ getChangedNodesForGroupId(groupId: string): Protocol.DOM.BackendNodeId[] {
130
127
  const nodes = new Set<Protocol.DOM.BackendNodeId>();
131
128
  for (const changes of this.#stylesheetChanges.values()) {
132
129
  for (const change of changes) {
133
- if (change.groupId === groupId && change.backendNodeId && (turnId === undefined || change.turnId === turnId)) {
130
+ if (change.groupId === groupId && change.backendNodeId) {
134
131
  nodes.add(change.backendNodeId);
135
132
  }
136
133
  }
@@ -2,10 +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 Host from '../../../core/host/host.js';
6
- import * as Root from '../../../core/root/root.js';
5
+ import * as Host from '../../core/host/host.js';
6
+ import * as Root from '../../core/root/root.js';
7
7
 
8
- import {AiAgent, type ContextResponse, ConversationContext, type RequestOptions, ResponseType} from './AiAgent.js';
8
+ import {runOneShotPrompt} from './AiUtils.js';
9
9
 
10
10
  const preamble = `### Role
11
11
  You are a Conversation Summarizer. Your task is to take a transcript of a conversation between a user and a DevTools AI agent and produce a succinct, actionable Markdown summary. This summary will be used to help apply fixes in an IDE, so it must capture all relevant technical details, findings, and proposed code changes without any conversational fluff.
@@ -98,90 +98,53 @@ color: red;
98
98
  - Professional, objective, and dense.
99
99
  - Past tense for actions; Present tense for technical facts.`;
100
100
 
101
- export class ConversationSummaryContext extends ConversationContext<string> {
102
- #conversation: string;
103
- constructor(conversation: string) {
104
- super();
105
- this.#conversation = conversation;
106
- }
107
-
108
- override getURL(): string {
109
- return 'devtools://ai-assistance';
110
- }
111
-
112
- getItem(): string {
113
- return this.#conversation;
114
- }
115
-
116
- override getTitle(): string {
117
- return 'Conversation';
118
- }
101
+ export interface ConversationSummaryOptions {
102
+ aidaClient: Host.AidaClient.AidaClient;
103
+ serverSideLoggingEnabled?: boolean;
119
104
  }
120
105
 
121
106
  /**
122
- * An agent that takes a full conversation between a user and an agent in markdown
107
+ * A class that takes a full conversation between a user and an agent in markdown
123
108
  * format and produces a succinct summary of the conversation.
124
109
  *
125
110
  * This summary is designed to be read by a local agent in the user's IDE and it
126
111
  * will be used to help apply fixes to the user's local codebase based on the
127
112
  * debugging information the devtools agent found.
128
- *
129
- * This agent is not intended to be used directly by users in the AI Assistance
130
- * panel when chatting with DevTools AI.
131
113
  */
132
- export class ConversationSummaryAgent extends AiAgent<string> {
133
- override preamble = preamble;
114
+ export class ConversationSummary {
115
+ readonly #aidaClient: Host.AidaClient.AidaClient;
116
+ readonly #serverSideLoggingEnabled: boolean;
134
117
 
135
- get clientFeature(): Host.AidaClient.ClientFeature {
136
- return Host.AidaClient.ClientFeature.CHROME_CONVERSATION_SUMMARY_AGENT;
118
+ constructor(options: ConversationSummaryOptions) {
119
+ this.#aidaClient = options.aidaClient;
120
+ this.#serverSideLoggingEnabled = options.serverSideLoggingEnabled ?? false;
137
121
  }
138
122
 
139
- get userTier(): string|undefined {
140
- // TODO(b/491772868): tidy up userTier & feature flags in the backend.
141
- return Root.Runtime.hostConfig.devToolsFreestyler?.userTier;
142
- }
123
+ async summarizeConversation(conversation: string): Promise<string> {
124
+ const enhancedQuery = `Summarize the following conversation:\n\n${conversation}`;
143
125
 
144
- get options(): RequestOptions {
145
126
  // TODO(b/491772868): tidy up userTier & feature flags in the backend.
146
127
  const temperature = Root.Runtime.hostConfig.devToolsFreestyler?.temperature;
147
128
  const modelId = Root.Runtime.hostConfig.devToolsFreestyler?.modelId;
129
+ const userTier = Root.Runtime.hostConfig.devToolsFreestyler?.userTier;
148
130
 
149
- return {
131
+ const resultText = await runOneShotPrompt({
132
+ aidaClient: this.#aidaClient,
133
+ preamble,
134
+ query: enhancedQuery,
135
+ clientFeature: Host.AidaClient.ClientFeature.CHROME_CONVERSATION_SUMMARY_AGENT,
150
136
  temperature,
151
137
  modelId,
152
- };
153
- }
138
+ userTier,
139
+ serverSideLoggingEnabled: this.#serverSideLoggingEnabled,
140
+ });
154
141
 
155
- async * handleContextDetails(context: ConversationContext<string>|null): AsyncGenerator<ContextResponse, void, void> {
156
- if (!context) {
157
- return;
142
+ if (!resultText) {
143
+ throw new Error('Failed to summarize conversation');
158
144
  }
159
145
 
160
- yield {
161
- type: ResponseType.CONTEXT,
162
- details: [
163
- {
164
- title: 'Conversation transcript',
165
- text: context.getItem(),
166
- },
167
- ],
168
- };
169
- }
170
-
171
- override async enhanceQuery(query: string, context: ConversationContext<string>|null): Promise<string> {
172
- const conversation = context ? context.getItem() : query;
173
- return `Summarize the following conversation:\n\n${conversation}`;
174
- }
175
-
176
- async summarizeConversation(conversation: string): Promise<string> {
177
- const context = new ConversationSummaryContext(conversation);
178
- const response = await Array.fromAsync(this.run('', {selected: context}));
179
- const lastResponse = response.at(-1);
180
- if (lastResponse && lastResponse.type === ResponseType.ANSWER && lastResponse.complete === true) {
181
- const disclaimer =
182
- '*Note: The code fixes and findings above were identified on a live page in DevTools. When applying them to your codebase, please adapt them to your project\'s specific technical stack (e.g., Tailwind CSS classes, CSS modules, framework components) rather than applying them as literal CSS overrides.*';
183
- return `${lastResponse.text.trim()}\n\n${disclaimer}`;
184
- }
185
- throw new Error('Failed to summarize conversation');
146
+ const disclaimer =
147
+ '*Note: The code fixes and findings above were identified on a live page in DevTools. When applying them to your codebase, please adapt them to your project\'s specific technical stack (e.g., Tailwind CSS classes, CSS modules, framework components) rather than applying them as literal CSS overrides.*';
148
+ return `${resultText.trim()}\n\n${disclaimer}`;
186
149
  }
187
150
  }
@@ -34,7 +34,6 @@ export class ExtensionScope {
34
34
  }) => Promise<void>> = [];
35
35
  #changeManager: ChangeManager;
36
36
  #agentId: string;
37
- #turnId?: number;
38
37
  /** Don't use directly use the getter */
39
38
  #frameId?: Protocol.Page.FrameId|null;
40
39
  /** Don't use directly use the getter */
@@ -42,12 +41,11 @@ export class ExtensionScope {
42
41
 
43
42
  readonly #bindingMutex = new Common.Mutex.Mutex();
44
43
 
45
- constructor(changes: ChangeManager, agentId: string, selectedNode: SDK.DOMModel.DOMNode|null, turnId?: number) {
44
+ constructor(changes: ChangeManager, agentId: string, selectedNode: SDK.DOMModel.DOMNode|null) {
46
45
  this.#changeManager = changes;
47
46
  const frameId = selectedNode?.frameId();
48
47
  const target = selectedNode?.domModel().target();
49
48
  this.#agentId = agentId;
50
- this.#turnId = turnId;
51
49
  this.#target = target;
52
50
  this.#frameId = frameId;
53
51
  }
@@ -357,7 +355,6 @@ export class ExtensionScope {
357
355
  const sanitizedStyles = await this.sanitizedStyleChanges(context.selector, arg.styles);
358
356
  const styleChanges = await this.#changeManager.addChange(cssModel, this.frameId, {
359
357
  groupId: this.#agentId,
360
- turnId: this.#turnId,
361
358
  sourceLocation: context.sourceLocation,
362
359
  selector: context.selector,
363
360
  simpleSelector: context.simpleSelector,
@@ -2,21 +2,12 @@
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 Host from '../../../core/host/host.js';
6
- import * as Root from '../../../core/root/root.js';
7
- import type {AICallTree} from '../performance/AICallTree.js';
8
- import type {AgentFocus} from '../performance/AIContext.js';
9
-
10
- import {AiAgent, type ContextResponse, type ConversationContext, type RequestOptions, ResponseType} from './AiAgent.js';
11
- import {PerformanceTraceContext} from './PerformanceAgent.js';
12
-
13
- /**
14
- * Preamble clocks in at ~970 tokens.
15
- * The prose is around 4.5 chars per token.
16
- * The data can be as bad as 1.8 chars per token
17
- *
18
- * Check token length in https://aistudio.google.com/
19
- */
5
+ import * as Host from '../../core/host/host.js';
6
+ import * as Root from '../../core/root/root.js';
7
+
8
+ import {runOneShotPrompt} from './AiUtils.js';
9
+ import type {AICallTree} from './performance/AICallTree.js';
10
+
20
11
  const callTreePreamble = `You are an expert performance analyst embedded within Chrome DevTools.
21
12
  You meticulously examine web application behavior captured by the Chrome DevTools Performance Panel and Chrome tracing.
22
13
  You will receive a structured text representation of a call tree, derived from a user-selected call frame within a performance trace's flame chart.
@@ -79,80 +70,6 @@ The 'calculatePosition' function, taking 80ms, is a potential bottleneck.
79
70
  Consider optimizing the position calculation logic or reducing the frequency of calls to improve animation performance.
80
71
  `;
81
72
 
82
- export class PerformanceAnnotationsAgent extends AiAgent<AgentFocus> {
83
- override preamble = callTreePreamble;
84
-
85
- get clientFeature(): Host.AidaClient.ClientFeature {
86
- return Host.AidaClient.ClientFeature.CHROME_PERFORMANCE_ANNOTATIONS_AGENT;
87
- }
88
-
89
- get userTier(): string|undefined {
90
- return Root.Runtime.hostConfig.devToolsAiAssistancePerformanceAgent?.userTier;
91
- }
92
-
93
- get options(): RequestOptions {
94
- const temperature = Root.Runtime.hostConfig.devToolsAiAssistancePerformanceAgent?.temperature;
95
- const modelId = Root.Runtime.hostConfig.devToolsAiAssistancePerformanceAgent?.modelId;
96
-
97
- return {
98
- temperature,
99
- modelId,
100
- };
101
- }
102
-
103
- async *
104
- handleContextDetails(context: ConversationContext<AgentFocus>|null): AsyncGenerator<ContextResponse, void, void> {
105
- if (!context) {
106
- return;
107
- }
108
-
109
- const focus = context.getItem();
110
- if (!focus.callTree) {
111
- throw new Error('unexpected context');
112
- }
113
-
114
- const callTree = focus.callTree;
115
-
116
- yield {
117
- type: ResponseType.CONTEXT,
118
- details: [
119
- {
120
- title: 'Selected call tree',
121
- text: callTree.serialize(),
122
- },
123
- ],
124
- };
125
- }
126
-
127
- override async enhanceQuery(query: string, context: ConversationContext<AgentFocus>|null): Promise<string> {
128
- if (!context) {
129
- return query;
130
- }
131
-
132
- const focus = context.getItem();
133
- if (!focus.callTree) {
134
- throw new Error('unexpected context');
135
- }
136
-
137
- const callTree = focus.callTree;
138
- const contextString = callTree.serialize();
139
- return `${contextString}\n\n# User request\n\n${query}`;
140
- }
141
-
142
- /**
143
- * Used in the Performance panel to automatically generate a label for a selected entry.
144
- */
145
- async generateAIEntryLabel(callTree: AICallTree): Promise<string> {
146
- const context = PerformanceTraceContext.fromCallTree(callTree);
147
- const response = await Array.fromAsync(this.run(AI_LABEL_GENERATION_PROMPT, {selected: context}));
148
- const lastResponse = response.at(-1);
149
- if (lastResponse && lastResponse.type === ResponseType.ANSWER && lastResponse.complete === true) {
150
- return lastResponse.text.trim();
151
- }
152
- throw new Error('Failed to generate AI entry label');
153
- }
154
- }
155
-
156
73
  const AI_LABEL_GENERATION_PROMPT = `## Instruction:
157
74
  Generate a concise label (max 60 chars, single line) describing the *user-visible effect* of the selected call tree's activity, based solely on the provided call tree data.
158
75
 
@@ -168,3 +85,44 @@ Generate a concise label (max 60 chars, single line) describing the *user-visibl
168
85
  - Only include third-party script names if their identification is highly confident.
169
86
  - Very important: Only output the 60 character label text, your response will be used in full to show to the user as an annotation in the timeline.
170
87
  `;
88
+
89
+ export interface PerformanceAnnotationsOptions {
90
+ aidaClient: Host.AidaClient.AidaClient;
91
+ serverSideLoggingEnabled?: boolean;
92
+ }
93
+
94
+ export class PerformanceAnnotations {
95
+ readonly #aidaClient: Host.AidaClient.AidaClient;
96
+ readonly #serverSideLoggingEnabled: boolean;
97
+
98
+ constructor(options: PerformanceAnnotationsOptions) {
99
+ this.#aidaClient = options.aidaClient;
100
+ this.#serverSideLoggingEnabled = options.serverSideLoggingEnabled ?? false;
101
+ }
102
+
103
+ async generateAIEntryLabel(callTree: AICallTree): Promise<string> {
104
+ const contextString = callTree.serialize();
105
+ const query = `${contextString}\n\n# User request\n\n${AI_LABEL_GENERATION_PROMPT}`;
106
+
107
+ const temperature = Root.Runtime.hostConfig.devToolsAiAssistancePerformanceAgent?.temperature;
108
+ const modelId = Root.Runtime.hostConfig.devToolsAiAssistancePerformanceAgent?.modelId;
109
+ const userTier = Root.Runtime.hostConfig.devToolsAiAssistancePerformanceAgent?.userTier;
110
+
111
+ const resultText = await runOneShotPrompt({
112
+ aidaClient: this.#aidaClient,
113
+ preamble: callTreePreamble,
114
+ query,
115
+ clientFeature: Host.AidaClient.ClientFeature.CHROME_PERFORMANCE_ANNOTATIONS_AGENT,
116
+ temperature,
117
+ modelId,
118
+ userTier,
119
+ serverSideLoggingEnabled: this.#serverSideLoggingEnabled,
120
+ });
121
+
122
+ if (!resultText) {
123
+ throw new Error('Failed to generate AI entry label');
124
+ }
125
+
126
+ return resultText.trim();
127
+ }
128
+ }
@@ -38,6 +38,14 @@ This work is currently in progress and behind a feature flag.
38
38
  To support dynamic loading of skills, we generate JavaScript files from Markdown files containing skill definitions.
39
39
  We use a nested `BUILD.gn` file in the `skills/` subdirectory specifically for this purpose. This ensures that GN's `target_gen_dir` points to `gen/front_end/models/ai_assistance/skills/`, placing the generated `.skill.js` files in the same relative structure as their source `.md` files. This allows TypeScript files in the `skills/` directory (like `map.ts`) to import the generated files using relative paths (e.g., `./styling.skill.js`) seamlessly.
40
40
 
41
+ ### Tools and ToolRegistry
42
+
43
+ To support skills requiring execution of code or fetching page state (like computed styles), the architecture defines **Tools** in the `tools/` directory.
44
+
45
+ - **BaseTool**: A non-generic base interface capturing tool metadata (`name`, `description`, `parameters`). This acts as the type-erased representation for generic registry storage and fallback string lookups.
46
+ - **Tool**: A generic interface extending `BaseTool` that binds argument types (`Args`) to the schema `parameters` and the `handler()` method. This ensures compile-time safety and alignment inside each tool implementation.
47
+ - **ToolRegistry**: A static registry (`ToolRegistry`) storing instantiated tools. It uses TypeScript function overloading and generic lookups (`static get<K extends keyof typeof TOOLS>(name: K): typeof TOOLS[K]`) to return the precise class type of each tool, preventing type-erasure and escape-hatches (such as `any` or `as unknown` type assertions) at integration points like `AiAgent.ts`.
48
+
41
49
  ## Performance specific documentation
42
50
 
43
51
  ### `TimelineUtils.AIContext.AgentFocus`
@@ -120,17 +120,15 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
120
120
  #javascriptExecutor: JavascriptExecutor;
121
121
  #changes: ChangeManager;
122
122
  #createExtensionScope: CreateExtensionScopeFunction;
123
- #currentTurnId = 0;
124
123
 
125
124
  constructor(opts: ExecuteJsAgentOptions) {
126
125
  super(opts);
127
126
  this.#lighthouseRecording = opts.lighthouseRecording;
128
127
  this.#changes = opts.changeManager || new ChangeManager();
129
128
  this.#execJs = opts.execJs ?? executeJsCode;
130
- this.#createExtensionScope =
131
- opts.createExtensionScope ?? ((changes: ChangeManager) => {
132
- return new ExtensionScope(changes, this.sessionId, this.#getDocumentBodyNode(), this.#currentTurnId);
133
- });
129
+ this.#createExtensionScope = opts.createExtensionScope ?? ((changes: ChangeManager) => {
130
+ return new ExtensionScope(changes, this.sessionId, this.#getDocumentBodyNode());
131
+ });
134
132
  this.#javascriptExecutor = new JavascriptExecutor(
135
133
  {
136
134
  executionMode: this.executionMode,
@@ -162,7 +160,6 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
162
160
  }
163
161
 
164
162
  protected override async preRun(): Promise<void> {
165
- this.#currentTurnId++;
166
163
  const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
167
164
  const domModel = target?.model(SDK.DOMModel.DOMModel);
168
165
  // We need to ensure the document is requested so that #getDocumentBodyNode()
@@ -235,85 +232,103 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
235
232
  }
236
233
 
237
234
  #declareFunctions(): void {
238
- this.declareFunction('executeJavaScript', executeJavaScriptFunction(this.#javascriptExecutor));
235
+ const isImported = this.context?.getItem().isImported;
239
236
 
240
- this.declareFunction<{explanation: string}, {audits: string}>('runAccessibilityAudits', {
237
+ this.declareFunction<{categoryId: LHModel.RunTypes.CategoryId}, {audits: string}>('getLighthouseAudits', {
241
238
  description:
242
- 'Triggers new Lighthouse accessibility audits in snapshot mode. Use this if the user has made changes to the page and you want to re-evaluate the accessibility audits.',
239
+ 'Returns the audits for a specific Lighthouse category. Use this to get more information about the performance, accessibility, best-practices, or seo audits.',
243
240
  parameters: {
244
241
  type: Host.AidaClient.ParametersTypes.OBJECT,
245
242
  description: '',
246
243
  nullable: false,
247
244
  properties: {
248
- explanation: {
245
+ categoryId: {
249
246
  type: Host.AidaClient.ParametersTypes.STRING,
250
- description: 'Explain why you want to run new audits.',
247
+ description:
248
+ 'The category of audits to retrieve. Valid values are "performance", "accessibility", "best-practices", "seo".',
251
249
  nullable: false,
252
250
  },
253
251
  },
254
- required: ['explanation'],
252
+ required: ['categoryId'],
255
253
  },
256
254
  displayInfoFromArgs: params => {
257
255
  return {
258
- title: i18n.i18n.lockedString('Running accessibility audits'),
259
- thought: params.explanation,
260
- action: 'runAccessibilityAudits()'
256
+ title: i18n.i18n.lockedString(`Getting Lighthouse audits for ${params.categoryId}`),
257
+ action: `getLighthouseAudits('${params.categoryId}')`
261
258
  };
262
259
  },
263
260
  handler: async params => {
264
- debugLog('Function call: runAccessibilityAudits', params);
265
- if (!this.#lighthouseRecording) {
266
- return {error: 'Lighthouse recording is not available.'};
267
- }
268
- const report = await this.#lighthouseRecording({
269
- mode: 'snapshot',
270
- categoryIds: ['accessibility'],
271
- isAIControlled: true,
272
- });
261
+ debugLog('Function call: getLighthouseAudits', params);
262
+ const report = this.context?.getItem();
273
263
  if (!report) {
274
- return {error: 'Failed to run accessibility audits.'};
264
+ return {error: 'No Lighthouse report available.'};
275
265
  }
276
- const audits = new LighthouseFormatter().audits(report, 'accessibility');
266
+ const audits = new LighthouseFormatter().audits(report, params.categoryId);
277
267
  return {
278
268
  result: {audits},
279
- widgets: [{name: 'LIGHTHOUSE_REPORT', data: {report, snapshotReport: true}}],
269
+ widgets: [{name: 'LIGHTHOUSE_REPORT', data: {report}}],
280
270
  };
281
271
  }
282
272
  });
283
273
 
284
- this.declareFunction<{categoryId: LHModel.RunTypes.CategoryId}, {audits: string}>('getLighthouseAudits', {
274
+ const executeJsDeclaration = executeJavaScriptFunction(this.#javascriptExecutor);
275
+ this.declareFunction('executeJavaScript', {
276
+ ...executeJsDeclaration,
277
+ handler: async (params, options) => {
278
+ if (isImported) {
279
+ return {
280
+ error: 'Cannot use this tool on an imported file.',
281
+ };
282
+ }
283
+ return await executeJsDeclaration.handler(params, options);
284
+ },
285
+ });
286
+
287
+ this.declareFunction<{explanation: string}, {audits: string}>('runAccessibilityAudits', {
285
288
  description:
286
- 'Returns the audits for a specific Lighthouse category. Use this to get more information about the performance, accessibility, best-practices, or seo audits.',
289
+ 'Triggers new Lighthouse accessibility audits in snapshot mode. Use this if the user has made changes to the page and you want to re-evaluate the accessibility audits.',
287
290
  parameters: {
288
291
  type: Host.AidaClient.ParametersTypes.OBJECT,
289
292
  description: '',
290
293
  nullable: false,
291
294
  properties: {
292
- categoryId: {
295
+ explanation: {
293
296
  type: Host.AidaClient.ParametersTypes.STRING,
294
- description:
295
- 'The category of audits to retrieve. Valid values are "performance", "accessibility", "best-practices", "seo".',
297
+ description: 'Explain why you want to run new audits.',
296
298
  nullable: false,
297
299
  },
298
300
  },
299
- required: ['categoryId'],
301
+ required: ['explanation'],
300
302
  },
301
303
  displayInfoFromArgs: params => {
302
304
  return {
303
- title: i18n.i18n.lockedString(`Getting Lighthouse audits for ${params.categoryId}`),
304
- action: `getLighthouseAudits('${params.categoryId}')`
305
+ title: i18n.i18n.lockedString('Running accessibility audits'),
306
+ thought: params.explanation,
307
+ action: 'runAccessibilityAudits()'
305
308
  };
306
309
  },
307
310
  handler: async params => {
308
- debugLog('Function call: getLighthouseAudits', params);
309
- const report = this.context?.getItem();
311
+ debugLog('Function call: runAccessibilityAudits', params);
312
+ if (isImported) {
313
+ return {
314
+ error: 'Cannot use this tool on an imported file.',
315
+ };
316
+ }
317
+ if (!this.#lighthouseRecording) {
318
+ return {error: 'Lighthouse recording is not available.'};
319
+ }
320
+ const report = await this.#lighthouseRecording({
321
+ mode: 'snapshot',
322
+ categoryIds: ['accessibility'],
323
+ isAIControlled: true,
324
+ });
310
325
  if (!report) {
311
- return {error: 'No Lighthouse report available.'};
326
+ return {error: 'Failed to run accessibility audits.'};
312
327
  }
313
- const audits = new LighthouseFormatter().audits(report, params.categoryId);
328
+ const audits = new LighthouseFormatter().audits(report, 'accessibility');
314
329
  return {
315
330
  result: {audits},
316
- widgets: [{name: 'LIGHTHOUSE_REPORT', data: {report}}],
331
+ widgets: [{name: 'LIGHTHOUSE_REPORT', data: {report, snapshotReport: true}}],
317
332
  };
318
333
  }
319
334
  });
@@ -363,6 +378,11 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
363
378
  },
364
379
  handler: async params => {
365
380
  debugLog('Function call: getStyles', params);
381
+ if (isImported) {
382
+ return {
383
+ error: 'Cannot use this tool on an imported file.',
384
+ };
385
+ }
366
386
  const node = await this.#resolvePathToNode(params.path);
367
387
  if (!node) {
368
388
  return {error: `Could not find the element with path: ${params.path}`};
@@ -433,6 +453,11 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
433
453
  },
434
454
  handler: async params => {
435
455
  debugLog('Function call: getElementAccessibilityDetails', params);
456
+ if (isImported) {
457
+ return {
458
+ error: 'Cannot use this tool on an imported file.',
459
+ };
460
+ }
436
461
  const node = await this.#resolvePathToNode(params.path);
437
462
  if (!node) {
438
463
  return {error: `Could not find the element with path: ${params.path}`};
@@ -226,6 +226,22 @@ export abstract class ConversationContext<T> {
226
226
  async getSuggestions(): Promise<ConversationSuggestions|undefined> {
227
227
  return;
228
228
  }
229
+
230
+ /**
231
+ * Returns a detailed description of the context item for inclusion in the AI model prompt.
232
+ * Currently only used by AiAgent2.
233
+ */
234
+ async getPromptDetails(): Promise<string|null> {
235
+ return null;
236
+ }
237
+
238
+ /**
239
+ * Returns a list of context details to display to the user in the UI.
240
+ * Currently only used by AiAgent2.
241
+ */
242
+ async getUserFacingDetails(): Promise<[ContextDetail, ...ContextDetail[]]|null> {
243
+ return null;
244
+ }
229
245
  }
230
246
 
231
247
  export interface ComputedStyleAiWidget {
@@ -316,6 +332,13 @@ export interface SourceFilesListAiWidget {
316
332
  };
317
333
  }
318
334
 
335
+ export interface NetworkRequestsListAiWidget {
336
+ name: 'NETWORK_REQUESTS_LIST';
337
+ data: {
338
+ requests: SDK.NetworkRequest.NetworkRequest[],
339
+ };
340
+ }
341
+
319
342
  export interface LighthouseReportAiWidget {
320
343
  name: 'LIGHTHOUSE_REPORT';
321
344
  data: {
@@ -350,10 +373,10 @@ export interface SourceCodeAiWidget {
350
373
  };
351
374
  }
352
375
 
353
- export type AiWidget =
354
- ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget|PerformanceTraceAiWidget|
355
- PerfInsightAiWidget|TimelineRangeSummaryAiWidget|BottomUpTreeAiWidget|SourceFileAiWidget|LighthouseReportAiWidget|
356
- TimelineEventSummaryAiWidget|NetworkRequestGeneralHeadersAiWidget|SourceCodeAiWidget|SourceFilesListAiWidget;
376
+ export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget|
377
+ PerformanceTraceAiWidget|PerfInsightAiWidget|TimelineRangeSummaryAiWidget|BottomUpTreeAiWidget|SourceFileAiWidget|
378
+ LighthouseReportAiWidget|TimelineEventSummaryAiWidget|NetworkRequestGeneralHeadersAiWidget|SourceCodeAiWidget|
379
+ SourceFilesListAiWidget|NetworkRequestsListAiWidget;
357
380
 
358
381
  export type FunctionCallHandlerResult<Result> = {
359
382
  requiresApproval: true,
@@ -447,7 +470,7 @@ export abstract class AiAgent<T> {
447
470
 
448
471
  readonly #sessionId: string;
449
472
  readonly #aidaClient: Host.AidaClient.AidaClient;
450
- readonly #serverSideLoggingEnabled: boolean;
473
+ #serverSideLoggingEnabled: boolean;
451
474
  readonly confirmSideEffect: typeof Promise.withResolvers;
452
475
  readonly #functionDeclarations = new Map<string, FunctionDeclaration<Record<string, unknown>, unknown>>();
453
476
  readonly #allowedOrigin?: () => AllowedOriginResult;
@@ -526,6 +549,10 @@ export abstract class AiAgent<T> {
526
549
  clearCache(): void {
527
550
  }
528
551
 
552
+ protected disableServerSideLogging(): void {
553
+ this.#serverSideLoggingEnabled = false;
554
+ }
555
+
529
556
  popPendingMultimodalInput(): MultimodalInput|undefined {
530
557
  return undefined;
531
558
  }
@@ -677,6 +704,10 @@ export abstract class AiAgent<T> {
677
704
  this.#functionDeclarations.clear();
678
705
  }
679
706
 
707
+ /**
708
+ * Executed immediately after the current context is populated with the selected
709
+ * context and before the request is built.
710
+ */
680
711
  protected async preRun(): Promise<void> {
681
712
  }
682
713
 
@@ -689,11 +720,11 @@ export abstract class AiAgent<T> {
689
720
  },
690
721
  multimodalInput?: MultimodalInput,
691
722
  ): AsyncGenerator<ResponseData, void, void> {
692
- await this.preRun();
693
723
  await options.selected?.refresh();
694
724
  if (options.selected) {
695
725
  this.context = options.selected;
696
726
  }
727
+ await this.preRun();
697
728
 
698
729
  const enhancedQuery = await this.enhanceQuery(initialQuery, options.selected, multimodalInput?.type);
699
730
  Host.userMetrics.freestylerQueryLength(enhancedQuery.length);
@@ -129,6 +129,17 @@ Content:
129
129
  "required": [],
130
130
  "properties": {}
131
131
  }
132
+ },
133
+ {
134
+ "name": "analyzeStorage",
135
+ "description": "Selects the page storage. Use this when asked about browser storage (localStorage, sessionStorage, cookies) and issues related to these.",
136
+ "parameters": {
137
+ "type": 6,
138
+ "description": "",
139
+ "nullable": true,
140
+ "required": [],
141
+ "properties": {}
142
+ }
132
143
  }
133
144
  ],
134
145
  "options": {},