chrome-devtools-frontend 1.0.1643855 → 1.0.1646286

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 (95) hide show
  1. package/.agents/skills/devtools-source-maps/SKILL.md +124 -0
  2. package/docs/README.md +1 -0
  3. package/docs/using_source_maps.md +159 -0
  4. package/front_end/core/host/AidaClientTypes.ts +2 -0
  5. package/front_end/core/host/UserMetrics.ts +5 -3
  6. package/front_end/core/root/Runtime.ts +10 -0
  7. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
  8. package/front_end/core/sdk/DebuggerModel.ts +7 -9
  9. package/front_end/core/sdk/NetworkRequest.ts +0 -25
  10. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
  11. package/front_end/generated/InspectorBackendCommands.ts +2 -2
  12. package/front_end/generated/SupportedCSSProperties.js +79 -2
  13. package/front_end/generated/protocol.ts +0 -5
  14. package/front_end/models/ai_assistance/AiAgent2.ts +52 -15
  15. package/front_end/models/ai_assistance/AiConversation.ts +4 -2
  16. package/front_end/models/ai_assistance/AiOrigins.ts +63 -2
  17. package/front_end/models/ai_assistance/README.md +20 -8
  18. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +2 -2
  19. package/front_end/models/ai_assistance/agents/FileAgent.ts +9 -42
  20. package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +2 -2
  21. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +9 -133
  22. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +25 -2
  23. package/front_end/models/ai_assistance/agents/README.md +64 -0
  24. package/front_end/models/ai_assistance/agents/StylingAgent.ts +26 -3
  25. package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
  26. package/front_end/models/ai_assistance/contexts/FileContext.ts +45 -0
  27. package/front_end/models/ai_assistance/contexts/RequestContext.snapshot.txt +48 -0
  28. package/front_end/models/ai_assistance/contexts/RequestContext.ts +116 -0
  29. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +2 -1
  30. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +9 -12
  31. package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
  32. package/front_end/models/ai_assistance/tools/README.md +45 -0
  33. package/front_end/models/ai_assistance/tools/Tool.ts +74 -11
  34. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
  35. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
  36. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
  37. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +17 -4
  38. package/front_end/models/stack_trace/StackTraceModel.ts +34 -1
  39. package/front_end/models/trace/Styles.ts +29 -7
  40. package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +15 -11
  41. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
  42. package/front_end/models/trace/helpers/Timing.ts +10 -0
  43. package/front_end/models/trace/types/TraceEvents.ts +22 -2
  44. package/front_end/models/web_mcp/WebMCPModel.ts +8 -48
  45. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +21 -15
  46. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
  47. package/front_end/panels/ai_assistance/components/ChatInput.ts +4 -4
  48. package/front_end/panels/application/ApplicationPanelSidebar.ts +69 -0
  49. package/front_end/panels/application/ServiceWorkersView.ts +2 -2
  50. package/front_end/panels/application/WebMCPView.ts +40 -71
  51. package/front_end/panels/application/components/AdsView.ts +31 -28
  52. package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
  53. package/front_end/panels/application/components/adsView.css +6 -0
  54. package/front_end/panels/common/ExtensionServer.ts +5 -0
  55. package/front_end/panels/console/ConsoleView.ts +6 -1
  56. package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
  57. package/front_end/panels/console/SymbolizedErrorWidget.ts +4 -1
  58. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
  59. package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
  60. package/front_end/panels/elements/PropertyRenderer.ts +0 -1
  61. package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
  62. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  63. package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
  64. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
  65. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
  66. package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
  67. package/front_end/panels/network/NetworkLogView.ts +34 -7
  68. package/front_end/panels/profiler/HeapProfileView.ts +0 -1
  69. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
  70. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
  71. package/front_end/panels/profiler/IsolateSelector.ts +4 -2
  72. package/front_end/panels/profiler/ProfileLauncherView.ts +194 -126
  73. package/front_end/panels/profiler/ProfilesPanel.ts +1 -3
  74. package/front_end/panels/settings/components/SyncSection.ts +1 -1
  75. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
  76. package/front_end/panels/timeline/TimelinePanel.ts +7 -0
  77. package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
  78. package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
  79. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
  80. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
  81. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
  82. package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
  83. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
  84. package/front_end/third_party/chromium/README.chromium +1 -1
  85. package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
  86. package/front_end/ui/kit/link/Link.ts +16 -2
  87. package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
  88. package/front_end/ui/legacy/InspectorView.ts +4 -1
  89. package/front_end/ui/legacy/PlusButton.ts +6 -1
  90. package/front_end/ui/legacy/Widget.ts +19 -1
  91. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
  92. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
  93. package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
  94. package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
  95. package/package.json +1 -1
@@ -0,0 +1,116 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Common from '../../../core/common/common.js';
6
+ import * as i18n from '../../../core/i18n/i18n.js';
7
+ import type * as Platform from '../../../core/platform/platform.js';
8
+ import type * as SDK from '../../../core/sdk/sdk.js';
9
+ import type * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
10
+ import {
11
+ type ContextDetail,
12
+ ConversationContext,
13
+ } from '../agents/AiAgent.js';
14
+ import {extractContextOrigin} from '../AiOrigins.js';
15
+ import {NetworkRequestFormatter} from '../data_formatters/NetworkRequestFormatter.js';
16
+
17
+ const UIStringsNotTranslate = {
18
+ request: 'Request',
19
+ response: 'Response',
20
+ requestUrl: 'Request URL',
21
+ timing: 'Timing',
22
+ requestInitiatorChain: 'Request initiator chain',
23
+ } as const;
24
+
25
+ const lockedString = i18n.i18n.lockedString;
26
+
27
+ /**
28
+ * Returns the origin for a network request in the AI context.
29
+ *
30
+ * To prevent cross-origin prompt injection attacks, HAR-imported requests
31
+ * are isolated from live pages. We assign them a virtual origin
32
+ * (`imported-har://${domain}`) so they do not share the origin of live pages
33
+ * (e.g., `https://${domain}`). This forces a conversation reset when transitioning
34
+ * between imported HAR data and live pages.
35
+ */
36
+ export function getRequestContextOrigin(request: SDK.NetworkRequest.NetworkRequest): string {
37
+ const origin = extractContextOrigin(request.documentURL);
38
+ if (request.isImportedHar()) {
39
+ const parsed = Common.ParsedURL.ParsedURL.fromString(origin as Platform.DevToolsPath.UrlString);
40
+ return `imported-har://${parsed ? parsed.domain() : origin}`;
41
+ }
42
+ return origin;
43
+ }
44
+
45
+ export class RequestContext extends ConversationContext<SDK.NetworkRequest.NetworkRequest> {
46
+ #request: SDK.NetworkRequest.NetworkRequest;
47
+ #calculator: NetworkTimeCalculator.NetworkTransferTimeCalculator;
48
+
49
+ constructor(
50
+ request: SDK.NetworkRequest.NetworkRequest,
51
+ calculator: NetworkTimeCalculator.NetworkTransferTimeCalculator,
52
+ ) {
53
+ super();
54
+ this.#request = request;
55
+ this.#calculator = calculator;
56
+ }
57
+
58
+ /**
59
+ * Note: this is not the literal origin of the network request. This URL
60
+ * is used to determine when we should force the user to start a new AI
61
+ * conversation when the context changes. We allow a single AI conversation to
62
+ * inspect all network requests that were made for that given target URL.
63
+ */
64
+ override getURL(): string {
65
+ return this.#request.documentURL;
66
+ }
67
+
68
+ override getOrigin(): string {
69
+ return getRequestContextOrigin(this.#request);
70
+ }
71
+
72
+ override getItem(): SDK.NetworkRequest.NetworkRequest {
73
+ return this.#request;
74
+ }
75
+
76
+ override getTitle(): string {
77
+ return this.#request.name();
78
+ }
79
+
80
+ override async getPromptDetails(): Promise<string|null> {
81
+ const formatter = new NetworkRequestFormatter(this.#request, this.#calculator);
82
+ return `# Selected network request\n${await formatter.formatNetworkRequest()}`;
83
+ }
84
+
85
+ override async getUserFacingDetails(): Promise<[ContextDetail, ...ContextDetail[]]|null> {
86
+ const formatter = new NetworkRequestFormatter(this.#request, this.#calculator);
87
+ const requestContextDetail: ContextDetail = {
88
+ title: lockedString(UIStringsNotTranslate.request),
89
+ text: lockedString(UIStringsNotTranslate.requestUrl) + ': ' + this.#request.url() + '\n\n' +
90
+ formatter.formatRequestHeaders(),
91
+ };
92
+ const responseBody = await formatter.formatResponseBody();
93
+ const responseBodyString = responseBody ? `\n\n${responseBody}` : '';
94
+
95
+ const responseContextDetail: ContextDetail = {
96
+ title: lockedString(UIStringsNotTranslate.response),
97
+ text: formatter.formatResponseHeaders() + responseBodyString +
98
+ `\n\n${formatter.formatStatus()}${formatter.formatFailureReasons()}`,
99
+ };
100
+ const timingContextDetail: ContextDetail = {
101
+ title: lockedString(UIStringsNotTranslate.timing),
102
+ text: formatter.formatNetworkRequestTiming(),
103
+ };
104
+ const initiatorChainContextDetail: ContextDetail = {
105
+ title: lockedString(UIStringsNotTranslate.requestInitiatorChain),
106
+ text: formatter.formatRequestInitiatorChain(),
107
+ };
108
+
109
+ return [
110
+ requestContextDetail,
111
+ responseContextDetail,
112
+ timingContextDetail,
113
+ initiatorChainContextDetail,
114
+ ];
115
+ }
116
+ }
@@ -92,7 +92,8 @@ export class NetworkRequestFormatter {
92
92
  }): string {
93
93
  let responseStatus = '';
94
94
  if (status.statusCode) {
95
- responseStatus = `Response status: ${status.statusCode} ${status.statusText}\n`;
95
+ const statusText = status.statusText ? ` ${status.statusText}` : '';
96
+ responseStatus = `Response status: ${status.statusCode}${statusText}\n`;
96
97
  }
97
98
  const flags = [];
98
99
  flags.push(status.finished ? 'finished' : 'pending');
@@ -8,9 +8,11 @@ import type {FunctionCallHandlerResult, FunctionHandlerOptions,} from '../agents
8
8
  import {JavascriptExecutor} from '../agents/ExecuteJavascript.js';
9
9
 
10
10
  import {
11
+ type BaseToolCapability,
12
+ type PageExecutionCapability,
13
+ type StyleMutationCapability,
11
14
  type Tool,
12
15
  type ToolArgs,
13
- type ToolContext,
14
16
  ToolName,
15
17
  } from './Tool.js';
16
18
 
@@ -20,7 +22,8 @@ export interface ExecuteJavaScriptArgs extends ToolArgs {
20
22
  title: string;
21
23
  }
22
24
 
23
- export class ExecuteJavaScriptTool implements Tool<ExecuteJavaScriptArgs, unknown> {
25
+ export class ExecuteJavaScriptTool implements
26
+ Tool<ExecuteJavaScriptArgs, unknown, BaseToolCapability&PageExecutionCapability&StyleMutationCapability> {
24
27
  readonly name = ToolName.EXECUTE_JAVASCRIPT;
25
28
 
26
29
  readonly description =
@@ -104,10 +107,10 @@ const data = {
104
107
 
105
108
  async handler(
106
109
  params: ExecuteJavaScriptArgs,
107
- context: ToolContext,
110
+ context: BaseToolCapability&PageExecutionCapability&StyleMutationCapability,
108
111
  options?: FunctionHandlerOptions,
109
112
  ): Promise<FunctionCallHandlerResult<unknown>> {
110
- const executionNode = context.getExecutionContextNode?.() ?? null;
113
+ const executionNode = context.getExecutionContextNode();
111
114
  if (!executionNode) {
112
115
  return {error: 'Error: Could not find the context node for execution.'};
113
116
  }
@@ -115,17 +118,11 @@ const data = {
115
118
  const executionMode = Root.Runtime.hostConfig.devToolsFreestyler?.executionMode ??
116
119
  Root.Runtime.HostConfigFreestylerExecutionMode.ALL_SCRIPTS;
117
120
 
118
- const changes = context.changeManager;
119
- const createExtensionScope = context.createExtensionScope;
120
- if (!changes || !createExtensionScope) {
121
- return {error: 'Internal Error: Required change manager or extension scope creator is missing.'};
122
- }
123
-
124
121
  const executor = new JavascriptExecutor({
125
122
  executionMode,
126
123
  getContextNode: () => executionNode,
127
- createExtensionScope,
128
- changes,
124
+ createExtensionScope: context.createExtensionScope,
125
+ changes: context.changeManager,
129
126
  },
130
127
  context.execJs);
131
128
 
@@ -9,7 +9,14 @@ import type {ComputedStyleAiWidget, FunctionCallHandlerResult, FunctionHandlerOp
9
9
  import {DOMNodeContext} from '../contexts/DOMNodeContext.js';
10
10
  import {debugLog} from '../debug.js';
11
11
 
12
- import {type Tool, type ToolArgs, type ToolContext, ToolName} from './Tool.js';
12
+ import {
13
+ type BaseToolCapability,
14
+ type OriginLockCapability,
15
+ type TargetCapability,
16
+ type Tool,
17
+ type ToolArgs,
18
+ ToolName,
19
+ } from './Tool.js';
13
20
 
14
21
  export interface GetStylesArgs extends ToolArgs {
15
22
  elements: number[];
@@ -17,7 +24,8 @@ export interface GetStylesArgs extends ToolArgs {
17
24
  explanation: string;
18
25
  }
19
26
 
20
- export class GetStylesTool implements Tool<GetStylesArgs, unknown> {
27
+ export class GetStylesTool implements
28
+ Tool<GetStylesArgs, unknown, BaseToolCapability&TargetCapability&OriginLockCapability> {
21
29
  readonly name = ToolName.GET_STYLES;
22
30
  readonly description =
23
31
  `Get computed and source styles for one or multiple elements on the inspected page for multiple elements at once by uid.
@@ -71,34 +79,33 @@ export class GetStylesTool implements Tool<GetStylesArgs, unknown> {
71
79
 
72
80
  async handler(
73
81
  params: GetStylesArgs,
74
- context: ToolContext,
82
+ context: BaseToolCapability&TargetCapability&OriginLockCapability,
75
83
  _options?: FunctionHandlerOptions,
76
84
  ): Promise<FunctionCallHandlerResult<unknown>> {
77
85
  const widgets: ComputedStyleAiWidget[] = [];
78
86
  const result:
79
87
  Record<string, {computed: Record<string, string|undefined>, authored: Record<string, string|undefined>}> = {};
80
88
 
81
- const activeContext = context.conversationContext;
82
- if (!activeContext || !(activeContext instanceof DOMNodeContext)) {
83
- return {error: 'Error: Could not find the currently selected element.'};
89
+ const target = context.getTarget();
90
+ if (!target) {
91
+ return {error: 'Error: Could not find the inspected page.'};
84
92
  }
85
93
 
86
- const selectedNode = activeContext.getItem();
87
- if (!selectedNode) {
88
- return {error: 'Error: Could not find the currently selected element.'};
94
+ const establishedOrigin = context.getEstablishedOrigin();
95
+ if (!establishedOrigin) {
96
+ return {error: 'Error: Origin lock is not established.'};
89
97
  }
90
98
 
91
99
  for (const uid of params.elements) {
92
100
  result[uid] = {computed: {}, authored: {}};
93
101
  debugLog(`Action to execute: uid=${uid}`);
94
- const node =
95
- new SDK.DOMModel.DeferredDOMNode(selectedNode.domModel().target(), uid as Protocol.DOM.BackendNodeId);
102
+ const node = new SDK.DOMModel.DeferredDOMNode(target, uid as Protocol.DOM.BackendNodeId);
96
103
  const resolved = await node.resolvePromise();
97
104
  if (!resolved) {
98
105
  return {error: 'Error: Could not find the element with uid=' + uid};
99
106
  }
100
107
  const newContext = new DOMNodeContext(resolved);
101
- if (activeContext.getOrigin() !== newContext.getOrigin()) {
108
+ if (establishedOrigin !== newContext.getOrigin()) {
102
109
  return {error: 'Error: Node does not belong to the current origin.'};
103
110
  }
104
111
  const styles = await resolved.domModel().cssModel().getComputedStyle(resolved.id);
@@ -0,0 +1,45 @@
1
+ # AI Assistant Tools Architecture
2
+
3
+ This directory defines the **Tools** used by `AiAgent2` and other AI agents to execute code, read page state, or apply mutations to the inspected page.
4
+
5
+ ## Core Concepts
6
+
7
+ The architecture relies on three primary concepts to guarantee 100% compile-time type safety for tools:
8
+ 1. **BaseTool**: An interface capturing non-generic tool metadata (e.g. `name`, `description`, `parameters`). This allows the agent and DevTools UI to generically store, register, and describe functions to the AIDA client without needing to know their specific types at runtime.
9
+ 2. **Capability Interfaces**: Narrow TypeScript interfaces wrapping individual DevTools dependencies (e.g., ChangeManager, executeJsCode). This decouples tools from monolithic agent environments, making them reusable and easier to unit test.
10
+ 3. **Tool**: A generic TypeScript interface (`Tool<Args, ReturnType, ContextType>`) binding the execution handler to specific capability requirements. This enforces compile-time safety, ensuring that a tool handler is only invoked by an agent that fulfills all the required capabilities.
11
+
12
+ ## Capability Interfaces
13
+
14
+ Instead of passing a monolithic "grab-bag" context object to all tool handlers, DevTools AI tools declare exactly the capabilities they require. Available capability interfaces defined in `Tool.ts` include:
15
+
16
+ - `BaseToolCapability`: Base interface providing access to the top-level conversation context step.
17
+ - `PageExecutionCapability`: For tools executing JavaScript code on the inspected page.
18
+ - `StyleMutationCapability`: For tools managing and applying style mutations via a `ChangeManager`.
19
+ - `TargetCapability`: For tools requiring access to the page's current SDK `Target`.
20
+ - `OriginLockCapability`: For tools enforcing cross-origin security via origin locks. Tools that fetch resources or state from the inspected page (such as styling or source code) must use this capability to verify that the target resource matches the conversation's established origin. This prevents the LLM from executing tools against out-of-origin elements or documents.
21
+
22
+ ### Unified Context
23
+
24
+ The Agent (e.g., `AiAgent2`) builds the complete dependency union (`AllToolsContext`) and fulfills all capabilities. When the handler is invoked, TypeScript validates that the provided context matches the intersection of capabilities requested by that tool.
25
+
26
+ ## Authoring a New Tool
27
+
28
+ To author a new tool:
29
+
30
+ 1. **Define the Args interface**: Extend `ToolArgs` to represent the JSON parameter schema expected by the LLM.
31
+ 2. **Declare Capability Dependencies**: Determine which capabilities your tool handler needs. Intersect them to define the tool's `ContextType`.
32
+ 3. **Implement the `Tool` interface**: Pass the args interface, return type, and intersected ContextType to the generic parameters of `Tool`.
33
+ 4. **Instantiate in `ToolRegistry.ts`**: Add the instantiated tool class to the static `TOOLS` registry.
34
+
35
+ See concrete, production examples of capability-based tools in this directory:
36
+ - [ExecuteJavaScript.ts](ExecuteJavaScript.ts): Demonstrates use of `PageExecutionCapability` and `StyleMutationCapability`.
37
+ - [GetStyles.ts](GetStyles.ts): Demonstrates use of `TargetCapability` and `OriginLockCapability`.
38
+
39
+ ## ToolRegistry Lookup
40
+
41
+ To retrieve a tool from the registry with 100% type safety, call `ToolRegistry.get()` with the literal `ToolName` key:
42
+
43
+ ```typescript
44
+ const getStylesTool = ToolRegistry.get(ToolName.GET_STYLES); // Returns GetStylesTool class type
45
+ ```
@@ -9,22 +9,76 @@ import type {executeJsCode} from '../agents/ExecuteJavascript.js';
9
9
  import type {ChangeManager} from '../ChangeManager.js';
10
10
 
11
11
  /**
12
- * Context provided to the tool's handler execution.
12
+ * Base capability for all tool contexts, providing access to the conversation context.
13
13
  */
14
- export interface ToolContext {
14
+ export interface BaseToolCapability {
15
+ /**
16
+ * The active context for the current conversation step, if any.
17
+ */
15
18
  conversationContext: ConversationContext<unknown>|null;
16
- changeManager?: ChangeManager;
17
- createExtensionScope?: (changes: ChangeManager) => {
18
- install(): Promise<void>, uninstall(): Promise<void>,
19
- };
20
- execJs?: typeof executeJsCode;
19
+ }
20
+
21
+ /**
22
+ * Capability for tools that need to execute JavaScript code on the inspected page.
23
+ */
24
+ export interface PageExecutionCapability {
25
+ /**
26
+ * Function to execute JavaScript code in the page context.
27
+ */
28
+ execJs: typeof executeJsCode;
29
+
21
30
  /**
22
31
  * Returns the DOM node that acts as the execution context (i.e. `$0` inside the execution context)
23
32
  * for running JavaScript.
24
33
  */
25
- getExecutionContextNode?: () => SDK.DOMModel.DOMNode | null;
34
+ getExecutionContextNode(): SDK.DOMModel.DOMNode|null;
26
35
  }
27
36
 
37
+ /**
38
+ * Capability for tools that need to manage and apply style mutations to the page.
39
+ */
40
+ export interface StyleMutationCapability {
41
+ /**
42
+ * The change manager for tracking and applying style changes.
43
+ */
44
+ changeManager: ChangeManager;
45
+
46
+ /**
47
+ * Creates an extension scope for applying changes, ensuring they can be uninstalled when done.
48
+ */
49
+ createExtensionScope(changes: ChangeManager): {
50
+ install(): Promise<void>,
51
+ uninstall(): Promise<void>,
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Capability for tools that need access to the current SDK Target of the inspected page.
57
+ */
58
+ export interface TargetCapability {
59
+ /**
60
+ * Returns the current SDK Target for the inspected page.
61
+ */
62
+ getTarget(): SDK.Target.Target|null;
63
+ }
64
+
65
+ /**
66
+ * Capability for tools that need to enforce origin locking for security.
67
+ */
68
+ export interface OriginLockCapability {
69
+ /**
70
+ * Returns the origin that the current conversation is locked to, if any.
71
+ */
72
+ getEstablishedOrigin(): string|undefined;
73
+ }
74
+
75
+ /**
76
+ * Unified context interface providing all capabilities available in the project.
77
+ * Used by the agent to pass a complete context to any tool type-safely.
78
+ */
79
+ export type AllToolsContext =
80
+ BaseToolCapability&PageExecutionCapability&StyleMutationCapability&TargetCapability&OriginLockCapability;
81
+
28
82
  /**
29
83
  * Base argument type for AI Tools.
30
84
  */
@@ -50,21 +104,30 @@ export interface BaseTool {
50
104
 
51
105
  /**
52
106
  * Main generic interface for defining a Tool.
53
- * Binds the parameter schema properties and the handler implementation to a strict `Args` contract.
107
+ * Binds the parameter schema properties and the handler implementation to a strict `Args` and `ContextType` contract.
54
108
  *
55
109
  * @template Args - The expected object type for tool arguments. Must be an object type.
56
110
  * @template ReturnType - The type of data returned by the handler function.
111
+ * @template ContextType - The interface defining the capabilities this tool requires. Defaults to `BaseToolCapability`.
57
112
  */
58
- export interface Tool<Args extends ToolArgs = ToolArgs, ReturnType = unknown, > extends BaseTool {
113
+ export interface Tool<Args extends ToolArgs = ToolArgs, ReturnType = unknown,
114
+ ContextType extends BaseToolCapability = BaseToolCapability> extends BaseTool {
59
115
  readonly parameters: Host.AidaClient.FunctionObjectParam<keyof Args>;
60
116
  readonly displayInfoFromArgs?: (
61
117
  args: Args,
62
118
  ) => {
63
119
  title?: string, thought?: string, action?: string, suggestions?: [string, ...string[]],
64
120
  };
121
+ /**
122
+ * The implementation function called when the AI invokes this tool.
123
+ *
124
+ * @param args The arguments provided by the AI model matching the tool's parameter schema.
125
+ * @param context The context object providing the capabilities requested by `ContextType`.
126
+ * @param options Additional runtime options for the handler execution.
127
+ */
65
128
  handler(
66
129
  args: Args,
67
- context: ToolContext,
130
+ context: ContextType,
68
131
  options?: FunctionHandlerOptions,
69
132
  ): Promise<FunctionCallHandlerResult<ReturnType>>;
70
133
  }
@@ -4,11 +4,15 @@
4
4
 
5
5
  import {ExecuteJavaScriptTool} from './ExecuteJavaScript.js';
6
6
  import {GetStylesTool} from './GetStyles.js';
7
- import {type Tool, ToolName} from './Tool.js';
7
+ import {type AllToolsContext, type Tool, type ToolArgs, ToolName} from './Tool.js';
8
8
 
9
9
  /**
10
10
  * Plain object registry containing concrete instantiated tools.
11
- * Keep this type concrete (no type-erasure) to preserve exact tool types.
11
+ *
12
+ * This object is deliberately declared as a plain object without an explicit type annotation
13
+ * (like `Record<ToolName, Tool>`) to preserve the exact concrete type of each registered tool.
14
+ * This is required to support compile-time type safety and inference in the overloaded
15
+ * `ToolRegistry.get()` method, which maps a literal `ToolName` key to its specific class type.
12
16
  */
13
17
  export const TOOLS = {
14
18
  [ToolName.EXECUTE_JAVASCRIPT]: new ExecuteJavaScriptTool(),
@@ -23,14 +27,26 @@ export class ToolRegistry {
23
27
  * Retrieves a tool by its literal name with 100% type safety.
24
28
  *
25
29
  * @template K - A key from the `TOOLS` registry.
30
+ * @param name The literal name of the tool to retrieve.
26
31
  * @returns The concrete class type of the requested tool.
27
32
  */
28
33
  static get<K extends keyof typeof TOOLS>(name: K): typeof TOOLS[K];
29
34
  /**
30
35
  * Fallback retrieval signature for general or runtime string lookups.
36
+ *
37
+ * @param name The string name of the tool to retrieve, used when the tool name is only known at runtime.
38
+ * @returns The generic Tool interface, or undefined if not found.
31
39
  */
32
- static get(name: string): Tool|undefined;
33
- static get(name: string): Tool|undefined {
34
- return Object.prototype.hasOwnProperty.call(TOOLS, name) ? TOOLS[name as keyof typeof TOOLS] as Tool : undefined;
40
+ static get(name: string): Tool<ToolArgs, unknown, AllToolsContext>|undefined;
41
+ static get(name: string): Tool<ToolArgs, unknown, AllToolsContext>|undefined {
42
+ // We use a double assertion (`as unknown as Tool<...>`) here. TypeScript's variance
43
+ // rules prevent direct casting from specific concrete tools (which have narrowed,
44
+ // capability-specific contexts) to the generic `Tool` signature that uses `AllToolsContext`.
45
+ // This cast is runtime-safe because any capability requested by a specific tool is
46
+ // guaranteed to be satisfied by `AllToolsContext`, and the handler will only access
47
+ // the capabilities it expects.
48
+ return Object.prototype.hasOwnProperty.call(TOOLS, name) ?
49
+ TOOLS[name as keyof typeof TOOLS] as unknown as Tool<ToolArgs, unknown, AllToolsContext>:
50
+ undefined;
35
51
  }
36
52
  }
@@ -136,10 +136,16 @@ export class Diff {
136
136
  removedCount = 0;
137
137
  addedSize = 0;
138
138
  removedSize = 0;
139
- deletedIndexes: number[] = [];
140
- addedIndexes: number[] = [];
141
139
  countDelta!: number;
142
140
  sizeDelta!: number;
141
+ // Data about added nodes
142
+ addedIndexes: number[] = [];
143
+ addedIds: number[] = [];
144
+ addedSelfSizes: number[] = [];
145
+ // Data about deleted nodes
146
+ deletedIndexes: number[] = [];
147
+ deletedIds: number[] = [];
148
+ deletedSelfSizes: number[] = [];
143
149
  constructor(name: string) {
144
150
  this.name = name;
145
151
  }
@@ -287,3 +293,13 @@ export interface RetainingPaths {
287
293
  siblings?: boolean,
288
294
  };
289
295
  }
296
+
297
+ export interface DominatorNode {
298
+ nodeId: number;
299
+ nodeIndex: number;
300
+ nodeName: string;
301
+ retainedSize: number;
302
+ selfSize: number;
303
+ }
304
+
305
+ export type DominatorChain = DominatorNode[];
@@ -370,6 +370,10 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
370
370
  return this.callMethodPromise('getRetainingPaths', nodeIndex, maxDepth, maxNodes, maxSiblings);
371
371
  }
372
372
 
373
+ getDominatorsOf(nodeIndex: number): Promise<HeapSnapshotModel.DominatorChain> {
374
+ return this.callMethodPromise('getDominatorsOf', nodeIndex);
375
+ }
376
+
373
377
  unignoreNodeInRetainersView(nodeIndex: number): Promise<void> {
374
378
  return this.callMethodPromise('unignoreNodeInRetainersView', nodeIndex);
375
379
  }
@@ -10,13 +10,15 @@ import type {RawFrame} from './Trie.js';
10
10
 
11
11
  const CALL_FRAME_REGEX = /^\s*at\s+/;
12
12
 
13
+ export type ResolveURLCallback = (url: Platform.DevToolsPath.UrlString) => Platform.DevToolsPath.UrlString|null;
14
+
13
15
  /**
14
16
  * Takes a V8 Error#stack string and extracts structured information.
15
17
  *
16
18
  * @returns Null if the provided string has an unexpected format. A
17
19
  * populated `RawFrame[]` otherwise.
18
20
  */
19
- export function parseRawFramesFromErrorStack(stack: string): RawFrame[]|null {
21
+ export function parseRawFramesFromErrorStack(stack: string, resolveURL?: ResolveURLCallback): RawFrame[]|null {
20
22
  const lines = stack.split('\n');
21
23
  const firstAtLineIndex = findFramesStartLine(lines);
22
24
  const rawFrames: RawFrame[] = [];
@@ -66,6 +68,8 @@ export function parseRawFramesFromErrorStack(stack: string): RawFrame[]|null {
66
68
  if (lineContent.endsWith(')') && openParenIndex !== -1) {
67
69
  functionName = lineContent.substring(0, openParenIndex).trim();
68
70
  location = lineContent.substring(openParenIndex + 2, lineContent.length - 1);
71
+ } else if (lineContent.startsWith('(') && lineContent.endsWith(')')) {
72
+ location = lineContent.substring(1, lineContent.length - 1);
69
73
  } else {
70
74
  location = lineContent;
71
75
  }
@@ -90,9 +94,9 @@ export function parseRawFramesFromErrorStack(stack: string): RawFrame[]|null {
90
94
  if (innerOpenParen !== -1) {
91
95
  evalFunctionName = evalOriginStr.substring(0, innerOpenParen).trim();
92
96
  evalLocation = evalOriginStr.substring(innerOpenParen + 2, evalOriginStr.length - 1);
93
- evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName} (${evalLocation})`)?.[0];
97
+ evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName} (${evalLocation})`, resolveURL)?.[0];
94
98
  } else {
95
- evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName}`)?.[0];
99
+ evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName}`, resolveURL)?.[0];
96
100
  }
97
101
  }
98
102
 
@@ -112,9 +116,18 @@ export function parseRawFramesFromErrorStack(stack: string): RawFrame[]|null {
112
116
  }
113
117
  } else if (location) {
114
118
  const splitResult = Common.ParsedURL.ParsedURL.splitLineAndColumn(location);
115
- url = splitResult.url;
116
119
  lineNumber = splitResult.lineNumber ?? -1;
117
120
  columnNumber = splitResult.columnNumber ?? -1;
121
+
122
+ if (resolveURL && splitResult.url !== '<anonymous>' && splitResult.url !== 'native') {
123
+ const resolved = resolveURL(splitResult.url);
124
+ if (!resolved) {
125
+ return null;
126
+ }
127
+ url = resolved;
128
+ } else {
129
+ url = splitResult.url;
130
+ }
118
131
  }
119
132
 
120
133
  // Handle "typeName.methodName [as alias]"
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
+ import type * as Platform from '../../core/platform/platform.js';
6
7
  import * as SDK from '../../core/sdk/sdk.js';
7
8
  import type * as Protocol from '../../generated/protocol.js';
8
9
 
@@ -65,7 +66,17 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
65
66
  async createFromErrorStackLikeString(
66
67
  stack: string, rawFramesToUIFrames: TranslateRawFrames,
67
68
  exceptionDetails?: Protocol.Runtime.ExceptionDetails): Promise<StackTrace.StackTrace.ParsedErrorStackTrace|null> {
68
- const rawFrames = parseRawFramesFromErrorStack(stack);
69
+ const debuggerModel = this.target().model(SDK.DebuggerModel.DebuggerModel) as SDK.DebuggerModel.DebuggerModel;
70
+ const baseURL = this.target().inspectedURL();
71
+ const resolveURL = (url: Platform.DevToolsPath.UrlString): Platform.DevToolsPath.UrlString|null => {
72
+ let urlWithScheme = parseOrScriptMatch(debuggerModel, url);
73
+ if (!urlWithScheme && Common.ParsedURL.ParsedURL.isRelativeURL(url)) {
74
+ urlWithScheme = parseOrScriptMatch(debuggerModel, Common.ParsedURL.ParsedURL.completeURL(baseURL, url));
75
+ }
76
+ return urlWithScheme;
77
+ };
78
+
79
+ const rawFrames = parseRawFramesFromErrorStack(stack, resolveURL);
69
80
  if (!rawFrames) {
70
81
  return null;
71
82
  }
@@ -267,4 +278,26 @@ async function translateEvalOrigin(
267
278
  return new EvalOrigin(frames, parentEvalOrigin);
268
279
  }
269
280
 
281
+ function parseOrScriptMatch(debuggerModel: SDK.DebuggerModel.DebuggerModel,
282
+ url: Platform.DevToolsPath.UrlString|null): Platform.DevToolsPath.UrlString|null {
283
+ if (!url) {
284
+ return null;
285
+ }
286
+ if (Common.ParsedURL.ParsedURL.isValidUrlString(url)) {
287
+ return url;
288
+ }
289
+ if (debuggerModel.scriptsForSourceURL(url).length) {
290
+ return url;
291
+ }
292
+ // nodejs stack traces contain (absolute) file paths, but v8 reports them as file: urls.
293
+ try {
294
+ const fileUrl = new URL(url, 'file://');
295
+ if (debuggerModel.scriptsForSourceURL(fileUrl.href as Platform.DevToolsPath.UrlString).length) {
296
+ return fileUrl.href as Platform.DevToolsPath.UrlString;
297
+ }
298
+ } catch {
299
+ }
300
+ return null;
301
+ }
302
+
270
303
  SDK.SDKModel.SDKModel.register(StackTraceModel, {capabilities: SDK.Target.Capability.NONE, autostart: false});