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.
- package/front_end/core/host/UserMetrics.ts +0 -1
- package/front_end/core/platform/api/HostRuntime.ts +9 -6
- package/front_end/core/platform/browser/HostRuntime.ts +2 -2
- package/front_end/core/platform/node/HostRuntime.ts +7 -7
- package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/sdk/CrashReportContextModel.ts +28 -0
- package/front_end/core/sdk/sdk.ts +2 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +3 -3
- package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker-entrypoint.ts +5 -4
- package/front_end/entrypoints/main/MainImpl.ts +0 -101
- package/front_end/models/ai_assistance/ChangeManager.ts +6 -6
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +112 -5
- package/front_end/models/ai_assistance/agents/AiAgent.ts +0 -24
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +6 -3
- package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +13 -1
- package/front_end/models/ai_assistance/agents/FileAgent.ts +15 -1
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +15 -1
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +24 -15
- package/front_end/models/ai_assistance/ai_assistance.ts +0 -2
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -5
- package/front_end/models/web_mcp/WebMCPModel.ts +187 -0
- package/front_end/models/web_mcp/web_mcp.ts +9 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -1
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/AccessibilityAgentMarkdownRenderer.ts +88 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +19 -3
- package/front_end/panels/ai_assistance/components/ChatView.ts +11 -4
- package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +12 -3
- package/front_end/panels/ai_assistance/components/chatMessage.css +10 -0
- package/front_end/panels/ai_assistance/components/chatView.css +0 -2
- package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +13 -0
- package/front_end/panels/application/WebMCPView.ts +126 -30
- package/front_end/panels/application/webMCPView.css +28 -2
- package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +0 -4
- package/front_end/panels/elements/elements.ts +3 -0
- package/front_end/panels/elements/stylePropertiesTreeOutline.css +7 -0
- package/front_end/panels/lighthouse/LighthousePanel.ts +7 -1
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +25 -2
- package/front_end/panels/profiler/HeapSnapshotProxy.ts +2 -6
- package/front_end/panels/profiler/HeapSnapshotView.ts +7 -16
- package/front_end/panels/profiler/ProfileHeader.ts +1 -4
- package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +27 -22
- package/front_end/panels/timeline/TimelinePanel.ts +0 -153
- package/front_end/panels/timeline/TimelineTreeView.ts +11 -1
- package/front_end/panels/timeline/TimelineUIUtils.ts +2 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/markdown_view/CodeBlock.ts +47 -1
- package/front_end/ui/components/markdown_view/codeBlock.css +8 -0
- package/front_end/ui/legacy/TabbedPane.ts +123 -3
- package/front_end/ui/legacy/Widget.ts +67 -28
- package/front_end/ui/legacy/components/source_frame/JSONView.ts +1 -1
- package/front_end/ui/legacy/components/utils/jsUtils.css +2 -0
- package/front_end/ui/legacy/infobar.css +1 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +0 -1
- package/package.json +1 -1
- package/front_end/core/sdk/WebMCPModel.ts +0 -159
- 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 {
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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 {
|
|
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:
|
|
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});
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
${
|
|
142
|
-
|
|
143
|
-
|
|
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">
|