chrome-devtools-frontend 1.0.1643855 → 1.0.1645245
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 +3 -2
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
- package/front_end/core/sdk/NetworkRequest.ts +0 -1
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
- package/front_end/generated/SupportedCSSProperties.js +4 -2
- package/front_end/models/ai_assistance/AiAgent2.ts +23 -13
- package/front_end/models/ai_assistance/README.md +5 -4
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +15 -0
- package/front_end/models/ai_assistance/agents/README.md +57 -0
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +26 -3
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +9 -12
- package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
- package/front_end/models/ai_assistance/tools/README.md +45 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +74 -11
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +17 -4
- package/front_end/models/stack_trace/StackTraceModel.ts +34 -1
- package/front_end/models/trace/Styles.ts +29 -7
- package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
- package/front_end/models/trace/helpers/Timing.ts +10 -0
- package/front_end/models/trace/types/TraceEvents.ts +22 -2
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -2
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
- package/front_end/panels/application/ApplicationPanelSidebar.ts +44 -0
- package/front_end/panels/application/ServiceWorkersView.ts +2 -2
- package/front_end/panels/application/WebMCPView.ts +0 -1
- package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
- package/front_end/panels/console/ConsoleView.ts +6 -1
- package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
- package/front_end/panels/console/SymbolizedErrorWidget.ts +4 -1
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
- package/front_end/panels/elements/PropertyRenderer.ts +0 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
- package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
- package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
- package/front_end/panels/network/NetworkLogView.ts +34 -7
- package/front_end/panels/profiler/HeapProfileView.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
- package/front_end/panels/settings/components/SyncSection.ts +1 -1
- package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
- package/front_end/panels/timeline/TimelinePanel.ts +7 -0
- package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
- package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
- package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
- package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
- package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
- package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
- package/front_end/ui/kit/link/Link.ts +16 -2
- package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
- package/front_end/ui/legacy/InspectorView.ts +4 -1
- package/front_end/ui/legacy/PlusButton.ts +6 -1
- package/front_end/ui/legacy/Widget.ts +19 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
- package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
- package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
- package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
- package/package.json +1 -1
|
@@ -496,8 +496,9 @@ export enum Action {
|
|
|
496
496
|
AiCodeGenerationRequestTriggeredFromSources = 205,
|
|
497
497
|
AiCodeCompletionFreCompletedFromConsole = 206,
|
|
498
498
|
AiCodeCompletionFreCompletedFromSources = 207,
|
|
499
|
-
|
|
500
|
-
|
|
499
|
+
AiAssistanceOpenedFromApplicationPanelFloatingButton = 208,
|
|
500
|
+
AiAssistanceOpenedFromApplicationPanel = 209,
|
|
501
|
+
MAX_VALUE = 210,
|
|
501
502
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
502
503
|
}
|
|
503
504
|
|
|
@@ -561,9 +561,8 @@ export class ColorMatcher extends matcherBase(ColorMatch) {
|
|
|
561
561
|
const colorText = args.length >= 2 ? matching.getComputedTextRange(args[0], args[args.length - 1]) : '';
|
|
562
562
|
// colorText holds the fully substituted parenthesized expression, so colorFunc + colorText is the color
|
|
563
563
|
// function call.
|
|
564
|
-
const isRelativeColorSyntax =
|
|
565
|
-
colorText.match(/^[^)]*\(\W*from\W+/) && !matching.hasUnresolvedSubstitutions(node)
|
|
566
|
-
CSS.supports('color', colorFunc + colorText));
|
|
564
|
+
const isRelativeColorSyntax =
|
|
565
|
+
Boolean(colorText.match(/^[^)]*\(\W*from\W+/) && !matching.hasUnresolvedSubstitutions(node));
|
|
567
566
|
if (!isRelativeColorSyntax) {
|
|
568
567
|
return new ColorMatch(text, node);
|
|
569
568
|
}
|
|
@@ -1920,7 +1920,6 @@ export interface EventTypes {
|
|
|
1920
1920
|
[Events.RESPONSE_HEADERS_CHANGED]: void;
|
|
1921
1921
|
[Events.WEBSOCKET_FRAME_ADDED]: WebSocketFrame;
|
|
1922
1922
|
[Events.DIRECTSOCKET_CHUNK_ADDED]: DirectSocketChunk;
|
|
1923
|
-
[Events.DIRECTSOCKET_CHUNK_ADDED]: DirectSocketChunk;
|
|
1924
1923
|
[Events.EVENT_SOURCE_MESSAGE_ADDED]: EventSourceMessage;
|
|
1925
1924
|
[Events.TRUST_TOKEN_RESULT_ADDED]: void;
|
|
1926
1925
|
}
|
|
@@ -2740,6 +2740,8 @@ export abstract class HeapSnapshot {
|
|
|
2740
2740
|
const nodeAId = baseIds[i];
|
|
2741
2741
|
if (nodeAId < nodeB.id()) {
|
|
2742
2742
|
diff.deletedIndexes.push(baseIndexes[i]);
|
|
2743
|
+
diff.deletedIds.push(nodeAId);
|
|
2744
|
+
diff.deletedSelfSizes.push(baseSelfSizes[i]);
|
|
2743
2745
|
diff.removedCount++;
|
|
2744
2746
|
diff.removedSize += baseSelfSizes[i];
|
|
2745
2747
|
++i;
|
|
@@ -2747,6 +2749,8 @@ export abstract class HeapSnapshot {
|
|
|
2747
2749
|
nodeAId >
|
|
2748
2750
|
nodeB.id()) { // Native nodes(e.g. dom groups) may have ids less than max JS object id in the base snapshot
|
|
2749
2751
|
diff.addedIndexes.push(indexes[j]);
|
|
2752
|
+
diff.addedIds.push(nodeB.id());
|
|
2753
|
+
diff.addedSelfSizes.push(nodeB.selfSize());
|
|
2750
2754
|
diff.addedCount++;
|
|
2751
2755
|
diff.addedSize += nodeB.selfSize();
|
|
2752
2756
|
nodeB.nodeIndex = indexes[++j];
|
|
@@ -2757,12 +2761,16 @@ export abstract class HeapSnapshot {
|
|
|
2757
2761
|
}
|
|
2758
2762
|
while (i < l) {
|
|
2759
2763
|
diff.deletedIndexes.push(baseIndexes[i]);
|
|
2764
|
+
diff.deletedIds.push(baseIds[i]);
|
|
2765
|
+
diff.deletedSelfSizes.push(baseSelfSizes[i]);
|
|
2760
2766
|
diff.removedCount++;
|
|
2761
2767
|
diff.removedSize += baseSelfSizes[i];
|
|
2762
2768
|
++i;
|
|
2763
2769
|
}
|
|
2764
2770
|
while (j < m) {
|
|
2765
2771
|
diff.addedIndexes.push(indexes[j]);
|
|
2772
|
+
diff.addedIds.push(nodeB.id());
|
|
2773
|
+
diff.addedSelfSizes.push(nodeB.selfSize());
|
|
2766
2774
|
diff.addedCount++;
|
|
2767
2775
|
diff.addedSize += nodeB.selfSize();
|
|
2768
2776
|
nodeB.nodeIndex = indexes[++j];
|
|
@@ -2975,6 +2983,35 @@ export abstract class HeapSnapshot {
|
|
|
2975
2983
|
return {paths, limitsReached};
|
|
2976
2984
|
}
|
|
2977
2985
|
|
|
2986
|
+
getDominatorsOf(nodeIndex: number): HeapSnapshotModel.HeapSnapshotModel.DominatorChain {
|
|
2987
|
+
const chain: HeapSnapshotModel.HeapSnapshotModel.DominatorNode[] = [];
|
|
2988
|
+
let currentIndex = nodeIndex;
|
|
2989
|
+
const rootIndex = this.rootNodeIndex;
|
|
2990
|
+
|
|
2991
|
+
while (currentIndex !== undefined) {
|
|
2992
|
+
const node = this.createNode(currentIndex);
|
|
2993
|
+
chain.push({
|
|
2994
|
+
nodeId: node.id(),
|
|
2995
|
+
nodeIndex: currentIndex,
|
|
2996
|
+
nodeName: node.name(),
|
|
2997
|
+
retainedSize: node.retainedSize(),
|
|
2998
|
+
selfSize: node.selfSize(),
|
|
2999
|
+
});
|
|
3000
|
+
|
|
3001
|
+
if (currentIndex === rootIndex) {
|
|
3002
|
+
break;
|
|
3003
|
+
}
|
|
3004
|
+
|
|
3005
|
+
const nextIndex = node.dominatorIndex();
|
|
3006
|
+
if (nextIndex === currentIndex) {
|
|
3007
|
+
break;
|
|
3008
|
+
}
|
|
3009
|
+
currentIndex = nextIndex;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
return chain;
|
|
3013
|
+
}
|
|
3014
|
+
|
|
2978
3015
|
createAddedNodesProvider(baseSnapshotId: number, classKey: string): HeapSnapshotNodesProvider {
|
|
2979
3016
|
const snapshotDiff = this.#snapshotDiffs[baseSnapshotId];
|
|
2980
3017
|
const diffForClass = snapshotDiff[classKey];
|
|
@@ -3860,7 +3860,8 @@ export const generatedProperties = [
|
|
|
3860
3860
|
{
|
|
3861
3861
|
"keywords": [
|
|
3862
3862
|
"auto",
|
|
3863
|
-
"none"
|
|
3863
|
+
"none",
|
|
3864
|
+
"normal"
|
|
3864
3865
|
],
|
|
3865
3866
|
"name": "position-anchor"
|
|
3866
3867
|
},
|
|
@@ -6987,7 +6988,8 @@ export const generatedPropertyValues = {
|
|
|
6987
6988
|
"position-anchor": {
|
|
6988
6989
|
"values": [
|
|
6989
6990
|
"auto",
|
|
6990
|
-
"none"
|
|
6991
|
+
"none",
|
|
6992
|
+
"normal"
|
|
6991
6993
|
]
|
|
6992
6994
|
},
|
|
6993
6995
|
"position-area": {
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as Host from '../../core/host/host.js';
|
|
6
|
+
import * as SDK from '../../core/sdk/sdk.js';
|
|
6
7
|
|
|
7
8
|
import {
|
|
8
9
|
AiAgent,
|
|
10
|
+
type AllowedOriginResult,
|
|
9
11
|
type ContextResponse,
|
|
10
12
|
type ConversationContext,
|
|
11
13
|
type MultimodalInputType,
|
|
@@ -19,7 +21,7 @@ import {debugLog} from './debug.js';
|
|
|
19
21
|
import {ExtensionScope} from './ExtensionScope.js';
|
|
20
22
|
import type {Skill, SkillName} from './skills/Skill.js';
|
|
21
23
|
import {SKILLS} from './skills/SkillRegistry.js';
|
|
22
|
-
import type {Tool} from './tools/Tool.js';
|
|
24
|
+
import type {AllToolsContext, Tool, ToolArgs} from './tools/Tool.js';
|
|
23
25
|
import {ToolRegistry} from './tools/ToolRegistry.js';
|
|
24
26
|
|
|
25
27
|
const SKILL_DISPLAY_NAMES: Record<SkillName, string> = {
|
|
@@ -35,6 +37,7 @@ export class AiAgent2 extends AiAgent<unknown> {
|
|
|
35
37
|
#skillsInjected = false;
|
|
36
38
|
#changes = new ChangeManager();
|
|
37
39
|
#execJs: typeof executeJsCode;
|
|
40
|
+
readonly #allowedOrigin?: () => AllowedOriginResult;
|
|
38
41
|
|
|
39
42
|
get options(): RequestOptions {
|
|
40
43
|
return {};
|
|
@@ -46,6 +49,7 @@ export class AiAgent2 extends AiAgent<unknown> {
|
|
|
46
49
|
constructor(opts: ExecuteJsAgentOptions) {
|
|
47
50
|
super(opts);
|
|
48
51
|
this.#execJs = opts.execJs ?? executeJsCode;
|
|
52
|
+
this.#allowedOrigin = opts.allowedOrigin;
|
|
49
53
|
this.#declaredTools.add('learnSkills');
|
|
50
54
|
const skillsList = Object.keys(SKILLS).join(', ');
|
|
51
55
|
this.declareFunction<{skills: SkillName[]}>('learnSkills', {
|
|
@@ -169,7 +173,7 @@ User query: ${enhancedQuery}`;
|
|
|
169
173
|
* Declares a tool to be available to the agent model, verifying first that
|
|
170
174
|
* it hasn't already been declared to prevent duplicate declaration errors.
|
|
171
175
|
*/
|
|
172
|
-
#declareTool(tool: Tool): void {
|
|
176
|
+
#declareTool(tool: Tool<ToolArgs, unknown, AllToolsContext>): void {
|
|
173
177
|
if (this.#declaredTools.has(tool.name)) {
|
|
174
178
|
debugLog(`AiAgent2: Tool ${tool.name} is already declared`);
|
|
175
179
|
return;
|
|
@@ -179,20 +183,26 @@ User query: ${enhancedQuery}`;
|
|
|
179
183
|
description: tool.description,
|
|
180
184
|
parameters: tool.parameters,
|
|
181
185
|
displayInfoFromArgs: tool.displayInfoFromArgs,
|
|
182
|
-
handler: (args, options) =>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
186
|
+
handler: (args, options) => {
|
|
187
|
+
const context: AllToolsContext = {
|
|
188
|
+
conversationContext: this.context ?? null,
|
|
189
|
+
changeManager: this.#changes,
|
|
190
|
+
createExtensionScope: this.#createExtensionScope.bind(this),
|
|
191
|
+
execJs: this.#execJs,
|
|
192
|
+
getExecutionContextNode: () => this.context instanceof DOMNodeContext ? this.context.getItem() : null,
|
|
193
|
+
getTarget: () => SDK.TargetManager.TargetManager.instance().primaryPageTarget(),
|
|
194
|
+
getEstablishedOrigin: () => this.#getConversationOrigin(),
|
|
195
|
+
};
|
|
196
|
+
return tool.handler(args, context, options);
|
|
197
|
+
},
|
|
193
198
|
});
|
|
194
199
|
}
|
|
195
200
|
|
|
201
|
+
#getConversationOrigin(): string|undefined {
|
|
202
|
+
const allowed = this.#allowedOrigin?.();
|
|
203
|
+
return allowed && 'origin' in allowed ? allowed.origin : undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
196
206
|
get activeSkills(): Set<SkillName> {
|
|
197
207
|
return this.#activeSkills;
|
|
198
208
|
}
|
|
@@ -24,7 +24,7 @@ To deal with the work to take an object that is the AI agent's context and turn
|
|
|
24
24
|
|
|
25
25
|
## Future Architecture (V2) - WIP
|
|
26
26
|
|
|
27
|
-
We are
|
|
27
|
+
We are migrating the DevTools AI Assistance from a siloed multi-agent architecture to a unified, skill-based single-agent architecture (`AIAgent2`).
|
|
28
28
|
|
|
29
29
|
In this new architecture:
|
|
30
30
|
- A single agent (`AIAgent2`) handles multiple domains.
|
|
@@ -36,15 +36,16 @@ This work is currently in progress and behind a feature flag.
|
|
|
36
36
|
### Skills Build System
|
|
37
37
|
|
|
38
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.
|
|
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. See the [Skills README](skills/README.md) for full details.
|
|
40
40
|
|
|
41
41
|
### Tools and ToolRegistry
|
|
42
42
|
|
|
43
43
|
To support skills requiring execution of code or fetching page state (like computed styles), the architecture defines **Tools** in the `tools/` directory.
|
|
44
44
|
|
|
45
45
|
- **BaseTool**: A non-generic base interface capturing tool metadata (`name`, `description`, `parameters`). This acts as the type-erased representation for generic registry storage and fallback string lookups.
|
|
46
|
-
- **Tool**: A generic interface
|
|
47
|
-
- **
|
|
46
|
+
- **Tool**: A generic interface parameterized by `<Args, ReturnType, ContextType>` that binds parameter argument types and handler execution to strict contracts. `ContextType` defaults to `BaseToolCapability`, ensuring that each tool explicitly requests only the dependencies it requires.
|
|
47
|
+
- **Capability Contexts**: Instead of passing a monolithic grab-bag context to all tools, dependencies are broken into narrow capability interfaces (e.g. `PageExecutionCapability`, `StyleMutationCapability`, `TargetCapability`, `OriginLockCapability`). Tools declare their required dependencies by intersecting these interfaces on their generic `ContextType` definition. The caller/Agent fulfills the complete capability context (`AllToolsContext`), guaranteeing 100% compile-time type safety for dependencies without runtime checks.
|
|
48
|
+
- **ToolRegistry**: A static registry (`ToolRegistry`) storing instantiated tools. It uses TypeScript function overloading and generic lookups (`static get<K extends keyof typeof TOOLS>(name: K): typeof TOOLS[K]`) to return the precise class type of each tool, preventing type-erasure and escape-hatches (such as `any` or `as unknown` type assertions) at integration points like `AiAgent2.ts`. See the [Tools README](tools/README.md) for authoring instructions.
|
|
48
49
|
|
|
49
50
|
## Performance specific documentation
|
|
50
51
|
|
|
@@ -751,11 +751,26 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
|
|
|
751
751
|
yield* super.run(initialQuery, options);
|
|
752
752
|
}
|
|
753
753
|
|
|
754
|
+
/**
|
|
755
|
+
* Clears performance-agent-specific caches and state.
|
|
756
|
+
* This is called when the conversation needs to be reset (e.g. on navigation)
|
|
757
|
+
* to prevent stale formatters, trace facts, or selection contexts from leaking
|
|
758
|
+
* into subsequent runs.
|
|
759
|
+
*/
|
|
754
760
|
override clearCache(): void {
|
|
761
|
+
super.clearCache();
|
|
755
762
|
// Clear the function call cache to prevent stashed tool execution results
|
|
756
763
|
// (which might contain cross-origin resource content fetched before navigation
|
|
757
764
|
// was detected) from being replayed as facts in subsequent runs.
|
|
758
765
|
this.#functionCallCacheForFocus.clear();
|
|
766
|
+
// Reset the formatter and trace facts so they are recreated with the
|
|
767
|
+
// correct target and origin on the next execution.
|
|
768
|
+
this.#formatter = null;
|
|
769
|
+
this.#traceFacts = [];
|
|
770
|
+
this.#lastEventForEnhancedQuery = undefined;
|
|
771
|
+
this.#lastInsightForEnhancedQuery = undefined;
|
|
772
|
+
this.#additionalSelectionsForDisclosure = [];
|
|
773
|
+
this.#callTreeContextSet = new WeakSet();
|
|
759
774
|
}
|
|
760
775
|
|
|
761
776
|
#createFactForTraceSummary(): void {
|
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
This directory contains the implementations of various AI agents used in the AI Assistance panel in Chrome DevTools.
|
|
4
4
|
|
|
5
|
+
## Agent Lifecycle & State Management
|
|
6
|
+
|
|
7
|
+
AI Agents can maintain internal stateful variables to optimize performance or store context (e.g., stashed tool results, formatters, or instruction flags). However, this state must be carefully managed to prevent security leaks (such as cross-origin data leaks after page navigation) or stale results.
|
|
8
|
+
|
|
9
|
+
### Cache Clearing (`clearCache()`)
|
|
10
|
+
|
|
11
|
+
The `AiAgent` base class defines a `clearCache()` method. This method is automatically invoked by the system when:
|
|
12
|
+
- An execution error occurs.
|
|
13
|
+
- The user aborts the execution.
|
|
14
|
+
- A cross-origin navigation is detected (via the top-level origin blocking mechanism).
|
|
15
|
+
|
|
16
|
+
#### Overriding `clearCache()` in Subclasses
|
|
17
|
+
|
|
18
|
+
If you introduce new stateful member variables in an `AiAgent` subclass, you **must** override `clearCache()` to reset them:
|
|
19
|
+
|
|
20
|
+
1. Always call `super.clearCache()` to ensure base class cleanup is executed.
|
|
21
|
+
2. Reset any subclass-specific state (e.g., setting formatters to `null`, clearing lists, resetting boolean flags).
|
|
22
|
+
|
|
23
|
+
Example from `PerformanceAgent`:
|
|
24
|
+
```typescript
|
|
25
|
+
override clearCache(): void {
|
|
26
|
+
super.clearCache();
|
|
27
|
+
this.#functionCallCacheForFocus.clear();
|
|
28
|
+
this.#formatter = null;
|
|
29
|
+
this.#traceFacts = [];
|
|
30
|
+
// ... reset other stateful fields
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
5
34
|
## Performance Agent
|
|
6
35
|
|
|
7
36
|
The `PerformanceAgent` analyzes performance traces. This documentation details the specific data provided to the agent and the data it can retrieve via functions.
|
|
@@ -70,3 +99,31 @@ The agent can request additional data by calling functions. Here is the data the
|
|
|
70
99
|
#### `selectEventByKey`
|
|
71
100
|
- **Arguments**: `eventKey` (string)
|
|
72
101
|
- **Data Returned to Agent**: Confirmation of success or an error message. (Note: This function also modifies the DevTools UI by selecting the event in the flamechart).
|
|
102
|
+
|
|
103
|
+
## Styling Agent
|
|
104
|
+
|
|
105
|
+
The `StylingAgent` assists with CSS styling and layout questions. It can interact with the page to inspect styles, execute Javascript, and optionally emulate devices or vision deficiencies.
|
|
106
|
+
|
|
107
|
+
### Initial Data Provided to the Agent
|
|
108
|
+
|
|
109
|
+
The agent is initialized with the selected DOM node context.
|
|
110
|
+
|
|
111
|
+
### Data Retrieval & Action Functions (Tools)
|
|
112
|
+
|
|
113
|
+
The agent can call the following functions to retrieve more details or perform actions:
|
|
114
|
+
|
|
115
|
+
#### `getStyles`
|
|
116
|
+
- **Arguments**: None (it uses the currently selected DOM node).
|
|
117
|
+
- **Data Returned to Agent**: Computed styles and authored styles (matching rules, active stylesheets, etc.) for the selected node.
|
|
118
|
+
|
|
119
|
+
#### `executeJavaScript`
|
|
120
|
+
- **Arguments**: `query` (string)
|
|
121
|
+
- **Data Returned to Agent**: The result of executing the JavaScript query in the context of the page.
|
|
122
|
+
|
|
123
|
+
#### `addElementAnnotation`
|
|
124
|
+
- **Arguments**: `elementId` (string), `annotationMessage` (string)
|
|
125
|
+
- **Data Returned to Agent**: Confirmation of success or an error message. (Note: This function also modifies the DevTools UI by adding a visual annotation to the node).
|
|
126
|
+
|
|
127
|
+
#### `activateDeviceEmulation`
|
|
128
|
+
- **Arguments**: `deviceName` (string), `visionDeficiency` (string, optional)
|
|
129
|
+
- **Data Returned to Agent**: Confirmation of success or an error message. (Note: This function modifies the DevTools UI by enabling device and/or vision deficiency emulation).
|
|
@@ -191,9 +191,18 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
|
|
|
191
191
|
description: getStylesTool.description,
|
|
192
192
|
parameters: getStylesTool.parameters,
|
|
193
193
|
displayInfoFromArgs: getStylesTool.displayInfoFromArgs,
|
|
194
|
-
handler: args =>
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
handler: async args => {
|
|
195
|
+
const context = this.context;
|
|
196
|
+
if (!context) {
|
|
197
|
+
return {error: 'Error: Could not find the currently selected element.'};
|
|
198
|
+
}
|
|
199
|
+
return await getStylesTool.handler(args, {
|
|
200
|
+
conversationContext: context,
|
|
201
|
+
getTarget: () =>
|
|
202
|
+
SDK.TargetManager.TargetManager.instance().primaryPageTarget() ?? context.getItem().domModel().target(),
|
|
203
|
+
getEstablishedOrigin: () => context.getOrigin(),
|
|
204
|
+
});
|
|
205
|
+
},
|
|
197
206
|
});
|
|
198
207
|
|
|
199
208
|
const executeJsTool = ToolRegistry.get(ToolName.EXECUTE_JAVASCRIPT);
|
|
@@ -281,6 +290,20 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
|
|
|
281
290
|
});
|
|
282
291
|
}
|
|
283
292
|
|
|
293
|
+
/**
|
|
294
|
+
* Clears styling-agent-specific caches and state.
|
|
295
|
+
* Resets cached emulation data (screenshots, accessibility tree) and the
|
|
296
|
+
* instructions flag to ensure they are re-evaluated in subsequent queries.
|
|
297
|
+
*/
|
|
298
|
+
override clearCache(): void {
|
|
299
|
+
super.clearCache();
|
|
300
|
+
// Reset emulation state so that subsequent queries will re-initialize
|
|
301
|
+
// emulation details and fetch fresh data.
|
|
302
|
+
this.#greenDevEmulationScreenshot = null;
|
|
303
|
+
this.#greenDevEmulationAxTree = null;
|
|
304
|
+
this.#hasAddedEmulationInstructions = false;
|
|
305
|
+
}
|
|
306
|
+
|
|
284
307
|
override preambleFeatures(): string[] {
|
|
285
308
|
return ['function_calling'];
|
|
286
309
|
}
|
|
@@ -8,9 +8,11 @@ import type {FunctionCallHandlerResult, FunctionHandlerOptions,} from '../agents
|
|
|
8
8
|
import {JavascriptExecutor} from '../agents/ExecuteJavascript.js';
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
+
type BaseToolCapability,
|
|
12
|
+
type PageExecutionCapability,
|
|
13
|
+
type StyleMutationCapability,
|
|
11
14
|
type Tool,
|
|
12
15
|
type ToolArgs,
|
|
13
|
-
type ToolContext,
|
|
14
16
|
ToolName,
|
|
15
17
|
} from './Tool.js';
|
|
16
18
|
|
|
@@ -20,7 +22,8 @@ export interface ExecuteJavaScriptArgs extends ToolArgs {
|
|
|
20
22
|
title: string;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
export class ExecuteJavaScriptTool implements
|
|
25
|
+
export class ExecuteJavaScriptTool implements
|
|
26
|
+
Tool<ExecuteJavaScriptArgs, unknown, BaseToolCapability&PageExecutionCapability&StyleMutationCapability> {
|
|
24
27
|
readonly name = ToolName.EXECUTE_JAVASCRIPT;
|
|
25
28
|
|
|
26
29
|
readonly description =
|
|
@@ -104,10 +107,10 @@ const data = {
|
|
|
104
107
|
|
|
105
108
|
async handler(
|
|
106
109
|
params: ExecuteJavaScriptArgs,
|
|
107
|
-
context:
|
|
110
|
+
context: BaseToolCapability&PageExecutionCapability&StyleMutationCapability,
|
|
108
111
|
options?: FunctionHandlerOptions,
|
|
109
112
|
): Promise<FunctionCallHandlerResult<unknown>> {
|
|
110
|
-
const executionNode = context.getExecutionContextNode
|
|
113
|
+
const executionNode = context.getExecutionContextNode();
|
|
111
114
|
if (!executionNode) {
|
|
112
115
|
return {error: 'Error: Could not find the context node for execution.'};
|
|
113
116
|
}
|
|
@@ -115,17 +118,11 @@ const data = {
|
|
|
115
118
|
const executionMode = Root.Runtime.hostConfig.devToolsFreestyler?.executionMode ??
|
|
116
119
|
Root.Runtime.HostConfigFreestylerExecutionMode.ALL_SCRIPTS;
|
|
117
120
|
|
|
118
|
-
const changes = context.changeManager;
|
|
119
|
-
const createExtensionScope = context.createExtensionScope;
|
|
120
|
-
if (!changes || !createExtensionScope) {
|
|
121
|
-
return {error: 'Internal Error: Required change manager or extension scope creator is missing.'};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
121
|
const executor = new JavascriptExecutor({
|
|
125
122
|
executionMode,
|
|
126
123
|
getContextNode: () => executionNode,
|
|
127
|
-
createExtensionScope,
|
|
128
|
-
changes,
|
|
124
|
+
createExtensionScope: context.createExtensionScope,
|
|
125
|
+
changes: context.changeManager,
|
|
129
126
|
},
|
|
130
127
|
context.execJs);
|
|
131
128
|
|
|
@@ -9,7 +9,14 @@ import type {ComputedStyleAiWidget, FunctionCallHandlerResult, FunctionHandlerOp
|
|
|
9
9
|
import {DOMNodeContext} from '../contexts/DOMNodeContext.js';
|
|
10
10
|
import {debugLog} from '../debug.js';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
type BaseToolCapability,
|
|
14
|
+
type OriginLockCapability,
|
|
15
|
+
type TargetCapability,
|
|
16
|
+
type Tool,
|
|
17
|
+
type ToolArgs,
|
|
18
|
+
ToolName,
|
|
19
|
+
} from './Tool.js';
|
|
13
20
|
|
|
14
21
|
export interface GetStylesArgs extends ToolArgs {
|
|
15
22
|
elements: number[];
|
|
@@ -17,7 +24,8 @@ export interface GetStylesArgs extends ToolArgs {
|
|
|
17
24
|
explanation: string;
|
|
18
25
|
}
|
|
19
26
|
|
|
20
|
-
export class GetStylesTool implements
|
|
27
|
+
export class GetStylesTool implements
|
|
28
|
+
Tool<GetStylesArgs, unknown, BaseToolCapability&TargetCapability&OriginLockCapability> {
|
|
21
29
|
readonly name = ToolName.GET_STYLES;
|
|
22
30
|
readonly description =
|
|
23
31
|
`Get computed and source styles for one or multiple elements on the inspected page for multiple elements at once by uid.
|
|
@@ -71,34 +79,33 @@ export class GetStylesTool implements Tool<GetStylesArgs, unknown> {
|
|
|
71
79
|
|
|
72
80
|
async handler(
|
|
73
81
|
params: GetStylesArgs,
|
|
74
|
-
context:
|
|
82
|
+
context: BaseToolCapability&TargetCapability&OriginLockCapability,
|
|
75
83
|
_options?: FunctionHandlerOptions,
|
|
76
84
|
): Promise<FunctionCallHandlerResult<unknown>> {
|
|
77
85
|
const widgets: ComputedStyleAiWidget[] = [];
|
|
78
86
|
const result:
|
|
79
87
|
Record<string, {computed: Record<string, string|undefined>, authored: Record<string, string|undefined>}> = {};
|
|
80
88
|
|
|
81
|
-
const
|
|
82
|
-
if (!
|
|
83
|
-
return {error: 'Error: Could not find the
|
|
89
|
+
const target = context.getTarget();
|
|
90
|
+
if (!target) {
|
|
91
|
+
return {error: 'Error: Could not find the inspected page.'};
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
const
|
|
87
|
-
if (!
|
|
88
|
-
return {error: 'Error:
|
|
94
|
+
const establishedOrigin = context.getEstablishedOrigin();
|
|
95
|
+
if (!establishedOrigin) {
|
|
96
|
+
return {error: 'Error: Origin lock is not established.'};
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
for (const uid of params.elements) {
|
|
92
100
|
result[uid] = {computed: {}, authored: {}};
|
|
93
101
|
debugLog(`Action to execute: uid=${uid}`);
|
|
94
|
-
const node =
|
|
95
|
-
new SDK.DOMModel.DeferredDOMNode(selectedNode.domModel().target(), uid as Protocol.DOM.BackendNodeId);
|
|
102
|
+
const node = new SDK.DOMModel.DeferredDOMNode(target, uid as Protocol.DOM.BackendNodeId);
|
|
96
103
|
const resolved = await node.resolvePromise();
|
|
97
104
|
if (!resolved) {
|
|
98
105
|
return {error: 'Error: Could not find the element with uid=' + uid};
|
|
99
106
|
}
|
|
100
107
|
const newContext = new DOMNodeContext(resolved);
|
|
101
|
-
if (
|
|
108
|
+
if (establishedOrigin !== newContext.getOrigin()) {
|
|
102
109
|
return {error: 'Error: Node does not belong to the current origin.'};
|
|
103
110
|
}
|
|
104
111
|
const styles = await resolved.domModel().cssModel().getComputedStyle(resolved.id);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# AI Assistant Tools Architecture
|
|
2
|
+
|
|
3
|
+
This directory defines the **Tools** used by `AiAgent2` and other AI agents to execute code, read page state, or apply mutations to the inspected page.
|
|
4
|
+
|
|
5
|
+
## Core Concepts
|
|
6
|
+
|
|
7
|
+
The architecture relies on three primary concepts to guarantee 100% compile-time type safety for tools:
|
|
8
|
+
1. **BaseTool**: An interface capturing non-generic tool metadata (e.g. `name`, `description`, `parameters`). This allows the agent and DevTools UI to generically store, register, and describe functions to the AIDA client without needing to know their specific types at runtime.
|
|
9
|
+
2. **Capability Interfaces**: Narrow TypeScript interfaces wrapping individual DevTools dependencies (e.g., ChangeManager, executeJsCode). This decouples tools from monolithic agent environments, making them reusable and easier to unit test.
|
|
10
|
+
3. **Tool**: A generic TypeScript interface (`Tool<Args, ReturnType, ContextType>`) binding the execution handler to specific capability requirements. This enforces compile-time safety, ensuring that a tool handler is only invoked by an agent that fulfills all the required capabilities.
|
|
11
|
+
|
|
12
|
+
## Capability Interfaces
|
|
13
|
+
|
|
14
|
+
Instead of passing a monolithic "grab-bag" context object to all tool handlers, DevTools AI tools declare exactly the capabilities they require. Available capability interfaces defined in `Tool.ts` include:
|
|
15
|
+
|
|
16
|
+
- `BaseToolCapability`: Base interface providing access to the top-level conversation context step.
|
|
17
|
+
- `PageExecutionCapability`: For tools executing JavaScript code on the inspected page.
|
|
18
|
+
- `StyleMutationCapability`: For tools managing and applying style mutations via a `ChangeManager`.
|
|
19
|
+
- `TargetCapability`: For tools requiring access to the page's current SDK `Target`.
|
|
20
|
+
- `OriginLockCapability`: For tools enforcing cross-origin security via origin locks.
|
|
21
|
+
|
|
22
|
+
### Unified Context
|
|
23
|
+
|
|
24
|
+
The Agent (e.g., `AiAgent2`) builds the complete dependency union (`AllToolsContext`) and fulfills all capabilities. When the handler is invoked, TypeScript validates that the provided context matches the intersection of capabilities requested by that tool.
|
|
25
|
+
|
|
26
|
+
## Authoring a New Tool
|
|
27
|
+
|
|
28
|
+
To author a new tool:
|
|
29
|
+
|
|
30
|
+
1. **Define the Args interface**: Extend `ToolArgs` to represent the JSON parameter schema expected by the LLM.
|
|
31
|
+
2. **Declare Capability Dependencies**: Determine which capabilities your tool handler needs. Intersect them to define the tool's `ContextType`.
|
|
32
|
+
3. **Implement the `Tool` interface**: Pass the args interface, return type, and intersected ContextType to the generic parameters of `Tool`.
|
|
33
|
+
4. **Instantiate in `ToolRegistry.ts`**: Add the instantiated tool class to the static `TOOLS` registry.
|
|
34
|
+
|
|
35
|
+
See concrete, production examples of capability-based tools in this directory:
|
|
36
|
+
- [ExecuteJavaScript.ts](ExecuteJavaScript.ts): Demonstrates use of `PageExecutionCapability` and `StyleMutationCapability`.
|
|
37
|
+
- [GetStyles.ts](GetStyles.ts): Demonstrates use of `TargetCapability` and `OriginLockCapability`.
|
|
38
|
+
|
|
39
|
+
## ToolRegistry Lookup
|
|
40
|
+
|
|
41
|
+
To retrieve a tool from the registry with 100% type safety, call `ToolRegistry.get()` with the literal `ToolName` key:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
const getStylesTool = ToolRegistry.get(ToolName.GET_STYLES); // Returns GetStylesTool class type
|
|
45
|
+
```
|