chrome-devtools-frontend
Version:
Chrome DevTools UI
125 lines (109 loc) • 4.17 kB
text/typescript
// 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 i18n from '../../../core/i18n/i18n.js';
import type * as SDK from '../../../core/sdk/sdk.js';
import * as Logs from '../../logs/logs.js';
import type {FunctionCallHandlerResult} from '../agents/AiAgent.js';
import {isOpaqueOrigin} from '../AiOrigins.js';
import {getRequestContextOrigin} from '../contexts/RequestContext.js';
import {
type BaseToolCapability,
type OriginLockCapability,
type Tool,
ToolName,
} from './Tool.js';
const UIStringsNotTranslate = {
listingNetworkRequests: 'Listing network requests',
} as const;
const lockedString = i18n.i18n.lockedString;
interface NetworkRequestSummary {
id: string;
url: string;
statusCode: number;
duration: string;
transferSize: string;
}
/**
* A tool that lists all network requests recorded by DevTools.
* Filters the list by the conversation's established origin to prevent cross-origin data exposure.
*/
export class ListNetworkRequestsTool implements
Tool<Record<string, never>, unknown, BaseToolCapability&OriginLockCapability> {
readonly name = ToolName.LIST_NETWORK_REQUESTS;
readonly description = 'Gives a list of network requests including URL, status code, and duration.';
readonly parameters: Host.AidaClient.FunctionObjectParam<never> = {
type: Host.AidaClient.ParametersTypes.OBJECT,
description: '',
nullable: true,
required: [],
properties: {},
};
displayInfoFromArgs(): {
title: string,
action: string,
} {
return {
title: lockedString(UIStringsNotTranslate.listingNetworkRequests),
action: 'listNetworkRequests()',
};
}
/**
* Handles the request to list network requests.
* Returns requests matching the conversation's established origin, if set.
*/
async handler(
_params: Record<string, never>,
context: BaseToolCapability&OriginLockCapability,
): Promise<FunctionCallHandlerResult<unknown>> {
const requests: NetworkRequestSummary[] = [];
// A conversation is locked to an origin once the first query is made.
// We only allow inspecting requests matching the conversation's established origin.
const origin = context.getEstablishedOrigin();
// Opaque origins are never allowed to be used as context.
if (origin && isOpaqueOrigin(origin)) {
return {
error: 'Opaque origin not allowed',
};
}
let hasCrossOriginRequest = false;
const requestsToShow: SDK.NetworkRequest.NetworkRequest[] = [];
for (const request of Logs.NetworkLog.NetworkLog.instance().requests()) {
// To prevent cross-origin prompt injection attacks, HAR-imported requests
// are assigned a virtual origin (e.g., `imported-har://${domain}`) rather than
// sharing the origin of live pages.
const requestOrigin = getRequestContextOrigin(request);
// If the conversation is locked to an origin, skip requests from other origins.
if (origin && requestOrigin !== origin) {
hasCrossOriginRequest = true;
continue;
}
requests.push({
id: request.requestId(),
url: request.url(),
statusCode: request.statusCode,
duration: i18n.TimeUtilities.secondsToString(request.duration),
transferSize: i18n.ByteUtilities.formatBytesToKb(request.transferSize),
});
requestsToShow.push(request);
}
if (requests.length === 0) {
return {
// If there were requests but they were filtered out due to the origin lock,
// we ask the user to start a new chat so they can select a request from the other origin.
error: hasCrossOriginRequest ? `No requests showing with origin ${origin}. Tell the user to start a new chat` :
'No requests recorded by DevTools',
};
}
return {
result: JSON.stringify(requests),
widgets: [{
name: 'NETWORK_REQUESTS_LIST',
data: {
requests: requestsToShow,
},
}],
};
}
}