chrome-devtools-frontend 1.0.1669021 → 1.0.1672388

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 (208) hide show
  1. package/.agents/skills/fixing-skipped-tests/SKILL.md +6 -6
  2. package/.agents/skills/gerrit-cli/SKILL.md +4 -10
  3. package/.agents/skills/gerrit-cli/references/commands.md +45 -2
  4. package/.agents/skills/gerrit-cli/references/workflows.md +7 -0
  5. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +34 -152
  6. package/config/typescript/tsconfig.eslint.json +1 -1
  7. package/docs/contributing/settings-experiments-features.md +2 -8
  8. package/docs/ecosystem/automatic_workspace_folders.md +0 -19
  9. package/eslint.config.mjs +2 -2
  10. package/front_end/core/common/Lazy.ts +3 -3
  11. package/front_end/core/common/SettingRegistration.ts +0 -7
  12. package/front_end/core/common/Settings.ts +0 -38
  13. package/front_end/core/host/UserMetrics.ts +187 -953
  14. package/front_end/core/host/UserMetricsEnums.d.ts +949 -0
  15. package/front_end/core/root/Runtime.ts +21 -133
  16. package/front_end/core/sdk/CSSMetadata.ts +70 -65
  17. package/front_end/core/sdk/CategorizedBreakpoint.ts +0 -1
  18. package/front_end/core/sdk/ChildTargetManager.ts +0 -2
  19. package/front_end/core/sdk/EmulationModel.ts +0 -2
  20. package/front_end/core/sdk/EventBreakpointsModel.ts +0 -4
  21. package/front_end/core/sdk/NetworkManager.ts +0 -3
  22. package/front_end/core/sdk/PageResourceLoader.ts +9 -9
  23. package/front_end/core/sdk/RehydratingConnection.ts +52 -4
  24. package/front_end/core/sdk/Target.ts +0 -4
  25. package/front_end/devtools_compatibility.js +886 -0
  26. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +7 -6
  27. package/front_end/entrypoints/main/ExecutionContextSelector.ts +0 -1
  28. package/front_end/entrypoints/main/MainImpl.ts +18 -22
  29. package/front_end/foundation/Universe.ts +7 -2
  30. package/front_end/generated/InspectorBackendCommands.ts +3 -10
  31. package/front_end/generated/protocol-mapping.d.ts +0 -42
  32. package/front_end/generated/protocol-proxy-api.d.ts +0 -38
  33. package/front_end/generated/protocol.ts +9 -169
  34. package/front_end/models/ai_assistance/AiAgent2.ts +11 -3
  35. package/front_end/models/ai_assistance/AiConversation.ts +1 -0
  36. package/front_end/models/ai_assistance/AiHistoryStorage.ts +8 -5
  37. package/front_end/models/ai_assistance/AiSetting.ts +108 -0
  38. package/front_end/models/ai_assistance/AiUtils.ts +47 -0
  39. package/front_end/models/ai_assistance/ChangeManager.ts +1 -0
  40. package/front_end/models/ai_assistance/ExtensionScope.ts +3 -12
  41. package/front_end/models/ai_assistance/agents/AiAgent.ts +11 -18
  42. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +3 -0
  43. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +42 -17
  44. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +3 -2
  45. package/front_end/models/ai_assistance/agents/StorageAgent.ts +253 -155
  46. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +2 -2
  47. package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
  48. package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +12 -6
  49. package/front_end/models/ai_assistance/data_formatters/FileFormatter.ts +3 -1
  50. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +1 -0
  51. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -0
  52. package/front_end/models/ai_assistance/injected.ts +1 -0
  53. package/front_end/models/ai_assistance/performance/AICallTree.ts +1 -0
  54. package/front_end/models/ai_assistance/skills/Skill.ts +1 -1
  55. package/front_end/models/ai_assistance/skills/SkillRegistry.ts +2 -0
  56. package/front_end/models/ai_assistance/skills/performance.md +10 -0
  57. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +52 -5
  58. package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +4 -4
  59. package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +4 -4
  60. package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +5 -4
  61. package/front_end/models/ai_assistance/tools/GetStyles.ts +5 -4
  62. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +5 -4
  63. package/front_end/models/ai_assistance/tools/README.md +1 -1
  64. package/front_end/models/ai_assistance/tools/RecordPerformanceTrace.ts +63 -0
  65. package/front_end/models/ai_assistance/tools/ResolveDevtoolsNodePath.ts +5 -5
  66. package/front_end/models/ai_assistance/tools/Tool.ts +118 -24
  67. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +2 -0
  68. package/front_end/models/badges/UserBadges.ts +2 -0
  69. package/front_end/models/bindings/NetworkProject.ts +5 -0
  70. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -0
  71. package/front_end/models/bindings/ResourceUtils.ts +3 -0
  72. package/front_end/models/crux-manager/CrUXManager.ts +1 -0
  73. package/front_end/models/emulation/DeviceModeModel.ts +140 -7
  74. package/front_end/models/emulation/EmulatedDevices.ts +246 -793
  75. package/front_end/models/issues_manager/IssuesManager.ts +10 -2
  76. package/front_end/models/live-metrics/LiveMetrics.ts +41 -9
  77. package/front_end/models/logs/LogManager.ts +8 -5
  78. package/front_end/models/logs/NetworkLog.ts +1 -0
  79. package/front_end/models/persistence/IsolatedFileSystemManager.ts +21 -3
  80. package/front_end/models/persistence/NetworkPersistenceManager.ts +6 -0
  81. package/front_end/models/project_settings/ProjectSettingsModel.ts +8 -12
  82. package/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +3 -1
  83. package/front_end/models/workspace/IgnoreListManager.ts +2 -0
  84. package/front_end/models/workspace/UISourceCode.ts +2 -0
  85. package/front_end/models/workspace_diff/WorkspaceDiff.ts +4 -0
  86. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +22 -22
  87. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +0 -7
  88. package/front_end/panels/ai_assistance/components/ChatInput.ts +4 -0
  89. package/front_end/panels/ai_assistance/components/ChatMessage.ts +79 -34
  90. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +1 -2
  91. package/front_end/panels/ai_assistance/components/walkthroughView.css +1 -1
  92. package/front_end/panels/application/ApplicationPanelSidebar.ts +9 -106
  93. package/front_end/panels/application/ApplicationPanelTreeElement.ts +1 -1
  94. package/front_end/panels/application/application.ts +0 -10
  95. package/front_end/panels/application/components/components.ts +0 -4
  96. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +0 -5
  97. package/front_end/panels/browser_debugger/EventListenerBreakpointsSidebarPane.ts +6 -12
  98. package/front_end/panels/browser_debugger/browser_debugger-meta.ts +5 -3
  99. package/front_end/panels/console/ConsoleFormat.ts +2 -62
  100. package/front_end/panels/console/ConsoleViewMessage.ts +14 -7
  101. package/front_end/panels/emulation/AdvancedApp.ts +1 -2
  102. package/front_end/panels/emulation/DeviceModeToolbar.ts +83 -69
  103. package/front_end/panels/emulation/DeviceModeView.ts +4 -134
  104. package/front_end/panels/emulation/DeviceModeWrapper.ts +52 -69
  105. package/front_end/panels/emulation/MediaQueryInspector.ts +2 -2
  106. package/front_end/panels/emulation/emulation-meta.ts +12 -12
  107. package/front_end/panels/issues/IssuesPane.ts +3 -1
  108. package/front_end/panels/lighthouse/LighthouseController.ts +6 -0
  109. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +17 -21
  110. package/front_end/panels/linear_memory_inspector/components/LinearMemoryNavigator.ts +206 -126
  111. package/front_end/panels/linear_memory_inspector/components/linearMemoryInspector.css +1 -1
  112. package/front_end/panels/mobile_throttling/CalibrationController.ts +1 -1
  113. package/front_end/panels/mobile_throttling/MobileThrottlingSelector.ts +3 -3
  114. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +6 -6
  115. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +11 -11
  116. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +13 -13
  117. package/front_end/panels/mobile_throttling/ThrottlingPresets.ts +10 -10
  118. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +28 -28
  119. package/front_end/panels/mobile_throttling/mobile_throttling-meta.ts +6 -6
  120. package/front_end/panels/network/RequestHeadersView.ts +1 -1
  121. package/front_end/panels/network/ResourceChunkView.ts +243 -144
  122. package/front_end/panels/network/ResourceDirectSocketChunkView.ts +52 -29
  123. package/front_end/panels/network/ResourceWebSocketFrameView.ts +21 -35
  124. package/front_end/panels/network/resourceChunkView.css +41 -36
  125. package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +21 -12
  126. package/front_end/panels/profiler/profiler.ts +4 -0
  127. package/front_end/panels/recorder/RecorderPanel.ts +6 -14
  128. package/front_end/panels/sensors/LocationsSettingsTab.ts +7 -7
  129. package/front_end/panels/settings/AISettingsTab.ts +75 -31
  130. package/front_end/panels/settings/SettingsScreen.ts +8 -13
  131. package/front_end/panels/settings/components/SyncSection.ts +3 -4
  132. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +200 -18
  133. package/front_end/panels/settings/emulation/devicesSettingsTab.css +18 -0
  134. package/front_end/panels/settings/settings-meta.ts +0 -1
  135. package/front_end/panels/sources/CategorizedBreakpointL10n.ts +0 -2
  136. package/front_end/panels/sources/DebuggerPausedMessage.ts +11 -5
  137. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  138. package/front_end/panels/sources/EditingLocationHistoryManager.ts +12 -9
  139. package/front_end/panels/sources/SourcesPanel.ts +19 -17
  140. package/front_end/panels/sources/SourcesView.ts +191 -96
  141. package/front_end/panels/sources/TabbedEditorContainer.ts +3 -4
  142. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +2 -5
  143. package/front_end/panels/timeline/TimelineUIUtils.ts +15 -0
  144. package/front_end/panels/timeline/timeline-meta.ts +3 -5
  145. package/front_end/third_party/chromium/README.chromium +1 -1
  146. package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
  147. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  148. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  149. package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
  150. package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
  151. package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
  152. package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
  153. package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
  154. package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
  155. package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
  156. package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
  157. package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
  158. package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
  159. package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
  160. package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
  161. package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
  162. package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
  163. package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
  164. package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
  165. package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
  166. package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
  167. package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
  168. package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
  169. package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
  170. package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
  171. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +1332 -193
  172. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  173. package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
  174. package/front_end/tsconfig.compatibility.json +2 -1
  175. package/front_end/tsconfig.json +1 -1
  176. package/front_end/ui/components/icon_button/IconButton.ts +1 -1
  177. package/front_end/ui/components/issue_counter/IssueCounter.ts +1 -1
  178. package/front_end/ui/components/settings/SettingCheckbox.ts +4 -7
  179. package/front_end/ui/components/settings/settings.ts +1 -2
  180. package/front_end/ui/legacy/Treeoutline.ts +52 -30
  181. package/front_end/ui/legacy/components/object_ui/CSSStyleSanitizer.ts +74 -0
  182. package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +6 -1
  183. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +47 -54
  184. package/front_end/ui/legacy/components/object_ui/object_ui.ts +2 -0
  185. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +5 -27
  186. package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +10 -12
  187. package/front_end/ui/settings/SettingUIRegistration.ts +0 -8
  188. package/front_end/ui/visual_logging/KnownContextValues.ts +21 -0
  189. package/inspector_overlay/tsconfig.json +8 -2
  190. package/package.json +3 -2
  191. package/.agents/skills/gerrit-cli/scripts/gerrit_client_wrapper.py +0 -47
  192. package/front_end/core/sdk/CSSModel.test.api.ts +0 -102
  193. package/front_end/core/sdk/DOMModel.test.api.ts +0 -104
  194. package/front_end/foundation/Universe.test.api.ts +0 -73
  195. package/front_end/panels/application/SharedStorageEventsView.ts +0 -128
  196. package/front_end/panels/application/SharedStorageItemsView.ts +0 -179
  197. package/front_end/panels/application/SharedStorageListTreeElement.ts +0 -65
  198. package/front_end/panels/application/SharedStorageModel.ts +0 -249
  199. package/front_end/panels/application/SharedStorageTreeElement.ts +0 -40
  200. package/front_end/panels/application/components/SharedStorageAccessGrid.ts +0 -191
  201. package/front_end/panels/application/components/SharedStorageMetadataView.ts +0 -133
  202. package/front_end/panels/application/components/sharedStorageAccessGrid.css +0 -29
  203. package/front_end/panels/application/components/sharedStorageMetadataView.css +0 -29
  204. package/front_end/panels/application/sharedStorageEventsView.css +0 -9
  205. package/front_end/panels/network/OriginalResourceChunkView.ts +0 -340
  206. package/front_end/panels/network/originalResourceChunkView.css +0 -49
  207. package/front_end/ui/components/settings/SettingDeprecationWarning.ts +0 -47
  208. package/front_end/ui/components/settings/settingDeprecationWarning.css +0 -14
@@ -35,9 +35,10 @@ const preamble =
35
35
 
36
36
  # Tools & Workflow
37
37
 
38
- - **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).
38
+ - **Top-Level Context**: Generally, questions refer to the primary page target ("my page", "this page", etc.). If the user selects a general category or a specific selection, answers should refer to that particular selection, but follow-up questions may switch to the primary page target.
39
39
  - **Storage Breakdown**: Calling \`getStorageBreakdown\` gives you the total usage and quota per storage for the top-level page.
40
40
  - **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.
41
+ - **General Category Selection**: If a general storage category (such as Cookies, Local Storage, or Session Storage) is selected in the active context (indicated by an empty context origin), your first step MUST be to look through all cookies or local/session storage entries across all active page origins (by calling \`listPageOrigins\` to discover origins, then passing all discovered origins to \`listCookies\` or \`listStorageKeys\`), unless the user's explicit request hints otherwise.
41
42
  - **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.
42
43
  - **Discovery**: Start by calling \`listPageOrigins\` to discover all active, non-empty frame origins loaded by the page.
43
44
  - **Storage Partitioning (LocalStorage / SessionStorage)**:
@@ -67,7 +68,7 @@ function isSamePrimaryPageOrigin(targetManager: SDK.TargetManager.TargetManager,
67
68
  return isSamePageOrigin(primaryPageTarget, context);
68
69
  }
69
70
 
70
- function isSamePageOrigin(target: SDK.Target.Target|null, context?: ConversationContext<StorageItem>): boolean {
71
+ export function isSamePageOrigin(target: SDK.Target.Target|null, context?: ConversationContext<StorageItem>): boolean {
71
72
  if (!target || !context) {
72
73
  return false;
73
74
  }
@@ -75,6 +76,15 @@ function isSamePageOrigin(target: SDK.Target.Target|null, context?: Conversation
75
76
  return pageOrigin !== '' && context.isOriginAllowed(pageOrigin);
76
77
  }
77
78
 
79
+ const MAX_TARGET_ORIGINS = 100;
80
+
81
+ function resolveTargetOrigins(context?: ConversationContext<StorageItem>, origins?: string[]): string[] {
82
+ const primaryOrigin = context?.getOrigin();
83
+ const rawList = (origins && origins.length > 0) ? origins : (primaryOrigin ? [primaryOrigin] : []);
84
+ const uniqueOrigins = Array.from(new Set(rawList));
85
+ return uniqueOrigins.slice(0, MAX_TARGET_ORIGINS);
86
+ }
87
+
78
88
  export class StorageContext extends ConversationContext<StorageItem> {
79
89
  #item: StorageItem;
80
90
 
@@ -93,10 +103,17 @@ export class StorageContext extends ConversationContext<StorageItem> {
93
103
 
94
104
  override getTitle(): string {
95
105
  if (this.#item instanceof CookieItem) {
96
- return `${this.#item.name ? `cookie: ${this.#item.name}` : 'cookies:'} ${this.#item.origin}`;
106
+ if (this.#item.name) {
107
+ return `cookie: ${this.#item.name}${this.#item.origin ? ` ${this.#item.origin}` : ''}`;
108
+ }
109
+ return `cookies${this.#item.isGenericContext ? '' : `: ${this.#item.origin}`}`;
97
110
  }
98
111
  if (this.#item instanceof DOMStorageItem) {
99
- return `${this.#item.key ? `entry: ${this.#item.key}` : 'storage:'} ${this.#item.origin}`;
112
+ if (this.#item.key) {
113
+ return `entry: ${this.#item.key}${this.#item.origin ? ` ${this.#item.origin}` : ''}`;
114
+ }
115
+ const prefix = this.#item.type === 'localStorage' ? 'local storage' : 'session storage';
116
+ return `${prefix}${this.#item.isGenericContext ? '' : `: ${this.#item.origin}`}`;
100
117
  }
101
118
  return `Storage: ${this.getOrigin()}`;
102
119
  }
@@ -186,7 +203,7 @@ export class StorageAgent extends AiAgent<StorageItem> {
186
203
 
187
204
  this.declareFunction<Record<string, never>, {origins: string[]}>('listPageOrigins', {
188
205
  description:
189
- 'Lists all active, non-empty frame origins loaded by the page. Use this first to discover what other targets/iframes exist on the page for querying their storage.',
206
+ 'Lists all active, non-empty frame origins loaded by the page. Use this first when generic category context is active to discover all page origins, then pass them to listCookies or listStorageKeys, unless the user\'s explicit request hints at focusing only on the primary page.',
190
207
  parameters: {
191
208
  type: Host.AidaClient.ParametersTypes.OBJECT,
192
209
  description: '',
@@ -223,17 +240,15 @@ export class StorageAgent extends AiAgent<StorageItem> {
223
240
 
224
241
  this.declareFunction<{
225
242
  type: 'localStorage' | 'sessionStorage',
226
- origin: string,
243
+ origins: string[],
227
244
  storageKey?: string,
228
245
  },
229
246
  {
230
- partitions: Array<{
231
- storageKey: string,
232
- keys: string[],
233
- }>,
247
+ storageKeysByOrigin: Record<
248
+ string, {partitions?: Array<{storageKey: string, keys: string[]}>, error?: string}>,
234
249
  }>('listStorageKeys', {
235
250
  description:
236
- 'Lists all keys for a given storage type for the requested origin. Returns keys grouped by storage partition.',
251
+ 'Lists all keys for a given storage type for requested origins. Returns keys grouped by storage partition under their origin.',
237
252
  parameters: {
238
253
  type: Host.AidaClient.ParametersTypes.OBJECT,
239
254
  description: '',
@@ -244,23 +259,24 @@ export class StorageAgent extends AiAgent<StorageItem> {
244
259
  description: 'Storage type: localStorage or sessionStorage',
245
260
  nullable: false,
246
261
  },
247
- origin: {
248
- type: Host.AidaClient.ParametersTypes.STRING,
249
- description: 'Specific origin to list keys for.',
262
+ origins: {
263
+ type: Host.AidaClient.ParametersTypes.ARRAY,
264
+ description: 'List of origins to list keys for.',
265
+ items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'An origin URL.'},
250
266
  nullable: false,
251
267
  },
252
268
  storageKey: {
253
269
  type: Host.AidaClient.ParametersTypes.STRING,
254
- description: 'Optional. Specific storageKey to to list keys for.',
270
+ description: 'Optional. Specific storageKey to list keys for. Only applies if single origin is provided.',
255
271
  nullable: true,
256
272
  },
257
273
  },
258
- required: ['type', 'origin'],
274
+ required: ['type', 'origins'],
259
275
  },
260
276
  displayInfoFromArgs: args => {
261
277
  return {
262
278
  title: lockedString('Reading storage keys'),
263
- action: `listStorageKeys('${args.type}', '${args.origin}')`,
279
+ action: `listStorageKeys('${args.type}', ${JSON.stringify(args.origins)})`,
264
280
  };
265
281
  },
266
282
 
@@ -270,41 +286,47 @@ export class StorageAgent extends AiAgent<StorageItem> {
270
286
  return {error: 'No origin available or not allowed.'};
271
287
  }
272
288
 
273
- const storages = resolveDOMStorages(this.context, args.type, args.origin, this.targetManager, args.storageKey);
274
-
275
- const keyAndItems = await Promise.all(storages.map(async storage => {
276
- const items = await storage.getItems();
277
- return {storageKey: storage.storageKey, items};
278
- }));
279
-
280
- const partitionsResult = [];
281
- for (const {storageKey, items} of keyAndItems) {
282
- if (!items) {
283
- continue;
284
- }
285
- const keys = items.map(([key]) => key);
286
- if (keys.length > 0) {
287
- partitionsResult.push({storageKey, keys});
289
+ const targetOrigins = resolveTargetOrigins(this.context, args.origins);
290
+ const storageKey = (targetOrigins.length === 1 && args.storageKey) ? args.storageKey : undefined;
291
+ const storageKeysByOrigin:
292
+ Record<string, {partitions?: Array<{storageKey: string, keys: string[]}>, error?: string}> = {};
293
+
294
+ await Promise.all(targetOrigins.map(async origin => {
295
+ const storages = resolveDOMStorages(this.context, args.type, origin, this.targetManager, storageKey);
296
+ const keyAndItems = await Promise.all(storages.map(async storage => {
297
+ const items = await storage.getItems();
298
+ return {storageKey: storage.storageKey, items};
299
+ }));
300
+
301
+ const partitions = [];
302
+ for (const {storageKey, items} of keyAndItems) {
303
+ if (!items) {
304
+ continue;
305
+ }
306
+ const keys = items.map(([key]) => key);
307
+ if (keys.length > 0) {
308
+ partitions.push({storageKey, keys});
309
+ }
288
310
  }
289
- }
311
+ storageKeysByOrigin[origin] = {partitions};
312
+ }));
290
313
 
291
- return {result: {partitions: partitionsResult}};
314
+ return {result: {storageKeysByOrigin}};
292
315
  },
293
316
  });
294
317
 
295
- this.declareFunction<{
296
- type: 'localStorage' | 'sessionStorage',
297
- keys: string[],
298
- origin: string,
299
- storageKey?: string,
300
- },
301
- {
302
- items: Array<{
303
- storageKey: string,
304
- values: Record<string, string>,
305
- }>,
306
- }>('getStorageValues', {
307
- description: 'Retrieve specific string values from storage partitions for requested keys.',
318
+ this.declareFunction<
319
+ {
320
+ type: 'localStorage' | 'sessionStorage',
321
+ keys: string[],
322
+ origins: string[],
323
+ storageKey?: string,
324
+ },
325
+ {
326
+ storageValuesByOrigin:
327
+ Record<string, {items?: Array<{storageKey: string, values: Record<string, string>}>, error?: string}>,
328
+ }>('getStorageValues', {
329
+ description: 'Retrieve specific string values from storage partitions for requested keys across origins.',
308
330
  parameters: {
309
331
  type: Host.AidaClient.ParametersTypes.OBJECT,
310
332
  description: '',
@@ -321,23 +343,25 @@ export class StorageAgent extends AiAgent<StorageItem> {
321
343
  items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'A storage key.'},
322
344
  nullable: false,
323
345
  },
324
- origin: {
325
- type: Host.AidaClient.ParametersTypes.STRING,
326
- description: 'Specific origin to get values for.',
346
+ origins: {
347
+ type: Host.AidaClient.ParametersTypes.ARRAY,
348
+ description: 'List of origins to get values for.',
349
+ items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'An origin URL.'},
327
350
  nullable: false,
328
351
  },
329
352
  storageKey: {
330
353
  type: Host.AidaClient.ParametersTypes.STRING,
331
- description: 'Optional. Specific storageKey partition to get values for.',
354
+ description:
355
+ 'Optional. Specific storageKey partition to get values for. Only applies if single origin is provided.',
332
356
  nullable: true,
333
357
  },
334
358
  },
335
- required: ['type', 'keys', 'origin'],
359
+ required: ['type', 'keys', 'origins'],
336
360
  },
337
361
  displayInfoFromArgs: args => {
338
362
  return {
339
363
  title: lockedString('Reading storage values'),
340
- action: `getStorageValues('${args.type}', ${JSON.stringify(args.keys)}, '${args.origin}'${
364
+ action: `getStorageValues('${args.type}', ${JSON.stringify(args.keys)}, ${JSON.stringify(args.origins)}${
341
365
  args.storageKey ? `, '${args.storageKey}'` : ''})`,
342
366
  };
343
367
  },
@@ -348,19 +372,26 @@ export class StorageAgent extends AiAgent<StorageItem> {
348
372
  return {error: 'No origin available or not allowed.'};
349
373
  }
350
374
 
351
- const storages = resolveDOMStorages(this.context, args.type, args.origin, this.targetManager, args.storageKey);
352
- if (storages.length === 0) {
375
+ const targetOrigins = resolveTargetOrigins(this.context, args.origins);
376
+ const storageKey = (targetOrigins.length === 1 && args.storageKey) ? args.storageKey : undefined;
377
+
378
+ const allStoragesMap: Record<string, SDK.DOMStorageModel.DOMStorage[]> = {};
379
+ let totalStoragesCount = 0;
380
+ for (const origin of targetOrigins) {
381
+ const storages = resolveDOMStorages(this.context, args.type, origin, this.targetManager, storageKey);
382
+ if (storages.length > 0) {
383
+ allStoragesMap[origin] = storages;
384
+ totalStoragesCount += storages.length;
385
+ }
386
+ }
387
+
388
+ if (totalStoragesCount === 0) {
353
389
  return {error: 'No matching storage partitions found.'};
354
390
  }
355
391
 
356
392
  if (options?.approved !== true) {
357
393
  const keyString = args.keys.map(k => `\`${k}\``).join(', ');
358
-
359
- const uniqueTargetOrigins = Array.from(new Set(storages.map(storage => {
360
- const parsed = SDK.StorageKeyManager.parseStorageKey(storage.storageKey || '');
361
- return parsed.origin;
362
- })));
363
- const targetsDesc = uniqueTargetOrigins.join(', ');
394
+ const targetsDesc = Object.keys(allStoragesMap).join(', ');
364
395
 
365
396
  return {
366
397
  requiresApproval: true,
@@ -369,60 +400,70 @@ export class StorageAgent extends AiAgent<StorageItem> {
369
400
  };
370
401
  }
371
402
 
372
- const itemsResult = [];
403
+ const storageValuesByOrigin:
404
+ Record<string, {items?: Array<{storageKey: string, values: Record<string, string>}>, error?: string}> = {};
373
405
 
374
- const keyAndItems = await Promise.all(storages.map(async storage => {
375
- const items = await storage.getItems();
376
- return {storageKey: storage.storageKey, items};
377
- }));
406
+ await Promise.all(targetOrigins.map(async origin => {
407
+ const storages = allStoragesMap[origin] || [];
408
+ const itemsResult = [];
378
409
 
379
- for (const {storageKey, items} of keyAndItems) {
380
- if (!items) {
381
- continue;
382
- }
383
- const itemMap = new Map<string, string>(items as Array<[string, string]>);
384
- const storageValues: Record<string, string> = {};
410
+ const keyAndItems = await Promise.all(storages.map(async storage => {
411
+ const items = await storage.getItems();
412
+ return {storageKey: storage.storageKey, items};
413
+ }));
385
414
 
386
- for (const key of args.keys) {
387
- const value = itemMap.get(key);
388
- if (value === undefined) {
415
+ for (const {storageKey: partitionKey, items} of keyAndItems) {
416
+ if (!items) {
389
417
  continue;
390
418
  }
391
- const truncatedValue = value.length > MAX_NUM_CHAR_LENGTH ?
392
- value.substring(0, MAX_NUM_CHAR_LENGTH) + '... <truncated>' :
393
- value;
394
- storageValues[key] = truncatedValue;
395
- }
419
+ const itemMap = new Map<string, string>(items as Array<[string, string]>);
420
+ const storageValues: Record<string, string> = {};
421
+
422
+ for (const key of args.keys) {
423
+ const value = itemMap.get(key);
424
+ if (value === undefined) {
425
+ continue;
426
+ }
427
+ const truncatedValue = value.length > MAX_NUM_CHAR_LENGTH ?
428
+ value.substring(0, MAX_NUM_CHAR_LENGTH) + '... <truncated>' :
429
+ value;
430
+ storageValues[key] = truncatedValue;
431
+ }
396
432
 
397
- itemsResult.push({storageKey, values: storageValues});
398
- }
433
+ itemsResult.push({storageKey: partitionKey, values: storageValues});
434
+ }
435
+ storageValuesByOrigin[origin] = {items: itemsResult};
436
+ }));
399
437
 
400
- return {result: {items: itemsResult}};
438
+ return {result: {storageValuesByOrigin}};
401
439
  },
402
440
  });
403
441
 
404
442
  this.declareFunction<{
405
- origin: string,
443
+ origins: string[],
406
444
  },
407
- {cookies: string[]}>('listCookies', {
408
- description: 'Lists all cookies for the requested origin, strictly excluding their values.',
445
+ {
446
+ cookieNamesByOrigin: Record<string, {cookies?: string[], error?: string}>,
447
+ }>('listCookies', {
448
+ description: 'Lists all cookies for requested origins, strictly excluding their values.',
409
449
  parameters: {
410
450
  type: Host.AidaClient.ParametersTypes.OBJECT,
411
451
  description: '',
412
452
  nullable: false,
413
453
  properties: {
414
- origin: {
415
- type: Host.AidaClient.ParametersTypes.STRING,
416
- description: 'Origin to list cookies for.',
454
+ origins: {
455
+ type: Host.AidaClient.ParametersTypes.ARRAY,
456
+ description: 'List of origins to list cookies for.',
457
+ items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'An origin URL.'},
417
458
  nullable: false,
418
459
  },
419
460
  },
420
- required: ['origin'],
461
+ required: ['origins'],
421
462
  },
422
463
  displayInfoFromArgs: args => {
423
464
  return {
424
465
  title: lockedString('Reading cookies'),
425
- action: `listCookies('${args.origin}')`,
466
+ action: `listCookies(${JSON.stringify(args.origins)})`,
426
467
  };
427
468
  },
428
469
  handler: async args => {
@@ -431,27 +472,34 @@ export class StorageAgent extends AiAgent<StorageItem> {
431
472
  return {error: 'No origin available or not allowed.'};
432
473
  }
433
474
 
434
- const frame = findFrameForOrigin(this.context, args.origin, this.targetManager);
435
- if (!frame) {
436
- return {result: {cookies: []}};
437
- }
475
+ const targetOrigins = resolveTargetOrigins(this.context, args.origins);
476
+ const cookieNamesByOrigin: Record<string, {cookies?: string[], error?: string}> = {};
477
+
478
+ await Promise.all(targetOrigins.map(async origin => {
479
+ const frame = findFrameForOrigin(this.context, origin, this.targetManager);
480
+ if (!frame) {
481
+ cookieNamesByOrigin[origin] = {error: 'Frame not found or origin disallowed'};
482
+ return;
483
+ }
438
484
 
439
- const target = frame.resourceTreeModel().target();
440
- const cookies = await getCookiesForDomain(target, args.origin);
441
- const uniqueNames = Array.from(new Set(cookies?.map(c => c.name())));
485
+ const target = frame.resourceTreeModel().target();
486
+ const cookies = await getCookiesForDomain(target, origin);
487
+ const uniqueNames = Array.from(new Set(cookies?.map(c => c.name())));
488
+ cookieNamesByOrigin[origin] = {cookies: uniqueNames};
489
+ }));
442
490
 
443
- return {result: {cookies: uniqueNames}};
491
+ return {result: {cookieNamesByOrigin}};
444
492
  },
445
493
  });
446
494
 
447
495
  this.declareFunction<{
448
496
  cookieNames: string[],
449
- origin: string,
497
+ origins: string[],
450
498
  },
451
499
  {
452
- cookies: CookieDetails[],
500
+ cookiesByOrigin: Record<string, {cookies?: CookieDetails[], error?: string}>,
453
501
  }>('getCookieValues', {
454
- description: 'Retrieve the values and detailed metadata of specific cookies by their names.',
502
+ description: 'Retrieve the values and detailed metadata of specific cookies by their names across origins.',
455
503
  parameters: {
456
504
  type: Host.AidaClient.ParametersTypes.OBJECT,
457
505
  description: '',
@@ -463,18 +511,19 @@ export class StorageAgent extends AiAgent<StorageItem> {
463
511
  items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'A cookie name.'},
464
512
  nullable: false,
465
513
  },
466
- origin: {
467
- type: Host.AidaClient.ParametersTypes.STRING,
468
- description: 'The specific origin the cookies belong to.',
514
+ origins: {
515
+ type: Host.AidaClient.ParametersTypes.ARRAY,
516
+ description: 'List of origins the cookies belong to.',
517
+ items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'An origin URL.'},
469
518
  nullable: false,
470
519
  },
471
520
  },
472
- required: ['cookieNames', 'origin'],
521
+ required: ['cookieNames', 'origins'],
473
522
  },
474
523
  displayInfoFromArgs: args => {
475
524
  return {
476
525
  title: lockedString('Reading cookie values and metadata'),
477
- action: `getCookieValues(${JSON.stringify(args.cookieNames)}, '${args.origin}')`,
526
+ action: `getCookieValues(${JSON.stringify(args.cookieNames)}, ${JSON.stringify(args.origins)})`,
478
527
  };
479
528
  },
480
529
  handler: async (args, options) => {
@@ -483,61 +532,67 @@ export class StorageAgent extends AiAgent<StorageItem> {
483
532
  return {error: 'No origin available or not allowed.'};
484
533
  }
485
534
 
486
- const frame = findFrameForOrigin(this.context, args.origin, this.targetManager);
487
- if (!frame) {
488
- return {result: {cookies: []}};
489
- }
490
-
491
- const target = frame.resourceTreeModel().target();
535
+ const targetOrigins = resolveTargetOrigins(this.context, args.origins);
492
536
 
493
537
  if (options?.approved !== true) {
494
538
  return {
495
539
  requiresApproval: true,
496
540
  description: lockedString(`The AI wants to access the value(s) and metadata of cookie(s) ${
497
- args.cookieNames.map(name => `\`${name}\``).join(', ')} on ${args.origin}.`),
541
+ args.cookieNames.map(name => `\`${name}\``).join(', ')} on ${targetOrigins.join(', ')}.`),
498
542
  };
499
543
  }
500
544
 
501
- const cookies = await getCookiesForDomain(target, args.origin);
502
- if (!cookies) {
503
- return {result: {cookies: []}};
504
- }
545
+ const cookiesByOrigin: Record<string, {cookies?: CookieDetails[], error?: string}> = {};
505
546
 
506
- const matchingCookies = cookies.filter(c => args.cookieNames.includes(c.name()));
507
- const cookieData = matchingCookies.map(cookie => {
508
- const value = cookie.value();
509
- const truncatedValue =
510
- value.length > MAX_NUM_CHAR_LENGTH ? value.substring(0, MAX_NUM_CHAR_LENGTH) + '... <truncated>' : value;
547
+ await Promise.all(targetOrigins.map(async origin => {
548
+ const frame = findFrameForOrigin(this.context, origin, this.targetManager);
549
+ if (!frame) {
550
+ cookiesByOrigin[origin] = {error: 'Frame not found or origin disallowed'};
551
+ return;
552
+ }
511
553
 
512
- return {
513
- value: truncatedValue,
514
- domain: cookie.domain(),
515
- path: cookie.path(),
516
- expires: cookie.expires(),
517
- size: cookie.size(),
518
- secure: cookie.secure(),
519
- sameSite: cookie.sameSite(),
520
- partitioned: cookie.partitioned(),
521
- priority: cookie.priority(),
522
- sourcePort: cookie.sourcePort(),
523
- sourceScheme: cookie.sourceScheme(),
524
- };
525
- });
554
+ const target = frame.resourceTreeModel().target();
555
+ const cookies = await getCookiesForDomain(target, origin);
556
+ if (!cookies) {
557
+ cookiesByOrigin[origin] = {cookies: []};
558
+ return;
559
+ }
526
560
 
527
- return {result: {cookies: cookieData}};
561
+ const matchingCookies = cookies.filter(c => args.cookieNames.includes(c.name()));
562
+ const cookieData = matchingCookies.map(cookie => {
563
+ const value = cookie.value();
564
+ const truncatedValue = value.length > MAX_NUM_CHAR_LENGTH ?
565
+ value.substring(0, MAX_NUM_CHAR_LENGTH) + '... <truncated>' :
566
+ value;
567
+
568
+ return {
569
+ value: truncatedValue,
570
+ domain: cookie.domain(),
571
+ path: cookie.path(),
572
+ expires: cookie.expires(),
573
+ size: cookie.size(),
574
+ secure: cookie.secure(),
575
+ sameSite: cookie.sameSite(),
576
+ partitioned: cookie.partitioned(),
577
+ priority: cookie.priority(),
578
+ sourcePort: cookie.sourcePort(),
579
+ sourceScheme: cookie.sourceScheme(),
580
+ };
581
+ });
582
+ cookiesByOrigin[origin] = {cookies: cookieData};
583
+ }));
584
+
585
+ return {result: {cookiesByOrigin}};
528
586
  },
529
587
  });
530
588
 
531
589
  this.declareFunction<Record<string, never>, {
532
- totalUsage: string,
533
- totalQuota: string,
534
590
  usageBreakdown: Array<{
535
591
  storageType: string,
536
592
  usage: string,
537
593
  }>,
538
594
  }>('getStorageBreakdown', {
539
- description:
540
- 'Retrieves the total storage usage, total storage quota, and a breakdown of active storage usage per storage type for the top-level page.',
595
+ description: 'Retrieves a breakdown of active storage usage per storage type for the top-level page.',
541
596
  parameters: {
542
597
  type: Host.AidaClient.ParametersTypes.OBJECT,
543
598
  description: '',
@@ -557,23 +612,49 @@ export class StorageAgent extends AiAgent<StorageItem> {
557
612
  return {error: 'No origin available or not allowed.'};
558
613
  }
559
614
 
560
- const origin = this.context.getOrigin();
615
+ const origin = this.context.getItem().primaryTargetOrigin;
561
616
  const response = await target.storageAgent().invoke_getUsageAndQuota({origin});
562
617
  if (response.getError()) {
563
618
  return {error: response.getError() || 'Unknown CDP error'};
564
619
  }
565
620
 
566
- const usageBreakdown = response.usageBreakdown.filter(entry => entry.usage > 0)
567
- .sort((a, b) => b.usage - a.usage)
568
- .map(entry => ({
569
- storageType: entry.storageType as string,
570
- usage: bytes(entry.usage),
571
- }));
621
+ const mainStorageKey = target.model(SDK.StorageKeyManager.StorageKeyManager)?.mainStorageKey() || undefined;
622
+ const localStorages =
623
+ resolveDOMStorages(this.context, 'localStorage', origin, this.targetManager, mainStorageKey);
624
+ const localStorageBytes = await calculateDOMStoragesUsage(localStorages);
625
+
626
+ const sessionStorages =
627
+ resolveDOMStorages(this.context, 'sessionStorage', origin, this.targetManager, mainStorageKey);
628
+ const sessionStorageBytes = await calculateDOMStoragesUsage(sessionStorages);
629
+
630
+ const cookies = await getCookiesForDomain(target, origin);
631
+ let cookieBytes = 0;
632
+ if (cookies) {
633
+ for (const cookie of cookies) {
634
+ cookieBytes += cookie.size();
635
+ }
636
+ }
637
+
638
+ const rawUsageBreakdown: Array<{storageType: string, rawUsage: number}> =
639
+ response.usageBreakdown.filter(entry => entry.usage > 0).map(entry => ({
640
+ storageType: entry.storageType as string,
641
+ rawUsage: entry.usage,
642
+ }));
643
+
644
+ rawUsageBreakdown.push(
645
+ {storageType: 'local_storage', rawUsage: localStorageBytes},
646
+ {storageType: 'session_storage', rawUsage: sessionStorageBytes},
647
+ {storageType: 'cookies', rawUsage: cookieBytes},
648
+ );
649
+
650
+ rawUsageBreakdown.sort((a, b) => b.rawUsage - a.rawUsage);
651
+ const usageBreakdown = rawUsageBreakdown.map(entry => ({
652
+ storageType: entry.storageType,
653
+ usage: bytes(entry.rawUsage),
654
+ }));
572
655
 
573
656
  return {
574
657
  result: {
575
- totalUsage: bytes(response.usage),
576
- totalQuota: bytes(response.quota),
577
658
  usageBreakdown,
578
659
  },
579
660
  };
@@ -586,13 +667,15 @@ export class StorageAgent extends AiAgent<StorageItem> {
586
667
  if (item instanceof CookieItem) {
587
668
  const parsedURL = Common.ParsedURL.ParsedURL.fromString(item.origin);
588
669
  const domain = parsedURL ? parsedURL.host : item.origin;
589
- return `${primaryTargetOrigin}\nUser-selected Context: Cookies\nDomain: ${domain}${
590
- item.name ? `\nCookie Name: ${item.name}` : ''}`;
670
+ return `${primaryTargetOrigin}\nUser-selected Context: Cookies${
671
+ item.isGenericContext ? '' : `\nDomain: ${domain}`}${item.name ? `\nCookie Name: ${item.name}` : ''}`;
591
672
  }
592
673
 
593
674
  if (item instanceof DOMStorageItem) {
594
- return `${primaryTargetOrigin}\nUser-selected Context: DOM Storage\n Type: ${item.type}\nStorageKey: ${
595
- item.storageKey}\nOrigin: ${item.origin}${item.key ? `\nKey: ${item.key}` : ''}`;
675
+ return `${primaryTargetOrigin}\nUser-selected Context: DOM Storage\n Type: ${item.type}${
676
+ item.isGenericContext ?
677
+ '' :
678
+ `\nStorageKey: ${item.storageKey}\nOrigin: ${item.origin}`}${item.key ? `\nKey: ${item.key}` : ''}`;
596
679
  }
597
680
 
598
681
  return primaryTargetOrigin;
@@ -670,6 +753,21 @@ export function findFrameForOrigin(
670
753
  }
671
754
  return null;
672
755
  }
756
+
757
+ async function calculateDOMStoragesUsage(storages: SDK.DOMStorageModel.DOMStorage[]): Promise<number> {
758
+ let totalBytes = 0;
759
+ for (const storage of storages) {
760
+ const items = await storage.getItems();
761
+ if (items) {
762
+ for (const [key, value] of items) {
763
+ // UTF-16 encoded strings use 2 bytes per character.
764
+ totalBytes += (key.length + value.length) * 2;
765
+ }
766
+ }
767
+ }
768
+ return totalBytes;
769
+ }
770
+
673
771
  export function resolveDOMStorages(
674
772
  context: ConversationContext<StorageItem>|undefined, type: 'localStorage'|'sessionStorage', origin: string,
675
773
  targetManager: SDK.TargetManager.TargetManager, storageKey?: string): SDK.DOMStorageModel.DOMStorage[] {