chrome-devtools-frontend 1.0.1605390 → 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 (60) hide show
  1. package/front_end/core/host/UserMetrics.ts +0 -1
  2. package/front_end/core/platform/api/HostRuntime.ts +9 -6
  3. package/front_end/core/platform/browser/HostRuntime.ts +2 -2
  4. package/front_end/core/platform/node/HostRuntime.ts +7 -7
  5. package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
  6. package/front_end/core/root/ExperimentNames.ts +0 -1
  7. package/front_end/core/sdk/CrashReportContextModel.ts +28 -0
  8. package/front_end/core/sdk/sdk.ts +2 -2
  9. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +3 -3
  10. package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker-entrypoint.ts +5 -4
  11. package/front_end/entrypoints/main/MainImpl.ts +0 -101
  12. package/front_end/models/ai_assistance/ChangeManager.ts +6 -6
  13. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +112 -5
  14. package/front_end/models/ai_assistance/agents/AiAgent.ts +0 -24
  15. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +2 -2
  16. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +6 -3
  17. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +13 -1
  18. package/front_end/models/ai_assistance/agents/FileAgent.ts +15 -1
  19. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +15 -1
  20. package/front_end/models/ai_assistance/agents/StylingAgent.ts +24 -15
  21. package/front_end/models/ai_assistance/ai_assistance.ts +0 -2
  22. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -5
  23. package/front_end/models/web_mcp/WebMCPModel.ts +187 -0
  24. package/front_end/models/web_mcp/web_mcp.ts +9 -0
  25. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -1
  26. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  27. package/front_end/panels/ai_assistance/components/AccessibilityAgentMarkdownRenderer.ts +88 -0
  28. package/front_end/panels/ai_assistance/components/ChatMessage.ts +19 -3
  29. package/front_end/panels/ai_assistance/components/ChatView.ts +11 -4
  30. package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +12 -3
  31. package/front_end/panels/ai_assistance/components/chatMessage.css +10 -0
  32. package/front_end/panels/ai_assistance/components/chatView.css +0 -2
  33. package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +13 -0
  34. package/front_end/panels/application/WebMCPView.ts +126 -30
  35. package/front_end/panels/application/webMCPView.css +28 -2
  36. package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
  37. package/front_end/panels/elements/StylePropertiesSection.ts +0 -4
  38. package/front_end/panels/elements/elements.ts +3 -0
  39. package/front_end/panels/elements/stylePropertiesTreeOutline.css +7 -0
  40. package/front_end/panels/lighthouse/LighthousePanel.ts +7 -1
  41. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +25 -2
  42. package/front_end/panels/profiler/HeapSnapshotProxy.ts +2 -6
  43. package/front_end/panels/profiler/HeapSnapshotView.ts +7 -16
  44. package/front_end/panels/profiler/ProfileHeader.ts +1 -4
  45. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +27 -22
  46. package/front_end/panels/timeline/TimelinePanel.ts +0 -153
  47. package/front_end/panels/timeline/TimelineTreeView.ts +11 -1
  48. package/front_end/panels/timeline/TimelineUIUtils.ts +2 -1
  49. package/front_end/third_party/chromium/README.chromium +1 -1
  50. package/front_end/ui/components/markdown_view/CodeBlock.ts +47 -1
  51. package/front_end/ui/components/markdown_view/codeBlock.css +8 -0
  52. package/front_end/ui/legacy/TabbedPane.ts +123 -3
  53. package/front_end/ui/legacy/Widget.ts +67 -28
  54. package/front_end/ui/legacy/components/source_frame/JSONView.ts +1 -1
  55. package/front_end/ui/legacy/components/utils/jsUtils.css +2 -0
  56. package/front_end/ui/legacy/infobar.css +1 -0
  57. package/front_end/ui/visual_logging/KnownContextValues.ts +0 -1
  58. package/package.json +1 -1
  59. package/front_end/core/sdk/WebMCPModel.ts +0 -159
  60. package/front_end/models/ai_assistance/ConversationHandler.ts +0 -325
@@ -12,10 +12,22 @@ import {debugLog} from '../debug.js';
12
12
  import {EvaluateAction, formatError, SideEffectError} from '../EvaluateAction.js';
13
13
  import {FREESTYLER_WORLD_NAME} from '../injected.js';
14
14
 
15
- import type {FunctionCallHandlerResult, FunctionDeclaration, FunctionHandlerOptions} from './AiAgent.js';
15
+ import type {
16
+ AgentOptions as BaseAgentOptions, FunctionCallHandlerResult, FunctionDeclaration, FunctionHandlerOptions,} from
17
+ './AiAgent.js';
16
18
 
17
19
  const lockedString = i18n.i18n.lockedString;
18
20
 
21
+ export type CreateExtensionScopeFunction = (changes: ChangeManager) => {
22
+ install(): Promise<void>, uninstall(): Promise<void>,
23
+ };
24
+
25
+ export interface ExecuteJsAgentOptions extends BaseAgentOptions {
26
+ changeManager?: ChangeManager;
27
+ createExtensionScope?: CreateExtensionScopeFunction;
28
+ execJs?: typeof executeJsCode;
29
+ }
30
+
19
31
  export function executeJavaScriptFunction(executor: JavascriptExecutor): FunctionDeclaration<
20
32
  {
21
33
  title: string,
@@ -35,7 +35,7 @@ Analyze the code and provide the following information:
35
35
  * (ONLY if request initiator chain is provided) Why the file was loaded?
36
36
 
37
37
  # Considerations
38
- * Keep your analysis concise and focused, highlighting only the most critical aspects for a software engineer.
38
+ * **CRITICAL**: 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.
39
39
  * Answer questions directly, using the provided links whenever relevant.
40
40
  * Always double-check links to make sure they are complete and correct.
41
41
  * **CRITICAL** If the user asks a question about religion, race, politics, sexuality, gender, or other sensitive topics, answer with "Sorry, I can't answer that. I'm best at questions about files."
@@ -43,6 +43,20 @@ Analyze the code and provide the following information:
43
43
  * **Important Note:** The provided code may represent an incomplete fragment of a larger file. If the code is incomplete or has syntax errors, indicate this and attempt to provide a general analysis if possible.
44
44
  * **Interactive Analysis:** If the code requires more context or is ambiguous, ask clarifying questions to the user. Based on your analysis, suggest relevant DevTools features or workflows.
45
45
 
46
+ ## Response Structure
47
+
48
+ If the user asks a question that requires an investigation of a problem, use this structure:
49
+ - If available, point out the root cause(s) of the problem.
50
+ - Example: "**Root Cause**: The page is slow because of [reason]."
51
+ - Example: "**Root Causes**:"
52
+ - [Reason 1]
53
+ - [Reason 2]
54
+ - if applicable, list actionable solution suggestion(s) in order of impact:
55
+ - Example: "**Suggestion**: [Suggestion 1]
56
+ - Example: "**Suggestions**:"
57
+ - [Suggestion 1]
58
+ - [Suggestion 2]
59
+
46
60
  ## Example session
47
61
 
48
62
  **User:** (Selects a file containing the following JavaScript code)
@@ -34,10 +34,24 @@ Provide a comprehensive analysis of the network request, focusing on areas cruci
34
34
  # Considerations
35
35
  * If the response payload or request payload contains sensitive data, redact or generalize it in your analysis to ensure privacy.
36
36
  * Tailor your explanations and suggestions to the specific context of the request and the technologies involved (if discernible from the provided details).
37
- * Keep your analysis concise and focused, highlighting only the most critical aspects for a software engineer.
37
+ * **CRITICAL** 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.
38
38
  * **CRITICAL** If the user asks a question about religion, race, politics, sexuality, gender, or other sensitive topics, answer with "Sorry, I can't answer that. I'm best at questions about network requests."
39
39
  * **CRITICAL** You are a network request debugging assistant. 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.
40
40
 
41
+ ## Response Structure
42
+
43
+ If the user asks a question that requires an investigation of a problem, use this structure:
44
+ - If available, point out the root cause(s) of the problem.
45
+ - Example: "**Root Cause**: The page is slow because of [reason]."
46
+ - Example: "**Root Causes**:"
47
+ - [Reason 1]
48
+ - [Reason 2]
49
+ - if applicable, list actionable solution suggestion(s) in order of impact:
50
+ - Example: "**Suggestion**: [Suggestion 1]
51
+ - Example: "**Suggestions**:"
52
+ - [Suggestion 1]
53
+ - [Suggestion 2]
54
+
41
55
  ## Example session
42
56
 
43
57
  Explain this network request
@@ -16,7 +16,6 @@ import {ExtensionScope} from '../ExtensionScope.js';
16
16
  import {AI_ASSISTANCE_CSS_CLASS_NAME} from '../injected.js';
17
17
 
18
18
  import {
19
- type AgentOptions as BaseAgentOptions,
20
19
  AiAgent,
21
20
  type ComputedStyleAiWidget,
22
21
  type ContextResponse,
@@ -28,7 +27,13 @@ import {
28
27
  type RequestOptions,
29
28
  ResponseType
30
29
  } from './AiAgent.js';
31
- import {executeJavaScriptFunction, executeJsCode, JavascriptExecutor} from './ExecuteJavascript.js';
30
+ import {
31
+ type CreateExtensionScopeFunction,
32
+ executeJavaScriptFunction,
33
+ type ExecuteJsAgentOptions,
34
+ executeJsCode,
35
+ JavascriptExecutor
36
+ } from './ExecuteJavascript.js';
32
37
 
33
38
  /*
34
39
  * Strings that don't need to be translated at this time.
@@ -67,10 +72,25 @@ First, examine the provided context, then use the functions to gather additional
67
72
  * Use functions available to you to investigate and fulfill the user request.
68
73
  * After applying a fix, please ask the user to confirm if the fix worked or not.
69
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.
70
76
  * **CRITICAL** NEVER write full Python programs - you should only write individual statements that invoke a single function from the provided library.
71
77
  * **CRITICAL** NEVER output text before a function call. Always do a function call first.
72
78
  * **CRITICAL** When answering questions about positioning or layout, ALWAYS inspect \`position\`, \`display\` and ALL related properties.
73
- * **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]`;
74
94
 
75
95
  const greenDevEmulationEnabled = Greendev.Prototypes.instance().isEnabled('emulationCapabilities');
76
96
  if (greenDevEmulationEnabled) {
@@ -145,17 +165,6 @@ const MULTIMODAL_ENHANCEMENT_PROMPTS: Record<MultimodalInputType, string> = {
145
165
 
146
166
  export const AI_ASSISTANCE_FILTER_REGEX = `\\.${AI_ASSISTANCE_CSS_CLASS_NAME}-.*&`;
147
167
 
148
- type CreateExtensionScopeFunction = (changes: ChangeManager) => {
149
- install(): Promise<void>, uninstall(): Promise<void>,
150
- };
151
-
152
- interface AgentOptions extends BaseAgentOptions {
153
- changeManager?: ChangeManager;
154
-
155
- createExtensionScope?: CreateExtensionScopeFunction;
156
- execJs?: typeof executeJsCode;
157
- }
158
-
159
168
  export class NodeContext extends ConversationContext<SDK.DOMModel.DOMNode> {
160
169
  #node: SDK.DOMModel.DOMNode;
161
170
 
@@ -271,7 +280,7 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
271
280
  #greenDevEmulationAxTree: string|null = null;
272
281
  #currentTurnId = 0;
273
282
 
274
- constructor(opts: AgentOptions) {
283
+ constructor(opts: ExecuteJsAgentOptions) {
275
284
  super(opts);
276
285
 
277
286
  this.#changes = opts.changeManager || new ChangeManager();
@@ -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';
@@ -0,0 +1,88 @@
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 SDK from '../../../core/sdk/sdk.js';
6
+ import type * as Protocol from '../../../generated/protocol.js';
7
+ import type * as Marked from '../../../third_party/marked/marked.js';
8
+ import * as Lit from '../../../ui/lit/lit.js';
9
+ import * as PanelsCommon from '../../common/common.js';
10
+
11
+ import {MarkdownRendererWithCodeBlock} from './MarkdownRendererWithCodeBlock.js';
12
+
13
+ const {html} = Lit.StaticHtml;
14
+ const {until} = Lit.Directives;
15
+
16
+ export class AccessibilityAgentMarkdownRenderer extends MarkdownRendererWithCodeBlock {
17
+ constructor(
18
+ private mainFrameId = '',
19
+ ) {
20
+ super();
21
+ }
22
+
23
+ override templateForToken(token: Marked.Marked.MarkedToken): Lit.LitTemplate|null {
24
+ if (token.type === 'link' && token.href.startsWith('#')) {
25
+ if (token.href.startsWith('#path-')) {
26
+ const path = token.href.replace('#path-', '');
27
+ return html`<span>${
28
+ until(this.#linkifyPath(path, token.text).then(node => node || token.text), token.text)}</span>`;
29
+ }
30
+
31
+ let nodeId = undefined;
32
+ if (token.href.startsWith('#node-')) {
33
+ nodeId = Number(token.href.replace('#node-', '')) as Protocol.DOM.BackendNodeId;
34
+ } else if (token.href.startsWith('#')) {
35
+ nodeId = Number(token.href.replace('#', '')) as Protocol.DOM.BackendNodeId;
36
+ }
37
+
38
+ if (nodeId) {
39
+ return html`<span>${
40
+ until(this.#linkifyNode(nodeId, token.text).then(node => node || token.text), token.text)}</span>`;
41
+ }
42
+ }
43
+
44
+ return super.templateForToken(token);
45
+ }
46
+
47
+ async #linkifyNode(backendNodeId: Protocol.DOM.BackendNodeId, label: string): Promise<Lit.LitTemplate|undefined> {
48
+ if (backendNodeId === undefined) {
49
+ return;
50
+ }
51
+
52
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
53
+ const domModel = target?.model(SDK.DOMModel.DOMModel);
54
+ if (!domModel) {
55
+ return undefined;
56
+ }
57
+ const domNodesMap = await domModel.pushNodesByBackendIdsToFrontend(new Set([backendNodeId]));
58
+ const node = domNodesMap?.get(backendNodeId);
59
+ if (!node) {
60
+ return;
61
+ }
62
+
63
+ if (node.frameId() !== this.mainFrameId) {
64
+ return;
65
+ }
66
+
67
+ const linkedNode = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(node, {textContent: label});
68
+ return linkedNode;
69
+ }
70
+
71
+ async #linkifyPath(path: string, label: string): Promise<Lit.LitTemplate|undefined> {
72
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
73
+ const domModel = target?.model(SDK.DOMModel.DOMModel);
74
+ if (!domModel) {
75
+ return undefined;
76
+ }
77
+ const nodeId = await domModel.pushNodeByPathToFrontend(path);
78
+ if (!nodeId) {
79
+ return;
80
+ }
81
+ const node = domModel.nodeForId(nodeId);
82
+ if (!node) {
83
+ return;
84
+ }
85
+ const linkedNode = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(node, {textContent: label});
86
+ return linkedNode;
87
+ }
88
+ }
@@ -45,6 +45,7 @@ const {widget} = UI.Widget;
45
45
 
46
46
  const REPORT_URL = 'https://crbug.com/364805393' as Platform.DevToolsPath.UrlString;
47
47
  const SCROLL_ROUNDING_OFFSET = 1;
48
+ const MAX_NUM_LINES_IN_CODEBLOCK = 11;
48
49
 
49
50
  /*
50
51
  * Strings that don't need to be translated at this time.
@@ -310,6 +311,7 @@ export interface MessageInput {
310
311
  onFeedbackSubmit: (rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void;
311
312
  onCopyResponseClick: (message: ModelChatMessage) => void;
312
313
  onExportClick?: () => void;
314
+ changeSummary?: string;
313
315
  walkthrough: {
314
316
  onOpen: (message: ModelChatMessage) => void,
315
317
  isExpanded: boolean,
@@ -404,6 +406,15 @@ export const DEFAULT_VIEW = (input: ChatMessageViewInput, output: ViewOutput, ta
404
406
  },
405
407
  )}
406
408
  ${renderError(message)}
409
+ ${input.isLastMessage && hasAiV2 && !input.isLoading && input.changeSummary ? html`
410
+ <devtools-code-block
411
+ .code=${input.changeSummary}
412
+ .codeLang=${'css'}
413
+ .displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
414
+ .displayNotice=${true}
415
+ class="ai-css-change"
416
+ ></devtools-code-block>
417
+ ` : Lit.nothing}
407
418
  ${input.showActions ? renderActions(input, output) : Lit.nothing}
408
419
  </div>
409
420
  </section>
@@ -471,6 +482,7 @@ function renderStepCode(step: Step): Lit.LitTemplate {
471
482
  <devtools-code-block
472
483
  .code=${step.code.trim()}
473
484
  .codeLang=${'js'}
485
+ .displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
474
486
  .displayNotice=${!Boolean(step.output)}
475
487
  .header=${codeHeadingText}
476
488
  .showCopyButton=${true}
@@ -481,6 +493,7 @@ function renderStepCode(step: Step): Lit.LitTemplate {
481
493
  <devtools-code-block
482
494
  .code=${step.output}
483
495
  .codeLang=${'js'}
496
+ .displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
484
497
  .displayNotice=${true}
485
498
  .header=${lockedString(UIStringsNotTranslate.dataReturned)}
486
499
  .showCopyButton=${false}
@@ -513,6 +526,7 @@ function renderStepDetails({
513
526
  <devtools-code-block
514
527
  .code=${contextDetail.text}
515
528
  .codeLang=${contextDetail.codeLang || ''}
529
+ .displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
516
530
  .displayNotice=${false}
517
531
  .header=${contextDetail.title}
518
532
  .showCopyButton=${true}
@@ -851,6 +865,7 @@ async function makeBottomUpTimelineTreeWidget(widgetData: BottomUpTreeAiWidget):
851
865
  startTime,
852
866
  endTime,
853
867
  compactMode: true,
868
+ maxLinkLength: 15,
854
869
  })}></devtools-widget>`;
855
870
 
856
871
  return {
@@ -1164,7 +1179,7 @@ function renderActions(input: ChatMessageViewInput, output: ViewOutput): Lit.Lit
1164
1179
  } as Buttons.Button.ButtonData}
1165
1180
  @click=${() => input.onRatingClick(Host.AidaClient.Rating.NEGATIVE)}
1166
1181
  ></devtools-button>
1167
- <div class="vertical-separator"></div>
1182
+ ${aiAssistanceV2 ? Lit.nothing : html`<div class="vertical-separator"></div>`}
1168
1183
  `: Lit.nothing}
1169
1184
  <devtools-button
1170
1185
  .data=${
@@ -1192,7 +1207,6 @@ function renderActions(input: ChatMessageViewInput, output: ViewOutput): Lit.Lit
1192
1207
  @click=${input.onCopyResponseClick}></devtools-button>
1193
1208
  `}
1194
1209
  ${input.onExportClick && aiAssistanceV2 && input.isLastMessage ? html`
1195
- <div class="vertical-separator"></div>
1196
1210
  <devtools-button
1197
1211
  class="export-for-agents-button"
1198
1212
  .jslogContext=${'ai-export-for-agents'}
@@ -1200,7 +1214,7 @@ function renderActions(input: ChatMessageViewInput, output: ViewOutput): Lit.Lit
1200
1214
  .iconName=${'copy'}
1201
1215
  @click=${input.onExportClick}
1202
1216
  >${lockedString(UIStringsNotTranslate.exportForAgents)}</devtools-button>
1203
- <div class="vertical-separator"></div>
1217
+ ${input.suggestions ? html`<div class="vertical-separator"></div>` : Lit.nothing}
1204
1218
  ` : Lit.nothing}
1205
1219
  </div>
1206
1220
  ${input.suggestions ? html`<div class="suggestions-container">
@@ -1312,6 +1326,7 @@ export class ChatMessage extends UI.Widget.Widget {
1312
1326
  (rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void = () => {};
1313
1327
  onCopyResponseClick: (message: ModelChatMessage) => void = () => {};
1314
1328
  onExportClick: () => void = () => {};
1329
+ changeSummary?: string;
1315
1330
  walkthrough: MessageInput['walkthrough'] = {
1316
1331
  onOpen: () => {},
1317
1332
  onToggle: () => {},
@@ -1380,6 +1395,7 @@ export class ChatMessage extends UI.Widget.Widget {
1380
1395
  currentRating: this.#currentRating,
1381
1396
  isShowingFeedbackForm: this.#isShowingFeedbackForm,
1382
1397
  onFeedbackSubmit: this.onFeedbackSubmit,
1398
+ changeSummary: this.changeSummary,
1383
1399
  walkthrough: this.walkthrough,
1384
1400
  },
1385
1401
  this.#viewOutput, this.contentElement);
@@ -7,6 +7,7 @@ import '../../../ui/components/spinners/spinners.js';
7
7
  import * as Host from '../../../core/host/host.js';
8
8
  import * as i18n from '../../../core/i18n/i18n.js';
9
9
  import type * as Platform from '../../../core/platform/platform.js';
10
+ import * as Root from '../../../core/root/root.js';
10
11
  import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
11
12
  import * as Buttons from '../../../ui/components/buttons/buttons.js';
12
13
  import type {MarkdownLitRenderer} from '../../../ui/components/markdown_view/MarkdownView.js';
@@ -103,9 +104,12 @@ interface ChatWidgetInput extends Props {
103
104
  }
104
105
 
105
106
  const DEFAULT_VIEW: View = (input, output, target) => {
107
+ const hasAiV2 = Boolean(Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled);
108
+
106
109
  const chatUiClasses = classMap({
107
110
  'chat-ui': true,
108
111
  gemini: AiAssistanceModel.AiUtils.isGeminiBranding(),
112
+ 'ai-v2': hasAiV2,
109
113
  });
110
114
 
111
115
  const inputWidgetClasses = classMap({
@@ -113,6 +117,8 @@ const DEFAULT_VIEW: View = (input, output, target) => {
113
117
  sticky: !input.isReadOnly,
114
118
  });
115
119
 
120
+ const shouldShowPatchWidget = !hasAiV2 && !input.isLoading;
121
+
116
122
  // clang-format off
117
123
  render(html`
118
124
  <style>${chatViewStyles}</style>
@@ -133,15 +139,16 @@ const DEFAULT_VIEW: View = (input, output, target) => {
133
139
  onFeedbackSubmit: input.onFeedbackSubmit,
134
140
  onCopyResponseClick: input.onCopyResponseClick,
135
141
  onExportClick: input.exportForAgentsClick,
142
+ changeSummary: input.changeSummary,
136
143
  walkthrough: {
137
144
  ...input.walkthrough,
138
145
  }
139
146
  })
140
147
  )}
141
- ${input.isLoading ? nothing : widget(PatchWidget, {
142
- changeSummary: input.changeSummary ?? '',
143
- changeManager: input.changeManager,
144
- })}
148
+ ${shouldShowPatchWidget ? widget(PatchWidget, {
149
+ changeSummary: input.changeSummary ?? '',
150
+ changeManager: input.changeManager,
151
+ }) : nothing}
145
152
  </div>
146
153
  ` : html`
147
154
  <div class="empty-state-container">