UNPKG

chrome-devtools-frontend

Version:
125 lines (112 loc) 4.19 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 SDK from '../../../core/sdk/sdk.js'; import type {FunctionCallHandlerResult} from '../agents/AiAgent.js'; import {DOMNodeContext} from '../contexts/DOMNodeContext.js'; import { type BaseToolCapability, type OriginLockCapability, type TargetCapability, type Tool, type ToolArgs, ToolName, } from './Tool.js'; /** * Arguments for resolving a Lighthouse path to a backend node ID. */ export interface ResolveLighthousePathArgs extends ToolArgs { /** * A Lighthouse-style element path. * This is typically a comma-separated list of child indices and tag names * representing the path from the root to the target element (e.g., "1,HTML,1,BODY"). */ path: string; explanation: string; } /** * A tool that resolves a Lighthouse-style element path to a backend node ID. * * This is used by the AI assistant to identify specific DOM nodes referred to in * Lighthouse reports. It ensures the resolved node belongs to the locked origin. */ export class ResolveLighthousePathTool implements Tool<ResolveLighthousePathArgs, {backendNodeId: number}, BaseToolCapability&TargetCapability&OriginLockCapability> { readonly name = ToolName.RESOLVE_LIGHTHOUSE_PATH; readonly description = 'Resolves a Lighthouse path to a backend node ID.'; readonly parameters: Host.AidaClient.FunctionObjectParam<keyof ResolveLighthousePathArgs> = { type: Host.AidaClient.ParametersTypes.OBJECT, description: 'Arguments for resolving a Lighthouse path to a backend node ID.', nullable: false, properties: { explanation: { type: Host.AidaClient.ParametersTypes.STRING, description: 'Reason for requesting this resolution.', nullable: false, }, path: { type: Host.AidaClient.ParametersTypes.STRING, description: 'Lighthouse path string.', nullable: false, }, }, required: ['explanation', 'path'], }; displayInfoFromArgs(params: ResolveLighthousePathArgs): { title: string, thought: string, action: string, } { return { title: 'Resolving element path', thought: params.explanation, action: `resolveLighthousePath('${params.path}')`, }; } /** * Handles the resolution request. * * It retrieves the node path using the target's DOMModel and verifies * that the node's origin matches the established origin lock to prevent * access to nodes from other origins. */ async handler( params: ResolveLighthousePathArgs, context: BaseToolCapability&TargetCapability&OriginLockCapability, ): Promise<FunctionCallHandlerResult<{backendNodeId: number}>> { const establishedOrigin = context.getEstablishedOrigin(); if (!establishedOrigin) { return {error: 'Error: Origin lock is not established.'}; } const target = context.getTarget(); const domModel = target?.model(SDK.DOMModel.DOMModel); if (!domModel) { return {error: 'Error: Inspected target not found.'}; } let nodeId; try { // Resolves the Lighthouse path (a representation of the path to a node) // and ensures the node is loaded into the frontend DOM model, returning its ID. nodeId = await domModel.pushNodeByPathToFrontend(params.path); } catch { return {error: 'Error: Could not find node by path.'}; } if (!nodeId) { return {error: 'Error: Could not find node by path.'}; } const node = domModel.nodeForId(nodeId); if (!node) { return {error: 'Error: Could not retrieve resolved node.'}; } const nodeContext = new DOMNodeContext(node); // Security check: Ensure the resolved node belongs to the same origin // that this AI assistance session is locked to, preventing cross-origin access. if (!nodeContext.isOriginAllowed(establishedOrigin)) { return {error: 'Error: Node does not belong to the locked origin.'}; } return { result: {backendNodeId: node.backendNodeId()}, }; } }