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.
- package/SECURITY.md +1 -0
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
- package/front_end/core/sdk/CSSMatchedStyles.ts +55 -26
- package/front_end/core/sdk/CSSRule.ts +1 -0
- package/front_end/core/sdk/DebuggerModel.ts +5 -0
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +4 -3
- package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +4 -3
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +4 -5
- package/front_end/generated/InspectorBackendCommands.ts +1 -1
- package/front_end/generated/protocol.ts +7 -0
- package/front_end/models/ai_assistance/AiAgent2.ts +100 -18
- package/front_end/models/ai_assistance/AiConversation.ts +18 -14
- package/front_end/models/ai_assistance/AiUtils.ts +71 -0
- package/front_end/models/ai_assistance/ChangeManager.ts +2 -5
- package/front_end/models/ai_assistance/{agents/ConversationSummaryAgent.ts → ConversationSummary.ts} +29 -66
- package/front_end/models/ai_assistance/ExtensionScope.ts +1 -4
- package/front_end/models/ai_assistance/{agents/PerformanceAnnotationsAgent.ts → PerformanceAnnotations.ts} +47 -89
- package/front_end/models/ai_assistance/README.md +8 -0
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +65 -40
- package/front_end/models/ai_assistance/agents/AiAgent.ts +37 -6
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +11 -0
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +55 -5
- package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +2 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +119 -78
- package/front_end/models/ai_assistance/agents/StorageAgent.ts +47 -38
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -25
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +46 -326
- package/front_end/models/ai_assistance/ai_assistance.ts +14 -4
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.snapshot.txt +51 -0
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +200 -0
- package/front_end/models/ai_assistance/skills/styling.md +44 -2
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +140 -0
- package/front_end/models/ai_assistance/tools/GetStyles.ts +141 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +64 -0
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +36 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +5 -7
- package/front_end/models/lighthouse/LighthouseReporterTypes.ts +5 -0
- package/front_end/models/live-metrics/LiveMetrics.ts +24 -13
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +2 -2
- package/front_end/models/stack_trace/StackTrace.ts +4 -1
- package/front_end/models/stack_trace/StackTraceImpl.ts +9 -2
- package/front_end/models/stack_trace/StackTraceModel.ts +17 -4
- package/front_end/models/stack_trace/Trie.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +25 -22
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +96 -4
- package/front_end/panels/ai_assistance/components/chatMessage.css +6 -0
- package/front_end/panels/application/DOMStorageItemsView.ts +4 -0
- package/front_end/panels/application/KeyValueStorageItemsView.ts +39 -7
- package/front_end/panels/application/components/AdsView.ts +219 -0
- package/front_end/panels/application/components/adsView.css +54 -0
- package/front_end/panels/application/components/components.ts +2 -0
- package/front_end/panels/common/ExtensionServer.ts +26 -15
- package/front_end/panels/console/SymbolizedErrorWidget.ts +73 -22
- package/front_end/panels/elements/StandaloneStylesContainer.ts +1 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +8 -0
- package/front_end/panels/elements/StylePropertyHighlighter.ts +4 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -5
- package/front_end/panels/elements/StylesContainer.ts +1 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +4 -4
- package/front_end/panels/layer_viewer/PaintProfilerView.ts +106 -132
- package/front_end/panels/lighthouse/LighthousePanel.ts +4 -3
- package/front_end/panels/network/NetworkLogView.ts +8 -1
- package/front_end/panels/network/networkLogView.css +0 -15
- package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +5 -4
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/lighthouse/README.chromium +2 -2
- package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1607 -5733
- package/front_end/third_party/lighthouse/locales/ar-XB.json +290 -65
- package/front_end/third_party/lighthouse/locales/ar.json +290 -65
- package/front_end/third_party/lighthouse/locales/bg.json +290 -65
- package/front_end/third_party/lighthouse/locales/ca.json +295 -70
- package/front_end/third_party/lighthouse/locales/cs.json +290 -65
- package/front_end/third_party/lighthouse/locales/da.json +294 -69
- package/front_end/third_party/lighthouse/locales/de.json +295 -70
- package/front_end/third_party/lighthouse/locales/el.json +290 -65
- package/front_end/third_party/lighthouse/locales/en-GB.json +290 -65
- package/front_end/third_party/lighthouse/locales/en-US.json +79 -67
- package/front_end/third_party/lighthouse/locales/en-XA.json +253 -64
- package/front_end/third_party/lighthouse/locales/en-XL.json +79 -67
- package/front_end/third_party/lighthouse/locales/es-419.json +290 -65
- package/front_end/third_party/lighthouse/locales/es.json +298 -73
- package/front_end/third_party/lighthouse/locales/fi.json +290 -65
- package/front_end/third_party/lighthouse/locales/fil.json +290 -65
- package/front_end/third_party/lighthouse/locales/fr.json +294 -69
- package/front_end/third_party/lighthouse/locales/he.json +293 -68
- package/front_end/third_party/lighthouse/locales/hi.json +291 -66
- package/front_end/third_party/lighthouse/locales/hr.json +290 -65
- package/front_end/third_party/lighthouse/locales/hu.json +290 -65
- package/front_end/third_party/lighthouse/locales/id.json +290 -65
- package/front_end/third_party/lighthouse/locales/it.json +294 -69
- package/front_end/third_party/lighthouse/locales/ja.json +290 -65
- package/front_end/third_party/lighthouse/locales/ko.json +290 -65
- package/front_end/third_party/lighthouse/locales/lt.json +290 -65
- package/front_end/third_party/lighthouse/locales/lv.json +290 -65
- package/front_end/third_party/lighthouse/locales/nl.json +290 -65
- package/front_end/third_party/lighthouse/locales/no.json +290 -65
- package/front_end/third_party/lighthouse/locales/pl.json +290 -65
- package/front_end/third_party/lighthouse/locales/pt-PT.json +291 -66
- package/front_end/third_party/lighthouse/locales/pt.json +290 -65
- package/front_end/third_party/lighthouse/locales/ro.json +290 -65
- package/front_end/third_party/lighthouse/locales/ru.json +301 -76
- package/front_end/third_party/lighthouse/locales/sk.json +291 -66
- package/front_end/third_party/lighthouse/locales/sl.json +290 -65
- package/front_end/third_party/lighthouse/locales/sr-Latn.json +290 -65
- package/front_end/third_party/lighthouse/locales/sr.json +290 -65
- package/front_end/third_party/lighthouse/locales/sv.json +297 -72
- package/front_end/third_party/lighthouse/locales/ta.json +291 -66
- package/front_end/third_party/lighthouse/locales/te.json +293 -68
- package/front_end/third_party/lighthouse/locales/th.json +291 -66
- package/front_end/third_party/lighthouse/locales/tr.json +290 -65
- package/front_end/third_party/lighthouse/locales/uk.json +290 -65
- package/front_end/third_party/lighthouse/locales/vi.json +291 -66
- package/front_end/third_party/lighthouse/locales/zh-HK.json +292 -67
- package/front_end/third_party/lighthouse/locales/zh-TW.json +291 -66
- package/front_end/third_party/lighthouse/locales/zh.json +291 -66
- package/front_end/third_party/lighthouse/report/bundle.d.ts +6 -6
- package/front_end/third_party/lighthouse/report/bundle.js +4 -7
- package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
- package/front_end/ui/legacy/Widget.ts +32 -8
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +36 -3
- package/front_end/ui/legacy/components/data_grid/dataGridAiButton.css +20 -0
- package/front_end/ui/legacy/components/utils/Linkifier.ts +19 -4
- package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
- package/mcp/mcp.ts +1 -0
- 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
|
|
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
|
|
130
|
+
if (change.groupId === groupId && change.backendNodeId) {
|
|
134
131
|
nodes.add(change.backendNodeId);
|
|
135
132
|
}
|
|
136
133
|
}
|
package/front_end/models/ai_assistance/{agents/ConversationSummaryAgent.ts → ConversationSummary.ts}
RENAMED
|
@@ -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 '
|
|
6
|
-
import * as Root from '
|
|
5
|
+
import * as Host from '../../core/host/host.js';
|
|
6
|
+
import * as Root from '../../core/root/root.js';
|
|
7
7
|
|
|
8
|
-
import {
|
|
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
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
*
|
|
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
|
|
133
|
-
|
|
114
|
+
export class ConversationSummary {
|
|
115
|
+
readonly #aidaClient: Host.AidaClient.AidaClient;
|
|
116
|
+
readonly #serverSideLoggingEnabled: boolean;
|
|
134
117
|
|
|
135
|
-
|
|
136
|
-
|
|
118
|
+
constructor(options: ConversationSummaryOptions) {
|
|
119
|
+
this.#aidaClient = options.aidaClient;
|
|
120
|
+
this.#serverSideLoggingEnabled = options.serverSideLoggingEnabled ?? false;
|
|
137
121
|
}
|
|
138
122
|
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
return;
|
|
142
|
+
if (!resultText) {
|
|
143
|
+
throw new Error('Failed to summarize conversation');
|
|
158
144
|
}
|
|
159
145
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
|
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 '
|
|
6
|
-
import * as Root from '
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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.
|
|
235
|
+
const isImported = this.context?.getItem().isImported;
|
|
239
236
|
|
|
240
|
-
this.declareFunction<{
|
|
237
|
+
this.declareFunction<{categoryId: LHModel.RunTypes.CategoryId}, {audits: string}>('getLighthouseAudits', {
|
|
241
238
|
description:
|
|
242
|
-
'
|
|
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
|
-
|
|
245
|
+
categoryId: {
|
|
249
246
|
type: Host.AidaClient.ParametersTypes.STRING,
|
|
250
|
-
description:
|
|
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: ['
|
|
252
|
+
required: ['categoryId'],
|
|
255
253
|
},
|
|
256
254
|
displayInfoFromArgs: params => {
|
|
257
255
|
return {
|
|
258
|
-
title: i18n.i18n.lockedString(
|
|
259
|
-
|
|
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:
|
|
265
|
-
|
|
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: '
|
|
264
|
+
return {error: 'No Lighthouse report available.'};
|
|
275
265
|
}
|
|
276
|
-
const audits = new LighthouseFormatter().audits(report,
|
|
266
|
+
const audits = new LighthouseFormatter().audits(report, params.categoryId);
|
|
277
267
|
return {
|
|
278
268
|
result: {audits},
|
|
279
|
-
widgets: [{name: 'LIGHTHOUSE_REPORT', data: {report
|
|
269
|
+
widgets: [{name: 'LIGHTHOUSE_REPORT', data: {report}}],
|
|
280
270
|
};
|
|
281
271
|
}
|
|
282
272
|
});
|
|
283
273
|
|
|
284
|
-
|
|
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
|
-
'
|
|
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
|
-
|
|
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: ['
|
|
301
|
+
required: ['explanation'],
|
|
300
302
|
},
|
|
301
303
|
displayInfoFromArgs: params => {
|
|
302
304
|
return {
|
|
303
|
-
title: i18n.i18n.lockedString(
|
|
304
|
-
|
|
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:
|
|
309
|
-
|
|
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: '
|
|
326
|
+
return {error: 'Failed to run accessibility audits.'};
|
|
312
327
|
}
|
|
313
|
-
const audits = new LighthouseFormatter().audits(report,
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
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": {},
|