chrome-devtools-frontend 1.0.1638082 → 1.0.1640418
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/AidaClientTypes.ts +8 -6
- package/front_end/core/sdk/DOMStorageModel.ts +1 -1
- package/front_end/core/sdk/TargetManager.ts +14 -1
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +147 -0
- package/front_end/generated/ARIAProperties.js +0 -4
- package/front_end/generated/InspectorBackendCommands.ts +7 -3
- package/front_end/generated/protocol-mapping.d.ts +7 -0
- package/front_end/generated/protocol-proxy-api.d.ts +14 -0
- package/front_end/generated/protocol.ts +80 -0
- package/front_end/global_typings/global_defs.d.ts +13 -0
- package/front_end/models/ai_assistance/AiAgent2.ts +116 -0
- package/front_end/models/ai_assistance/AiConversation.ts +22 -28
- package/front_end/models/ai_assistance/AiOrigins.ts +46 -0
- package/front_end/models/ai_assistance/README.md +16 -0
- package/front_end/models/ai_assistance/StorageItem.ts +30 -26
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +2 -2
- package/front_end/models/ai_assistance/agents/AiAgent.ts +73 -36
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +25 -10
- package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +1 -1
- package/front_end/models/ai_assistance/agents/FileAgent.ts +2 -2
- package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +1 -1
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +3 -4
- package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +41 -12
- package/front_end/models/ai_assistance/agents/StorageAgent.ts +442 -122
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +2 -2
- package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/performance/AIContext.ts +7 -8
- package/front_end/models/ai_assistance/skills/README.md +40 -0
- package/front_end/models/ai_assistance/skills/Skill.ts +13 -0
- package/front_end/models/ai_assistance/skills/SkillRegistry.ts +10 -0
- package/front_end/models/ai_assistance/skills/styling.md +6 -0
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +20 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +5 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +6 -2
- package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +1 -1
- package/front_end/models/stack_trace/StackTraceImpl.ts +29 -9
- package/front_end/models/stack_trace/StackTraceModel.ts +23 -11
- package/front_end/models/stack_trace/Trie.ts +11 -1
- package/front_end/models/trace/extras/TraceTree.ts +20 -1
- package/front_end/models/trace/insights/Common.ts +9 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +114 -2
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +2 -3
- package/front_end/panels/ai_assistance/components/chatMessage.css +27 -0
- package/front_end/panels/application/CookieItemsView.ts +24 -0
- package/front_end/panels/application/DOMStorageItemsView.ts +9 -4
- package/front_end/panels/application/preloading/components/PreloadingString.ts +6 -0
- package/front_end/panels/console/ConsoleViewMessage.ts +13 -12
- package/front_end/panels/elements/StandaloneStylesContainer.ts +10 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +30 -1
- package/front_end/panels/elements/StylesContainer.ts +3 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +45 -4
- package/front_end/panels/layer_viewer/layerDetailsView.css +1 -1
- package/front_end/panels/lighthouse/LighthouseController.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +4 -4
- package/front_end/panels/network/NetworkDataGridNode.ts +14 -0
- package/front_end/panels/network/NetworkLogViewColumns.ts +2 -2
- package/front_end/panels/network/networkTimingTable.css +2 -4
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +169 -106
- package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/marked/README.chromium +3 -6
- package/front_end/third_party/marked/package/README.md +5 -5
- package/front_end/third_party/marked/package/bin/main.js +27 -22
- package/front_end/third_party/marked/package/bin/marked.js +2 -1
- package/front_end/third_party/marked/package/lib/marked.esm.d.ts +346 -256
- package/front_end/third_party/marked/package/lib/marked.esm.js +67 -2698
- package/front_end/third_party/marked/package/lib/marked.esm.js.map +7 -1
- package/front_end/third_party/marked/package/lib/marked.umd.js +69 -2722
- package/front_end/third_party/marked/package/lib/marked.umd.js.map +7 -1
- package/front_end/third_party/marked/package/man/marked.1 +4 -2
- package/front_end/third_party/marked/package/man/marked.1.md +2 -1
- package/front_end/third_party/marked/package/package.json +49 -57
- package/front_end/tsconfig.json +1 -1
- package/front_end/ui/components/markdown_view/CodeBlock.ts +17 -6
- package/front_end/ui/components/markdown_view/MarkdownView.ts +39 -3
- package/front_end/ui/components/markdown_view/codeBlock.css +11 -0
- package/front_end/ui/components/markdown_view/markdownView.css +17 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +0 -79
- package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +16 -4
- package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
- package/inspector_overlay/testing/InspectorOverlayHelpers.ts +2 -0
- package/mcp/mcp.ts +1 -1
- package/package.json +7 -9
- package/front_end/third_party/marked/package/bin/marked +0 -215
- package/front_end/third_party/marked/package/lib/marked.cjs +0 -2726
- package/front_end/third_party/marked/package/lib/marked.cjs.map +0 -1
- package/front_end/third_party/marked/package/lib/marked.d.cts +0 -670
- package/front_end/third_party/marked/package/lib/marked.js +0 -2780
- package/front_end/third_party/marked/package/man/marked.1.txt +0 -86
- package/front_end/third_party/marked/package/marked.min.js +0 -6
- package/front_end/third_party/marked/package/src/Lexer.js +0 -492
- package/front_end/third_party/marked/package/src/Parser.js +0 -286
- package/front_end/third_party/marked/package/src/Renderer.js +0 -166
- package/front_end/third_party/marked/package/src/Slugger.js +0 -49
- package/front_end/third_party/marked/package/src/TextRenderer.js +0 -42
- package/front_end/third_party/marked/package/src/Tokenizer.js +0 -755
- package/front_end/third_party/marked/package/src/defaults.js +0 -29
- package/front_end/third_party/marked/package/src/helpers.js +0 -249
- package/front_end/third_party/marked/package/src/marked.js +0 -350
- package/front_end/third_party/marked/package/src/rules.js +0 -285
- /package/front_end/third_party/marked/package/{LICENSE.md → LICENSE} +0 -0
|
@@ -13,6 +13,7 @@ import type * as NetworkTimeCalculator from '../network_time_calculator/network_
|
|
|
13
13
|
|
|
14
14
|
import {AccessibilityAgent, AccessibilityContext} from './agents/AccessibilityAgent.js';
|
|
15
15
|
import {
|
|
16
|
+
type AgentOptions,
|
|
16
17
|
type AiAgent,
|
|
17
18
|
type AllowedOriginResult,
|
|
18
19
|
type ContextDetail,
|
|
@@ -29,6 +30,7 @@ import {NetworkAgent, RequestContext} from './agents/NetworkAgent.js';
|
|
|
29
30
|
import {PerformanceAgent, PerformanceTraceContext} from './agents/PerformanceAgent.js';
|
|
30
31
|
import {StorageAgent, StorageContext} from './agents/StorageAgent.js';
|
|
31
32
|
import {NodeContext, StylingAgent} from './agents/StylingAgent.js';
|
|
33
|
+
import {AiAgent2} from './AiAgent2.js';
|
|
32
34
|
import {AiHistoryStorage, ConversationType, type SerializedConversation} from './AiHistoryStorage.js';
|
|
33
35
|
import type {ChangeManager} from './ChangeManager.js';
|
|
34
36
|
|
|
@@ -356,35 +358,27 @@ export class AiConversation {
|
|
|
356
358
|
allowedOrigin: this.allowedOrigin,
|
|
357
359
|
history,
|
|
358
360
|
};
|
|
361
|
+
|
|
362
|
+
this.#agent = Root.Runtime.hostConfig.devToolsAiV2Architecture?.enabled ? new AiAgent2(options) :
|
|
363
|
+
this.#createV1Agent(type, options);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
#createV1Agent(type: ConversationType, options: AgentOptions): AiAgent<unknown> {
|
|
359
367
|
switch (type) {
|
|
360
|
-
case ConversationType.STYLING:
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
case ConversationType.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
case ConversationType.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
case ConversationType.
|
|
373
|
-
|
|
374
|
-
break;
|
|
375
|
-
}
|
|
376
|
-
case ConversationType.ACCESSIBILITY: {
|
|
377
|
-
this.#agent = new AccessibilityAgent(options);
|
|
378
|
-
break;
|
|
379
|
-
}
|
|
380
|
-
case ConversationType.STORAGE: {
|
|
381
|
-
this.#agent = new StorageAgent(options);
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
case ConversationType.NONE: {
|
|
385
|
-
this.#agent = new ContextSelectionAgent(options);
|
|
386
|
-
break;
|
|
387
|
-
}
|
|
368
|
+
case ConversationType.STYLING:
|
|
369
|
+
return new StylingAgent(options);
|
|
370
|
+
case ConversationType.NETWORK:
|
|
371
|
+
return new NetworkAgent(options);
|
|
372
|
+
case ConversationType.FILE:
|
|
373
|
+
return new FileAgent(options);
|
|
374
|
+
case ConversationType.PERFORMANCE:
|
|
375
|
+
return new PerformanceAgent(options);
|
|
376
|
+
case ConversationType.ACCESSIBILITY:
|
|
377
|
+
return new AccessibilityAgent(options);
|
|
378
|
+
case ConversationType.STORAGE:
|
|
379
|
+
return new StorageAgent(options);
|
|
380
|
+
case ConversationType.NONE:
|
|
381
|
+
return new ContextSelectionAgent(options);
|
|
388
382
|
default:
|
|
389
383
|
Platform.assertNever(type, 'Unknown conversation type');
|
|
390
384
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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 Common from '../../core/common/common.js';
|
|
6
|
+
import type * as Platform from '../../core/platform/platform.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Returns true if the origin is considered opaque and should be blocked from
|
|
10
|
+
* AI assistance to prevent potential data leakage.
|
|
11
|
+
*
|
|
12
|
+
* @see https://crbug.com/513732588
|
|
13
|
+
*/
|
|
14
|
+
export function isOpaqueOrigin(origin: string): boolean {
|
|
15
|
+
/**
|
|
16
|
+
* Origins starting with 'about' (like about:blank or about:srcdoc) are
|
|
17
|
+
* considered opaque. 'about://' is the sentinel used by DevTools
|
|
18
|
+
* ParsedURL.securityOrigin() for these.
|
|
19
|
+
*/
|
|
20
|
+
return origin === 'null' || origin === 'data:' || origin.startsWith('about') || origin.startsWith('detached');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Extracts the origin from a context URL or identifier.
|
|
25
|
+
* Handles special cases like "detached" nodes and trace identifiers.
|
|
26
|
+
*/
|
|
27
|
+
export function extractContextOrigin(contextURL: string): string {
|
|
28
|
+
if (isOpaqueOrigin(contextURL)) {
|
|
29
|
+
return contextURL;
|
|
30
|
+
}
|
|
31
|
+
if (contextURL.startsWith('trace-')) {
|
|
32
|
+
return contextURL;
|
|
33
|
+
}
|
|
34
|
+
return Common.ParsedURL.ParsedURL.extractOrigin(contextURL as Platform.DevToolsPath.UrlString);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Determines if two origins are equivalent and safe to be used together.
|
|
39
|
+
* Opaque origins are never equivalent to anything, not even themselves.
|
|
40
|
+
*/
|
|
41
|
+
export function areOriginsEquivalent(origin1: string, origin2: string): boolean {
|
|
42
|
+
if (isOpaqueOrigin(origin1) || isOpaqueOrigin(origin2)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return origin1 === origin2;
|
|
46
|
+
}
|
|
@@ -22,6 +22,22 @@ When the user begins a conversation with the AI, we want to include information
|
|
|
22
22
|
|
|
23
23
|
To deal with the work to take an object that is the AI agent's context and turn it into a text representation that can be sent to the AI, we use _formatters_. These are typically classes with `static` methods that can take in objects and return their text representation that we want to pass to the AI.
|
|
24
24
|
|
|
25
|
+
## Future Architecture (V2) - WIP
|
|
26
|
+
|
|
27
|
+
We are currently working on migrating the DevTools AI Assistance from a siloed multi-agent architecture to a unified, skill-based single-agent architecture (`AIAgent2`).
|
|
28
|
+
|
|
29
|
+
In this new architecture:
|
|
30
|
+
- A single agent (`AIAgent2`) handles multiple domains.
|
|
31
|
+
- Capabilities are defined as **Skills** in Markdown files.
|
|
32
|
+
- The agent can dynamically load skills as needed via a `learnSkill` tool.
|
|
33
|
+
|
|
34
|
+
This work is currently in progress and behind a feature flag.
|
|
35
|
+
|
|
36
|
+
### Skills Build System
|
|
37
|
+
|
|
38
|
+
To support dynamic loading of skills, we generate JavaScript files from Markdown files containing skill definitions.
|
|
39
|
+
We use a nested `BUILD.gn` file in the `skills/` subdirectory specifically for this purpose. This ensures that GN's `target_gen_dir` points to `gen/front_end/models/ai_assistance/skills/`, placing the generated `.skill.js` files in the same relative structure as their source `.md` files. This allows TypeScript files in the `skills/` directory (like `map.ts`) to import the generated files using relative paths (e.g., `./styling.skill.js`) seamlessly.
|
|
40
|
+
|
|
25
41
|
## Performance specific documentation
|
|
26
42
|
|
|
27
43
|
### `TimelineUtils.AIContext.AgentFocus`
|
|
@@ -2,35 +2,39 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export class StorageItem {
|
|
6
|
+
constructor(
|
|
7
|
+
/**
|
|
8
|
+
* The origin of the top-level primary page target being inspected.
|
|
9
|
+
* Used to restrict AI agent tools from accessing unauthorized pages.
|
|
10
|
+
*/
|
|
11
|
+
readonly primaryTargetOrigin: string,
|
|
12
|
+
/**
|
|
13
|
+
* The origin of the selected storage or cookie item (if any).
|
|
14
|
+
* If no item is selected, this is the same as primaryTargetOrigin.
|
|
15
|
+
*/
|
|
16
|
+
readonly origin: string,
|
|
17
|
+
) {
|
|
18
|
+
}
|
|
10
19
|
}
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
export class DOMStorageItem extends StorageItem {
|
|
22
|
+
constructor(
|
|
23
|
+
primaryTargetOrigin: string,
|
|
24
|
+
origin: string,
|
|
25
|
+
/** The storage key partition identifier used by the browser storage engine. */
|
|
26
|
+
readonly storageKey: string,
|
|
27
|
+
/** The sub-category of DOM storage: 'localStorage' or 'sessionStorage'. */
|
|
28
|
+
readonly type: string,
|
|
29
|
+
/** The optional specific key of the selected item in this storage partition. */
|
|
30
|
+
readonly key?: string,
|
|
31
|
+
) {
|
|
32
|
+
super(primaryTargetOrigin, origin);
|
|
33
|
+
}
|
|
17
34
|
}
|
|
18
35
|
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// If the user selects a row in e.g. the cookies table, the storageType and key
|
|
23
|
-
// will be populated.
|
|
24
|
-
export class StorageItem {
|
|
25
|
-
readonly origin: string;
|
|
26
|
-
readonly storageKey?: string;
|
|
27
|
-
readonly storageType?: 'cookie'|'localStorage'|'sessionStorage';
|
|
28
|
-
readonly key?: string;
|
|
29
|
-
|
|
30
|
-
constructor(data: StorageItemData) {
|
|
31
|
-
this.origin = data.origin;
|
|
32
|
-
this.storageKey = data.storageKey;
|
|
33
|
-
this.storageType = data.storageType;
|
|
34
|
-
this.key = data.key;
|
|
36
|
+
export class CookieItem extends StorageItem {
|
|
37
|
+
constructor(primaryTargetOrigin: string, origin: string, readonly name?: string) {
|
|
38
|
+
super(primaryTargetOrigin, origin);
|
|
35
39
|
}
|
|
36
40
|
}
|
|
@@ -93,8 +93,8 @@ export class AccessibilityContext extends ConversationContext<LHModel.ReporterTy
|
|
|
93
93
|
return this.#lh.finalUrl ?? this.#lh.finalDisplayedUrl;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
override
|
|
97
|
-
return
|
|
96
|
+
override getURL(): string {
|
|
97
|
+
return this.#url();
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
override getItem(): LHModel.ReporterTypes.ReportJSON {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as Host from '../../../core/host/host.js';
|
|
6
|
+
import type {UrlString} from '../../../core/platform/DevToolsPath.js';
|
|
6
7
|
import * as Root from '../../../core/root/root.js';
|
|
7
8
|
import type * as SDK from '../../../core/sdk/sdk.js';
|
|
8
9
|
import type * as Protocol from '../../../generated/protocol.js';
|
|
@@ -10,6 +11,7 @@ import type * as LHModel from '../../lighthouse/lighthouse.js';
|
|
|
10
11
|
import type * as TextUtils from '../../text_utils/text_utils.js';
|
|
11
12
|
import type * as Trace from '../../trace/trace.js';
|
|
12
13
|
import type * as Workspace from '../../workspace/workspace.js';
|
|
14
|
+
import {areOriginsEquivalent, extractContextOrigin, isOpaqueOrigin} from '../AiOrigins.js';
|
|
13
15
|
import {debugLog, isStructuredLogEnabled} from '../debug.js';
|
|
14
16
|
|
|
15
17
|
const MAX_SUGGESTION_LENGTH = 200;
|
|
@@ -36,21 +38,6 @@ export const enum ErrorType {
|
|
|
36
38
|
CROSS_ORIGIN = 'cross-origin'
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
/**
|
|
40
|
-
* Returns true if the origin is considered opaque and should be blocked from
|
|
41
|
-
* AI assistance to prevent potential data leakage.
|
|
42
|
-
*
|
|
43
|
-
* @see https://crbug.com/513732588
|
|
44
|
-
*/
|
|
45
|
-
export function isOpaqueOrigin(origin: string): boolean {
|
|
46
|
-
/**
|
|
47
|
-
* Origins starting with 'about' (like about:blank or about:srcdoc) are
|
|
48
|
-
* considered opaque. 'about://' is the sentinel used by DevTools
|
|
49
|
-
* ParsedURL.securityOrigin() for these.
|
|
50
|
-
*/
|
|
51
|
-
return origin === 'null' || origin === 'data:' || origin.startsWith('about') || origin.startsWith('detached');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
41
|
export const enum MultimodalInputType {
|
|
55
42
|
SCREENSHOT = 'screenshot',
|
|
56
43
|
UPLOADED_IMAGE = 'uploaded-image',
|
|
@@ -197,10 +184,14 @@ export interface ConversationSuggestion {
|
|
|
197
184
|
export type ConversationSuggestions = [ConversationSuggestion, ...ConversationSuggestion[]];
|
|
198
185
|
|
|
199
186
|
export abstract class ConversationContext<T> {
|
|
200
|
-
abstract
|
|
187
|
+
abstract getURL(): string;
|
|
201
188
|
abstract getItem(): T;
|
|
202
189
|
abstract getTitle(): string;
|
|
203
190
|
|
|
191
|
+
getOrigin(): string {
|
|
192
|
+
return extractContextOrigin(this.getURL());
|
|
193
|
+
}
|
|
194
|
+
|
|
204
195
|
/**
|
|
205
196
|
* Returns true if this data context (e.g., a DOM node or Network Request) is
|
|
206
197
|
* allowed to be included in a conversation that is locked to the provided
|
|
@@ -214,17 +205,14 @@ export abstract class ConversationContext<T> {
|
|
|
214
205
|
* If undefined, the conversation has not yet been locked to an origin.
|
|
215
206
|
*/
|
|
216
207
|
isOriginAllowed(establishedOrigin: string|undefined): boolean {
|
|
217
|
-
const
|
|
218
|
-
// Opaque origins are never allowed to be used as context.
|
|
219
|
-
if (isOpaqueOrigin(dataOrigin)) {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
208
|
+
const origin = this.getOrigin();
|
|
222
209
|
// If no origin is established yet, this context will be the one to lock the conversation.
|
|
210
|
+
// Opaque origins are never allowed to be used as context.
|
|
223
211
|
if (!establishedOrigin) {
|
|
224
|
-
return
|
|
212
|
+
return !isOpaqueOrigin(origin);
|
|
225
213
|
}
|
|
226
214
|
// Only allow data that matches the origin the conversation is already locked to.
|
|
227
|
-
return
|
|
215
|
+
return areOriginsEquivalent(origin, establishedOrigin);
|
|
228
216
|
}
|
|
229
217
|
|
|
230
218
|
/**
|
|
@@ -321,6 +309,13 @@ export interface SourceFileAiWidget {
|
|
|
321
309
|
};
|
|
322
310
|
}
|
|
323
311
|
|
|
312
|
+
export interface SourceFilesListAiWidget {
|
|
313
|
+
name: 'SOURCE_FILES_LIST';
|
|
314
|
+
data: {
|
|
315
|
+
uiSourceCodes: Workspace.UISourceCode.UISourceCode[],
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
324
319
|
export interface LighthouseReportAiWidget {
|
|
325
320
|
name: 'LIGHTHOUSE_REPORT';
|
|
326
321
|
data: {
|
|
@@ -344,9 +339,21 @@ export interface NetworkRequestGeneralHeadersAiWidget {
|
|
|
344
339
|
};
|
|
345
340
|
}
|
|
346
341
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
342
|
+
// If the line and column are not provided, we assume the whole file was sent to the agent.s
|
|
343
|
+
export interface SourceCodeAiWidget {
|
|
344
|
+
name: 'SOURCE_CODE';
|
|
345
|
+
data: {
|
|
346
|
+
url: UrlString,
|
|
347
|
+
code: string,
|
|
348
|
+
line?: number,
|
|
349
|
+
column?: number,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export type AiWidget =
|
|
354
|
+
ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget|PerformanceTraceAiWidget|
|
|
355
|
+
PerfInsightAiWidget|TimelineRangeSummaryAiWidget|BottomUpTreeAiWidget|SourceFileAiWidget|LighthouseReportAiWidget|
|
|
356
|
+
TimelineEventSummaryAiWidget|NetworkRequestGeneralHeadersAiWidget|SourceCodeAiWidget|SourceFilesListAiWidget;
|
|
350
357
|
|
|
351
358
|
export type FunctionCallHandlerResult<Result> = {
|
|
352
359
|
requiresApproval: true,
|
|
@@ -409,6 +416,13 @@ interface AidaFetchResult {
|
|
|
409
416
|
rpcId?: Host.AidaClient.RpcGlobalId;
|
|
410
417
|
}
|
|
411
418
|
|
|
419
|
+
class CrossOriginError extends Error {
|
|
420
|
+
constructor() {
|
|
421
|
+
super('Cross-origin navigation detected');
|
|
422
|
+
this.name = 'CrossOriginError';
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
412
426
|
/**
|
|
413
427
|
* AiAgent is a base class for implementing an interaction with AIDA
|
|
414
428
|
* that involves one or more requests being sent to AIDA optionally
|
|
@@ -504,6 +518,14 @@ export abstract class AiAgent<T> {
|
|
|
504
518
|
this.#facts.clear();
|
|
505
519
|
}
|
|
506
520
|
|
|
521
|
+
/**
|
|
522
|
+
* Clears any subclass-specific caches. This is called when a run encounters
|
|
523
|
+
* an error (e.g., cross-origin navigation, abort, or execution error) to
|
|
524
|
+
* prevent unvalidated cached data from being replayed in subsequent runs.
|
|
525
|
+
*/
|
|
526
|
+
clearCache(): void {
|
|
527
|
+
}
|
|
528
|
+
|
|
507
529
|
popPendingMultimodalInput(): MultimodalInput|undefined {
|
|
508
530
|
return undefined;
|
|
509
531
|
}
|
|
@@ -778,11 +800,6 @@ export abstract class AiAgent<T> {
|
|
|
778
800
|
},
|
|
779
801
|
);
|
|
780
802
|
|
|
781
|
-
if ('result' in result && result.result === 'BLOCKED_CROSS_ORIGIN') {
|
|
782
|
-
yield this.#createErrorResponse(ErrorType.CROSS_ORIGIN);
|
|
783
|
-
break;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
803
|
if (options.signal?.aborted) {
|
|
787
804
|
yield this.#createErrorResponse(ErrorType.ABORT);
|
|
788
805
|
break;
|
|
@@ -808,6 +825,10 @@ export abstract class AiAgent<T> {
|
|
|
808
825
|
};
|
|
809
826
|
request = this.buildRequest(query, Host.AidaClient.Role.ROLE_UNSPECIFIED);
|
|
810
827
|
} catch (err) {
|
|
828
|
+
if (err instanceof CrossOriginError) {
|
|
829
|
+
yield this.#createErrorResponse(ErrorType.CROSS_ORIGIN);
|
|
830
|
+
break;
|
|
831
|
+
}
|
|
811
832
|
debugLog('Error handling function call', err);
|
|
812
833
|
yield this.#createErrorResponse(ErrorType.UNKNOWN);
|
|
813
834
|
break;
|
|
@@ -876,8 +897,19 @@ export abstract class AiAgent<T> {
|
|
|
876
897
|
}
|
|
877
898
|
}
|
|
878
899
|
|
|
900
|
+
const isOriginBlocked = (): boolean => {
|
|
901
|
+
const allowedOriginResult = this.#allowedOrigin?.();
|
|
902
|
+
return Boolean(allowedOriginResult && 'blocked' in allowedOriginResult);
|
|
903
|
+
};
|
|
904
|
+
|
|
879
905
|
let result = await call.handler(args, options);
|
|
880
906
|
|
|
907
|
+
// Check 1: After first handler execution.
|
|
908
|
+
// Navigation could have occurred during the async handler execution.
|
|
909
|
+
if (isOriginBlocked()) {
|
|
910
|
+
throw new CrossOriginError();
|
|
911
|
+
}
|
|
912
|
+
|
|
881
913
|
if ('requiresApproval' in result) {
|
|
882
914
|
if (code) {
|
|
883
915
|
yield {
|
|
@@ -926,17 +958,21 @@ export abstract class AiAgent<T> {
|
|
|
926
958
|
// Re-check allowed origin after the approval await to prevent a TOCTOU (Time-of-Check
|
|
927
959
|
// to Time-of-Use) race condition where the page might have navigated cross-origin
|
|
928
960
|
// while the user was confirming the action.
|
|
929
|
-
|
|
930
|
-
if (
|
|
931
|
-
|
|
932
|
-
result: 'BLOCKED_CROSS_ORIGIN',
|
|
933
|
-
};
|
|
961
|
+
// Check 2: After waiting for user approval.
|
|
962
|
+
if (isOriginBlocked()) {
|
|
963
|
+
throw new CrossOriginError();
|
|
934
964
|
}
|
|
935
965
|
|
|
936
966
|
result = await call.handler(args, {
|
|
937
967
|
...options,
|
|
938
968
|
approved: true,
|
|
939
969
|
});
|
|
970
|
+
|
|
971
|
+
// Check 3: After second handler execution (approved run).
|
|
972
|
+
// Navigation could have occurred during the async execution of the approved action.
|
|
973
|
+
if (isOriginBlocked()) {
|
|
974
|
+
throw new CrossOriginError();
|
|
975
|
+
}
|
|
940
976
|
}
|
|
941
977
|
|
|
942
978
|
if ('result' in result) {
|
|
@@ -1012,6 +1048,7 @@ export abstract class AiAgent<T> {
|
|
|
1012
1048
|
|
|
1013
1049
|
#createErrorResponse(error: ErrorType): ResponseData {
|
|
1014
1050
|
this.#removeLastRunParts();
|
|
1051
|
+
this.clearCache();
|
|
1015
1052
|
if (error !== ErrorType.ABORT) {
|
|
1016
1053
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceError);
|
|
1017
1054
|
}
|
|
@@ -91,7 +91,7 @@ Content:
|
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
"name": "performanceRecordAndReload",
|
|
94
|
-
"description": "Records a new performance trace. Use this to measure and debug performance metrics and Core Web Vitals like Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS).",
|
|
94
|
+
"description": "Records a new performance trace. Use this to measure, analyze, and debug page performance, general performance issues, performance metrics, and Core Web Vitals like Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS).",
|
|
95
95
|
"parameters": {
|
|
96
96
|
"type": 6,
|
|
97
97
|
"description": "",
|
|
@@ -102,7 +102,7 @@ Content:
|
|
|
102
102
|
},
|
|
103
103
|
{
|
|
104
104
|
"name": "runLighthouseAudits",
|
|
105
|
-
"description": "Records a Lighthouse audit on the current page. Use this to debug accessibility, SEO, and best practices. (For performance
|
|
105
|
+
"description": "Records a Lighthouse audit on the current page. Use this to debug accessibility, SEO, and best practices. (For any performance-related questions or performance issues, do NOT use this; use performanceRecordAndReload instead).",
|
|
106
106
|
"parameters": {
|
|
107
107
|
"type": 6,
|
|
108
108
|
"description": "",
|
|
@@ -12,6 +12,7 @@ import * as Logs from '../../logs/logs.js';
|
|
|
12
12
|
import * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
|
|
13
13
|
import type * as Trace from '../../trace/trace.js';
|
|
14
14
|
import * as Workspace from '../../workspace/workspace.js';
|
|
15
|
+
import {isOpaqueOrigin} from '../AiOrigins.js';
|
|
15
16
|
import {debugLog} from '../debug.js';
|
|
16
17
|
|
|
17
18
|
import {AccessibilityContext} from './AccessibilityAgent.js';
|
|
@@ -20,7 +21,6 @@ import {
|
|
|
20
21
|
AiAgent,
|
|
21
22
|
type AllowedOriginResult,
|
|
22
23
|
type ContextResponse,
|
|
23
|
-
isOpaqueOrigin,
|
|
24
24
|
type RequestOptions,
|
|
25
25
|
} from './AiAgent.js';
|
|
26
26
|
import {FileContext} from './FileAgent.js';
|
|
@@ -35,19 +35,26 @@ const lockedString = i18n.i18n.lockedString;
|
|
|
35
35
|
* chrome_preambles.gcl). Sync local changes with the server-side.
|
|
36
36
|
*/
|
|
37
37
|
const preamble = `
|
|
38
|
-
You are
|
|
39
|
-
|
|
38
|
+
You are an advanced Web Development Assistant and AI routing agent integrated into Chrome DevTools. Your tone is educational, supportive, and technically precise. You aim to help developers of all levels, prioritizing teaching web concepts as the primary entry point for any solution.
|
|
39
|
+
|
|
40
|
+
Your role is to understand the user's query, identify the appropriate specialized agent to handle it, and select the relevant context from the page to assist that agent.
|
|
41
|
+
|
|
42
|
+
# Workflow
|
|
43
|
+
1. **Analyze**: Understand the user's intent and what they are trying to achieve.
|
|
44
|
+
2. **Classify**: Determine which specialized agent is best suited for the task (e.g., StylingAgent for CSS/styling issues, NetworkAgent for network requests, FileAgent for source files, PerformanceAgent for performance details, AccessibilityAgent for accessibility reports, or StorageAgent for storage issues).
|
|
45
|
+
3. **Gather Context**: Identify what information the specialized agent will need. Proactively use your tools to find and select this context (e.g., finding the relevant DOM node, network request, file, or performance trace). Always try to select a single specific context before answering the question.
|
|
46
|
+
4. **Delegate**: Once context is selected, hand over to the specialized agent. If you are unable to delegate or gather more information, provide a comprehensive guide on how to fix the issue using Chrome DevTools, explaining how and why, or suggest any panel/flow that may help.
|
|
40
47
|
|
|
41
48
|
# Considerations
|
|
42
49
|
* Determine what is the domain of the question - styling, network, sources, performance or other part of DevTools.
|
|
43
|
-
* For questions about
|
|
44
|
-
* Proactively try to gather additional data. If a
|
|
45
|
-
* Always try select single specific context before answering the question.
|
|
50
|
+
* For questions about performance (e.g., general performance issues, page speed, performance metrics like LCP, INP, CLS), use performanceRecordAndReload to record a performance trace.
|
|
51
|
+
* Proactively try to gather additional data. If a specific piece of data can be selected, select it.
|
|
52
|
+
* Always try to select a single specific context before answering the question.
|
|
46
53
|
* Avoid making assumptions without sufficient evidence, and always seek further clarification if needed.
|
|
47
54
|
* When presenting solutions, clearly distinguish between the primary cause and contributing factors.
|
|
48
55
|
* Please answer only if you are sure about the answer. Otherwise, explain why you're not able to answer.
|
|
49
56
|
* If you are unable to gather more information provide a comprehensive guide to how to fix the issue using Chrome DevTools and explain how and why.
|
|
50
|
-
* You can suggest any panel or flow in Chrome DevTools that may help the user out
|
|
57
|
+
* You can suggest any panel or flow in Chrome DevTools that may help the user out.
|
|
51
58
|
|
|
52
59
|
# Formatting Guidelines
|
|
53
60
|
* Use Markdown for all code snippets.
|
|
@@ -55,7 +62,7 @@ You aim to help developers of all levels, prioritizing teaching web concepts as
|
|
|
55
62
|
* **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.
|
|
56
63
|
|
|
57
64
|
* **CRITICAL** If a tool returns an empty list, immediately pivot to the next logical tool (e.g., from sources to network).
|
|
58
|
-
* **CRITICAL** Always exhaust all possible
|
|
65
|
+
* **CRITICAL** Always exhaust all possible ways to find and select context from different domains.
|
|
59
66
|
* **CRITICAL** NEVER write full Python programs - you should only write individual statements that invoke a single function from the provided library.
|
|
60
67
|
* **CRITICAL** NEVER output text before a function call. Always do a function call first.
|
|
61
68
|
* **CRITICAL** You are a debugging assistant in DevTools. 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.
|
|
@@ -261,6 +268,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
261
268
|
const origin = allowedOriginResult.origin;
|
|
262
269
|
|
|
263
270
|
const files: Array<{file: string, id: number | undefined}> = [];
|
|
271
|
+
const uiSourceCodes: Workspace.UISourceCode.UISourceCode[] = [];
|
|
264
272
|
for (const file of ContextSelectionAgent.getUISourceCodes()) {
|
|
265
273
|
const fileUrl = file.url();
|
|
266
274
|
const fileOrigin = Common.ParsedURL.ParsedURL.extractOrigin(fileUrl);
|
|
@@ -273,10 +281,17 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
273
281
|
file: file.fullDisplayName(),
|
|
274
282
|
id: ContextSelectionAgent.uiSourceCodeId.get(file),
|
|
275
283
|
});
|
|
284
|
+
uiSourceCodes.push(file);
|
|
276
285
|
}
|
|
277
286
|
|
|
278
287
|
return {
|
|
279
288
|
result: files,
|
|
289
|
+
widgets: [{
|
|
290
|
+
name: 'SOURCE_FILES_LIST',
|
|
291
|
+
data: {
|
|
292
|
+
uiSourceCodes,
|
|
293
|
+
},
|
|
294
|
+
}],
|
|
280
295
|
};
|
|
281
296
|
},
|
|
282
297
|
});
|
|
@@ -342,7 +357,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
342
357
|
|
|
343
358
|
this.declareFunction('performanceRecordAndReload', {
|
|
344
359
|
description:
|
|
345
|
-
'Records a new performance trace. Use this to measure and debug performance metrics and Core Web Vitals like Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS).',
|
|
360
|
+
'Records a new performance trace. Use this to measure, analyze, and debug page performance, general performance issues, performance metrics, and Core Web Vitals like Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS).',
|
|
346
361
|
parameters: {
|
|
347
362
|
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
348
363
|
description: '',
|
|
@@ -379,7 +394,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
379
394
|
|
|
380
395
|
this.declareFunction<{mode: LHSupportedRunMode}>('runLighthouseAudits', {
|
|
381
396
|
description:
|
|
382
|
-
'Records a Lighthouse audit on the current page. Use this to debug accessibility, SEO, and best practices. (For performance
|
|
397
|
+
'Records a Lighthouse audit on the current page. Use this to debug accessibility, SEO, and best practices. (For any performance-related questions or performance issues, do NOT use this; use performanceRecordAndReload instead).',
|
|
383
398
|
parameters: {
|
|
384
399
|
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
385
400
|
description: '',
|
|
@@ -83,8 +83,8 @@ export class FileContext extends ConversationContext<Workspace.UISourceCode.UISo
|
|
|
83
83
|
this.#file = file;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
override
|
|
87
|
-
return
|
|
86
|
+
override getURL(): string {
|
|
87
|
+
return this.#file.url();
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
override getItem(): Workspace.UISourceCode.UISourceCode {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
-
import * as Common from '../../../core/common/common.js';
|
|
6
5
|
import * as Host from '../../../core/host/host.js';
|
|
7
6
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
8
7
|
import * as Root from '../../../core/root/root.js';
|
|
@@ -111,13 +110,13 @@ export class RequestContext extends ConversationContext<SDK.NetworkRequest.Netwo
|
|
|
111
110
|
}
|
|
112
111
|
|
|
113
112
|
/**
|
|
114
|
-
* Note: this is not the literal origin of the network request. This
|
|
113
|
+
* Note: this is not the literal origin of the network request. This URL
|
|
115
114
|
* is used to determine when we should force the user to start a new AI
|
|
116
115
|
* conversation when the context changes. We allow a single AI conversation to
|
|
117
116
|
* inspect all network requests that were made for that given target URL.
|
|
118
117
|
*/
|
|
119
|
-
override
|
|
120
|
-
return
|
|
118
|
+
override getURL(): string {
|
|
119
|
+
return this.#request.documentURL;
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
override getItem(): SDK.NetworkRequest.NetworkRequest {
|