chrome-devtools-frontend
Version:
Chrome DevTools UI
147 lines (125 loc) • 5.26 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 * as SDK from '../../../core/sdk/sdk.js';
import type {FunctionHandlerOptions} from '../agents/AiAgent.js';
import {
type CookieDetails,
getCookiesForOrigin,
resolveAllowedTargetOrigins,
} from './CookieUtils.js';
import {
type BaseToolCapability,
type DataHandlerResult,
type DataTool,
type OriginLockCapability,
type ServerLoggingCapability,
ToolAnnotation,
type ToolArgs,
ToolName,
} from './Tool.js';
const lockedString = i18n.i18n.lockedString;
// Maximum character length allowed per cookie value to prevent oversized cookies
// from exceeding the LLM context window.
export const MAX_NUM_CHAR_LENGTH = 10000;
export interface GetCookieValuesArgs extends ToolArgs {
cookieNames: string[];
origins?: string[];
}
export interface GetCookieValuesResult {
cookiesByOrigin: Record<string, {cookies?: CookieDetails[], error?: string}>;
}
export class GetCookieValuesTool implements DataTool<GetCookieValuesArgs, GetCookieValuesResult,
BaseToolCapability&OriginLockCapability&ServerLoggingCapability> {
readonly name: ToolName = ToolName.GET_COOKIE_VALUES;
readonly description: string =
'Retrieve the values and detailed metadata of specific cookies by their names across origins.';
readonly annotations: ToolAnnotation[] = [ToolAnnotation.REDACT_FROM_HISTORY];
readonly parameters: Host.AidaClient.FunctionObjectParam<keyof GetCookieValuesArgs> = {
type: Host.AidaClient.ParametersTypes.OBJECT,
description: '',
nullable: false,
properties: {
cookieNames: {
type: Host.AidaClient.ParametersTypes.ARRAY,
description: 'A list of cookie names to retrieve values and metadata for.',
items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'A cookie name.'},
nullable: false,
},
origins: {
type: Host.AidaClient.ParametersTypes.ARRAY,
description: 'Optional list of origins the cookies belong to. Defaults to the current page origin if omitted.',
items: {type: Host.AidaClient.ParametersTypes.STRING, description: 'An origin URL.'},
nullable: true,
},
},
required: ['cookieNames'],
};
displayInfoFromArgs(args: GetCookieValuesArgs): {
title: string,
action: string,
} {
return {
title: lockedString('Reading cookie values and metadata'),
action: `getCookieValues(${JSON.stringify(args?.cookieNames ?? [])}, ${JSON.stringify(args?.origins ?? [])})`,
};
}
async handler(
args: GetCookieValuesArgs,
context: BaseToolCapability&OriginLockCapability&ServerLoggingCapability,
options?: FunctionHandlerOptions,
): Promise<DataHandlerResult<GetCookieValuesResult>> {
context.disableLogging();
if (!args || !Array.isArray(args.cookieNames) || args.cookieNames.length === 0) {
return {error: 'No cookie names provided.'};
}
// eslint-disable-next-line @devtools/no-instance-of-migrated-singletons
const targetManager = SDK.TargetManager.TargetManager.instance();
const targetOriginsResult = resolveAllowedTargetOrigins(args?.origins, context, targetManager);
if ('error' in targetOriginsResult) {
return {error: targetOriginsResult.error};
}
const {targetOrigins, primaryPageTarget} = targetOriginsResult;
if (options?.approved !== true) {
const cookieString = args.cookieNames.map(name => `\`${name}\``).join(', ');
const targetsDesc = targetOrigins.join(', ');
return {
requiresApproval: true,
description: lockedString(
`The AI wants to access the value(s) and metadata of cookie(s) ${cookieString} on ${targetsDesc}.`),
};
}
const cookiesByOrigin: GetCookieValuesResult['cookiesByOrigin'] = {};
await Promise.all(targetOrigins.map(async origin => {
const result = await getCookiesForOrigin(origin, targetManager, primaryPageTarget);
if ('error' in result) {
cookiesByOrigin[origin] = {error: result.error};
return;
}
const matchingCookies = result.cookies.filter(c => args.cookieNames.includes(c.name()));
const cookieData: CookieDetails[] = matchingCookies.map(cookie => {
const value = cookie.value();
const truncatedValue =
value.length > MAX_NUM_CHAR_LENGTH ? value.substring(0, MAX_NUM_CHAR_LENGTH) + '... <truncated>' : value;
return {
name: cookie.name(),
value: truncatedValue,
domain: cookie.domain(),
path: cookie.path(),
expires: cookie.expires(),
size: cookie.size(),
secure: cookie.secure(),
sameSite: cookie.sameSite(),
partitioned: cookie.partitioned(),
priority: cookie.priority(),
sourcePort: cookie.sourcePort(),
sourceScheme: cookie.sourceScheme(),
};
});
cookiesByOrigin[origin] = {cookies: cookieData};
}));
return {result: {cookiesByOrigin}};
}
}