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.
- package/.agents/skills/devtools-source-maps/SKILL.md +124 -0
- package/docs/README.md +1 -0
- package/docs/using_source_maps.md +159 -0
- package/front_end/core/host/AidaClientTypes.ts +2 -0
- package/front_end/core/host/UserMetrics.ts +5 -3
- package/front_end/core/root/Runtime.ts +10 -0
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
- package/front_end/core/sdk/DebuggerModel.ts +7 -9
- package/front_end/core/sdk/NetworkRequest.ts +0 -25
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
- package/front_end/generated/InspectorBackendCommands.ts +2 -2
- package/front_end/generated/SupportedCSSProperties.js +79 -2
- package/front_end/generated/protocol.ts +0 -5
- package/front_end/models/ai_assistance/AiAgent2.ts +52 -15
- package/front_end/models/ai_assistance/AiConversation.ts +4 -2
- package/front_end/models/ai_assistance/AiOrigins.ts +63 -2
- package/front_end/models/ai_assistance/README.md +20 -8
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +2 -2
- package/front_end/models/ai_assistance/agents/FileAgent.ts +9 -42
- package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +9 -133
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +25 -2
- package/front_end/models/ai_assistance/agents/README.md +64 -0
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +26 -3
- package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
- package/front_end/models/ai_assistance/contexts/FileContext.ts +45 -0
- package/front_end/models/ai_assistance/contexts/RequestContext.snapshot.txt +48 -0
- package/front_end/models/ai_assistance/contexts/RequestContext.ts +116 -0
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +2 -1
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +9 -12
- package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
- package/front_end/models/ai_assistance/tools/README.md +45 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +74 -11
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +17 -4
- package/front_end/models/stack_trace/StackTraceModel.ts +34 -1
- package/front_end/models/trace/Styles.ts +29 -7
- package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +15 -11
- package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
- package/front_end/models/trace/helpers/Timing.ts +10 -0
- package/front_end/models/trace/types/TraceEvents.ts +22 -2
- package/front_end/models/web_mcp/WebMCPModel.ts +8 -48
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +21 -15
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +4 -4
- package/front_end/panels/application/ApplicationPanelSidebar.ts +69 -0
- package/front_end/panels/application/ServiceWorkersView.ts +2 -2
- package/front_end/panels/application/WebMCPView.ts +40 -71
- package/front_end/panels/application/components/AdsView.ts +31 -28
- package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
- package/front_end/panels/application/components/adsView.css +6 -0
- package/front_end/panels/common/ExtensionServer.ts +5 -0
- package/front_end/panels/console/ConsoleView.ts +6 -1
- package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
- package/front_end/panels/console/SymbolizedErrorWidget.ts +4 -1
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
- package/front_end/panels/elements/PropertyRenderer.ts +0 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
- package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
- package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
- package/front_end/panels/network/NetworkLogView.ts +34 -7
- package/front_end/panels/profiler/HeapProfileView.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
- package/front_end/panels/profiler/IsolateSelector.ts +4 -2
- package/front_end/panels/profiler/ProfileLauncherView.ts +194 -126
- package/front_end/panels/profiler/ProfilesPanel.ts +1 -3
- package/front_end/panels/settings/components/SyncSection.ts +1 -1
- package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
- package/front_end/panels/timeline/TimelinePanel.ts +7 -0
- package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
- package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
- package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
- package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
- package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
- package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
- package/front_end/ui/kit/link/Link.ts +16 -2
- package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
- package/front_end/ui/legacy/InspectorView.ts +4 -1
- package/front_end/ui/legacy/PlusButton.ts +6 -1
- package/front_end/ui/legacy/Widget.ts +19 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
- package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
- package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
- package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
- 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
|
-
|
|
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
|
|
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:
|
|
110
|
+
context: BaseToolCapability&PageExecutionCapability&StyleMutationCapability,
|
|
108
111
|
options?: FunctionHandlerOptions,
|
|
109
112
|
): Promise<FunctionCallHandlerResult<unknown>> {
|
|
110
|
-
const executionNode = context.getExecutionContextNode
|
|
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 {
|
|
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
|
|
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:
|
|
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
|
|
82
|
-
if (!
|
|
83
|
-
return {error: 'Error: Could not find the
|
|
89
|
+
const target = context.getTarget();
|
|
90
|
+
if (!target) {
|
|
91
|
+
return {error: 'Error: Could not find the inspected page.'};
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
const
|
|
87
|
-
if (!
|
|
88
|
-
return {error: 'Error:
|
|
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 (
|
|
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
|
-
*
|
|
12
|
+
* Base capability for all tool contexts, providing access to the conversation context.
|
|
13
13
|
*/
|
|
14
|
-
export interface
|
|
14
|
+
export interface BaseToolCapability {
|
|
15
|
+
/**
|
|
16
|
+
* The active context for the current conversation step, if any.
|
|
17
|
+
*/
|
|
15
18
|
conversationContext: ConversationContext<unknown>|null;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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,
|
|
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:
|
|
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
|
-
*
|
|
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
|
|
33
|
-
static get(name: string): Tool
|
|
34
|
-
|
|
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})
|
|
97
|
+
evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName} (${evalLocation})`, resolveURL)?.[0];
|
|
94
98
|
} else {
|
|
95
|
-
evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName}
|
|
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
|
|
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});
|