UNPKG

chrome-devtools-frontend

Version:
132 lines (114 loc) 4.61 kB
// Copyright 2026 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Host from '../../../core/host/host.js'; import * as Root from '../../../core/root/root.js'; import type {FunctionCallHandlerResult, FunctionHandlerOptions,} from '../agents/AiAgent.js'; import {JavascriptExecutor} from '../agents/ExecuteJavascript.js'; import { type BaseToolCapability, type PageExecutionCapability, type StyleMutationCapability, type Tool, type ToolArgs, ToolName, } from './Tool.js'; export interface ExecuteJavaScriptArgs extends ToolArgs { code: string; explanation: string; title: string; } export class ExecuteJavaScriptTool implements Tool<ExecuteJavaScriptArgs, unknown, BaseToolCapability&PageExecutionCapability&StyleMutationCapability> { readonly name = ToolName.EXECUTE_JAVASCRIPT; readonly description = 'This function allows you to run JavaScript code on the inspected page to access the element styles and page content.\nCall this function to gather additional information or modify the page state. Call this function enough times to investigate the user request.'; readonly parameters: Host.AidaClient.FunctionObjectParam<keyof ExecuteJavaScriptArgs> = { type: Host.AidaClient.ParametersTypes.OBJECT, description: '', nullable: false, properties: { code: { type: Host.AidaClient.ParametersTypes.STRING, description: `JavaScript code snippet to run on the inspected page. Make sure the code is formatted for readability. # Instructions * To return data, define a top-level \`data\` variable and populate it with data you want to get. Only JSON-serializable objects can be assigned to \`data\`. * If you modify styles on an element, ALWAYS call the pre-defined global \`async setElementStyles(el: Element, styles: object)\` function. This function is an internal mechanism for you and should never be presented as a command/advice to the user. * **CRITICAL** Only get styles that might be relevant to the user request. * **CRITICAL** Never assume a selector for the elements unless you verified your knowledge. * **CRITICAL** Consider that \`data\` variable from the previous function calls are not available in a new function call. For example, the code to change element styles: \`\`\` await setElementStyles($0, { color: 'blue', }); \`\`\` For example, the code to get overlapping elements: \`\`\` const data = { overlappingElements: Array.from(document.querySelectorAll('*')) .filter(el => { const rect = el.getBoundingClientRect(); const popupRect = $0.getBoundingClientRect(); return ( el !== $0 && rect.left < popupRect.right && rect.right > popupRect.left && rect.top < popupRect.bottom && rect.bottom > popupRect.top ); }) .map(el => ({ tagName: el.tagName, id: el.id, className: el.className, zIndex: window.getComputedStyle(el)['z-index'] })) }; \`\`\` `, }, explanation: { type: Host.AidaClient.ParametersTypes.STRING, description: 'Explain why you want to run this code', }, title: { type: Host.AidaClient.ParametersTypes.STRING, description: 'Provide a summary of what the code does. For example, "Checking related element styles".', }, }, required: ['code', 'explanation', 'title'], }; displayInfoFromArgs(params: ExecuteJavaScriptArgs): { title: string, thought: string, action: string, } { return { title: params.title, thought: params.explanation, action: params.code, }; } async handler( params: ExecuteJavaScriptArgs, context: BaseToolCapability&PageExecutionCapability&StyleMutationCapability, options?: FunctionHandlerOptions, ): Promise<FunctionCallHandlerResult<unknown>> { const executionNode = context.getExecutionContextNode(); if (!executionNode) { return {error: 'Error: Could not find the context node for execution.'}; } const executionMode = Root.Runtime.hostConfig.devToolsFreestyler?.executionMode ?? Root.Runtime.HostConfigFreestylerExecutionMode.ALL_SCRIPTS; const executor = new JavascriptExecutor({ executionMode, getContextNode: () => executionNode, createExtensionScope: context.createExtensionScope, changes: context.changeManager, }, context.execJs); return await executor.executeAction(params.code, options); } }