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.
Files changed (67) hide show
  1. package/front_end/core/host/UserMetrics.ts +3 -2
  2. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
  3. package/front_end/core/sdk/NetworkRequest.ts +0 -1
  4. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
  5. package/front_end/generated/SupportedCSSProperties.js +4 -2
  6. package/front_end/models/ai_assistance/AiAgent2.ts +23 -13
  7. package/front_end/models/ai_assistance/README.md +5 -4
  8. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +15 -0
  9. package/front_end/models/ai_assistance/agents/README.md +57 -0
  10. package/front_end/models/ai_assistance/agents/StylingAgent.ts +26 -3
  11. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +9 -12
  12. package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
  13. package/front_end/models/ai_assistance/tools/README.md +45 -0
  14. package/front_end/models/ai_assistance/tools/Tool.ts +74 -11
  15. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
  16. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
  17. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
  18. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +17 -4
  19. package/front_end/models/stack_trace/StackTraceModel.ts +34 -1
  20. package/front_end/models/trace/Styles.ts +29 -7
  21. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
  22. package/front_end/models/trace/helpers/Timing.ts +10 -0
  23. package/front_end/models/trace/types/TraceEvents.ts +22 -2
  24. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -2
  25. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
  26. package/front_end/panels/application/ApplicationPanelSidebar.ts +44 -0
  27. package/front_end/panels/application/ServiceWorkersView.ts +2 -2
  28. package/front_end/panels/application/WebMCPView.ts +0 -1
  29. package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
  30. package/front_end/panels/console/ConsoleView.ts +6 -1
  31. package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
  32. package/front_end/panels/console/SymbolizedErrorWidget.ts +4 -1
  33. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
  34. package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
  35. package/front_end/panels/elements/PropertyRenderer.ts +0 -1
  36. package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
  37. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  38. package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
  39. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
  40. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
  41. package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
  42. package/front_end/panels/network/NetworkLogView.ts +34 -7
  43. package/front_end/panels/profiler/HeapProfileView.ts +0 -1
  44. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
  45. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
  46. package/front_end/panels/settings/components/SyncSection.ts +1 -1
  47. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
  48. package/front_end/panels/timeline/TimelinePanel.ts +7 -0
  49. package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
  50. package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
  51. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
  52. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
  53. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
  54. package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
  55. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
  56. package/front_end/third_party/chromium/README.chromium +1 -1
  57. package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
  58. package/front_end/ui/kit/link/Link.ts +16 -2
  59. package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
  60. package/front_end/ui/legacy/InspectorView.ts +4 -1
  61. package/front_end/ui/legacy/PlusButton.ts +6 -1
  62. package/front_end/ui/legacy/Widget.ts +19 -1
  63. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
  64. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
  65. package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
  66. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  67. 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
- AiAssistanceOpenedFromStoragePanelFloatingButton = 208,
500
- MAX_VALUE = 209,
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 = Boolean(
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) => tool.handler(
183
- args,
184
- {
185
- conversationContext: this.context ?? null,
186
- changeManager: this.#changes,
187
- createExtensionScope: this.#createExtensionScope.bind(this),
188
- execJs: this.#execJs,
189
- getExecutionContextNode: () => this.context instanceof DOMNodeContext ? this.context.getItem() : null,
190
- },
191
- options,
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 currently working on migrating the DevTools AI Assistance from a siloed multi-agent architecture to a unified, skill-based single-agent architecture (`AIAgent2`).
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 extending `BaseTool` that binds argument types (`Args`) to the schema `parameters` and the `handler()` method. This ensures compile-time safety and alignment inside each tool implementation.
47
- - **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 `AiAgent.ts`.
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 => getStylesTool.handler(args, {
195
- conversationContext: this.context ?? null,
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 Tool<ExecuteJavaScriptArgs, unknown> {
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: ToolContext,
110
+ context: BaseToolCapability&PageExecutionCapability&StyleMutationCapability,
108
111
  options?: FunctionHandlerOptions,
109
112
  ): Promise<FunctionCallHandlerResult<unknown>> {
110
- const executionNode = context.getExecutionContextNode?.() ?? null;
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 {type Tool, type ToolArgs, type ToolContext, ToolName} from './Tool.js';
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 Tool<GetStylesArgs, unknown> {
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: ToolContext,
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 activeContext = context.conversationContext;
82
- if (!activeContext || !(activeContext instanceof DOMNodeContext)) {
83
- return {error: 'Error: Could not find the currently selected element.'};
89
+ const target = context.getTarget();
90
+ if (!target) {
91
+ return {error: 'Error: Could not find the inspected page.'};
84
92
  }
85
93
 
86
- const selectedNode = activeContext.getItem();
87
- if (!selectedNode) {
88
- return {error: 'Error: Could not find the currently selected element.'};
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 (activeContext.getOrigin() !== newContext.getOrigin()) {
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
+ ```