chrome-devtools-frontend 1.0.1643099 → 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 (99) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/extension-api/ExtensionAPI.d.ts +83 -12
  3. package/front_end/core/host/UserMetrics.ts +3 -3
  4. package/front_end/core/root/ExperimentNames.ts +0 -1
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
  6. package/front_end/core/sdk/ConsoleModel.ts +4 -0
  7. package/front_end/core/sdk/NetworkRequest.ts +12 -1
  8. package/front_end/core/sdk/SourceMap.ts +15 -18
  9. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
  10. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
  11. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
  12. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  13. package/front_end/generated/SupportedCSSProperties.js +4 -2
  14. package/front_end/models/ai_assistance/AiAgent2.ts +23 -12
  15. package/front_end/models/ai_assistance/README.md +5 -4
  16. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +22 -16
  17. package/front_end/models/ai_assistance/agents/AiAgent.ts +19 -6
  18. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +5 -5
  19. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +1 -94
  20. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +25 -0
  21. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +37 -0
  22. package/front_end/models/ai_assistance/agents/README.md +57 -0
  23. package/front_end/models/ai_assistance/agents/StorageAgent.ts +54 -1
  24. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -2
  25. package/front_end/models/ai_assistance/agents/StylingAgent.ts +31 -3
  26. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +12 -21
  27. package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
  28. package/front_end/models/ai_assistance/tools/README.md +45 -0
  29. package/front_end/models/ai_assistance/tools/Tool.ts +78 -9
  30. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
  31. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +6 -9
  32. package/front_end/models/bindings/DefaultScriptMapping.ts +2 -1
  33. package/front_end/models/bindings/SymbolizedError.ts +45 -35
  34. package/front_end/models/extensions/ExtensionAPI.ts +138 -47
  35. package/front_end/models/har/Importer.ts +1 -0
  36. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
  37. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
  38. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -2
  39. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +58 -52
  40. package/front_end/models/stack_trace/StackTrace.ts +7 -0
  41. package/front_end/models/stack_trace/StackTraceImpl.ts +13 -4
  42. package/front_end/models/stack_trace/StackTraceModel.ts +43 -9
  43. package/front_end/models/trace/Styles.ts +29 -7
  44. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
  45. package/front_end/models/trace/helpers/Timing.ts +10 -0
  46. package/front_end/models/trace/types/TraceEvents.ts +22 -2
  47. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -1
  48. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +9 -2
  49. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
  50. package/front_end/panels/application/ApplicationPanelSidebar.ts +83 -0
  51. package/front_end/panels/application/ApplicationPanelTreeElement.ts +39 -0
  52. package/front_end/panels/application/CookieItemsView.ts +2 -2
  53. package/front_end/panels/application/ServiceWorkersView.ts +2 -2
  54. package/front_end/panels/application/WebMCPView.ts +0 -1
  55. package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
  56. package/front_end/panels/application/resourcesSidebar.css +11 -0
  57. package/front_end/panels/console/ConsoleView.ts +6 -1
  58. package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
  59. package/front_end/panels/console/SymbolizedErrorWidget.ts +14 -8
  60. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
  61. package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
  62. package/front_end/panels/elements/PropertyRenderer.ts +0 -1
  63. package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
  64. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  65. package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
  66. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
  67. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
  68. package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
  69. package/front_end/panels/network/NetworkLogView.ts +34 -7
  70. package/front_end/panels/profiler/HeapProfileView.ts +0 -1
  71. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
  72. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
  73. package/front_end/panels/settings/components/SyncSection.ts +1 -1
  74. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -0
  75. package/front_end/panels/sources/SourcesPanel.ts +2 -1
  76. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
  77. package/front_end/panels/timeline/TimelinePanel.ts +7 -0
  78. package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
  79. package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
  80. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
  81. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
  82. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
  83. package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
  84. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
  85. package/front_end/third_party/chromium/README.chromium +1 -1
  86. package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
  87. package/front_end/ui/kit/link/Link.ts +16 -2
  88. package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
  89. package/front_end/ui/legacy/InspectorView.ts +4 -1
  90. package/front_end/ui/legacy/PlusButton.ts +6 -1
  91. package/front_end/ui/legacy/StackedPane.ts +229 -0
  92. package/front_end/ui/legacy/ViewManager.ts +59 -169
  93. package/front_end/ui/legacy/Widget.ts +19 -1
  94. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
  95. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
  96. package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
  97. package/front_end/ui/legacy/legacy.ts +3 -1
  98. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  99. package/package.json +1 -1
@@ -509,8 +509,8 @@ export abstract class AiAgent<T> {
509
509
  this.#allowedOrigin = opts.allowedOrigin;
510
510
  }
511
511
 
512
- async enhanceQuery(query: string, selected: ConversationContext<T>|null, multimodalInputType?: MultimodalInputType):
513
- Promise<string>;
512
+ async enhanceQuery(query: string, selected: ConversationContext<T>|null,
513
+ multimodalInputType?: MultimodalInputType): Promise<string>;
514
514
  async enhanceQuery(query: string): Promise<string> {
515
515
  return query;
516
516
  }
@@ -557,9 +557,21 @@ export abstract class AiAgent<T> {
557
557
  return undefined;
558
558
  }
559
559
 
560
- buildRequest(
561
- part: Host.AidaClient.Part|Host.AidaClient.Part[],
562
- role: Host.AidaClient.Role.USER|Host.AidaClient.Role.ROLE_UNSPECIFIED): Host.AidaClient.DoConversationRequest {
560
+ /**
561
+ * Preamble features appended to the `client_version` in metadata.
562
+ * This is required ONLY for the Styling Agent for legacy reasons to serve
563
+ * different server-side preambles based on the Chrome version.
564
+ * Other agents should NOT set or override this.
565
+ * If you are curious about this, look for `do_conversation_handler.cc` in
566
+ * Google3 or chat to @jacktfranklin.
567
+ */
568
+ preambleFeatures(): string[] {
569
+ return [];
570
+ }
571
+
572
+ buildRequest(part: Host.AidaClient.Part|Host.AidaClient.Part[],
573
+ role: Host.AidaClient.Role.USER|
574
+ Host.AidaClient.Role.ROLE_UNSPECIFIED): Host.AidaClient.DoConversationRequest {
563
575
  const parts = Array.isArray(part) ? part : [part];
564
576
  const currentMessage: Host.AidaClient.Content = {
565
577
  parts,
@@ -600,7 +612,8 @@ export abstract class AiAgent<T> {
600
612
  disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
601
613
  string_session_id: this.#sessionId,
602
614
  user_tier: userTier,
603
- client_version: Root.Runtime.getChromeVersion(),
615
+ client_version:
616
+ Root.Runtime.getChromeVersion() + this.preambleFeatures().map(feature => `+${feature}`).join(''),
604
617
  },
605
618
 
606
619
  functionality_type: enableAidaFunctionCalling ? Host.AidaClient.FunctionalityType.AGENTIC_CHAT :
@@ -27,7 +27,7 @@ import {
27
27
  type RequestOptions,
28
28
  } from './AiAgent.js';
29
29
  import {FileContext} from './FileAgent.js';
30
- import {RequestContext} from './NetworkAgent.js';
30
+ import {getRequestContextOrigin, RequestContext} from './NetworkAgent.js';
31
31
  import {PerformanceTraceContext} from './PerformanceAgent.js';
32
32
  import {StorageContext} from './StorageAgent.js';
33
33
 
@@ -146,7 +146,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
146
146
  let hasCrossOriginRequest = false;
147
147
  const requestsToShow: NetworkRequest[] = [];
148
148
  for (const request of Logs.NetworkLog.NetworkLog.instance().requests()) {
149
- const documentOrigin = Common.ParsedURL.ParsedURL.extractOrigin(request.documentURL);
149
+ const requestOrigin = getRequestContextOrigin(request);
150
150
  /**
151
151
  * NOTE: this origin check does not ensure that all the requests are
152
152
  * from the same origin as the target page. Instead, it ensures that
@@ -155,7 +155,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
155
155
  * during the loading of the target page, and do not leak URLs from
156
156
  * other pages.
157
157
  */
158
- if (origin && documentOrigin !== origin) {
158
+ if (origin && requestOrigin !== origin) {
159
159
  hasCrossOriginRequest = true;
160
160
  continue;
161
161
  }
@@ -230,8 +230,8 @@ export class ContextSelectionAgent extends AiAgent<never> {
230
230
  return false;
231
231
  }
232
232
 
233
- const documentOrigin = Common.ParsedURL.ParsedURL.extractOrigin(req.documentURL);
234
- return !origin || documentOrigin === origin;
233
+ const requestOrigin = getRequestContextOrigin(req);
234
+ return !origin || requestOrigin === origin;
235
235
  });
236
236
 
237
237
  if (request) {
@@ -12,9 +12,7 @@ import {debugLog} from '../debug.js';
12
12
  import {EvaluateAction, formatError, SideEffectError} from '../EvaluateAction.js';
13
13
  import {FREESTYLER_WORLD_NAME} from '../injected.js';
14
14
 
15
- import type {
16
- AgentOptions as BaseAgentOptions, FunctionCallHandlerResult, FunctionDeclaration, FunctionHandlerOptions,} from
17
- './AiAgent.js';
15
+ import type {AgentOptions as BaseAgentOptions, FunctionCallHandlerResult, FunctionHandlerOptions,} from './AiAgent.js';
18
16
 
19
17
  const lockedString = i18n.i18n.lockedString;
20
18
 
@@ -28,97 +26,6 @@ export interface ExecuteJsAgentOptions extends BaseAgentOptions {
28
26
  execJs?: typeof executeJsCode;
29
27
  }
30
28
 
31
- // TODO(crbug.com/510206549): De-duplicate this function by migrating AccessibilityAgent to use
32
- // the registry-based ExecuteJavaScriptTool in tools/ExecuteJavaScript.ts.
33
- export function executeJavaScriptFunction(executor: JavascriptExecutor): FunctionDeclaration<
34
- {
35
- title: string,
36
- explanation: string,
37
- code: string,
38
- },
39
- unknown> {
40
- return {
41
- description:
42
- 'This function allows you to run JavaScript code on the inspected page to access the element styles and page content.\nCall this function to gather additional information or modify the page state. Call this function enough times to investigate the user request.',
43
- parameters: {
44
- type: Host.AidaClient.ParametersTypes.OBJECT,
45
- description: '',
46
- nullable: false,
47
- properties: {
48
- code: {
49
- type: Host.AidaClient.ParametersTypes.STRING,
50
- description:
51
- `JavaScript code snippet to run on the inspected page. Make sure the code is formatted for readability.
52
-
53
- # Instructions
54
-
55
- * To return data, define a top-level \`data\` variable and populate it with data you want to get. Only JSON-serializable objects can be assigned to \`data\`.
56
- * If you modify styles on an element, ALWAYS call the pre-defined global \`async setElementStyles(el: Element, styles: object)\` function. This function is an internal mechanism for you and should never be presented as a command/advice to the user.
57
- * **CRITICAL** Only get styles that might be relevant to the user request.
58
- * **CRITICAL** Never assume a selector for the elements unless you verified your knowledge.
59
- * **CRITICAL** Consider that \`data\` variable from the previous function calls are not available in a new function call.
60
-
61
- For example, the code to change element styles:
62
-
63
- \`\`\`
64
- await setElementStyles($0, {
65
- color: 'blue',
66
- });
67
- \`\`\`
68
-
69
- For example, the code to get overlapping elements:
70
-
71
- \`\`\`
72
- const data = {
73
- overlappingElements: Array.from(document.querySelectorAll('*'))
74
- .filter(el => {
75
- const rect = el.getBoundingClientRect();
76
- const popupRect = $0.getBoundingClientRect();
77
- return (
78
- el !== $0 &&
79
- rect.left < popupRect.right &&
80
- rect.right > popupRect.left &&
81
- rect.top < popupRect.bottom &&
82
- rect.bottom > popupRect.top
83
- );
84
- })
85
- .map(el => ({
86
- tagName: el.tagName,
87
- id: el.id,
88
- className: el.className,
89
- zIndex: window.getComputedStyle(el)['z-index']
90
- }))
91
- };
92
- \`\`\`
93
- `,
94
- },
95
- explanation: {
96
- type: Host.AidaClient.ParametersTypes.STRING,
97
- description: 'Explain why you want to run this code',
98
- },
99
- title: {
100
- type: Host.AidaClient.ParametersTypes.STRING,
101
- description: 'Provide a summary of what the code does. For example, "Checking related element styles".',
102
- },
103
- },
104
- required: ['code', 'explanation', 'title']
105
- },
106
- displayInfoFromArgs: params => {
107
- return {
108
- title: params.title,
109
- thought: params.explanation,
110
- action: params.code,
111
- };
112
- },
113
- handler: async (
114
- params,
115
- options,
116
- ) => {
117
- return await executor.executeAction(params.code, options);
118
- },
119
- };
120
- }
121
-
122
29
  export async function executeJsCode(
123
30
  functionDeclaration: string,
124
31
  {throwOnSideEffect, contextNode}: {throwOnSideEffect: boolean, contextNode: SDK.DOMModel.DOMNode|null}):
@@ -2,11 +2,14 @@
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';
5
6
  import * as Host from '../../../core/host/host.js';
6
7
  import * as i18n from '../../../core/i18n/i18n.js';
8
+ import type * as Platform from '../../../core/platform/platform.js';
7
9
  import * as Root from '../../../core/root/root.js';
8
10
  import type * as SDK from '../../../core/sdk/sdk.js';
9
11
  import type * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
12
+ import {extractContextOrigin} from '../AiOrigins.js';
10
13
  import {NetworkRequestFormatter} from '../data_formatters/NetworkRequestFormatter.js';
11
14
 
12
15
  import {
@@ -98,6 +101,24 @@ const UIStringsNotTranslate = {
98
101
 
99
102
  const lockedString = i18n.i18n.lockedString;
100
103
 
104
+ /**
105
+ * Returns the origin for a network request in the AI context.
106
+ *
107
+ * To prevent cross-origin prompt injection attacks, HAR-imported requests
108
+ * are isolated from live pages. We assign them a virtual origin
109
+ * (`imported-har://${domain}`) so they do not share the origin of live pages
110
+ * (e.g., `https://${domain}`). This forces a conversation reset when transitioning
111
+ * between imported HAR data and live pages.
112
+ */
113
+ export function getRequestContextOrigin(request: SDK.NetworkRequest.NetworkRequest): string {
114
+ const origin = extractContextOrigin(request.documentURL);
115
+ if (request.isImportedHar()) {
116
+ const parsed = Common.ParsedURL.ParsedURL.fromString(origin as Platform.DevToolsPath.UrlString);
117
+ return `imported-har://${parsed ? parsed.domain() : origin}`;
118
+ }
119
+ return origin;
120
+ }
121
+
101
122
  export class RequestContext extends ConversationContext<SDK.NetworkRequest.NetworkRequest> {
102
123
  #request: SDK.NetworkRequest.NetworkRequest;
103
124
  #calculator: NetworkTimeCalculator.NetworkTransferTimeCalculator;
@@ -119,6 +140,10 @@ export class RequestContext extends ConversationContext<SDK.NetworkRequest.Netwo
119
140
  return this.#request.documentURL;
120
141
  }
121
142
 
143
+ override getOrigin(): string {
144
+ return getRequestContextOrigin(this.#request);
145
+ }
146
+
122
147
  override getItem(): SDK.NetworkRequest.NetworkRequest {
123
148
  return this.#request;
124
149
  }
@@ -15,6 +15,7 @@ import * as Logs from '../../logs/logs.js';
15
15
  import * as SourceMapScopes from '../../source_map_scopes/source_map_scopes.js';
16
16
  import * as TextUtils from '../../text_utils/text_utils.js';
17
17
  import * as Trace from '../../trace/trace.js';
18
+ import {extractContextOrigin} from '../AiOrigins.js';
18
19
  import {sanitizeHeaders} from '../data_formatters/NetworkRequestFormatter.js';
19
20
  import {
20
21
  PerformanceInsightFormatter,
@@ -256,6 +257,27 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
256
257
  }
257
258
  }
258
259
 
260
+ /**
261
+ * Returns the origin for a performance trace in the AI context.
262
+ *
263
+ * To prevent cross-origin prompt injection attacks, imported traces
264
+ * are isolated from live pages. We assign them a virtual origin
265
+ * (`imported-trace://${domain}`) so they do not share the origin of live pages
266
+ * (e.g., `https://${domain}`). This forces a conversation reset when transitioning
267
+ * between imported trace data and live pages.
268
+ */
269
+ override getOrigin(): string {
270
+ const parsedTrace = this.#focus.parsedTrace;
271
+ const url = this.getURL();
272
+ const origin = extractContextOrigin(url);
273
+ const isFresh = Tracing.FreshRecording.Tracker.instance().recordingIsFresh(parsedTrace);
274
+ if (!isFresh) {
275
+ const parsed = Common.ParsedURL.ParsedURL.fromString(origin as Platform.DevToolsPath.UrlString);
276
+ return `imported-trace://${parsed ? parsed.domain() : origin}`;
277
+ }
278
+ return origin;
279
+ }
280
+
259
281
  override getItem(): AgentFocus {
260
282
  return this.#focus;
261
283
  }
@@ -729,11 +751,26 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
729
751
  yield* super.run(initialQuery, options);
730
752
  }
731
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
+ */
732
760
  override clearCache(): void {
761
+ super.clearCache();
733
762
  // Clear the function call cache to prevent stashed tool execution results
734
763
  // (which might contain cross-origin resource content fetched before navigation
735
764
  // was detected) from being replayed as facts in subsequent runs.
736
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();
737
774
  }
738
775
 
739
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).
@@ -23,7 +23,7 @@ const lockedString = i18n.i18n.lockedString;
23
23
  const preamble =
24
24
  `You are a Senior Software Engineer specializing in state audit and storage analysis within Chrome DevTools. Your mission is to help developers debug storage-related issues faster by analyzing the evidence in LocalStorage, SessionStorage, and Cookies.
25
25
 
26
- You have access to the site's storage using tools like \`listPageOrigins\`, \`listStorageKeys\`, \`getStorageValues\`, \`listCookies\`, and \`getCookieValues\`.
26
+ You have access to the site's storage using tools like \`getStorageBreakdown\`, \`listPageOrigins\`, \`listStorageKeys\`, \`getStorageValues\`, \`listCookies\`, and \`getCookieValues\`.
27
27
 
28
28
  # Goals
29
29
 
@@ -34,6 +34,7 @@ const preamble =
34
34
  # Tools & Workflow
35
35
 
36
36
  - **Prioritize Top-Level Context**: Always initiate your investigation from the top-level page's storage. Explicitly state if you are analyzing storage from a different context (e.g., an iframe).
37
+ - **Storage Breakdown**: Calling \`getStorageBreakdown\` gives you the total usage and quota per storage for the top-level page.
37
38
  - **Address Specific Selections**: The user can select individual storage items in the DevTools UI (provided in the '# Active Context' section of the prompt). If the query is about a selected item (e.g., "Why is this cookie set?"), focus your response on that specific item.
38
39
  - **Expand Scope When Necessary**: For general questions or those implying a wider scope (e.g., "Check all storages," "Are there related cookies on subdomains?"), proactively use your tools to explore other relevant storage contexts, including iframes and different origins.
39
40
  - **Discovery**: Start by calling \`listPageOrigins\` to discover all active, non-empty frame origins loaded by the page.
@@ -479,6 +480,58 @@ export class StorageAgent extends AiAgent<StorageItem> {
479
480
  return {result: {cookies: cookieData}};
480
481
  },
481
482
  });
483
+
484
+ this.declareFunction<Record<string, never>, {
485
+ totalUsage: string,
486
+ totalQuota: string,
487
+ usageBreakdown: Array<{
488
+ storageType: string,
489
+ usage: string,
490
+ }>,
491
+ }>('getStorageBreakdown', {
492
+ description:
493
+ 'Retrieves the total storage usage, total storage quota, and a breakdown of active storage usage per storage type for the top-level page.',
494
+ parameters: {
495
+ type: Host.AidaClient.ParametersTypes.OBJECT,
496
+ description: '',
497
+ nullable: false,
498
+ properties: {},
499
+ required: [],
500
+ },
501
+ displayInfoFromArgs: () => {
502
+ return {
503
+ title: lockedString('Retrieving storage breakdown'),
504
+ action: 'getStorageBreakdown()',
505
+ };
506
+ },
507
+ handler: async () => {
508
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
509
+ if (!target || !this.context || !isSamePageOrigin(target, this.context)) {
510
+ return {error: 'No origin available or not allowed.'};
511
+ }
512
+
513
+ const origin = this.context.getOrigin();
514
+ const response = await target.storageAgent().invoke_getUsageAndQuota({origin});
515
+ if (response.getError()) {
516
+ return {error: response.getError() || 'Unknown CDP error'};
517
+ }
518
+
519
+ const usageBreakdown = response.usageBreakdown.filter(entry => entry.usage > 0)
520
+ .sort((a, b) => b.usage - a.usage)
521
+ .map(entry => ({
522
+ storageType: entry.storageType as string,
523
+ usage: i18n.ByteUtilities.bytesToString(entry.usage),
524
+ }));
525
+
526
+ return {
527
+ result: {
528
+ totalUsage: i18n.ByteUtilities.bytesToString(response.usage),
529
+ totalQuota: i18n.ByteUtilities.bytesToString(response.quota),
530
+ usageBreakdown,
531
+ },
532
+ };
533
+ },
534
+ });
482
535
  }
483
536
 
484
537
  static #formatContext(item: StorageItem): string {
@@ -1,4 +1,3 @@
1
-
2
1
  Title: StylingAgent buildRequest structure matches the snapshot
3
2
  Content:
4
3
  {
@@ -129,7 +128,7 @@ Content:
129
128
  "disable_user_content_logging": false,
130
129
  "string_session_id": "sessionId",
131
130
  "user_tier": 3,
132
- "client_version": "unit_test"
131
+ "client_version": "unit_test+function_calling"
133
132
  },
134
133
  "functionality_type": 5,
135
134
  "client_feature": 2
@@ -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);
@@ -211,6 +220,7 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
211
220
  changeManager: this.#changes,
212
221
  createExtensionScope: this.#createExtensionScope.bind(this),
213
222
  execJs: this.#execJs,
223
+ getExecutionContextNode: () => this.context?.getItem() ?? null,
214
224
  },
215
225
  options,
216
226
  ),
@@ -280,6 +290,24 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
280
290
  });
281
291
  }
282
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
+
307
+ override preambleFeatures(): string[] {
308
+ return ['function_calling'];
309
+ }
310
+
283
311
  #getSelectedNode(): SDK.DOMModel.DOMNode|null {
284
312
  return this.context?.getItem() ?? null;
285
313
  }
@@ -6,12 +6,13 @@ import * as Host from '../../../core/host/host.js';
6
6
  import * as Root from '../../../core/root/root.js';
7
7
  import type {FunctionCallHandlerResult, FunctionHandlerOptions,} from '../agents/AiAgent.js';
8
8
  import {JavascriptExecutor} from '../agents/ExecuteJavascript.js';
9
- import {DOMNodeContext} from '../contexts/DOMNodeContext.js';
10
9
 
11
10
  import {
11
+ type BaseToolCapability,
12
+ type PageExecutionCapability,
13
+ type StyleMutationCapability,
12
14
  type Tool,
13
15
  type ToolArgs,
14
- type ToolContext,
15
16
  ToolName,
16
17
  } from './Tool.js';
17
18
 
@@ -21,7 +22,8 @@ export interface ExecuteJavaScriptArgs extends ToolArgs {
21
22
  title: string;
22
23
  }
23
24
 
24
- export class ExecuteJavaScriptTool implements Tool<ExecuteJavaScriptArgs, unknown> {
25
+ export class ExecuteJavaScriptTool implements
26
+ Tool<ExecuteJavaScriptArgs, unknown, BaseToolCapability&PageExecutionCapability&StyleMutationCapability> {
25
27
  readonly name = ToolName.EXECUTE_JAVASCRIPT;
26
28
 
27
29
  readonly description =
@@ -105,33 +107,22 @@ const data = {
105
107
 
106
108
  async handler(
107
109
  params: ExecuteJavaScriptArgs,
108
- context: ToolContext,
110
+ context: BaseToolCapability&PageExecutionCapability&StyleMutationCapability,
109
111
  options?: FunctionHandlerOptions,
110
112
  ): Promise<FunctionCallHandlerResult<unknown>> {
111
- const activeContext = context.conversationContext;
112
- if (!activeContext || !(activeContext instanceof DOMNodeContext)) {
113
- return {error: 'Error: Could not find the currently selected element.'};
114
- }
115
-
116
- const selectedNode = activeContext.getItem();
117
- if (!selectedNode) {
118
- return {error: 'Error: Could not find the currently selected element.'};
113
+ const executionNode = context.getExecutionContextNode();
114
+ if (!executionNode) {
115
+ return {error: 'Error: Could not find the context node for execution.'};
119
116
  }
120
117
 
121
118
  const executionMode = Root.Runtime.hostConfig.devToolsFreestyler?.executionMode ??
122
119
  Root.Runtime.HostConfigFreestylerExecutionMode.ALL_SCRIPTS;
123
120
 
124
- const changes = context.changeManager;
125
- const createExtensionScope = context.createExtensionScope;
126
- if (!changes || !createExtensionScope) {
127
- return {error: 'Internal Error: Required change manager or extension scope creator is missing.'};
128
- }
129
-
130
121
  const executor = new JavascriptExecutor({
131
122
  executionMode,
132
- getContextNode: () => selectedNode,
133
- createExtensionScope,
134
- changes,
123
+ getContextNode: () => executionNode,
124
+ createExtensionScope: context.createExtensionScope,
125
+ changes: context.changeManager,
135
126
  },
136
127
  context.execJs);
137
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);