chrome-devtools-frontend 1.0.1642899 → 1.0.1643855

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 (125) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/extension-api/ExtensionAPI.d.ts +83 -12
  3. package/front_end/core/host/UserMetrics.ts +0 -1
  4. package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
  5. package/front_end/core/root/ExperimentNames.ts +0 -1
  6. package/front_end/core/sdk/ConsoleModel.ts +4 -0
  7. package/front_end/core/sdk/NetworkRequest.ts +12 -0
  8. package/front_end/core/sdk/SourceMap.ts +15 -18
  9. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
  10. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
  11. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +4 -5
  12. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  13. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  14. package/front_end/generated/protocol.ts +7 -0
  15. package/front_end/models/ai_assistance/AiAgent2.ts +24 -5
  16. package/front_end/models/ai_assistance/AiConversation.ts +15 -12
  17. package/front_end/models/ai_assistance/AiUtils.ts +71 -0
  18. package/front_end/models/ai_assistance/ChangeManager.ts +2 -5
  19. package/front_end/models/ai_assistance/{agents/ConversationSummaryAgent.ts → ConversationSummary.ts} +29 -66
  20. package/front_end/models/ai_assistance/ExtensionScope.ts +1 -4
  21. package/front_end/models/ai_assistance/{agents/PerformanceAnnotationsAgent.ts → PerformanceAnnotations.ts} +47 -89
  22. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +47 -31
  23. package/front_end/models/ai_assistance/agents/AiAgent.ts +40 -12
  24. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +11 -0
  25. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +58 -8
  26. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +1 -92
  27. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +25 -0
  28. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +94 -79
  29. package/front_end/models/ai_assistance/agents/StorageAgent.ts +101 -39
  30. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -2
  31. package/front_end/models/ai_assistance/agents/StylingAgent.ts +27 -21
  32. package/front_end/models/ai_assistance/ai_assistance.ts +6 -4
  33. package/front_end/models/ai_assistance/skills/styling.md +12 -4
  34. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +134 -0
  35. package/front_end/models/ai_assistance/tools/GetStyles.ts +6 -2
  36. package/front_end/models/ai_assistance/tools/Tool.ts +16 -1
  37. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +2 -0
  38. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +6 -9
  39. package/front_end/models/bindings/DefaultScriptMapping.ts +2 -1
  40. package/front_end/models/bindings/SymbolizedError.ts +45 -35
  41. package/front_end/models/extensions/ExtensionAPI.ts +138 -47
  42. package/front_end/models/har/Importer.ts +1 -0
  43. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +5 -7
  44. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -2
  45. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +44 -51
  46. package/front_end/models/stack_trace/StackTrace.ts +7 -0
  47. package/front_end/models/stack_trace/StackTraceImpl.ts +13 -4
  48. package/front_end/models/stack_trace/StackTraceModel.ts +9 -8
  49. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -1
  50. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -8
  51. package/front_end/panels/ai_assistance/components/ChatMessage.ts +96 -4
  52. package/front_end/panels/ai_assistance/components/chatMessage.css +6 -0
  53. package/front_end/panels/application/ApplicationPanelSidebar.ts +39 -0
  54. package/front_end/panels/application/ApplicationPanelTreeElement.ts +39 -0
  55. package/front_end/panels/application/CookieItemsView.ts +2 -2
  56. package/front_end/panels/application/components/AdsView.ts +219 -0
  57. package/front_end/panels/application/components/adsView.css +54 -0
  58. package/front_end/panels/application/components/components.ts +2 -0
  59. package/front_end/panels/application/resourcesSidebar.css +11 -0
  60. package/front_end/panels/console/SymbolizedErrorWidget.ts +79 -25
  61. package/front_end/panels/network/NetworkLogView.ts +5 -1
  62. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -0
  63. package/front_end/panels/sources/SourcesPanel.ts +2 -1
  64. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +5 -4
  65. package/front_end/third_party/chromium/README.chromium +1 -1
  66. package/front_end/third_party/lighthouse/README.chromium +2 -2
  67. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1607 -5733
  68. package/front_end/third_party/lighthouse/locales/ar-XB.json +290 -65
  69. package/front_end/third_party/lighthouse/locales/ar.json +290 -65
  70. package/front_end/third_party/lighthouse/locales/bg.json +290 -65
  71. package/front_end/third_party/lighthouse/locales/ca.json +295 -70
  72. package/front_end/third_party/lighthouse/locales/cs.json +290 -65
  73. package/front_end/third_party/lighthouse/locales/da.json +294 -69
  74. package/front_end/third_party/lighthouse/locales/de.json +295 -70
  75. package/front_end/third_party/lighthouse/locales/el.json +290 -65
  76. package/front_end/third_party/lighthouse/locales/en-GB.json +290 -65
  77. package/front_end/third_party/lighthouse/locales/en-US.json +79 -67
  78. package/front_end/third_party/lighthouse/locales/en-XA.json +253 -64
  79. package/front_end/third_party/lighthouse/locales/en-XL.json +79 -67
  80. package/front_end/third_party/lighthouse/locales/es-419.json +290 -65
  81. package/front_end/third_party/lighthouse/locales/es.json +298 -73
  82. package/front_end/third_party/lighthouse/locales/fi.json +290 -65
  83. package/front_end/third_party/lighthouse/locales/fil.json +290 -65
  84. package/front_end/third_party/lighthouse/locales/fr.json +294 -69
  85. package/front_end/third_party/lighthouse/locales/he.json +293 -68
  86. package/front_end/third_party/lighthouse/locales/hi.json +291 -66
  87. package/front_end/third_party/lighthouse/locales/hr.json +290 -65
  88. package/front_end/third_party/lighthouse/locales/hu.json +290 -65
  89. package/front_end/third_party/lighthouse/locales/id.json +290 -65
  90. package/front_end/third_party/lighthouse/locales/it.json +294 -69
  91. package/front_end/third_party/lighthouse/locales/ja.json +290 -65
  92. package/front_end/third_party/lighthouse/locales/ko.json +290 -65
  93. package/front_end/third_party/lighthouse/locales/lt.json +290 -65
  94. package/front_end/third_party/lighthouse/locales/lv.json +290 -65
  95. package/front_end/third_party/lighthouse/locales/nl.json +290 -65
  96. package/front_end/third_party/lighthouse/locales/no.json +290 -65
  97. package/front_end/third_party/lighthouse/locales/pl.json +290 -65
  98. package/front_end/third_party/lighthouse/locales/pt-PT.json +291 -66
  99. package/front_end/third_party/lighthouse/locales/pt.json +290 -65
  100. package/front_end/third_party/lighthouse/locales/ro.json +290 -65
  101. package/front_end/third_party/lighthouse/locales/ru.json +301 -76
  102. package/front_end/third_party/lighthouse/locales/sk.json +291 -66
  103. package/front_end/third_party/lighthouse/locales/sl.json +290 -65
  104. package/front_end/third_party/lighthouse/locales/sr-Latn.json +290 -65
  105. package/front_end/third_party/lighthouse/locales/sr.json +290 -65
  106. package/front_end/third_party/lighthouse/locales/sv.json +297 -72
  107. package/front_end/third_party/lighthouse/locales/ta.json +291 -66
  108. package/front_end/third_party/lighthouse/locales/te.json +293 -68
  109. package/front_end/third_party/lighthouse/locales/th.json +291 -66
  110. package/front_end/third_party/lighthouse/locales/tr.json +290 -65
  111. package/front_end/third_party/lighthouse/locales/uk.json +290 -65
  112. package/front_end/third_party/lighthouse/locales/vi.json +291 -66
  113. package/front_end/third_party/lighthouse/locales/zh-HK.json +292 -67
  114. package/front_end/third_party/lighthouse/locales/zh-TW.json +291 -66
  115. package/front_end/third_party/lighthouse/locales/zh.json +291 -66
  116. package/front_end/third_party/lighthouse/report/bundle.d.ts +6 -6
  117. package/front_end/third_party/lighthouse/report/bundle.js +4 -7
  118. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  119. package/front_end/ui/legacy/StackedPane.ts +229 -0
  120. package/front_end/ui/legacy/ViewManager.ts +59 -169
  121. package/front_end/ui/legacy/Widget.ts +32 -8
  122. package/front_end/ui/legacy/legacy.ts +3 -1
  123. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  124. package/mcp/mcp.ts +1 -0
  125. package/package.json +1 -1
@@ -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
+ }
@@ -13,6 +13,8 @@ import {ChangeManager} from '../ChangeManager.js';
13
13
  import {LighthouseFormatter} from '../data_formatters/LighthouseFormatter.js';
14
14
  import {debugLog} from '../debug.js';
15
15
  import {ExtensionScope} from '../ExtensionScope.js';
16
+ import {ToolName} from '../tools/Tool.js';
17
+ import {ToolRegistry} from '../tools/ToolRegistry.js';
16
18
 
17
19
  import {
18
20
  AiAgent,
@@ -25,10 +27,8 @@ import {
25
27
  } from './AiAgent.js';
26
28
  import {
27
29
  type CreateExtensionScopeFunction,
28
- executeJavaScriptFunction,
29
30
  type ExecuteJsAgentOptions,
30
31
  executeJsCode,
31
- JavascriptExecutor
32
32
  } from './ExecuteJavascript.js';
33
33
 
34
34
  /**
@@ -81,12 +81,6 @@ If the user asks a question that requires an investigation of a problem, use thi
81
81
  - [Suggestion 2]
82
82
  `;
83
83
 
84
- const SECURITY_WARNING = `**CRITICAL CONSTRAINT**: This Lighthouse report was imported from a file and is static.
85
- You do NOT have access to the inspected page.
86
- Tools like \`executeJavaScript\`, \`getStyles\`, or \`runAccessibilityAudits\` are disabled.
87
- Do NOT attempt to use them or instruct the user that you will use them.
88
- Rely ONLY on the static report data below.`;
89
-
90
84
  export class AccessibilityContext extends ConversationContext<LHModel.ReporterTypes.ReportJSON> {
91
85
  #lh: LHModel.ReporterTypes.ReportJSON;
92
86
 
@@ -123,28 +117,17 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
123
117
  (overrides?: LHModel.RunTypes.RunOverrides) => Promise<LHModel.ReporterTypes.ReportJSON|null>;
124
118
 
125
119
  #execJs: typeof executeJsCode;
126
- #javascriptExecutor: JavascriptExecutor;
127
120
  #changes: ChangeManager;
128
121
  #createExtensionScope: CreateExtensionScopeFunction;
129
- #currentTurnId = 0;
130
122
 
131
123
  constructor(opts: ExecuteJsAgentOptions) {
132
124
  super(opts);
133
125
  this.#lighthouseRecording = opts.lighthouseRecording;
134
126
  this.#changes = opts.changeManager || new ChangeManager();
135
127
  this.#execJs = opts.execJs ?? executeJsCode;
136
- this.#createExtensionScope =
137
- opts.createExtensionScope ?? ((changes: ChangeManager) => {
138
- return new ExtensionScope(changes, this.sessionId, this.#getDocumentBodyNode(), this.#currentTurnId);
139
- });
140
- this.#javascriptExecutor = new JavascriptExecutor(
141
- {
142
- executionMode: this.executionMode,
143
- getContextNode: () => this.#getDocumentBodyNode(),
144
- createExtensionScope: this.#createExtensionScope.bind(this),
145
- changes: this.#changes,
146
- },
147
- this.#execJs);
128
+ this.#createExtensionScope = opts.createExtensionScope ?? ((changes: ChangeManager) => {
129
+ return new ExtensionScope(changes, this.sessionId, this.#getDocumentBodyNode());
130
+ });
148
131
  }
149
132
 
150
133
  get userTier(): string|undefined {
@@ -168,7 +151,6 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
168
151
  }
169
152
 
170
153
  protected override async preRun(): Promise<void> {
171
- this.#currentTurnId++;
172
154
  const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
173
155
  const domModel = target?.model(SDK.DOMModel.DOMModel);
174
156
  // We need to ensure the document is requested so that #getDocumentBodyNode()
@@ -280,11 +262,33 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
280
262
  }
281
263
  });
282
264
 
283
- if (isImported) {
284
- return;
265
+ const executeJsTool = ToolRegistry.get(ToolName.EXECUTE_JAVASCRIPT);
266
+ if (!executeJsTool) {
267
+ throw new Error('Required tool "executeJavaScript" not found');
285
268
  }
286
-
287
- this.declareFunction('executeJavaScript', executeJavaScriptFunction(this.#javascriptExecutor));
269
+ this.declareFunction(executeJsTool.name, {
270
+ description: executeJsTool.description,
271
+ parameters: executeJsTool.parameters,
272
+ displayInfoFromArgs: executeJsTool.displayInfoFromArgs,
273
+ handler: async (args, options) => {
274
+ if (isImported) {
275
+ return {
276
+ error: 'Cannot use this tool on an imported file.',
277
+ };
278
+ }
279
+ return await executeJsTool.handler(
280
+ args,
281
+ {
282
+ conversationContext: this.context ?? null,
283
+ changeManager: this.#changes,
284
+ createExtensionScope: this.#createExtensionScope.bind(this),
285
+ execJs: this.#execJs,
286
+ getExecutionContextNode: () => this.#getDocumentBodyNode(),
287
+ },
288
+ options,
289
+ );
290
+ },
291
+ });
288
292
 
289
293
  this.declareFunction<{explanation: string}, {audits: string}>('runAccessibilityAudits', {
290
294
  description:
@@ -311,6 +315,11 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
311
315
  },
312
316
  handler: async params => {
313
317
  debugLog('Function call: runAccessibilityAudits', params);
318
+ if (isImported) {
319
+ return {
320
+ error: 'Cannot use this tool on an imported file.',
321
+ };
322
+ }
314
323
  if (!this.#lighthouseRecording) {
315
324
  return {error: 'Lighthouse recording is not available.'};
316
325
  }
@@ -375,6 +384,11 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
375
384
  },
376
385
  handler: async params => {
377
386
  debugLog('Function call: getStyles', params);
387
+ if (isImported) {
388
+ return {
389
+ error: 'Cannot use this tool on an imported file.',
390
+ };
391
+ }
378
392
  const node = await this.#resolvePathToNode(params.path);
379
393
  if (!node) {
380
394
  return {error: `Could not find the element with path: ${params.path}`};
@@ -445,6 +459,11 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
445
459
  },
446
460
  handler: async params => {
447
461
  debugLog('Function call: getElementAccessibilityDetails', params);
462
+ if (isImported) {
463
+ return {
464
+ error: 'Cannot use this tool on an imported file.',
465
+ };
466
+ }
448
467
  const node = await this.#resolvePathToNode(params.path);
449
468
  if (!node) {
450
469
  return {error: `Could not find the element with path: ${params.path}`};
@@ -522,10 +541,7 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
522
541
  if (lhr) {
523
542
  this.#declareFunctions();
524
543
  }
525
- let enhancedQuery = lhr ? `${this.#getInitialPayload(lhr)}\n# User request:\n\n` : '';
526
- if (lhr?.getItem().isImported) {
527
- enhancedQuery = `${SECURITY_WARNING}\n\n${enhancedQuery}`;
528
- }
544
+ const enhancedQuery = lhr ? `${this.#getInitialPayload(lhr)}\n# User request:\n\n` : '';
529
545
  return `${enhancedQuery}${query}`;
530
546
  }
531
547
 
@@ -332,6 +332,13 @@ export interface SourceFilesListAiWidget {
332
332
  };
333
333
  }
334
334
 
335
+ export interface NetworkRequestsListAiWidget {
336
+ name: 'NETWORK_REQUESTS_LIST';
337
+ data: {
338
+ requests: SDK.NetworkRequest.NetworkRequest[],
339
+ };
340
+ }
341
+
335
342
  export interface LighthouseReportAiWidget {
336
343
  name: 'LIGHTHOUSE_REPORT';
337
344
  data: {
@@ -366,10 +373,10 @@ export interface SourceCodeAiWidget {
366
373
  };
367
374
  }
368
375
 
369
- export type AiWidget =
370
- ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget|PerformanceTraceAiWidget|
371
- PerfInsightAiWidget|TimelineRangeSummaryAiWidget|BottomUpTreeAiWidget|SourceFileAiWidget|LighthouseReportAiWidget|
372
- 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;
373
380
 
374
381
  export type FunctionCallHandlerResult<Result> = {
375
382
  requiresApproval: true,
@@ -463,7 +470,7 @@ export abstract class AiAgent<T> {
463
470
 
464
471
  readonly #sessionId: string;
465
472
  readonly #aidaClient: Host.AidaClient.AidaClient;
466
- readonly #serverSideLoggingEnabled: boolean;
473
+ #serverSideLoggingEnabled: boolean;
467
474
  readonly confirmSideEffect: typeof Promise.withResolvers;
468
475
  readonly #functionDeclarations = new Map<string, FunctionDeclaration<Record<string, unknown>, unknown>>();
469
476
  readonly #allowedOrigin?: () => AllowedOriginResult;
@@ -502,8 +509,8 @@ export abstract class AiAgent<T> {
502
509
  this.#allowedOrigin = opts.allowedOrigin;
503
510
  }
504
511
 
505
- async enhanceQuery(query: string, selected: ConversationContext<T>|null, multimodalInputType?: MultimodalInputType):
506
- Promise<string>;
512
+ async enhanceQuery(query: string, selected: ConversationContext<T>|null,
513
+ multimodalInputType?: MultimodalInputType): Promise<string>;
507
514
  async enhanceQuery(query: string): Promise<string> {
508
515
  return query;
509
516
  }
@@ -542,13 +549,29 @@ export abstract class AiAgent<T> {
542
549
  clearCache(): void {
543
550
  }
544
551
 
552
+ protected disableServerSideLogging(): void {
553
+ this.#serverSideLoggingEnabled = false;
554
+ }
555
+
545
556
  popPendingMultimodalInput(): MultimodalInput|undefined {
546
557
  return undefined;
547
558
  }
548
559
 
549
- buildRequest(
550
- part: Host.AidaClient.Part|Host.AidaClient.Part[],
551
- role: Host.AidaClient.Role.USER|Host.AidaClient.Role.ROLE_UNSPECIFIED): Host.AidaClient.DoConversationRequest {
560
+ /**
561
+ * Preamble features appended to the `client_version` in metadata.
562
+ * This is required ONLY for the Styling Agent for legacy reasons to serve
563
+ * different server-side preambles based on the Chrome version.
564
+ * Other agents should NOT set or override this.
565
+ * If you are curious about this, look for `do_conversation_handler.cc` in
566
+ * Google3 or chat to @jacktfranklin.
567
+ */
568
+ preambleFeatures(): string[] {
569
+ return [];
570
+ }
571
+
572
+ buildRequest(part: Host.AidaClient.Part|Host.AidaClient.Part[],
573
+ role: Host.AidaClient.Role.USER|
574
+ Host.AidaClient.Role.ROLE_UNSPECIFIED): Host.AidaClient.DoConversationRequest {
552
575
  const parts = Array.isArray(part) ? part : [part];
553
576
  const currentMessage: Host.AidaClient.Content = {
554
577
  parts,
@@ -589,7 +612,8 @@ export abstract class AiAgent<T> {
589
612
  disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
590
613
  string_session_id: this.#sessionId,
591
614
  user_tier: userTier,
592
- client_version: Root.Runtime.getChromeVersion(),
615
+ client_version:
616
+ Root.Runtime.getChromeVersion() + this.preambleFeatures().map(feature => `+${feature}`).join(''),
593
617
  },
594
618
 
595
619
  functionality_type: enableAidaFunctionCalling ? Host.AidaClient.FunctionalityType.AGENTIC_CHAT :
@@ -693,6 +717,10 @@ export abstract class AiAgent<T> {
693
717
  this.#functionDeclarations.clear();
694
718
  }
695
719
 
720
+ /**
721
+ * Executed immediately after the current context is populated with the selected
722
+ * context and before the request is built.
723
+ */
696
724
  protected async preRun(): Promise<void> {
697
725
  }
698
726
 
@@ -705,11 +733,11 @@ export abstract class AiAgent<T> {
705
733
  },
706
734
  multimodalInput?: MultimodalInput,
707
735
  ): AsyncGenerator<ResponseData, void, void> {
708
- await this.preRun();
709
736
  await options.selected?.refresh();
710
737
  if (options.selected) {
711
738
  this.context = options.selected;
712
739
  }
740
+ await this.preRun();
713
741
 
714
742
  const enhancedQuery = await this.enhanceQuery(initialQuery, options.selected, multimodalInput?.type);
715
743
  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": {},