chrome-devtools-frontend 1.0.1605219 → 1.0.1606789

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 (78) hide show
  1. package/front_end/Images/src/dots-circle.svg +10 -0
  2. package/front_end/core/host/UserMetrics.ts +0 -1
  3. package/front_end/core/platform/api/HostRuntime.ts +9 -6
  4. package/front_end/core/platform/browser/HostRuntime.ts +2 -2
  5. package/front_end/core/platform/node/HostRuntime.ts +7 -7
  6. package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
  7. package/front_end/core/root/ExperimentNames.ts +0 -1
  8. package/front_end/core/sdk/CrashReportContextModel.ts +28 -0
  9. package/front_end/core/sdk/sdk.ts +2 -2
  10. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +3 -3
  11. package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker-entrypoint.ts +5 -4
  12. package/front_end/entrypoints/main/MainImpl.ts +0 -101
  13. package/front_end/models/ai_assistance/ChangeManager.ts +6 -6
  14. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +112 -5
  15. package/front_end/models/ai_assistance/agents/AiAgent.ts +9 -25
  16. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +2 -2
  17. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +6 -3
  18. package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +10 -9
  19. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +313 -0
  20. package/front_end/models/ai_assistance/agents/FileAgent.ts +15 -1
  21. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +15 -1
  22. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +19 -8
  23. package/front_end/models/ai_assistance/agents/StylingAgent.ts +35 -278
  24. package/front_end/models/ai_assistance/ai_assistance.ts +0 -2
  25. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -5
  26. package/front_end/models/web_mcp/WebMCPModel.ts +187 -0
  27. package/front_end/models/web_mcp/web_mcp.ts +9 -0
  28. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -1
  29. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  30. package/front_end/panels/ai_assistance/components/AccessibilityAgentMarkdownRenderer.ts +88 -0
  31. package/front_end/panels/ai_assistance/components/ChatMessage.ts +149 -49
  32. package/front_end/panels/ai_assistance/components/ChatView.ts +11 -15
  33. package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +13 -4
  34. package/front_end/panels/ai_assistance/components/chatMessage.css +53 -1
  35. package/front_end/panels/ai_assistance/components/chatView.css +0 -10
  36. package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +13 -0
  37. package/front_end/panels/application/WebMCPView.ts +471 -25
  38. package/front_end/panels/application/webMCPView.css +53 -2
  39. package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
  40. package/front_end/panels/elements/ElementsTreeOutline.ts +87 -0
  41. package/front_end/panels/elements/StylePropertiesSection.ts +0 -4
  42. package/front_end/panels/elements/elements.ts +3 -0
  43. package/front_end/panels/elements/elementsTreeOutline.css +21 -0
  44. package/front_end/panels/elements/stylePropertiesTreeOutline.css +7 -0
  45. package/front_end/panels/lighthouse/LighthousePanel.ts +7 -1
  46. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +25 -2
  47. package/front_end/panels/profiler/HeapDetachedElementsView.ts +0 -4
  48. package/front_end/panels/profiler/HeapProfileView.ts +0 -4
  49. package/front_end/panels/profiler/HeapProfilerPanel.ts +4 -13
  50. package/front_end/panels/profiler/HeapSnapshotProxy.ts +2 -6
  51. package/front_end/panels/profiler/HeapSnapshotView.ts +19 -32
  52. package/front_end/panels/profiler/ProfileHeader.ts +1 -15
  53. package/front_end/panels/profiler/ProfileTypeRegistry.ts +4 -12
  54. package/front_end/panels/profiler/ProfileView.ts +1 -6
  55. package/front_end/panels/profiler/ProfilesPanel.ts +60 -7
  56. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +27 -22
  57. package/front_end/panels/timeline/EventsTimelineTreeView.ts +1 -1
  58. package/front_end/panels/timeline/ThirdPartyTreeView.ts +2 -2
  59. package/front_end/panels/timeline/TimelinePanel.ts +11 -153
  60. package/front_end/panels/timeline/TimelineTreeView.ts +147 -48
  61. package/front_end/panels/timeline/TimelineUIUtils.ts +2 -1
  62. package/front_end/panels/timeline/components/CWVMetrics.ts +18 -2
  63. package/front_end/panels/timeline/timeline-meta.ts +11 -0
  64. package/front_end/panels/timeline/utils/Helpers.ts +5 -0
  65. package/front_end/third_party/chromium/README.chromium +1 -1
  66. package/front_end/ui/components/markdown_view/CodeBlock.ts +47 -1
  67. package/front_end/ui/components/markdown_view/codeBlock.css +8 -0
  68. package/front_end/ui/legacy/FilterBar.ts +2 -0
  69. package/front_end/ui/legacy/SplitWidget.ts +33 -27
  70. package/front_end/ui/legacy/TabbedPane.ts +123 -3
  71. package/front_end/ui/legacy/Widget.ts +95 -48
  72. package/front_end/ui/legacy/components/source_frame/JSONView.ts +1 -1
  73. package/front_end/ui/legacy/components/utils/jsUtils.css +2 -0
  74. package/front_end/ui/legacy/infobar.css +1 -0
  75. package/front_end/ui/visual_logging/KnownContextValues.ts +6 -1
  76. package/package.json +1 -1
  77. package/front_end/core/sdk/WebMCPModel.ts +0 -104
  78. package/front_end/models/ai_assistance/ConversationHandler.ts +0 -325
@@ -0,0 +1,10 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_367_65863)">
3
+ <path d="M5.2 8.875C5.44306 8.875 5.64722 8.79236 5.8125 8.62708C5.9875 8.45208 6.075 8.24306 6.075 8C6.075 7.75694 5.9875 7.55278 5.8125 7.3875C5.64722 7.2125 5.44306 7.125 5.2 7.125C4.95694 7.125 4.74792 7.2125 4.57292 7.3875C4.40764 7.55278 4.325 7.75694 4.325 8C4.325 8.24306 4.40764 8.45208 4.57292 8.62708C4.74792 8.79236 4.95694 8.875 5.2 8.875ZM8 8.875C8.24306 8.875 8.44722 8.79236 8.6125 8.62708C8.7875 8.45208 8.875 8.24306 8.875 8C8.875 7.75694 8.7875 7.55278 8.6125 7.3875C8.44722 7.2125 8.24306 7.125 8 7.125C7.75694 7.125 7.54792 7.2125 7.37292 7.3875C7.20764 7.55278 7.125 7.75694 7.125 8C7.125 8.24306 7.20764 8.45208 7.37292 8.62708C7.54792 8.79236 7.75694 8.875 8 8.875ZM10.8 8.875C11.0431 8.875 11.2472 8.79236 11.4125 8.62708C11.5875 8.45208 11.675 8.24306 11.675 8C11.675 7.75694 11.5875 7.55278 11.4125 7.3875C11.2472 7.2125 11.0431 7.125 10.8 7.125C10.5569 7.125 10.3479 7.2125 10.1729 7.3875C10.0076 7.55278 9.925 7.75694 9.925 8C9.925 8.24306 10.0076 8.45208 10.1729 8.62708C10.3479 8.79236 10.5569 8.875 10.8 8.875ZM8 13.6C7.23194 13.6 6.50764 13.4542 5.82708 13.1625C5.14653 12.8708 4.54861 12.4722 4.03333 11.9667C3.52778 11.4514 3.12917 10.8535 2.8375 10.1729C2.54583 9.49236 2.4 8.76806 2.4 8C2.4 7.22222 2.54583 6.49792 2.8375 5.82708C3.12917 5.14653 3.52778 4.55347 4.03333 4.04792C4.54861 3.53264 5.14653 3.12917 5.82708 2.8375C6.50764 2.54583 7.23194 2.4 8 2.4C8.77778 2.4 9.50208 2.54583 10.1729 2.8375C10.8535 3.12917 11.4465 3.53264 11.9521 4.04792C12.4674 4.55347 12.8708 5.14653 13.1625 5.82708C13.4542 6.49792 13.6 7.22222 13.6 8C13.6 8.76806 13.4542 9.49236 13.1625 10.1729C12.8708 10.8535 12.4674 11.4514 11.9521 11.9667C11.4465 12.4722 10.8535 12.8708 10.1729 13.1625C9.50208 13.4542 8.77778 13.6 8 13.6ZM8 12.55C9.26389 12.55 10.3382 12.1076 11.2229 11.2229C12.1076 10.3382 12.55 9.26389 12.55 8C12.55 6.73611 12.1076 5.66181 11.2229 4.77708C10.3382 3.89236 9.26389 3.45 8 3.45C6.73611 3.45 5.66181 3.89236 4.77708 4.77708C3.89236 5.66181 3.45 6.73611 3.45 8C3.45 9.26389 3.89236 10.3382 4.77708 11.2229C5.66181 12.1076 6.73611 12.55 8 12.55Z" fill="#474747"/>
4
+ </g>
5
+ <defs>
6
+ <clipPath id="clip0_367_65863">
7
+ <rect width="16" height="16" fill="white"/>
8
+ </clipPath>
9
+ </defs>
10
+ </svg>
@@ -815,7 +815,6 @@ export enum DevtoolsExperiments {
815
815
  'live-heap-profile' = 11,
816
816
  'protocol-monitor' = 13,
817
817
  'sampling-heap-profiler-timeline' = 17,
818
- 'show-option-to-expose-internals-in-heap-snapshot' = 18,
819
818
  'timeline-invalidation-tracking' = 26,
820
819
  'timeline-show-all-events' = 27,
821
820
  apca = 39,
@@ -25,21 +25,24 @@ export interface Worker {
25
25
  set onerror(listener: (event: any) => void);
26
26
  }
27
27
 
28
+ type WorkerMessagePort = typeof MessagePort.prototype;
29
+
28
30
  /**
29
- * Currently we ony transfer MessagePorts to workers, but it's possible to add
31
+ * Currently we only transfer MessagePorts to workers, but it's possible to add
30
32
  * more things (like ReadableStream) as long as it's present in all runtimes.
31
33
  */
32
- export type WorkerTransferable = typeof MessagePort.prototype;
34
+ export type WorkerTransferable = WorkerMessagePort;
33
35
 
34
36
  /**
35
37
  * Used by workers to communicate with their parent.
36
38
  */
37
39
  export interface WorkerScope {
38
40
  postMessage(message: unknown): void;
39
- set onmessage(listener: (event: WorkerMessageEvent) => void);
41
+ set onmessage(listener: (event: WorkerMessageEvent) => Promise<void>| void);
40
42
  }
41
43
 
42
- export interface WorkerMessageEvent {
43
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- readonly data: any;
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ export interface WorkerMessageEvent<T = any> {
46
+ readonly data: T;
47
+ ports: readonly WorkerMessagePort[];
45
48
  }
@@ -9,8 +9,8 @@ class WebWorkerScope implements Api.HostRuntime.WorkerScope {
9
9
  self.postMessage(message);
10
10
  }
11
11
 
12
- set onmessage(listener: (event: Api.HostRuntime.WorkerMessageEvent) => void) {
13
- self.onmessage = listener;
12
+ set onmessage(listener: (event: Api.HostRuntime.WorkerMessageEvent) => Promise<void>| void) {
13
+ self.addEventListener('message', listener);
14
14
  }
15
15
  }
16
16
 
@@ -12,8 +12,8 @@ class NodeWorkerScope implements Api.HostRuntime.WorkerScope {
12
12
  }
13
13
 
14
14
  set onmessage(listener: (event: Api.HostRuntime.WorkerMessageEvent) => void) {
15
- WorkerThreads.parentPort?.on('message', data => {
16
- listener({data});
15
+ WorkerThreads.parentPort?.addEventListener('message', msg => {
16
+ listener(msg as unknown as Api.HostRuntime.WorkerMessageEvent);
17
17
  });
18
18
  }
19
19
  }
@@ -36,10 +36,10 @@ class NodeWorker implements Api.HostRuntime.Worker {
36
36
  });
37
37
  }
38
38
 
39
- postMessage(message: unknown): void {
39
+ postMessage(message: unknown, transfer?: Api.HostRuntime.WorkerTransferable[]): void {
40
40
  void this.#workerPromise.then(worker => {
41
41
  if (!this.#disposed) {
42
- worker.postMessage(message);
42
+ worker.postMessage(message, transfer);
43
43
  }
44
44
  });
45
45
  }
@@ -56,11 +56,11 @@ class NodeWorker implements Api.HostRuntime.Worker {
56
56
  this.dispose();
57
57
  }
58
58
 
59
- set onmessage(listener: (event: unknown) => void) {
59
+ set onmessage(listener: (event: Api.HostRuntime.WorkerMessageEvent) => void) {
60
60
  void this.#workerPromise.then(worker => {
61
- worker.on('message', data => {
61
+ worker.on('message', (data: unknown) => {
62
62
  if (!this.#disposed) {
63
- listener({data});
63
+ listener({data, ports: []});
64
64
  }
65
65
  });
66
66
  });
@@ -351,6 +351,10 @@ export class TargetBase {
351
351
  return this.getAgent('CacheStorage');
352
352
  }
353
353
 
354
+ crashReportContextAgent(): ProtocolProxyApi.CrashReportContextApi {
355
+ return this.getAgent('CrashReportContext');
356
+ }
357
+
354
358
  cssAgent(): ProtocolProxyApi.CSSApi {
355
359
  return this.getAgent('CSS');
356
360
  }
@@ -8,7 +8,6 @@ export enum ExperimentName {
8
8
  LIVE_HEAP_PROFILE = 'live-heap-profile',
9
9
  PROTOCOL_MONITOR = 'protocol-monitor',
10
10
  SAMPLING_HEAP_PROFILER_TIMELINE = 'sampling-heap-profiler-timeline',
11
- SHOW_OPTION_TO_EXPOSE_INTERNALS_IN_HEAP_SNAPSHOT = 'show-option-to-expose-internals-in-heap-snapshot',
12
11
  TIMELINE_INVALIDATION_TRACKING = 'timeline-invalidation-tracking',
13
12
  TIMELINE_SHOW_ALL_EVENTS = 'timeline-show-all-events',
14
13
  APCA = 'apca',
@@ -0,0 +1,28 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
6
+ import type * as Protocol from '../../generated/protocol.js';
7
+
8
+ import {SDKModel} from './SDKModel.js';
9
+ import {Capability, type Target} from './Target.js';
10
+
11
+ export class CrashReportContextModel extends SDKModel<void> {
12
+ readonly #agent: ProtocolProxyApi.CrashReportContextApi;
13
+
14
+ constructor(target: Target) {
15
+ super(target);
16
+ this.#agent = target.crashReportContextAgent();
17
+ }
18
+
19
+ async getEntries(): Promise<Protocol.CrashReportContext.CrashReportContextEntry[]|null> {
20
+ const response = await this.#agent.invoke_getEntries();
21
+ if (response.getError()) {
22
+ return null;
23
+ }
24
+ return response.entries;
25
+ }
26
+ }
27
+
28
+ SDKModel.register(CrashReportContextModel, {capabilities: Capability.JS, autostart: false});
@@ -22,6 +22,7 @@ import * as CookieModel from './CookieModel.js';
22
22
  import * as CookieParser from './CookieParser.js';
23
23
  import * as CPUProfilerModel from './CPUProfilerModel.js';
24
24
  import * as CPUThrottlingManager from './CPUThrottlingManager.js';
25
+ import * as CrashReportContextModel from './CrashReportContextModel.js';
25
26
  import * as CSSContainerQuery from './CSSContainerQuery.js';
26
27
  import * as CSSFontFace from './CSSFontFace.js';
27
28
  import * as CSSLayer from './CSSLayer.js';
@@ -90,7 +91,6 @@ import * as Target from './Target.js';
90
91
  import * as TargetManager from './TargetManager.js';
91
92
  import * as TraceObject from './TraceObject.js';
92
93
  import * as WebAuthnModel from './WebAuthnModel.js';
93
- import * as WebMCPModel from './WebMCPModel.js';
94
94
 
95
95
  export {
96
96
  AccessibilityModel,
@@ -106,6 +106,7 @@ export {
106
106
  CookieParser,
107
107
  CPUProfilerModel,
108
108
  CPUThrottlingManager,
109
+ CrashReportContextModel,
109
110
  CSSContainerQuery,
110
111
  CSSFontFace,
111
112
  CSSLayer,
@@ -174,5 +175,4 @@ export {
174
175
  TargetManager,
175
176
  TraceObject,
176
177
  WebAuthnModel,
177
- WebMCPModel,
178
178
  };
@@ -2,6 +2,7 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ import type * as PlatformApi from '../../core/platform/api/api.js';
5
6
  import * as HeapSnapshotModel from '../../models/heap_snapshot_model/heap_snapshot_model.js';
6
7
 
7
8
  // We mirror what heap_snapshot_worker.ts does, but we can't use it here as we'd have a
@@ -33,9 +34,8 @@ export class HeapSnapshotWorkerDispatcher {
33
34
  this.#postMessage({eventName: name, data});
34
35
  }
35
36
 
36
- async dispatchMessage({data, ports}:
37
- {data: HeapSnapshotModel.HeapSnapshotModel.WorkerCommand, ports: readonly MessagePort[]}):
38
- Promise<void> {
37
+ async dispatchMessage({data, ports}: PlatformApi.HostRuntime
38
+ .WorkerMessageEvent<HeapSnapshotModel.HeapSnapshotModel.WorkerCommand>): Promise<void> {
39
39
  const response: DispatcherResponse = {
40
40
  callId: data.callId,
41
41
  result: null,
@@ -1,10 +1,11 @@
1
1
  // Copyright 2020 The Chromium Authors
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
+ import * as Platform from '../../core/platform/platform.js';
4
5
 
5
6
  import * as HeapSnapshotWorker from './heap_snapshot_worker.js';
6
7
 
7
- const dispatcher =
8
- new HeapSnapshotWorker.HeapSnapshotWorkerDispatcher.HeapSnapshotWorkerDispatcher(self.postMessage.bind(self));
9
- self.addEventListener('message', dispatcher.dispatchMessage.bind(dispatcher), false);
10
- self.postMessage('workerReady');
8
+ const dispatcher = new HeapSnapshotWorker.HeapSnapshotWorkerDispatcher.HeapSnapshotWorkerDispatcher(
9
+ Platform.HostRuntime.HOST_RUNTIME.workerScope.postMessage.bind(Platform.HostRuntime.HOST_RUNTIME.workerScope));
10
+ Platform.HostRuntime.HOST_RUNTIME.workerScope.onmessage = dispatcher.dispatchMessage.bind(dispatcher);
11
+ Platform.HostRuntime.HOST_RUNTIME.workerScope.postMessage('workerReady');
@@ -121,10 +121,6 @@ const UIStrings = {
121
121
  * @description Text describing how to navigate the dock side menu
122
122
  */
123
123
  dockSideNavigation: 'Use left and right arrow keys to navigate the options',
124
- /**
125
- * @description Notification shown to the user whenever DevTools receives an external request.
126
- */
127
- externalRequestReceived: '`DevTools` received an external request',
128
124
  /**
129
125
  * @description Notification shown to the user whenever DevTools has finished downloading a local AI model.
130
126
  */
@@ -360,9 +356,6 @@ export class MainImpl {
360
356
  Root.ExperimentNames.ExperimentName.PROTOCOL_MONITOR, protocolMonitorExperiment);
361
357
  Root.Runtime.experiments.register(
362
358
  Root.ExperimentNames.ExperimentName.SAMPLING_HEAP_PROFILER_TIMELINE, 'Sampling heap profiler timeline');
363
- Root.Runtime.experiments.register(
364
- Root.ExperimentNames.ExperimentName.SHOW_OPTION_TO_EXPOSE_INTERNALS_IN_HEAP_SNAPSHOT,
365
- 'Show option to expose internals in heap snapshots');
366
359
 
367
360
  // Timeline
368
361
  Root.Runtime.experiments.register(
@@ -581,14 +574,6 @@ export class MainImpl {
581
574
  });
582
575
  }
583
576
 
584
- const conversationHandler = AiAssistanceModel.ConversationHandler.ConversationHandler.instance();
585
- conversationHandler.addEventListener(
586
- AiAssistanceModel.ConversationHandler.ConversationHandlerEvents.EXTERNAL_REQUEST_RECEIVED,
587
- () => Snackbar.Snackbar.Snackbar.show({message: i18nString(UIStrings.externalRequestReceived)}));
588
- conversationHandler.addEventListener(
589
- AiAssistanceModel.ConversationHandler.ConversationHandlerEvents.EXTERNAL_CONVERSATION_STARTED,
590
- event => void VisualLogging.logFunctionCall(`start-conversation-${event.data}`, 'external'));
591
-
592
577
  if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
593
578
  await PanelCommon.GeminiRebrandPromoDialog.maybeShow();
594
579
  }
@@ -1104,89 +1089,3 @@ export class ReloadActionDelegate implements UI.ActionRegistration.ActionDelegat
1104
1089
  return false;
1105
1090
  }
1106
1091
  }
1107
-
1108
- type ExternalRequestInput = {
1109
- kind: 'LIVE_STYLE_DEBUGGER',
1110
- args: {prompt: string, selector: string},
1111
- }|{
1112
- kind: 'PERFORMANCE_RELOAD_GATHER_INSIGHTS',
1113
- }|{
1114
- kind: 'PERFORMANCE_ANALYZE',
1115
- args: {prompt: string},
1116
- }|{
1117
- kind: 'NETWORK_DEBUGGER',
1118
- args: {requestUrl: string, prompt: string},
1119
- };
1120
-
1121
- /**
1122
- * For backwards-compatibility we iterate over the generator and drop the
1123
- * intermediate results. The final response is transformed to its legacy type.
1124
- * Instead of sending responses of type error, errors are throws.
1125
- **/
1126
- export async function handleExternalRequest(input: ExternalRequestInput):
1127
- Promise<{response: string, devToolsLogs: object[]}> {
1128
- const generator = await handleExternalRequestGenerator(input);
1129
- let result: IteratorResult<
1130
- AiAssistanceModel.AiAgent.ExternalRequestResponse, AiAssistanceModel.AiAgent.ExternalRequestResponse>;
1131
- do {
1132
- result = await generator.next();
1133
- } while (!result.done);
1134
- const response = result.value;
1135
- if (response.type === AiAssistanceModel.AiAgent.ExternalRequestResponseType.ERROR) {
1136
- throw new Error(response.message);
1137
- }
1138
- if (response.type === AiAssistanceModel.AiAgent.ExternalRequestResponseType.ANSWER) {
1139
- return {
1140
- response: response.message,
1141
- devToolsLogs: response.devToolsLogs,
1142
- };
1143
- }
1144
- throw new Error('Received no response of type answer or type error');
1145
- }
1146
-
1147
- // @ts-expect-error
1148
- globalThis.handleExternalRequest = handleExternalRequest;
1149
-
1150
- export async function handleExternalRequestGenerator(input: ExternalRequestInput): Promise<AsyncGenerator<
1151
- AiAssistanceModel.AiAgent.ExternalRequestResponse, AiAssistanceModel.AiAgent.ExternalRequestResponse>> {
1152
- switch (input.kind) {
1153
- case 'PERFORMANCE_RELOAD_GATHER_INSIGHTS': {
1154
- const TimelinePanel = await import('../../panels/timeline/timeline.js');
1155
- return TimelinePanel.TimelinePanel.TimelinePanel.handleExternalRecordRequest();
1156
- }
1157
- case 'PERFORMANCE_ANALYZE': {
1158
- const TimelinePanel = await import('../../panels/timeline/timeline.js');
1159
- return await TimelinePanel.TimelinePanel.TimelinePanel.handleExternalAnalyzeRequest(input.args.prompt);
1160
- }
1161
- case 'NETWORK_DEBUGGER': {
1162
- const AiAssistanceModel = await import('../../models/ai_assistance/ai_assistance.js');
1163
- const conversationHandler = AiAssistanceModel.ConversationHandler.ConversationHandler.instance();
1164
- return await conversationHandler.handleExternalRequest({
1165
- conversationType: AiAssistanceModel.AiHistoryStorage.ConversationType.NETWORK,
1166
- prompt: input.args.prompt,
1167
- requestUrl: input.args.requestUrl,
1168
- });
1169
- }
1170
- case 'LIVE_STYLE_DEBUGGER': {
1171
- const AiAssistanceModel = await import('../../models/ai_assistance/ai_assistance.js');
1172
- const conversationHandler = AiAssistanceModel.ConversationHandler.ConversationHandler.instance();
1173
- return await conversationHandler.handleExternalRequest({
1174
- conversationType: AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING,
1175
- prompt: input.args.prompt,
1176
- selector: input.args.selector,
1177
- });
1178
- }
1179
- }
1180
- // eslint-disable-next-line require-yield
1181
- return (async function*(): AsyncGenerator<
1182
- AiAssistanceModel.AiAgent.ExternalRequestResponse, AiAssistanceModel.AiAgent.ExternalRequestResponse> {
1183
- return {
1184
- type: AiAssistanceModel.AiAgent.ExternalRequestResponseType.ERROR,
1185
- // @ts-expect-error
1186
- message: `Debugging with an agent of type '${input.kind}' is not implemented yet.`,
1187
- };
1188
- })();
1189
- }
1190
-
1191
- // @ts-expect-error
1192
- globalThis.handleExternalRequestGenerator = handleExternalRequestGenerator;
@@ -117,11 +117,11 @@ export class ChangeManager {
117
117
  return content;
118
118
  }
119
119
 
120
- formatChangesForPatching(groupId: string, includeSourceLocation = false): string {
120
+ formatChangesForPatching(groupId: string, includeMetadata = false): string {
121
121
  return Array.from(this.#stylesheetChanges.values())
122
122
  .flatMap(
123
123
  changesPerStylesheet => changesPerStylesheet.filter(change => change.groupId === groupId)
124
- .map(change => this.#formatChange(change, includeSourceLocation)))
124
+ .map(change => this.#formatChange(change, includeMetadata)))
125
125
  .filter(change => change !== '')
126
126
  .join('\n\n');
127
127
  }
@@ -150,13 +150,13 @@ ${formatStyles(change.styles, 4)}
150
150
  .join('\n');
151
151
  }
152
152
 
153
- #formatChange(change: Change, includeSourceLocation = false): string {
153
+ #formatChange(change: Change, includeMetadata = false): string {
154
154
  const sourceLocation =
155
- includeSourceLocation && change.sourceLocation ? `/* related resource: ${change.sourceLocation} */\n` : '';
156
- // TODO: includeSourceLocation indicates whether we are using Patch
155
+ includeMetadata && change.sourceLocation ? `/* related resource: ${change.sourceLocation} */\n` : '';
156
+ // TODO: includeMetadata indicates whether we are using Patch
157
157
  // agent. If needed we can have an separate knob.
158
158
  const simpleSelector =
159
- includeSourceLocation && change.simpleSelector ? ` /* the element was ${change.simpleSelector} */` : '';
159
+ includeMetadata && change.simpleSelector ? ` /* the element was ${change.simpleSelector} */` : '';
160
160
  return `${sourceLocation}${change.selector} {${simpleSelector}
161
161
  ${formatStyles(change.styles)}
162
162
  }`;
@@ -7,18 +7,27 @@ import * as i18n from '../../../core/i18n/i18n.js';
7
7
  import * as Root from '../../../core/root/root.js';
8
8
  import * as SDK from '../../../core/sdk/sdk.js';
9
9
  import type * as LHModel from '../../lighthouse/lighthouse.js';
10
+ import {ChangeManager} from '../ChangeManager.js';
10
11
  import {LighthouseFormatter} from '../data_formatters/LighthouseFormatter.js';
11
12
  import {debugLog} from '../debug.js';
13
+ import {ExtensionScope} from '../ExtensionScope.js';
12
14
 
13
15
  import {
14
- type AgentOptions,
15
16
  AiAgent,
17
+ type AiWidget,
16
18
  type ContextDetail,
17
19
  type ContextResponse,
18
20
  ConversationContext,
19
21
  type RequestOptions,
20
22
  ResponseType,
21
23
  } from './AiAgent.js';
24
+ import {
25
+ type CreateExtensionScopeFunction,
26
+ executeJavaScriptFunction,
27
+ type ExecuteJsAgentOptions,
28
+ executeJsCode,
29
+ JavascriptExecutor
30
+ } from './ExecuteJavascript.js';
22
31
 
23
32
  /**
24
33
  * WARNING: preamble defined in code is only used when userTier is
@@ -29,7 +38,7 @@ const preamble = `You are an accessibility expert agent integrated into Chrome D
29
38
  Your role is to help users understand and fix accessibility issues found in Lighthouse reports.
30
39
 
31
40
  # Style Guidelines
32
- * **Concise and Direct**: Use short sentences and bullet points. Avoid paragraphs and long explanations.
41
+ * **General style**: Use the precision of Strunk & White, the brevity of Hemingway, and the simple clarity of Vonnegut. Don't add repeated information, and keep the whole answer short.
33
42
  * **Structured**: Organize your findings by problem, root cause, and next steps, but do NOT use those literal words as headings.
34
43
  * **No Internal Identifiers**: NEVER show Lighthouse paths (e.g., "1,HTML,1,BODY...") to the user. Refer to elements by their tag name, classes, or IDs.
35
44
  * **Managing Volume**: If the report contains many issues, provide a brief summary of the top 2-3 most critical ones. Tell the user that there are more issues and invite them to ask for more details or to explore a specific area.
@@ -45,10 +54,28 @@ Your role is to help users understand and fix accessibility issues found in Ligh
45
54
  * \`runAccessibilityAudits\`: Trigger new accessibility snapshot audits.
46
55
  * \`getStyles\`: Get computed styles for an element by its path.
47
56
  * \`getElementAccessibilityDetails\`: Get A11y properties for an element by its path.
57
+ * \`executeJavaScript\`: Run JavaScript code on the inspected page to gather additional information or investigate the page state.
58
+
59
+ # Linkification
60
+ * **Linkify elements**: When you know the Lighthouse path of an element (found in the report audits), linkify it using \`([Label](#path-PATH))\` syntax. Never show the path to the user directly, only use it in the link href.
48
61
 
49
62
  # Constraints
50
63
  * **CRITICAL**: ALWAYS call a tool before providing an answer if an element path is available.
51
64
  * **CRITICAL**: You are an accessibility agent. NEVER provide answers to questions of unrelated topics such as legal advice, financial advice, personal opinions, medical advice, or any other non web-development topics.
65
+
66
+ ## Response Structure
67
+
68
+ If the user asks a question that requires an investigation of a problem, use this structure:
69
+ - If available, point out the root cause(s) of the problem.
70
+ - Example: "**Root Cause**: The page is slow because of [reason]."
71
+ - Example: "**Root Causes**:"
72
+ - [Reason 1]
73
+ - [Reason 2]
74
+ - if applicable, list actionable solution suggestion(s) in order of impact:
75
+ - Example: "**Suggestion**: [Suggestion 1]
76
+ - Example: "**Suggestions**:"
77
+ - [Suggestion 1]
78
+ - [Suggestion 2]
52
79
  `;
53
80
 
54
81
  export class AccessibilityContext extends ConversationContext<LHModel.ReporterTypes.ReportJSON> {
@@ -86,15 +113,40 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
86
113
  readonly #lighthouseRecording?:
87
114
  (overrides?: LHModel.RunTypes.RunOverrides) => Promise<LHModel.ReporterTypes.ReportJSON|null>;
88
115
 
89
- constructor(opts: AgentOptions) {
116
+ #execJs: typeof executeJsCode;
117
+ #javascriptExecutor: JavascriptExecutor;
118
+ #changes: ChangeManager;
119
+ #createExtensionScope: CreateExtensionScopeFunction;
120
+ #currentTurnId = 0;
121
+
122
+ constructor(opts: ExecuteJsAgentOptions) {
90
123
  super(opts);
91
124
  this.#lighthouseRecording = opts.lighthouseRecording;
125
+ this.#changes = opts.changeManager || new ChangeManager();
126
+ this.#execJs = opts.execJs ?? executeJsCode;
127
+ this.#createExtensionScope =
128
+ opts.createExtensionScope ?? ((changes: ChangeManager) => {
129
+ return new ExtensionScope(changes, this.sessionId, this.#getDocumentBodyNode(), this.#currentTurnId);
130
+ });
131
+ this.#javascriptExecutor = new JavascriptExecutor(
132
+ {
133
+ executionMode: this.executionMode,
134
+ getContextNode: () => this.#getDocumentBodyNode(),
135
+ createExtensionScope: this.#createExtensionScope.bind(this),
136
+ changes: this.#changes,
137
+ },
138
+ this.#execJs);
92
139
  }
93
140
 
94
141
  get userTier(): string|undefined {
95
142
  return Root.Runtime.hostConfig.devToolsFreestyler?.userTier;
96
143
  }
97
144
 
145
+ get executionMode(): Root.Runtime.HostConfigFreestylerExecutionMode {
146
+ return Root.Runtime.hostConfig.devToolsFreestyler?.executionMode ??
147
+ Root.Runtime.HostConfigFreestylerExecutionMode.ALL_SCRIPTS;
148
+ }
149
+
98
150
  get options(): RequestOptions {
99
151
  // TODO(b/491772868): tidy up userTier & feature flags in the backend.
100
152
  const temperature = Root.Runtime.hostConfig.devToolsAiAssistanceFileAgent?.temperature;
@@ -106,6 +158,38 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
106
158
  };
107
159
  }
108
160
 
161
+ override preambleFeatures(): string[] {
162
+ return ['function_calling'];
163
+ }
164
+
165
+ protected override async preRun(): Promise<void> {
166
+ this.#currentTurnId++;
167
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
168
+ const domModel = target?.model(SDK.DOMModel.DOMModel);
169
+ // We need to ensure the document is requested so that #getDocumentBodyNode()
170
+ // can return a valid node for the JavaScript execution context.
171
+ if (domModel && !domModel.existingDocument()) {
172
+ try {
173
+ await domModel.requestDocument();
174
+ } catch (e) {
175
+ debugLog('Failed to request document', e);
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * For the Accessibility Agent, there is no single "selected" node.
182
+ * We use the document body as the default context node for JavaScript execution
183
+ * so that the AI has a valid $0 to start with.
184
+ */
185
+ #getDocumentBodyNode(): SDK.DOMModel.DOMNode|null {
186
+ const document = SDK.TargetManager.TargetManager.instance()
187
+ .primaryPageTarget()
188
+ ?.model(SDK.DOMModel.DOMModel)
189
+ ?.existingDocument();
190
+ return document?.body ?? document ?? null;
191
+ }
192
+
109
193
  async *
110
194
  handleContextDetails(lhr: ConversationContext<LHModel.ReporterTypes.ReportJSON>|null):
111
195
  AsyncGenerator<ContextResponse, void, void> {
@@ -136,6 +220,8 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
136
220
  }
137
221
 
138
222
  #declareFunctions(): void {
223
+ this.declareFunction('executeJavaScript', executeJavaScriptFunction(this.#javascriptExecutor));
224
+
139
225
  this.declareFunction<{explanation: string}, {audits: string}>('runAccessibilityAudits', {
140
226
  description:
141
227
  '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.',
@@ -262,11 +348,31 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
262
348
  if (!styles) {
263
349
  return {error: 'Could not get computed styles.'};
264
350
  }
265
- const result: Record<string, string|undefined> = {};
351
+ const result: Record<string, string|number|undefined> = {};
266
352
  for (const prop of params.styleProperties) {
267
353
  result[prop] = styles.get(prop);
268
354
  }
269
- return {result: JSON.stringify(result, null, 2)};
355
+
356
+ result['backendNodeId'] = node.backendNodeId();
357
+
358
+ const widgets: AiWidget[] = [];
359
+ const matchedStyles = await node.domModel().cssModel().getMatchedStyles(node.id);
360
+ if (matchedStyles) {
361
+ widgets.push({
362
+ name: 'COMPUTED_STYLES',
363
+ data: {
364
+ computedStyles: styles,
365
+ backendNodeId: node.backendNodeId(),
366
+ matchedCascade: matchedStyles,
367
+ properties: params.styleProperties,
368
+ }
369
+ });
370
+ }
371
+
372
+ return {
373
+ result: JSON.stringify(result, null, 2),
374
+ widgets: widgets.length > 0 ? widgets : undefined,
375
+ };
270
376
  },
271
377
  });
272
378
 
@@ -337,6 +443,7 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
337
443
  {} as Record<string, string>),
338
444
  isIgnored: axNode.ignored(),
339
445
  ignoredReasons: axNode.ignoredReasons(),
446
+ backendNodeId: node.backendNodeId(),
340
447
  };
341
448
 
342
449
  return {result: JSON.stringify(result, null, 2)};
@@ -172,30 +172,6 @@ export interface ConversationSuggestion {
172
172
  /** At least one. */
173
173
  export type ConversationSuggestions = [ConversationSuggestion, ...ConversationSuggestion[]];
174
174
 
175
- export const enum ExternalRequestResponseType {
176
- ANSWER = 'answer',
177
- NOTIFICATION = 'notification',
178
- ERROR = 'error',
179
- }
180
-
181
- export interface ExternalRequestAnswer {
182
- type: ExternalRequestResponseType.ANSWER;
183
- message: string;
184
- devToolsLogs: object[];
185
- }
186
-
187
- export interface ExternalRequestNotification {
188
- type: ExternalRequestResponseType.NOTIFICATION;
189
- message: string;
190
- }
191
-
192
- export interface ExternalRequestError {
193
- type: ExternalRequestResponseType.ERROR;
194
- message: string;
195
- }
196
-
197
- export type ExternalRequestResponse = ExternalRequestAnswer|ExternalRequestNotification|ExternalRequestError;
198
-
199
175
  export abstract class ConversationContext<T> {
200
176
  abstract getOrigin(): string;
201
177
  abstract getItem(): T;
@@ -290,9 +266,17 @@ export interface TimelineRangeSummaryAiWidget {
290
266
  };
291
267
  }
292
268
 
269
+ export interface BottomUpTreeAiWidget {
270
+ name: 'BOTTOM_UP_TREE';
271
+ data: {
272
+ bounds: Trace.Types.Timing.TraceWindowMicro,
273
+ parsedTrace: Trace.TraceModel.ParsedTrace,
274
+ };
275
+ }
276
+
293
277
  // This type will grow as we add more widgets.
294
278
  export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget|
295
- PerformanceTraceAiWidget|LcpBreakdownAiWidget|TimelineRangeSummaryAiWidget;
279
+ PerformanceTraceAiWidget|LcpBreakdownAiWidget|TimelineRangeSummaryAiWidget|BottomUpTreeAiWidget;
296
280
 
297
281
  export type FunctionCallHandlerResult<Result> = {
298
282
  requiresApproval: true,
@@ -91,7 +91,7 @@ Content:
91
91
  },
92
92
  {
93
93
  "name": "performanceRecordAndReload",
94
- "description": "Records a new performance trace, to help debug performance issue.",
94
+ "description": "Records a new performance trace. Use this to measure and debug performance metrics and Core Web Vitals like Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS).",
95
95
  "parameters": {
96
96
  "type": 6,
97
97
  "description": "",
@@ -102,7 +102,7 @@ Content:
102
102
  },
103
103
  {
104
104
  "name": "runLighthouseAudits",
105
- "description": "Records a Lighthouse audit on the current page, to help debug accessibility issues.",
105
+ "description": "Records a Lighthouse audit on the current page. Use this to debug accessibility, SEO, and best practices. (For performance metrics like LCP, use performanceRecordAndReload instead).",
106
106
  "parameters": {
107
107
  "type": 6,
108
108
  "description": "",