Path: blob/main/src/vs/workbench/contrib/browserView/electron-browser/tools/navigateBrowserTool.ts
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import type { CancellationToken } from '../../../../../base/common/cancellation.js';6import { Codicon } from '../../../../../base/common/codicons.js';7import { MarkdownString } from '../../../../../base/common/htmlContent.js';8import { URI } from '../../../../../base/common/uri.js';9import { localize } from '../../../../../nls.js';10import { IPlaywrightService } from '../../../../../platform/browserView/common/playwrightService.js';11import { ToolDataSource, type CountTokensCallback, type IPreparedToolInvocation, type IToolData, type IToolImpl, type IToolInvocation, type IToolInvocationPreparationContext, type IToolResult, type ToolProgress } from '../../../chat/common/tools/languageModelToolsService.js';12import { IAgentNetworkFilterService } from '../../../../../platform/networkFilter/common/networkFilterService.js';13import { createBrowserPageLink, errorResult, playwrightInvoke } from './browserToolHelpers.js';14import { BrowserChatToolReferenceName } from '../../common/browserChatToolReferenceNames.js';15import { OpenPageToolId } from './openBrowserTool.js';1617export const NavigateBrowserToolData: IToolData = {18id: 'navigate_page',19toolReferenceName: BrowserChatToolReferenceName.NavigatePage,20displayName: localize('navigateBrowserTool.displayName', 'Navigate Page'),21userDescription: localize('navigateBrowserTool.userDescription', 'Navigate or reload a browser page'),22modelDescription: 'Navigate a browser page by URL, history, or reload.',23icon: Codicon.arrowRight,24source: ToolDataSource.Internal,25inputSchema: {26type: 'object',27properties: {28pageId: {29type: 'string',30description: `The browser page ID to navigate, acquired from context or the open tool.`31},32type: {33type: 'string',34enum: ['url', 'back', 'forward', 'reload'],35description: 'Navigation type: "url" to navigate to a URL (default, requires "url" param), "back" or "forward" for history, "reload" to refresh.'36},37url: {38type: 'string',39description: 'The URL to navigate to. Required when type is "url".'40},41},42required: ['pageId'],43},44};4546interface INavigateBrowserToolParams {47pageId: string;48type?: 'url' | 'back' | 'forward' | 'reload';49url?: string;50}5152export class NavigateBrowserTool implements IToolImpl {53constructor(54@IPlaywrightService private readonly playwrightService: IPlaywrightService,55@IAgentNetworkFilterService private readonly agentNetworkFilterService: IAgentNetworkFilterService,56) { }5758async prepareToolInvocation(context: IToolInvocationPreparationContext, _token: CancellationToken): Promise<IPreparedToolInvocation | undefined> {59const params = context.parameters as INavigateBrowserToolParams;60const link = createBrowserPageLink(params.pageId);61switch (params.type) {62case 'reload':63return {64invocationMessage: new MarkdownString(localize('browser.reload.invocation', "Reloading {0}", link)),65pastTenseMessage: new MarkdownString(localize('browser.reload.past', "Reloaded {0}", link)),66icon: Codicon.refresh,67};68case 'back':69return {70invocationMessage: new MarkdownString(localize('browser.goBack.invocation', "Navigating backward in {0}", link)),71pastTenseMessage: new MarkdownString(localize('browser.goBack.past', "Navigated backward in {0}", link)),72icon: Codicon.arrowLeft,73};74case 'forward':75return {76invocationMessage: new MarkdownString(localize('browser.goForward.invocation', "Navigating forward in {0}", link)),77pastTenseMessage: new MarkdownString(localize('browser.goForward.past', "Navigated forward in {0}", link)),78icon: Codicon.arrowRight,79};80default: {81if (!params.url) {82throw new Error('The "url" parameter is required when type is "url".');83}84const parsed = URL.parse(params.url);85if (!parsed) {86throw new Error('You must provide a complete, valid URL.');87}8889const uri = URI.parse(params.url);90if (!this.agentNetworkFilterService.isUriAllowed(uri)) {91throw new Error(this.agentNetworkFilterService.formatError(uri));92}9394return {95invocationMessage: new MarkdownString(localize('browser.navigate.invocation', "Navigating to {0} in {1}", parsed.href, link)),96pastTenseMessage: new MarkdownString(localize('browser.navigate.past', "Navigated to {0} in {1}", parsed.href, link)),97confirmationMessages: {98title: localize('browser.navigate.confirmTitle', 'Navigate Browser?'),99message: localize('browser.navigate.confirmMessage', 'This will navigate the browser to {0} and allow the agent to access its contents.', parsed.href),100allowAutoConfirm: true,101},102};103}104}105}106107async invoke(invocation: IToolInvocation, _countTokens: CountTokensCallback, _progress: ToolProgress, _token: CancellationToken): Promise<IToolResult> {108const params = invocation.parameters as INavigateBrowserToolParams;109110if (!params.pageId) {111return errorResult(`No page ID provided. Use '${OpenPageToolId}' first.`);112}113114switch (params.type) {115case 'reload':116return playwrightInvoke(this.playwrightService, params.pageId, (page) => page.reload({ waitUntil: 'domcontentloaded' }));117case 'back':118return playwrightInvoke(this.playwrightService, params.pageId, (page) => page.goBack({ waitUntil: 'domcontentloaded' }));119case 'forward':120return playwrightInvoke(this.playwrightService, params.pageId, (page) => page.goForward({ waitUntil: 'domcontentloaded' }));121default: {122return playwrightInvoke(this.playwrightService, params.pageId, (page, url) => {123return page.goto(url, { waitUntil: 'domcontentloaded' });124}, params.url!);125}126}127}128}129130131