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
@@ -4,7 +4,6 @@
4
4
 
5
5
  import * as Host from '../../../core/host/host.js';
6
6
  import * as i18n from '../../../core/i18n/i18n.js';
7
- import * as Platform from '../../../core/platform/platform.js';
8
7
  import * as Root from '../../../core/root/root.js';
9
8
  import * as SDK from '../../../core/sdk/sdk.js';
10
9
  import * as Protocol from '../../../generated/protocol.js';
@@ -13,24 +12,28 @@ import * as Annotations from '../../annotations/annotations.js';
13
12
  import * as Emulation from '../../emulation/emulation.js';
14
13
  import {ChangeManager} from '../ChangeManager.js';
15
14
  import {debugLog} from '../debug.js';
16
- import {EvaluateAction, formatError, SideEffectError} from '../EvaluateAction.js';
17
15
  import {ExtensionScope} from '../ExtensionScope.js';
18
- import {AI_ASSISTANCE_CSS_CLASS_NAME, FREESTYLER_WORLD_NAME} from '../injected.js';
16
+ import {AI_ASSISTANCE_CSS_CLASS_NAME} from '../injected.js';
19
17
 
20
18
  import {
21
- type AgentOptions as BaseAgentOptions,
22
19
  AiAgent,
23
20
  type ComputedStyleAiWidget,
24
21
  type ContextResponse,
25
22
  ConversationContext,
26
23
  type ConversationSuggestions,
27
24
  type FunctionCallHandlerResult,
28
- type FunctionHandlerOptions,
29
25
  type MultimodalInput,
30
26
  MultimodalInputType,
31
27
  type RequestOptions,
32
28
  ResponseType
33
29
  } from './AiAgent.js';
30
+ import {
31
+ type CreateExtensionScopeFunction,
32
+ executeJavaScriptFunction,
33
+ type ExecuteJsAgentOptions,
34
+ executeJsCode,
35
+ JavascriptExecutor
36
+ } from './ExecuteJavascript.js';
34
37
 
35
38
  /*
36
39
  * Strings that don't need to be translated at this time.
@@ -69,10 +72,25 @@ First, examine the provided context, then use the functions to gather additional
69
72
  * Use functions available to you to investigate and fulfill the user request.
70
73
  * After applying a fix, please ask the user to confirm if the fix worked or not.
71
74
  * ALWAYS OUTPUT a list of follow-up queries at the end of your text response. The format is SUGGESTIONS: ["suggestion1", "suggestion2", "suggestion3"]. Make sure that the array and the \`SUGGESTIONS: \` text is in the same line. You're also capable of executing the fix for the issue user mentioned. Reflect this in your suggestions.
75
+ * 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.
72
76
  * **CRITICAL** NEVER write full Python programs - you should only write individual statements that invoke a single function from the provided library.
73
77
  * **CRITICAL** NEVER output text before a function call. Always do a function call first.
74
78
  * **CRITICAL** When answering questions about positioning or layout, ALWAYS inspect \`position\`, \`display\` and ALL related properties.
75
- * **CRITICAL** You are a CSS/DOM/HTML debugging assistant. NEVER provide answers to questions of unrelated topics such as legal advice, financial advice, personal opinions, medical advice, religion, race, politics, sexuality, gender, or any other non web-development topics. Answer "Sorry, I can't answer that. I'm best at questions about debugging web pages." to such questions.`;
79
+ * **CRITICAL** You are a CSS/DOM/HTML debugging assistant. NEVER provide answers to questions of unrelated topics such as legal advice, financial advice, personal opinions, medical advice, religion, race, politics, sexuality, gender, or any other non web-development topics. Answer "Sorry, I can't answer that. I'm best at questions about debugging web pages." to such questions.
80
+
81
+ ## Response Structure
82
+
83
+ If the user asks a question that requires an investigation of a problem, use this structure:
84
+ - If available, point out the root cause(s) of the problem.
85
+ - Example: "**Root Cause**: The page is slow because of [reason]."
86
+ - Example: "**Root Causes**:"
87
+ - [Reason 1]
88
+ - [Reason 2]
89
+ - if applicable, list actionable solution suggestion(s) in order of impact:
90
+ - Example: "**Suggestion**: [Suggestion 1]
91
+ - Example: "**Suggestions**:"
92
+ - [Suggestion 1]
93
+ - [Suggestion 2]`;
76
94
 
77
95
  const greenDevEmulationEnabled = Greendev.Prototypes.instance().isEnabled('emulationCapabilities');
78
96
  if (greenDevEmulationEnabled) {
@@ -145,64 +163,8 @@ const MULTIMODAL_ENHANCEMENT_PROMPTS: Record<MultimodalInputType, string> = {
145
163
  [MultimodalInputType.UPLOADED_IMAGE]: promptForUploadedImage + considerationsForMultimodalInputEvaluation,
146
164
  };
147
165
 
148
- async function executeJsCode(
149
- functionDeclaration: string,
150
- {throwOnSideEffect, contextNode}: {throwOnSideEffect: boolean, contextNode: SDK.DOMModel.DOMNode|null}):
151
- Promise<string> {
152
- if (!contextNode) {
153
- throw new Error('Cannot execute JavaScript because of missing context node');
154
- }
155
- const target = contextNode.domModel().target();
156
-
157
- if (!target) {
158
- throw new Error('Target is not found for executing code');
159
- }
160
-
161
- const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
162
- const frameId = contextNode.frameId() ?? resourceTreeModel?.mainFrame?.id;
163
-
164
- if (!frameId) {
165
- throw new Error('Main frame is not found for executing code');
166
- }
167
-
168
- const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel);
169
- const pageAgent = target.pageAgent();
170
-
171
- // This returns previously created world if it exists for the frame.
172
- const {executionContextId} = await pageAgent.invoke_createIsolatedWorld({frameId, worldName: FREESTYLER_WORLD_NAME});
173
- const executionContext = runtimeModel?.executionContext(executionContextId);
174
- if (!executionContext) {
175
- throw new Error('Execution context is not found for executing code');
176
- }
177
-
178
- if (executionContext.debuggerModel.selectedCallFrame()) {
179
- return formatError('Cannot evaluate JavaScript because the execution is paused on a breakpoint.');
180
- }
181
-
182
- const remoteObject = await contextNode.resolveToObject(undefined, executionContextId);
183
- if (!remoteObject) {
184
- throw new Error('Cannot execute JavaScript because remote object cannot be resolved');
185
- }
186
-
187
- return await EvaluateAction.execute(functionDeclaration, [remoteObject], executionContext, {throwOnSideEffect});
188
- }
189
-
190
- const MAX_OBSERVATION_BYTE_LENGTH = 25_000;
191
- const OBSERVATION_TIMEOUT = 5_000;
192
-
193
166
  export const AI_ASSISTANCE_FILTER_REGEX = `\\.${AI_ASSISTANCE_CSS_CLASS_NAME}-.*&`;
194
167
 
195
- type CreateExtensionScopeFunction = (changes: ChangeManager) => {
196
- install(): Promise<void>, uninstall(): Promise<void>,
197
- };
198
-
199
- interface AgentOptions extends BaseAgentOptions {
200
- changeManager?: ChangeManager;
201
-
202
- createExtensionScope?: CreateExtensionScopeFunction;
203
- execJs?: typeof executeJsCode;
204
- }
205
-
206
168
  export class NodeContext extends ConversationContext<SDK.DOMModel.DOMNode> {
207
169
  #node: SDK.DOMModel.DOMNode;
208
170
 
@@ -310,6 +272,7 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
310
272
  }
311
273
 
312
274
  #execJs: typeof executeJsCode;
275
+ #javascriptExecutor: JavascriptExecutor;
313
276
 
314
277
  #changes: ChangeManager;
315
278
  #createExtensionScope: CreateExtensionScopeFunction;
@@ -317,7 +280,7 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
317
280
  #greenDevEmulationAxTree: string|null = null;
318
281
  #currentTurnId = 0;
319
282
 
320
- constructor(opts: AgentOptions) {
283
+ constructor(opts: ExecuteJsAgentOptions) {
321
284
  super(opts);
322
285
 
323
286
  this.#changes = opts.changeManager || new ChangeManager();
@@ -326,6 +289,14 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
326
289
  opts.createExtensionScope ?? ((changes: ChangeManager) => {
327
290
  return new ExtensionScope(changes, this.sessionId, this.context?.getItem() ?? null, this.#currentTurnId);
328
291
  });
292
+ this.#javascriptExecutor = new JavascriptExecutor(
293
+ {
294
+ executionMode: this.executionMode,
295
+ getContextNode: () => this.#getSelectedNode(),
296
+ createExtensionScope: this.#createExtensionScope.bind(this),
297
+ changes: this.#changes,
298
+ },
299
+ this.#execJs);
329
300
 
330
301
  this.declareFunction<{
331
302
  elements: number[],
@@ -378,91 +349,7 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
378
349
  },
379
350
  });
380
351
 
381
- this.declareFunction<{
382
- title: string,
383
- explanation: string,
384
- code: string,
385
- }>('executeJavaScript', {
386
- description:
387
- `This function allows you to run JavaScript code on the inspected page to access the element styles and page content.
388
- Call this function to gather additional information or modify the page state. Call this function enough times to investigate the user request.`,
389
- parameters: {
390
- type: Host.AidaClient.ParametersTypes.OBJECT,
391
- description: '',
392
- nullable: false,
393
- properties: {
394
- code: {
395
- type: Host.AidaClient.ParametersTypes.STRING,
396
- description:
397
- `JavaScript code snippet to run on the inspected page. Make sure the code is formatted for readability.
398
-
399
- # Instructions
400
-
401
- * To return data, define a top-level \`data\` variable and populate it with data you want to get. Only JSON-serializable objects can be assigned to \`data\`.
402
- * If you modify styles on an element, ALWAYS call the pre-defined global \`async setElementStyles(el: Element, styles: object)\` function. This function is an internal mechanism for you and should never be presented as a command/advice to the user.
403
- * **CRITICAL** Only get styles that might be relevant to the user request.
404
- * **CRITICAL** Never assume a selector for the elements unless you verified your knowledge.
405
- * **CRITICAL** Consider that \`data\` variable from the previous function calls are not available in a new function call.
406
-
407
- For example, the code to change element styles:
408
-
409
- \`\`\`
410
- await setElementStyles($0, {
411
- color: 'blue',
412
- });
413
- \`\`\`
414
-
415
- For example, the code to get overlapping elements:
416
-
417
- \`\`\`
418
- const data = {
419
- overlappingElements: Array.from(document.querySelectorAll('*'))
420
- .filter(el => {
421
- const rect = el.getBoundingClientRect();
422
- const popupRect = $0.getBoundingClientRect();
423
- return (
424
- el !== $0 &&
425
- rect.left < popupRect.right &&
426
- rect.right > popupRect.left &&
427
- rect.top < popupRect.bottom &&
428
- rect.bottom > popupRect.top
429
- );
430
- })
431
- .map(el => ({
432
- tagName: el.tagName,
433
- id: el.id,
434
- className: el.className,
435
- zIndex: window.getComputedStyle(el)['z-index']
436
- }))
437
- };
438
- \`\`\`
439
- `,
440
- },
441
- explanation: {
442
- type: Host.AidaClient.ParametersTypes.STRING,
443
- description: 'Explain why you want to run this code',
444
- },
445
- title: {
446
- type: Host.AidaClient.ParametersTypes.STRING,
447
- description: 'Provide a summary of what the code does. For example, "Checking related element styles".',
448
- },
449
- },
450
- required: ['code', 'explanation', 'title']
451
- },
452
- displayInfoFromArgs: params => {
453
- return {
454
- title: params.title,
455
- thought: params.explanation,
456
- action: params.code,
457
- };
458
- },
459
- handler: async (
460
- params,
461
- options,
462
- ) => {
463
- return await this.executeAction(params.code, options);
464
- },
465
- });
352
+ this.declareFunction('executeJavaScript', executeJavaScriptFunction(this.#javascriptExecutor));
466
353
 
467
354
  if (Annotations.AnnotationRepository.annotationsEnabled()) {
468
355
  this.declareFunction<{
@@ -528,68 +415,6 @@ const data = {
528
415
  });
529
416
  }
530
417
 
531
- async generateObservation(
532
- action: string,
533
- {
534
- throwOnSideEffect,
535
- }: {
536
- throwOnSideEffect: boolean,
537
- },
538
- ): Promise<{
539
- observation: string,
540
- sideEffect: boolean,
541
- canceled: boolean,
542
- }> {
543
- const functionDeclaration = `async function ($0) {
544
- try {
545
- ${action}
546
- ;
547
- return ((typeof data !== "undefined") ? data : undefined);
548
- } catch (error) {
549
- return error;
550
- }
551
- }`;
552
- try {
553
- const result = await Promise.race([
554
- this.#execJs(
555
- functionDeclaration,
556
- {
557
- throwOnSideEffect,
558
- contextNode: this.context?.getItem() || null,
559
- },
560
- ),
561
- new Promise<never>((_, reject) => {
562
- setTimeout(
563
- () => reject(new Error('Script execution exceeded the maximum allowed time.')), OBSERVATION_TIMEOUT);
564
- }),
565
- ]);
566
- const byteCount = Platform.StringUtilities.countWtf8Bytes(result);
567
- Host.userMetrics.freestylerEvalResponseSize(byteCount);
568
- if (byteCount > MAX_OBSERVATION_BYTE_LENGTH) {
569
- throw new Error('Output exceeded the maximum allowed length.');
570
- }
571
- return {
572
- observation: result,
573
- sideEffect: false,
574
- canceled: false,
575
- };
576
- } catch (error) {
577
- if (error instanceof SideEffectError) {
578
- return {
579
- observation: error.message,
580
- sideEffect: true,
581
- canceled: false,
582
- };
583
- }
584
-
585
- return {
586
- observation: `Error: ${error.message}`,
587
- sideEffect: false,
588
- canceled: false,
589
- };
590
- }
591
- }
592
-
593
418
  static async describeElement(element: SDK.DOMModel.DOMNode): Promise<string> {
594
419
  let output = `* Element's uid is ${element.backendNodeId()}.
595
420
  * Its selector is \`${element.simpleSelector()}\``;
@@ -742,74 +567,6 @@ const data = {
742
567
  };
743
568
  }
744
569
 
745
- async executeAction(action: string, options?: FunctionHandlerOptions): Promise<FunctionCallHandlerResult<unknown>> {
746
- debugLog(`Action to execute: ${action}`);
747
-
748
- if (options?.approved === false) {
749
- return {
750
- error: 'Error: User denied code execution with side effects.',
751
- };
752
- }
753
-
754
- if (this.executionMode === Root.Runtime.HostConfigFreestylerExecutionMode.NO_SCRIPTS) {
755
- return {
756
- error: 'Error: JavaScript execution is currently disabled.',
757
- };
758
- }
759
-
760
- const selectedNode = this.#getSelectedNode();
761
- if (!selectedNode) {
762
- return {error: 'Error: no selected node found.'};
763
- }
764
- const target = selectedNode.domModel().target();
765
- if (target.model(SDK.DebuggerModel.DebuggerModel)?.selectedCallFrame()) {
766
- return {
767
- error: 'Error: Cannot evaluate JavaScript because the execution is paused on a breakpoint.',
768
- };
769
- }
770
-
771
- const scope = this.#createExtensionScope(this.#changes);
772
- await scope.install();
773
- try {
774
- let throwOnSideEffect = true;
775
- if (options?.approved) {
776
- throwOnSideEffect = false;
777
- }
778
-
779
- const result = await this.generateObservation(action, {throwOnSideEffect});
780
- debugLog(`Action result: ${JSON.stringify(result)}`);
781
- if (result.sideEffect) {
782
- if (this.executionMode === Root.Runtime.HostConfigFreestylerExecutionMode.SIDE_EFFECT_FREE_SCRIPTS_ONLY) {
783
- return {
784
- error: 'Error: JavaScript execution that modifies the page is currently disabled.',
785
- };
786
- }
787
-
788
- if (options?.signal?.aborted) {
789
- return {
790
- error: 'Error: evaluation has been cancelled',
791
- };
792
- }
793
-
794
- return {
795
- requiresApproval: true,
796
- description: lockedString('This code may modify page content. Continue?'),
797
- };
798
- }
799
- if (result.canceled) {
800
- return {
801
- error: result.observation,
802
- };
803
- }
804
-
805
- return {
806
- result: result.observation,
807
- };
808
- } finally {
809
- await scope.uninstall();
810
- }
811
- }
812
-
813
570
  async addElementAnnotation(elementId: string, annotationMessage: string):
814
571
  Promise<FunctionCallHandlerResult<unknown>> {
815
572
  if (!Annotations.AnnotationRepository.annotationsEnabled()) {
@@ -19,7 +19,6 @@ import * as AiHistoryStorage from './AiHistoryStorage.js';
19
19
  import * as AiUtils from './AiUtils.js';
20
20
  import * as BuiltInAi from './BuiltInAi.js';
21
21
  import * as ChangeManager from './ChangeManager.js';
22
- import * as ConversationHandler from './ConversationHandler.js';
23
22
  import * as FileFormatter from './data_formatters/FileFormatter.js';
24
23
  import * as LighthouseFormatter from './data_formatters/LighthouseFormatter.js';
25
24
  import * as NetworkRequestFormatter from './data_formatters/NetworkRequestFormatter.js';
@@ -48,7 +47,6 @@ export {
48
47
  BuiltInAi,
49
48
  ChangeManager,
50
49
  ContextSelectionAgent,
51
- ConversationHandler,
52
50
  ConversationSummaryAgent,
53
51
  Debug,
54
52
  EvaluateAction,
@@ -7402,11 +7402,7 @@ export const NativeFunctions = [
7402
7402
  },
7403
7403
  {
7404
7404
  name: "registerTool",
7405
- signatures: [["tool"]]
7406
- },
7407
- {
7408
- name: "unregisterTool",
7409
- signatures: [["name"]]
7405
+ signatures: [["tool","?options"]]
7410
7406
  },
7411
7407
  {
7412
7408
  name: "SnapEvent",
@@ -0,0 +1,187 @@
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 Common from '../../core/common/common.js';
6
+ import * as SDK from '../../core/sdk/sdk.js';
7
+ import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
8
+ import type * as Protocol from '../../generated/protocol.js';
9
+ import * as Bindings from '../bindings/bindings.js';
10
+ import type * as StackTrace from '../stack_trace/stack_trace.js';
11
+
12
+ export const enum Events {
13
+ TOOLS_ADDED = 'ToolsAdded',
14
+ TOOLS_REMOVED = 'ToolsRemoved',
15
+ TOOL_INVOKED = 'ToolInvoked',
16
+ TOOL_RESPONDED = 'ToolResponded',
17
+ }
18
+
19
+ export interface Call {
20
+ invocationId: string;
21
+ tool: Tool;
22
+ input: string;
23
+ result?: {
24
+ status: Protocol.WebMCP.InvocationStatus,
25
+ output?: unknown,
26
+ errorText?: string,
27
+ exception?: Protocol.Runtime.RemoteObject,
28
+ };
29
+ }
30
+
31
+ export class Tool {
32
+ #protocolTool: Readonly<Protocol.WebMCP.Tool>;
33
+ #stackTrace?: Promise<StackTrace.StackTrace.StackTrace>;
34
+ #target: WeakRef<SDK.Target.Target>;
35
+
36
+ constructor(tool: Protocol.WebMCP.Tool, target: SDK.Target.Target) {
37
+ this.#target = new WeakRef(target);
38
+ this.#protocolTool = tool;
39
+ this.#stackTrace = tool.stackTrace &&
40
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().createStackTraceFromProtocolRuntime(
41
+ tool.stackTrace, target);
42
+ }
43
+
44
+ get stackTrace(): Promise<StackTrace.StackTrace.StackTrace>|undefined {
45
+ return this.#stackTrace;
46
+ }
47
+
48
+ get name(): string {
49
+ return this.#protocolTool.name;
50
+ }
51
+
52
+ get description(): string {
53
+ return this.#protocolTool.description;
54
+ }
55
+
56
+ get frame(): SDK.ResourceTreeModel.ResourceTreeFrame|undefined {
57
+ return this.#target.deref()
58
+ ?.model(SDK.ResourceTreeModel.ResourceTreeModel)
59
+ ?.frameForId(this.#protocolTool.frameId) ??
60
+ undefined;
61
+ }
62
+
63
+ get isDeclarative(): boolean {
64
+ return Boolean(this.#protocolTool.backendNodeId);
65
+ }
66
+
67
+ get node(): SDK.DOMModel.DeferredDOMNode|undefined {
68
+ const target = this.#target.deref();
69
+ return this.#protocolTool.backendNodeId && target &&
70
+ new SDK.DOMModel.DeferredDOMNode(target, this.#protocolTool.backendNodeId);
71
+ }
72
+ }
73
+ export interface EventTypes {
74
+ [Events.TOOLS_ADDED]: readonly Tool[];
75
+ [Events.TOOLS_REMOVED]: readonly Tool[];
76
+ [Events.TOOL_INVOKED]: Call;
77
+ [Events.TOOL_RESPONDED]: Call;
78
+ }
79
+
80
+ export class WebMCPModel extends SDK.SDKModel.SDKModel<EventTypes> implements ProtocolProxyApi.WebMCPDispatcher {
81
+ readonly #tools = new Map<Protocol.Page.FrameId, Map<string, Tool>>();
82
+ readonly #calls = new Map<string, Call>();
83
+ readonly agent: ProtocolProxyApi.WebMCPApi;
84
+ #enabled = false;
85
+
86
+ constructor(target: SDK.Target.Target) {
87
+ super(target);
88
+ this.agent = target.webMCPAgent();
89
+ target.registerWebMCPDispatcher(this);
90
+
91
+ const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel);
92
+ if (runtimeModel) {
93
+ runtimeModel.addEventListener(
94
+ SDK.RuntimeModel.Events.ExecutionContextDestroyed, this.#executionContextDestroyed, this);
95
+ }
96
+
97
+ void this.enable();
98
+ }
99
+
100
+ get tools(): IteratorObject<Tool> {
101
+ return this.#tools.values().flatMap(toolMap => toolMap.values());
102
+ }
103
+
104
+ get toolCalls(): Call[] {
105
+ return [...this.#calls.values()];
106
+ }
107
+
108
+ clearCalls(): void {
109
+ this.#calls.clear();
110
+ }
111
+
112
+ async enable(): Promise<void> {
113
+ if (this.#enabled) {
114
+ return;
115
+ }
116
+ await this.agent.invoke_enable();
117
+ this.#enabled = true;
118
+ }
119
+
120
+ #executionContextDestroyed(event: Common.EventTarget.EventTargetEvent<SDK.RuntimeModel.ExecutionContext>): void {
121
+ const executionContext = event.data;
122
+ if (executionContext.isDefault && executionContext.frameId) {
123
+ const frameTools = this.#tools.get(executionContext.frameId);
124
+ if (frameTools) {
125
+ const toolsToRemove = [...frameTools.values()];
126
+ this.#tools.delete(executionContext.frameId);
127
+ this.dispatchEventToListeners(Events.TOOLS_REMOVED, toolsToRemove);
128
+ }
129
+ }
130
+ }
131
+
132
+ toolsRemoved(params: Protocol.WebMCP.ToolsRemovedEvent): void {
133
+ const deletedTools = [];
134
+ for (const protocolTool of params.tools) {
135
+ const tool = this.#tools.get(protocolTool.frameId)?.get(protocolTool.name);
136
+ if (tool) {
137
+ this.#tools.get(protocolTool.frameId)?.delete(protocolTool.name);
138
+ deletedTools.push(tool);
139
+ }
140
+ }
141
+ this.dispatchEventToListeners(Events.TOOLS_REMOVED, deletedTools);
142
+ }
143
+
144
+ toolsAdded(params: Protocol.WebMCP.ToolsAddedEvent): void {
145
+ const addedTools = [];
146
+ for (const protocolTool of params.tools) {
147
+ const tool = new Tool(protocolTool, this.target());
148
+ const frameTools = this.#tools.get(protocolTool.frameId) ?? new Map();
149
+ if (!this.#tools.has(protocolTool.frameId)) {
150
+ this.#tools.set(protocolTool.frameId, frameTools);
151
+ }
152
+ frameTools.set(tool.name, tool);
153
+ addedTools.push(tool);
154
+ }
155
+ this.dispatchEventToListeners(Events.TOOLS_ADDED, addedTools);
156
+ }
157
+
158
+ toolInvoked(params: Protocol.WebMCP.ToolInvokedEvent): void {
159
+ const tool = this.#tools.get(params.frameId)?.get(params.toolName);
160
+ if (!tool) {
161
+ return;
162
+ }
163
+ const call: Call = {
164
+ invocationId: params.invocationId,
165
+ input: params.input,
166
+ tool,
167
+ };
168
+ this.#calls.set(params.invocationId, call);
169
+ this.dispatchEventToListeners(Events.TOOL_INVOKED, call);
170
+ }
171
+
172
+ toolResponded(params: Protocol.WebMCP.ToolRespondedEvent): void {
173
+ const call = this.#calls.get(params.invocationId);
174
+ if (!call) {
175
+ return;
176
+ }
177
+ call.result = {
178
+ status: params.status,
179
+ output: params.output,
180
+ errorText: params.errorText,
181
+ exception: params.exception,
182
+ };
183
+ this.dispatchEventToListeners(Events.TOOL_RESPONDED, call);
184
+ }
185
+ }
186
+
187
+ SDK.SDKModel.SDKModel.register(WebMCPModel, {capabilities: SDK.Target.Capability.WEB_MCP, autostart: true});
@@ -0,0 +1,9 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as WebMCPModel from './WebMCPModel.js';
6
+
7
+ export {
8
+ WebMCPModel,
9
+ };
@@ -29,6 +29,7 @@ import * as NetworkPanel from '../network/network.js';
29
29
  import * as TimelinePanel from '../timeline/timeline.js';
30
30
 
31
31
  import aiAssistancePanelStyles from './aiAssistancePanel.css.js';
32
+ import {AccessibilityAgentMarkdownRenderer} from './components/AccessibilityAgentMarkdownRenderer.js';
32
33
  import {
33
34
  type AnswerPart,
34
35
  ChatMessageEntity,
@@ -363,6 +364,11 @@ function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiC
363
364
  const resourceTreeModel = domModel?.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
364
365
  const mainFrameId = resourceTreeModel?.mainFrame?.id;
365
366
  return new StylingAgentMarkdownRenderer(mainFrameId);
367
+ } else if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.ACCESSIBILITY) {
368
+ const domModel = SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel);
369
+ const resourceTreeModel = domModel?.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
370
+ const mainFrameId = resourceTreeModel?.mainFrame?.id;
371
+ return new AccessibilityAgentMarkdownRenderer(mainFrameId);
366
372
  }
367
373
 
368
374
  return new MarkdownRendererWithCodeBlock();
@@ -1281,7 +1287,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1281
1287
  return;
1282
1288
  }
1283
1289
 
1284
- return this.#changeManager.formatChangesForPatching(this.#conversation.id, /* includeSourceLocation= */ true);
1290
+ const hasAiV2 = Boolean(Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled);
1291
+ return this.#changeManager.formatChangesForPatching(this.#conversation.id, /* includeMetadata= */ !hasAiV2);
1285
1292
  }
1286
1293
 
1287
1294
  override async performUpdate(): Promise<void> {
@@ -4,6 +4,7 @@
4
4
 
5
5
  export * from './AiAssistancePanel.js';
6
6
  export * from './components/ChatView.js';
7
+ export * from './components/AccessibilityAgentMarkdownRenderer.js';
7
8
  export * as ChatInput from './components/ChatInput.js';
8
9
  export * from './components/MarkdownRendererWithCodeBlock.js';
9
10
  export * from './SelectWorkspaceDialog.js';