chrome-devtools-frontend 1.0.1636056 → 1.0.1640418

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/front_end/core/common/Color.ts +0 -4
  2. package/front_end/core/host/AidaClientTypes.ts +8 -6
  3. package/front_end/core/root/Runtime.ts +2 -2
  4. package/front_end/core/sdk/DOMStorageModel.ts +1 -1
  5. package/front_end/core/sdk/SourceMap.ts +8 -3
  6. package/front_end/core/sdk/TargetManager.ts +14 -1
  7. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +147 -0
  8. package/front_end/generated/ARIAProperties.js +17 -4
  9. package/front_end/generated/InspectorBackendCommands.ts +13 -7
  10. package/front_end/generated/SupportedCSSProperties.js +1 -0
  11. package/front_end/generated/protocol-mapping.d.ts +7 -0
  12. package/front_end/generated/protocol-proxy-api.d.ts +14 -0
  13. package/front_end/generated/protocol.ts +120 -2
  14. package/front_end/global_typings/global_defs.d.ts +13 -0
  15. package/front_end/models/ai_assistance/AiAgent2.ts +116 -0
  16. package/front_end/models/ai_assistance/AiConversation.ts +22 -36
  17. package/front_end/models/ai_assistance/AiHistoryStorage.ts +0 -1
  18. package/front_end/models/ai_assistance/AiOrigins.ts +46 -0
  19. package/front_end/models/ai_assistance/AiUtils.ts +9 -0
  20. package/front_end/models/ai_assistance/README.md +16 -0
  21. package/front_end/models/ai_assistance/StorageItem.ts +30 -26
  22. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +12 -5
  23. package/front_end/models/ai_assistance/agents/AiAgent.ts +86 -32
  24. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +2 -2
  25. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +31 -10
  26. package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +1 -1
  27. package/front_end/models/ai_assistance/agents/FileAgent.ts +2 -2
  28. package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +1 -3
  29. package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +19 -0
  30. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +9 -4
  31. package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +2 -2
  32. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +41 -12
  33. package/front_end/models/ai_assistance/agents/StorageAgent.ts +442 -122
  34. package/front_end/models/ai_assistance/agents/StylingAgent.ts +2 -2
  35. package/front_end/models/ai_assistance/ai_assistance.ts +4 -2
  36. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +2 -2
  37. package/front_end/models/ai_assistance/performance/AIContext.ts +7 -8
  38. package/front_end/models/ai_assistance/skills/README.md +40 -0
  39. package/front_end/models/ai_assistance/skills/Skill.ts +13 -0
  40. package/front_end/models/ai_assistance/skills/SkillRegistry.ts +10 -0
  41. package/front_end/models/ai_assistance/skills/styling.md +6 -0
  42. package/front_end/models/bindings/CompilerScriptMapping.ts +12 -4
  43. package/front_end/models/breakpoints/BreakpointManager.ts +54 -2
  44. package/front_end/models/greendev/Prototypes.ts +0 -7
  45. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +20 -0
  46. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +5 -0
  47. package/front_end/models/issues_manager/EmailVerificationRequestIssue.ts +293 -0
  48. package/front_end/models/issues_manager/IssuesManager.ts +5 -0
  49. package/front_end/models/issues_manager/descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
  50. package/front_end/models/issues_manager/descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
  51. package/front_end/models/issues_manager/descriptions/emailVerificationRequestInvalidEmail.md +1 -0
  52. package/front_end/models/issues_manager/descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
  53. package/front_end/models/issues_manager/descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
  54. package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
  55. package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
  56. package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
  57. package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
  58. package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
  59. package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
  60. package/front_end/models/issues_manager/descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
  61. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
  62. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
  63. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
  64. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
  65. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
  66. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
  67. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
  68. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
  69. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
  70. package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
  71. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  72. package/front_end/models/javascript_metadata/NativeFunctions.js +1748 -1739
  73. package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +1 -1
  74. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +9 -1
  75. package/front_end/models/stack_trace/StackTraceImpl.ts +29 -9
  76. package/front_end/models/stack_trace/StackTraceModel.ts +23 -11
  77. package/front_end/models/stack_trace/Trie.ts +11 -1
  78. package/front_end/models/trace/extras/TraceTree.ts +20 -1
  79. package/front_end/models/trace/insights/Common.ts +9 -0
  80. package/front_end/models/trace/lantern/core/NetworkAnalyzer.ts +21 -25
  81. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +19 -75
  82. package/front_end/panels/ai_assistance/components/AccessibilityAgentMarkdownRenderer.ts +10 -3
  83. package/front_end/panels/ai_assistance/components/ChatMessage.ts +148 -2
  84. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +2 -3
  85. package/front_end/panels/ai_assistance/components/chatMessage.css +27 -0
  86. package/front_end/panels/application/CookieItemsView.ts +24 -0
  87. package/front_end/panels/application/DOMStorageItemsView.ts +9 -4
  88. package/front_end/panels/application/preloading/components/PreloadingString.ts +6 -0
  89. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +4 -4
  90. package/front_end/panels/console/ConsoleViewMessage.ts +13 -102
  91. package/front_end/panels/elements/StandaloneStylesContainer.ts +10 -0
  92. package/front_end/panels/elements/StylePropertiesSection.ts +6 -2
  93. package/front_end/panels/elements/StylePropertyTreeElement.ts +30 -1
  94. package/front_end/panels/elements/StylesContainer.ts +3 -0
  95. package/front_end/panels/elements/StylesSidebarPane.ts +54 -4
  96. package/front_end/panels/elements/elements-meta.ts +14 -0
  97. package/front_end/panels/layer_viewer/layerDetailsView.css +1 -1
  98. package/front_end/panels/lighthouse/LighthouseController.ts +1 -1
  99. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +4 -4
  100. package/front_end/panels/network/NetworkDataGridNode.ts +14 -0
  101. package/front_end/panels/network/NetworkLogViewColumns.ts +2 -2
  102. package/front_end/panels/network/RequestHeadersView.ts +55 -19
  103. package/front_end/panels/network/networkTimingTable.css +2 -4
  104. package/front_end/panels/recorder/components/ReplaySection.ts +28 -16
  105. package/front_end/panels/recorder/converters/LighthouseConverter.snapshot.txt +47 -0
  106. package/front_end/panels/recorder/converters/PuppeteerConverter.snapshot.txt +49 -0
  107. package/front_end/panels/recorder/converters/PuppeteerReplayConverter.snapshot.txt +33 -0
  108. package/front_end/panels/settings/SettingsScreen.ts +1 -2
  109. package/front_end/panels/sources/BreakpointsView.ts +23 -42
  110. package/front_end/panels/sources/DebuggerPlugin.ts +12 -5
  111. package/front_end/panels/sources/ScopeChainSidebarPane.ts +169 -106
  112. package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -0
  113. package/front_end/third_party/chromium/README.chromium +1 -1
  114. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +2 -2
  115. package/front_end/third_party/marked/README.chromium +3 -6
  116. package/front_end/third_party/marked/package/README.md +5 -5
  117. package/front_end/third_party/marked/package/bin/main.js +27 -22
  118. package/front_end/third_party/marked/package/bin/marked.js +2 -1
  119. package/front_end/third_party/marked/package/lib/marked.esm.d.ts +346 -256
  120. package/front_end/third_party/marked/package/lib/marked.esm.js +67 -2698
  121. package/front_end/third_party/marked/package/lib/marked.esm.js.map +7 -1
  122. package/front_end/third_party/marked/package/lib/marked.umd.js +69 -2722
  123. package/front_end/third_party/marked/package/lib/marked.umd.js.map +7 -1
  124. package/front_end/third_party/marked/package/man/marked.1 +4 -2
  125. package/front_end/third_party/marked/package/man/marked.1.md +2 -1
  126. package/front_end/third_party/marked/package/package.json +49 -57
  127. package/front_end/third_party/puppeteer-replay/README.chromium +2 -2
  128. package/front_end/third_party/puppeteer-replay/package/lib/cli.js +84 -80
  129. package/front_end/third_party/puppeteer-replay/package/lib/cli.js.map +1 -1
  130. package/front_end/third_party/puppeteer-replay/package/lib/extension-test.js +79 -83
  131. package/front_end/third_party/puppeteer-replay/package/lib/extension-test.js.map +1 -1
  132. package/front_end/third_party/puppeteer-replay/package/lib/main.d.ts +43 -171
  133. package/front_end/third_party/puppeteer-replay/package/lib/main.js +51 -206
  134. package/front_end/third_party/puppeteer-replay/package/lib/main.js.map +1 -1
  135. package/front_end/third_party/puppeteer-replay/package/package.json +37 -67
  136. package/front_end/tsconfig.json +1 -1
  137. package/front_end/ui/components/markdown_view/CodeBlock.ts +17 -6
  138. package/front_end/ui/components/markdown_view/MarkdownView.ts +39 -3
  139. package/front_end/ui/components/markdown_view/codeBlock.css +11 -0
  140. package/front_end/ui/components/markdown_view/markdownView.css +17 -0
  141. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +0 -79
  142. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +16 -4
  143. package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
  144. package/inspector_overlay/testing/InspectorOverlayHelpers.ts +2 -0
  145. package/mcp/mcp.ts +1 -6
  146. package/package.json +14 -16
  147. package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgent.ts +0 -1015
  148. package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgentOverlay.ts +0 -87
  149. package/front_end/third_party/marked/package/bin/marked +0 -215
  150. package/front_end/third_party/marked/package/lib/marked.cjs +0 -2726
  151. package/front_end/third_party/marked/package/lib/marked.cjs.map +0 -1
  152. package/front_end/third_party/marked/package/lib/marked.d.cts +0 -670
  153. package/front_end/third_party/marked/package/lib/marked.js +0 -2780
  154. package/front_end/third_party/marked/package/man/marked.1.txt +0 -86
  155. package/front_end/third_party/marked/package/marked.min.js +0 -6
  156. package/front_end/third_party/marked/package/src/Lexer.js +0 -492
  157. package/front_end/third_party/marked/package/src/Parser.js +0 -286
  158. package/front_end/third_party/marked/package/src/Renderer.js +0 -166
  159. package/front_end/third_party/marked/package/src/Slugger.js +0 -49
  160. package/front_end/third_party/marked/package/src/TextRenderer.js +0 -42
  161. package/front_end/third_party/marked/package/src/Tokenizer.js +0 -755
  162. package/front_end/third_party/marked/package/src/defaults.js +0 -29
  163. package/front_end/third_party/marked/package/src/helpers.js +0 -249
  164. package/front_end/third_party/marked/package/src/marked.js +0 -350
  165. package/front_end/third_party/marked/package/src/rules.js +0 -285
  166. package/front_end/third_party/puppeteer-replay/package/lib/cjs/main.cjs +0 -2099
  167. package/front_end/third_party/puppeteer-replay/package/lib/cjs/main.cjs.map +0 -1
  168. package/front_end/third_party/puppeteer-replay/package/lib/cjs/main.d.cts +0 -686
  169. package/front_end/third_party/puppeteer-replay/package/lib/cjs/main.d.ts +0 -35
  170. package/mcp/HostBindings.ts +0 -319
  171. /package/front_end/third_party/marked/package/{LICENSE.md → LICENSE} +0 -0
@@ -2,11 +2,12 @@
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';
7
8
  import * as Root from '../../../core/root/root.js';
8
9
  import * as SDK from '../../../core/sdk/sdk.js';
9
- import type {StorageItem} from '../StorageItem.js';
10
+ import {CookieItem, DOMStorageItem, type StorageItem} from '../StorageItem.js';
10
11
 
11
12
  import {
12
13
  type AgentOptions,
@@ -19,37 +20,56 @@ import {
19
20
 
20
21
  const lockedString = i18n.i18n.lockedString;
21
22
 
22
- // TODO(kimanh): Replace temporary preamble as soon as functions are implemented
23
23
  const preamble =
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 and SessionStorage.
25
-
26
- You have access to the site's storage using tools like \`listStorageKeys\` and \`getStorageValues\` to analyze storage state.
27
-
28
- # Goals
29
-
30
- 1. **Explain Purpose**: Identify what specific storage entries are for.
31
- 2. **Understand Application State**: Help users inspect, understand, and audit the state stored in their browser storage, and how it relates to their application's behavior or potential issues (such as state mismatch or drift).
32
-
33
- # Tools & Workflow
34
-
35
- - Use \`listStorageKeys\` to survey the keys available for Local or Session storage.
36
- - Use \`getStorageValues\` to access the values of specific Local or Session storage keys.
37
- - **CRITICAL**: Only access storage values when the keys/names are not enough, and if you have a good reason to access them.
38
-
39
- If the user asks a question that requires an investigation of a problem, use this structure for answering:
40
-
41
- - If available, point out the root cause(s) of the problem.
42
- - Example: "**Root Cause**: The UI theme is resetting because the 'uiTheme' local storage key is set to an invalid value."
43
- - If applicable, list actionable solution suggestion(s) in order of impact:
44
- - Example: "**Suggestion**: Clear the 'uiTheme' local storage key or set it to 'light' or 'dark'."
45
-
46
- # Considerations
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
+
26
+ You have access to the site's storage using tools like \`listPageOrigins\`, \`listStorageKeys\`, \`getStorageValues\`, \`listCookies\`, and \`getCookieValues\`.
27
+
28
+ # Goals
29
+
30
+ 1. **Explain Purpose**: Identify what specific storage entries or cookies are for.
31
+ 2. **Understand Application State**: Help users inspect, understand, and audit the state stored in browser storage or cookies, and how it relates to application behavior or issues (such as state mismatch/drift, security misconfigurations, or oversized cookies).
32
+ 3. **Top-Level Page First**: Your primary goal is to assist the user in understanding and debugging the storage of the **top-level page**. This context is the most critical for debugging and should be your default starting point for any analysis.
33
+
34
+ # Tools & Workflow
35
+
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
+ - **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
+ - **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
+ - **Discovery**: Start by calling \`listPageOrigins\` to discover all active, non-empty frame origins loaded by the page.
40
+ - **Storage Partitioning (LocalStorage / SessionStorage)**:
41
+ - Use \`listStorageKeys\` to survey keys. The results are grouped into **partitions** characterized by unique \`storageKey\` strings.
42
+ - Be aware that the same origin can have multiple storage partitions depending on frame ancestry.
43
+ - Use \`getStorageValues\` to inspect specific keys. The results are grouped into an array of partition \`items\` matching the requested keys under their unique \`storageKey\`.
44
+ - **Cookies**:
45
+ - Use \`listCookies\` to discover active cookies for an origin. Note that cookies are visible by domain scopes, paths, and partition status.
46
+ - Use \`getCookieValues\` to retrieve the values and detailed metadata of specific cookies by name.
47
+ - **HttpOnly Protection**: You don't have access to \`HttpOnly\` cookies. They are filtered out from both discovery and retrieval tools for security reasons.
48
+ - **Active Context**: Start by inspecting the active context's origin (provided in the '# Active Context' section of the prompt).
49
+ - **Value Minimization**: Only request values using \`getStorageValues\` or \`getCookieValues\` when key names/cookie names alone are insufficient.
50
+
51
+ # Considerations
52
+
53
+ - **Strictly Read-Only**: You cannot write, clear, delete, or edit storage or cookies.
54
+ - **DevTools UI Fallback**: If the user asks you to modify state, politely decline and provide exact step-by-step visual navigation directions on how they can perform the edit manually in the DevTools Application panel. Do NOT supply Console scripts.
55
+ - **Raw Evidence**: Treat storage data as raw evidence. Do not make assumptions about values without reading them first.
56
+ - **Dynamic State**: Always re-request values if you suspect they might have changed, rather than relying on past tool outputs.
57
+ - **CRITICAL**: Use the precision of Strunk & White, the brevity of Hemingway, and the simple clarity of Vonnegut. Don't add repeated information, and keep the whole answer short.
58
+ - **CRITICAL**: You are a storage debugging assistant. NEVER answer unrelated topics (legal, financial, race, sexuality, medical, religion, politics). If asked, respond: "Sorry, I can't answer that. I'm best at questions about debugging web pages."
59
+ `;
60
+
61
+ function isSamePrimaryPageOrigin(context?: ConversationContext<StorageItem>): boolean {
62
+ const primaryPageTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
63
+ return isSamePageOrigin(primaryPageTarget, context);
64
+ }
47
65
 
48
- - **Raw Evidence**: Treat storage data as "raw evidence". Do not make assumptions.
49
- - **Dynamic State**: Storage values may change over time as the user interacts with the page. ALWAYS re-request values using the \`getStorageValues\` tool when you need to inspect them, even if you have already requested them in the past. Do NOT rely on previously cached values in your memory.
50
- - **Brevity**: Use the precision of Strunk & White, the brevity of Hemingway, and the simple clarity of Vonnegut. Keep answers short and actionable.
51
- - **CRITICAL**: You are a storage debugging assistant. NEVER provide answers to questions of unrelated topics such as legal advice, financial advice, personal opinions, medical advice, religion, race, politics, sexuality, gender, or any other non web-development topics. Answer "Sorry, I can't answer that. I'm best at questions about debugging web pages." to such questions.
52
- `;
66
+ function isSamePageOrigin(target: SDK.Target.Target|null, context?: ConversationContext<StorageItem>): boolean {
67
+ if (!target || !context) {
68
+ return false;
69
+ }
70
+ const pageOrigin = Common.ParsedURL.ParsedURL.extractOrigin(target.inspectedURL());
71
+ return pageOrigin !== '' && context.isOriginAllowed(pageOrigin);
72
+ }
53
73
 
54
74
  export class StorageContext extends ConversationContext<StorageItem> {
55
75
  #item: StorageItem;
@@ -59,8 +79,8 @@ export class StorageContext extends ConversationContext<StorageItem> {
59
79
  this.#item = item;
60
80
  }
61
81
 
62
- override getOrigin(): string {
63
- return this.#item.origin;
82
+ override getURL(): string {
83
+ return this.#item.primaryTargetOrigin;
64
84
  }
65
85
 
66
86
  override getItem(): StorageItem {
@@ -68,13 +88,33 @@ export class StorageContext extends ConversationContext<StorageItem> {
68
88
  }
69
89
 
70
90
  override getTitle(): string {
71
- if (this.#item.key) {
72
- return `${this.#item.storageType}: ${this.#item.key}`;
91
+ if (this.#item instanceof CookieItem) {
92
+ return `${this.#item.name ? `cookie: ${this.#item.name}` : 'cookies:'} ${this.#item.origin}`;
93
+ }
94
+ if (this.#item instanceof DOMStorageItem) {
95
+ return `${this.#item.key ? `entry: ${this.#item.key}` : 'storage:'} ${this.#item.origin}`;
73
96
  }
74
- return `Storage for ${this.#item.origin}`;
97
+ return `Storage: ${this.getOrigin()}`;
75
98
  }
76
99
  }
77
100
 
101
+ // Maximum character length of values allowed.
102
+ const MAX_NUM_CHAR_LENGTH = 10000;
103
+
104
+ interface CookieDetails {
105
+ value: string;
106
+ domain: string;
107
+ path: string;
108
+ expires: number;
109
+ size: number;
110
+ secure: boolean;
111
+ sameSite: string;
112
+ partitioned: boolean;
113
+ priority: string;
114
+ sourcePort: number;
115
+ sourceScheme: string;
116
+ }
117
+
78
118
  export class StorageAgent extends AiAgent<StorageItem> {
79
119
  readonly preamble = preamble;
80
120
  readonly clientFeature = Host.AidaClient.ClientFeature.CHROME_STORAGE_AGENT;
@@ -96,10 +136,57 @@ export class StorageAgent extends AiAgent<StorageItem> {
96
136
  constructor(opts: AgentOptions) {
97
137
  super(opts);
98
138
 
99
- this.declareFunction<{
100
- type: 'localStorage' | 'sessionStorage',
101
- }>('listStorageKeys', {
102
- description: 'Lists all keys for a given storage type for the current origin.',
139
+ this.declareFunction<Record<string, never>, {origins: string[]}>('listPageOrigins', {
140
+ description:
141
+ '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.',
142
+ parameters: {
143
+ type: Host.AidaClient.ParametersTypes.OBJECT,
144
+ description: '',
145
+ nullable: false,
146
+ properties: {},
147
+ required: [],
148
+ },
149
+ displayInfoFromArgs: () => {
150
+ return {
151
+ title: lockedString('Listing page origins'),
152
+ action: 'listPageOrigins()',
153
+ };
154
+ },
155
+ handler: async () => {
156
+ if (!isSamePrimaryPageOrigin(this.context)) {
157
+ return {error: 'No origin available or not allowed.'};
158
+ }
159
+
160
+ const origins = new Set<string>();
161
+ for (const frame of SDK.ResourceTreeModel.ResourceTreeModel.frames()) {
162
+ if (!isSamePageOrigin(frame.resourceTreeModel().target().outermostTarget(), this.context)) {
163
+ continue;
164
+ }
165
+ const origin = frame.securityOrigin;
166
+ if (!origin || origins.has(origin)) {
167
+ continue;
168
+ }
169
+ origins.add(origin);
170
+ }
171
+
172
+ return {result: {origins: Array.from(origins)}};
173
+ },
174
+ });
175
+
176
+ this.declareFunction<
177
+ {
178
+ type: 'localStorage' | 'sessionStorage',
179
+ origin: string,
180
+ storageKey?: string,
181
+ },
182
+ {
183
+ partitions: Array<{
184
+ storageKey: string,
185
+ keys: string[],
186
+ }>,
187
+ }>('listStorageKeys', {
188
+ description:
189
+ 'Lists all keys for a given storage type for the requested origin. Returns keys grouped by storage partition.',
103
190
  parameters: {
104
191
  type: Host.AidaClient.ParametersTypes.OBJECT,
105
192
  description: '',
@@ -110,36 +197,67 @@ export class StorageAgent extends AiAgent<StorageItem> {
110
197
  description: 'Storage type: localStorage or sessionStorage',
111
198
  nullable: false,
112
199
  },
200
+ origin: {
201
+ type: Host.AidaClient.ParametersTypes.STRING,
202
+ description: 'Specific origin to list keys for.',
203
+ nullable: false,
204
+ },
205
+ storageKey: {
206
+ type: Host.AidaClient.ParametersTypes.STRING,
207
+ description: 'Optional. Specific storageKey to to list keys for.',
208
+ nullable: true,
209
+ }
113
210
  },
114
- required: ['type'],
211
+ required: ['type', 'origin'],
115
212
  },
116
213
  displayInfoFromArgs: args => {
117
214
  return {
118
215
  title: lockedString('Reading storage keys'),
119
- action: `listStorageKeys('${args.type}')`,
216
+ action: `listStorageKeys('${args.type}', '${args.origin}')`,
120
217
  };
121
218
  },
122
219
 
123
220
  handler: async args => {
124
- const storageOrError = this.getDOMStorage(args.type);
125
- if ('error' in storageOrError) {
126
- return storageOrError;
221
+ if (!isSamePrimaryPageOrigin(this.context)) {
222
+ return {error: 'No origin available or not allowed.'};
127
223
  }
128
- const items = await storageOrError.storage.getItems();
129
- if (!items) {
130
- return {result: JSON.stringify({keys: []})};
224
+
225
+ const storages = resolveDOMStorages(this.context, args.type, args.origin, args.storageKey);
226
+
227
+ const keyAndItems = await Promise.all(storages.map(async storage => {
228
+ const items = await storage.getItems();
229
+ return {storageKey: storage.storageKey, items};
230
+ }));
231
+
232
+ const partitionsResult = [];
233
+ for (const {storageKey, items} of keyAndItems) {
234
+ if (!items) {
235
+ continue;
236
+ }
237
+ const keys = items.map(([key]) => key);
238
+ if (keys.length > 0) {
239
+ partitionsResult.push({storageKey, keys});
240
+ }
131
241
  }
132
242
 
133
- const keys = items.map(item => item[0]);
134
- return {result: JSON.stringify({keys})};
243
+ return {result: {partitions: partitionsResult}};
135
244
  },
136
245
  });
137
246
 
138
- this.declareFunction<{
139
- type: 'localStorage' | 'sessionStorage',
140
- keys: string[],
141
- }>('getStorageValues', {
142
- description: 'Retrieve specific string values from storage for requested keys.',
247
+ this.declareFunction<
248
+ {
249
+ type: 'localStorage' | 'sessionStorage',
250
+ keys: string[],
251
+ origin: string,
252
+ storageKey?: string,
253
+ },
254
+ {
255
+ items: Array<{
256
+ storageKey: string,
257
+ values: Record<string, string>,
258
+ }>,
259
+ }>('getStorageValues', {
260
+ description: 'Retrieve specific string values from storage partitions for requested keys.',
143
261
  parameters: {
144
262
  type: Host.AidaClient.ParametersTypes.OBJECT,
145
263
  description: '',
@@ -156,105 +274,228 @@ export class StorageAgent extends AiAgent<StorageItem> {
156
274
  items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'A storage key.'},
157
275
  nullable: false,
158
276
  },
277
+ origin: {
278
+ type: Host.AidaClient.ParametersTypes.STRING,
279
+ description: 'Specific origin to get values for.',
280
+ nullable: false,
281
+ },
282
+ storageKey: {
283
+ type: Host.AidaClient.ParametersTypes.STRING,
284
+ description: 'Optional. Specific storageKey partition to get values for.',
285
+ nullable: true,
286
+ }
159
287
  },
160
- required: ['type', 'keys'],
288
+ required: ['type', 'keys', 'origin'],
161
289
  },
162
290
  displayInfoFromArgs: args => {
163
291
  return {
164
292
  title: lockedString('Reading storage values'),
165
- action: `getStorageValues('${args.type}', ${JSON.stringify(args.keys)})`,
293
+ action: `getStorageValues('${args.type}', ${JSON.stringify(args.keys)}, '${args.origin}'${
294
+ args.storageKey ? `, '${args.storageKey}'` : ''})`,
166
295
  };
167
296
  },
168
297
 
169
298
  handler: async (args, options) => {
299
+ if (!isSamePrimaryPageOrigin(this.context)) {
300
+ return {error: 'No origin available or not allowed.'};
301
+ }
302
+
303
+ const storages = resolveDOMStorages(this.context, args.type, args.origin, args.storageKey);
304
+ if (storages.length === 0) {
305
+ return {error: 'No matching storage partitions found.'};
306
+ }
307
+
170
308
  if (options?.approved !== true) {
171
309
  const keyString = args.keys.map(k => `\`${k}\``).join(', ');
310
+
311
+ const uniqueTargetOrigins = Array.from(new Set(storages.map(storage => {
312
+ const parsed = SDK.StorageKeyManager.parseStorageKey(storage.storageKey || '');
313
+ return parsed.origin;
314
+ })));
315
+ const targetsDesc = uniqueTargetOrigins.join(', ');
316
+
172
317
  return {
173
318
  requiresApproval: true,
174
- description: lockedString(`The AI wants to access the value(s) of ${args.type} keys ${keyString}.`),
319
+ description: lockedString(
320
+ `The AI wants to access the value(s) of ${args.type} keys ${keyString} on ${targetsDesc}.`),
175
321
  };
176
322
  }
177
323
 
178
- const storageOrError = this.getDOMStorage(args.type);
179
- if ('error' in storageOrError) {
180
- return storageOrError;
181
- }
182
- const items = await storageOrError.storage.getItems();
183
- if (!items) {
184
- return {result: JSON.stringify({items: {}})};
324
+ const itemsResult = [];
325
+
326
+ const keyAndItems = await Promise.all(storages.map(async storage => {
327
+ const items = await storage.getItems();
328
+ return {storageKey: storage.storageKey, items};
329
+ }));
330
+
331
+ for (const {storageKey, items} of keyAndItems) {
332
+ if (!items) {
333
+ continue;
334
+ }
335
+ const itemMap = new Map<string, string>(items as Array<[string, string]>);
336
+ const storageValues: Record<string, string> = {};
337
+
338
+ for (const key of args.keys) {
339
+ const value = itemMap.get(key);
340
+ if (value === undefined) {
341
+ continue;
342
+ }
343
+ const truncatedValue = value.length > MAX_NUM_CHAR_LENGTH ?
344
+ value.substring(0, MAX_NUM_CHAR_LENGTH) + '... <truncated>' :
345
+ value;
346
+ storageValues[key] = truncatedValue;
347
+ }
348
+
349
+ itemsResult.push({storageKey, values: storageValues});
185
350
  }
186
351
 
187
- const itemMap = new Map<string, string>(items as Array<[string, string]>);
352
+ return {result: {items: itemsResult}};
353
+ },
354
+ });
188
355
 
189
- const resultRecord: Record<string, string|null> = {};
190
- for (const key of args.keys) {
191
- resultRecord[key] = itemMap.get(key) ?? null;
356
+ this.declareFunction<
357
+ {
358
+ origin: string,
359
+ },
360
+ {cookies: string[]}>('listCookies', {
361
+ description: 'Lists all cookies for the requested origin, strictly excluding their values.',
362
+ parameters: {
363
+ type: Host.AidaClient.ParametersTypes.OBJECT,
364
+ description: '',
365
+ nullable: false,
366
+ properties: {
367
+ origin: {
368
+ type: Host.AidaClient.ParametersTypes.STRING,
369
+ description: 'Origin to list cookies for.',
370
+ nullable: false,
371
+ }
372
+ },
373
+ required: ['origin'],
374
+ },
375
+ displayInfoFromArgs: args => {
376
+ return {
377
+ title: lockedString('Reading cookies'),
378
+ action: `listCookies('${args.origin}')`,
379
+ };
380
+ },
381
+ handler: async args => {
382
+ if (!isSamePrimaryPageOrigin(this.context)) {
383
+ return {error: 'No origin available or not allowed.'};
192
384
  }
193
385
 
194
- return {result: JSON.stringify({items: resultRecord})};
195
- },
386
+ const frame = findFrameForOrigin(this.context, args.origin);
387
+ if (!frame) {
388
+ return {result: {cookies: []}};
389
+ }
196
390
 
391
+ const target = frame.resourceTreeModel().target();
392
+ const cookies = await getCookiesForDomain(target, args.origin);
393
+ const uniqueNames = Array.from(new Set(cookies?.map(c => c.name())));
394
+
395
+ return {result: {cookies: uniqueNames}};
396
+ },
197
397
  });
198
- }
199
398
 
200
- private getDOMStorage(type: 'localStorage'|'sessionStorage'): {storage: SDK.DOMStorageModel.DOMStorage}|{
201
- error: string,
202
- }
203
- {
204
- const origin = this.context?.getOrigin();
205
- if (!origin) {
206
- return {error: 'No origin available.'};
207
- }
208
- const storageKey = this.context?.getItem().storageKey;
209
- const isLocalStorage = type === 'localStorage';
210
-
211
- // If a storage key is defined, restrict access to that one.
212
- if (storageKey) {
213
- const domStorageModels = SDK.TargetManager.TargetManager.instance().models(SDK.DOMStorageModel.DOMStorageModel);
214
- for (const domStorageModel of domStorageModels) {
215
- domStorageModel.enable();
216
- const storage = domStorageModel.storageForId({storageKey, isLocalStorage});
217
- if (storage) {
218
- return {storage};
399
+ this.declareFunction<
400
+ {
401
+ cookieNames: string[],
402
+ origin: string,
403
+ },
404
+ {
405
+ cookies: CookieDetails[],
406
+ }>('getCookieValues', {
407
+ description: 'Retrieve the values and detailed metadata of specific cookies by their names.',
408
+ parameters: {
409
+ type: Host.AidaClient.ParametersTypes.OBJECT,
410
+ description: '',
411
+ nullable: false,
412
+ properties: {
413
+ cookieNames: {
414
+ type: Host.AidaClient.ParametersTypes.ARRAY,
415
+ description: 'A list of cookie names to retrieve values and metadata for.',
416
+ items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'A cookie name.'},
417
+ nullable: false,
418
+ },
419
+ origin: {
420
+ type: Host.AidaClient.ParametersTypes.STRING,
421
+ description: 'The specific origin the cookies belong to.',
422
+ nullable: false,
423
+ }
424
+ },
425
+ required: ['cookieNames', 'origin'],
426
+ },
427
+ displayInfoFromArgs: args => {
428
+ return {
429
+ title: lockedString('Reading cookie values and metadata'),
430
+ action: `getCookieValues(${JSON.stringify(args.cookieNames)}, '${args.origin}')`,
431
+ };
432
+ },
433
+ handler: async (args, options) => {
434
+ if (!isSamePrimaryPageOrigin(this.context)) {
435
+ return {error: 'No origin available or not allowed.'};
219
436
  }
220
- }
221
- return {error: `Storage not found for key ${storageKey} and type ${type}`};
222
- }
223
437
 
224
- // If no storage key is defined, take the primary target's local/session storage.
225
- const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
226
- if (!target) {
227
- return {error: 'No primary page target found.'};
228
- }
229
- const domStorageModel = target.model(SDK.DOMStorageModel.DOMStorageModel);
230
- if (!domStorageModel) {
231
- return {error: 'DOMStorageModel not found.'};
232
- }
438
+ const frame = findFrameForOrigin(this.context, args.origin);
439
+ if (!frame) {
440
+ return {result: {cookies: []}};
441
+ }
233
442
 
234
- domStorageModel.enable();
443
+ const target = frame.resourceTreeModel().target();
235
444
 
236
- const storages = domStorageModel.storages();
237
- const storage = storages.find(s => {
238
- const storageKey = s.storageKey;
239
- if (!storageKey) {
240
- return false;
241
- }
242
- const parsedKey = SDK.StorageKeyManager.parseStorageKey(storageKey);
243
- return parsedKey.origin === origin && s.isLocalStorage === isLocalStorage;
244
- });
445
+ if (options?.approved !== true) {
446
+ return {
447
+ requiresApproval: true,
448
+ description: lockedString(`The AI wants to access the value(s) and metadata of cookie(s) ${
449
+ args.cookieNames.map(name => `\`${name}\``).join(', ')} on ${args.origin}.`),
450
+ };
451
+ }
245
452
 
246
- if (!storage) {
247
- return {error: `Storage not found for origin ${origin} and type ${type}`};
248
- }
453
+ const cookies = await getCookiesForDomain(target, args.origin);
454
+ if (!cookies) {
455
+ return {result: {cookies: []}};
456
+ }
249
457
 
250
- return {storage};
458
+ const matchingCookies = cookies.filter(c => args.cookieNames.includes(c.name()));
459
+ const cookieData = matchingCookies.map(cookie => {
460
+ const value = cookie.value();
461
+ const truncatedValue =
462
+ value.length > MAX_NUM_CHAR_LENGTH ? value.substring(0, MAX_NUM_CHAR_LENGTH) + '... <truncated>' : value;
463
+
464
+ return {
465
+ value: truncatedValue,
466
+ domain: cookie.domain(),
467
+ path: cookie.path(),
468
+ expires: cookie.expires(),
469
+ size: cookie.size(),
470
+ secure: cookie.secure(),
471
+ sameSite: cookie.sameSite(),
472
+ partitioned: cookie.partitioned(),
473
+ priority: cookie.priority(),
474
+ sourcePort: cookie.sourcePort(),
475
+ sourceScheme: cookie.sourceScheme(),
476
+ };
477
+ });
478
+
479
+ return {result: {cookies: cookieData}};
480
+ },
481
+ });
251
482
  }
252
483
 
253
- static #formatContext(origin: string, item: StorageItem): string {
254
- if (item.storageType && item.key) {
255
- return `Storage Type: ${item.storageType}\nOrigin: ${origin}\nKey: ${item.key}`;
484
+ static #formatContext(item: StorageItem): string {
485
+ const primaryTargetOrigin = `Primary target: ${item.primaryTargetOrigin}`;
486
+ if (item instanceof CookieItem) {
487
+ const parsedURL = Common.ParsedURL.ParsedURL.fromString(item.origin);
488
+ const domain = parsedURL ? parsedURL.host : item.origin;
489
+ return `${primaryTargetOrigin}\nUser-selected Context: Cookies\nDomain: ${domain}${
490
+ item.name ? `\nCookie Name: ${item.name}` : ''}`;
256
491
  }
257
- return `Origin: ${origin}`;
492
+
493
+ if (item instanceof DOMStorageItem) {
494
+ return `${primaryTargetOrigin}\nUser-selected Context: DOM Storage\n Type: ${item.type}\nStorageKey: ${
495
+ item.storageKey}\nOrigin: ${item.origin}${item.key ? `\nKey: ${item.key}` : ''}`;
496
+ }
497
+
498
+ return primaryTargetOrigin;
258
499
  }
259
500
 
260
501
  async *
@@ -269,7 +510,7 @@ export class StorageAgent extends AiAgent<StorageItem> {
269
510
  details: [
270
511
  {
271
512
  title: 'Selected Storage Context',
272
- text: StorageAgent.#formatContext(context.getOrigin(), context.getItem()),
513
+ text: StorageAgent.#formatContext(context.getItem()),
273
514
  },
274
515
  ],
275
516
  };
@@ -279,6 +520,85 @@ export class StorageAgent extends AiAgent<StorageItem> {
279
520
  if (!context) {
280
521
  return query;
281
522
  }
282
- return `# Active Context\n${StorageAgent.#formatContext(context.getOrigin(), context.getItem())}\n\n${query}`;
523
+ return `# Active Context\n${StorageAgent.#formatContext(context.getItem())}\n\n${query}`;
283
524
  }
284
525
  }
526
+
527
+ /**
528
+ * Resolves and filters active DOM storage partitions from the Target Manager matching the given context constraints.
529
+ *
530
+ * @param context The conversation context containing origin permissions. Only storage partitions under targets allowed
531
+ * by this context will be returned.
532
+ * @param type The DOM storage type ('localStorage' or 'sessionStorage') to filter for.
533
+ * @param origin The partition origin to match.
534
+ * @param storageKey Optional. If specified, resolves only the partition exactly matching this unique key, bypassing origin comparison.
535
+ */
536
+
537
+ export async function getCookiesForDomain(
538
+ target: SDK.Target.Target, origin: string): Promise<SDK.Cookie.Cookie[]|null> {
539
+ const cookieModel = target.model(SDK.CookieModel.CookieModel);
540
+ if (!cookieModel) {
541
+ return null;
542
+ }
543
+
544
+ const allCookies = await cookieModel.getCookiesForDomain(origin);
545
+ if (!allCookies) {
546
+ return null;
547
+ }
548
+ return allCookies.filter(cookie => !cookie.httpOnly());
549
+ }
550
+
551
+ export function findFrameForOrigin(
552
+ context: ConversationContext<StorageItem>|undefined, origin: string): SDK.ResourceTreeModel.ResourceTreeFrame|null {
553
+ for (const frame of SDK.ResourceTreeModel.ResourceTreeModel.frames()) {
554
+ if (frame.securityOrigin === origin) {
555
+ const target = frame.resourceTreeModel().target();
556
+ if (isSamePageOrigin(target.outermostTarget(), context)) {
557
+ return frame;
558
+ }
559
+ }
560
+ }
561
+ return null;
562
+ }
563
+ export function resolveDOMStorages(
564
+ context: ConversationContext<StorageItem>|undefined, type: 'localStorage'|'sessionStorage', origin: string,
565
+ storageKey?: string): SDK.DOMStorageModel.DOMStorage[] {
566
+ const resolvedStorages: SDK.DOMStorageModel.DOMStorage[] = [];
567
+ const isLocalStorage = type === 'localStorage';
568
+
569
+ const domStorageModels = SDK.TargetManager.TargetManager.instance().models(SDK.DOMStorageModel.DOMStorageModel);
570
+ for (const domStorageModel of domStorageModels) {
571
+ if (!isSamePageOrigin(domStorageModel.target().outermostTarget(), context)) {
572
+ // Skip DOMStorageModels that don't point to the same outermost target.
573
+ continue;
574
+ }
575
+
576
+ for (const storage of domStorageModel.storages()) {
577
+ if (storage.isLocalStorage !== isLocalStorage) {
578
+ continue;
579
+ }
580
+ const currentStorageKey = storage.storageKey;
581
+ if (!currentStorageKey) {
582
+ continue;
583
+ }
584
+
585
+ // If we search by storageKey, verify the storage key matches AND the underlying origin matches the request origin.
586
+ if (storageKey) {
587
+ if (storageKey === currentStorageKey) {
588
+ const parsedKey = SDK.StorageKeyManager.parseStorageKey(currentStorageKey);
589
+ if (parsedKey.origin === origin) {
590
+ resolvedStorages.push(storage);
591
+ }
592
+ }
593
+ continue;
594
+ }
595
+
596
+ const parsedKey = SDK.StorageKeyManager.parseStorageKey(currentStorageKey);
597
+ if (parsedKey.origin === origin) {
598
+ resolvedStorages.push(storage);
599
+ }
600
+ }
601
+ }
602
+
603
+ return resolvedStorages;
604
+ }