Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/browserView/electron-browser/tools/browserTools.contribution.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Codicon } from '../../../../../base/common/codicons.js';
7
import { Disposable, DisposableMap, DisposableStore } from '../../../../../base/common/lifecycle.js';
8
import { localize } from '../../../../../nls.js';
9
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
10
import { IAgentNetworkFilterService } from '../../../../../platform/networkFilter/common/networkFilterService.js';
11
import { registerWorkbenchContribution2, WorkbenchPhase, type IWorkbenchContribution } from '../../../../common/contributions.js';
12
import { IEditorService } from '../../../../services/editor/common/editorService.js';
13
import { IChatContextService } from '../../../chat/browser/contextContrib/chatContextService.js';
14
import { ILanguageModelToolsService, ToolDataSource, ToolSet } from '../../../chat/common/tools/languageModelToolsService.js';
15
import { BrowserViewSharingState, IBrowserViewWorkbenchService } from '../../common/browserView.js';
16
import { formatBrowserEditorList } from './browserToolHelpers.js';
17
import { ClickBrowserTool, ClickBrowserToolData } from './clickBrowserTool.js';
18
import { DragElementTool, DragElementToolData } from './dragElementTool.js';
19
import { HandleDialogBrowserTool, HandleDialogBrowserToolData } from './handleDialogBrowserTool.js';
20
import { HoverElementTool, HoverElementToolData } from './hoverElementTool.js';
21
import { NavigateBrowserTool, NavigateBrowserToolData } from './navigateBrowserTool.js';
22
import { OpenBrowserTool, OpenBrowserToolData } from './openBrowserTool.js';
23
import { OpenBrowserToolNonAgentic, OpenBrowserToolNonAgenticData } from './openBrowserToolNonAgentic.js';
24
import { ReadBrowserTool, ReadBrowserToolData } from './readBrowserTool.js';
25
import { RunPlaywrightCodeTool, RunPlaywrightCodeToolData } from './runPlaywrightCodeTool.js';
26
import { ScreenshotBrowserTool, ScreenshotBrowserToolData } from './screenshotBrowserTool.js';
27
import { TypeBrowserTool, TypeBrowserToolData } from './typeBrowserTool.js';
28
29
30
class BrowserChatAgentToolsContribution extends Disposable implements IWorkbenchContribution {
31
32
static readonly ID = 'browserView.chatAgentTools';
33
private static readonly CONTEXT_ID = 'browserView.trackedPages';
34
35
private readonly _toolsStore = this._register(new DisposableStore());
36
private readonly _modelListeners = this._register(new DisposableMap<string, DisposableStore>());
37
private readonly _browserToolSet: ToolSet;
38
39
constructor(
40
@IInstantiationService private readonly instantiationService: IInstantiationService,
41
@ILanguageModelToolsService private readonly toolsService: ILanguageModelToolsService,
42
@IChatContextService private readonly chatContextService: IChatContextService,
43
@IEditorService private readonly editorService: IEditorService,
44
@IBrowserViewWorkbenchService private readonly browserViewService: IBrowserViewWorkbenchService,
45
@IAgentNetworkFilterService private readonly agentNetworkFilterService: IAgentNetworkFilterService,
46
) {
47
super();
48
49
this._browserToolSet = this._register(this.toolsService.createToolSet(
50
ToolDataSource.Internal,
51
'browser',
52
'browser',
53
{
54
icon: Codicon.globe,
55
description: localize('browserToolSet.description', 'Open and interact with integrated browser pages'),
56
}
57
));
58
59
this._updateToolRegistrations();
60
61
this._register(this.browserViewService.onDidChangeSharingAvailable(() => {
62
this._updateToolRegistrations();
63
}));
64
}
65
66
private _updateToolRegistrations(): void {
67
this._toolsStore.clear();
68
this._modelListeners.clearAndDisposeAll();
69
70
if (!this.browserViewService.isSharingAvailable) {
71
// If chat tools are disabled, we only register the non-agentic open tool,
72
// which allows opening browser pages without granting access to their contents.
73
this._toolsStore.add(this.toolsService.registerTool(OpenBrowserToolNonAgenticData, this.instantiationService.createInstance(OpenBrowserToolNonAgentic)));
74
this._toolsStore.add(this._browserToolSet.addTool(OpenBrowserToolNonAgenticData));
75
this.chatContextService.updateWorkspaceContextItems(BrowserChatAgentToolsContribution.CONTEXT_ID, []);
76
return;
77
}
78
79
this._toolsStore.add(this.toolsService.registerTool(OpenBrowserToolData, this.instantiationService.createInstance(OpenBrowserTool)));
80
this._toolsStore.add(this.toolsService.registerTool(ReadBrowserToolData, this.instantiationService.createInstance(ReadBrowserTool)));
81
this._toolsStore.add(this.toolsService.registerTool(ScreenshotBrowserToolData, this.instantiationService.createInstance(ScreenshotBrowserTool)));
82
this._toolsStore.add(this.toolsService.registerTool(NavigateBrowserToolData, this.instantiationService.createInstance(NavigateBrowserTool)));
83
this._toolsStore.add(this.toolsService.registerTool(ClickBrowserToolData, this.instantiationService.createInstance(ClickBrowserTool)));
84
this._toolsStore.add(this.toolsService.registerTool(DragElementToolData, this.instantiationService.createInstance(DragElementTool)));
85
this._toolsStore.add(this.toolsService.registerTool(HoverElementToolData, this.instantiationService.createInstance(HoverElementTool)));
86
this._toolsStore.add(this.toolsService.registerTool(TypeBrowserToolData, this.instantiationService.createInstance(TypeBrowserTool)));
87
this._toolsStore.add(this.toolsService.registerTool(RunPlaywrightCodeToolData, this.instantiationService.createInstance(RunPlaywrightCodeTool)));
88
this._toolsStore.add(this.toolsService.registerTool(HandleDialogBrowserToolData, this.instantiationService.createInstance(HandleDialogBrowserTool)));
89
90
this._toolsStore.add(this._browserToolSet.addTool(OpenBrowserToolData));
91
this._toolsStore.add(this._browserToolSet.addTool(ReadBrowserToolData));
92
this._toolsStore.add(this._browserToolSet.addTool(ScreenshotBrowserToolData));
93
this._toolsStore.add(this._browserToolSet.addTool(NavigateBrowserToolData));
94
this._toolsStore.add(this._browserToolSet.addTool(ClickBrowserToolData));
95
this._toolsStore.add(this._browserToolSet.addTool(DragElementToolData));
96
this._toolsStore.add(this._browserToolSet.addTool(HoverElementToolData));
97
this._toolsStore.add(this._browserToolSet.addTool(TypeBrowserToolData));
98
this._toolsStore.add(this._browserToolSet.addTool(RunPlaywrightCodeToolData));
99
this._toolsStore.add(this._browserToolSet.addTool(HandleDialogBrowserToolData));
100
101
// Subscribe to browser view changes and model sharing state changes
102
this._syncModelListeners();
103
this._toolsStore.add(this.browserViewService.onDidChangeBrowserViews(() => {
104
this._syncModelListeners();
105
this._updateBrowserContext();
106
}));
107
this._toolsStore.add(this.editorService.onDidActiveEditorChange(() => this._updateBrowserContext()));
108
this._toolsStore.add(this.editorService.onDidVisibleEditorsChange(() => this._updateBrowserContext()));
109
this._toolsStore.add(this.agentNetworkFilterService.onDidChange(() => this._updateBrowserContext()));
110
111
this._updateBrowserContext();
112
}
113
114
/**
115
* Subscribe to sharingState changes on each known model so the workspace
116
* context updates whenever a page is shared or unshared.
117
*/
118
private _syncModelListeners(): void {
119
const views = this.browserViewService.getKnownBrowserViews();
120
// Remove listeners for views that no longer exist
121
for (const id of this._modelListeners.keys()) {
122
if (!views.has(id)) {
123
this._modelListeners.deleteAndDispose(id);
124
}
125
}
126
// Add listeners for new views
127
for (const [id, input] of views) {
128
if (!this._modelListeners.has(id) && input.model) {
129
const store = new DisposableStore();
130
store.add(input.model.onDidChangeSharingState(() => this._updateBrowserContext()));
131
this._modelListeners.set(id, store);
132
}
133
}
134
}
135
136
private _updateBrowserContext(): void {
137
const views = [...this.browserViewService.getKnownBrowserViews().values()];
138
const sharedViews = views.filter(v => v.model?.sharingState === BrowserViewSharingState.Shared);
139
const unsharedCount = views.length - sharedViews.length;
140
141
if (sharedViews.length === 0 && unsharedCount === 0) {
142
this.chatContextService.updateWorkspaceContextItems(BrowserChatAgentToolsContribution.CONTEXT_ID, []);
143
return;
144
}
145
146
let value = '';
147
if (sharedViews.length > 0) {
148
value = 'The following browser pages are currently shared with you and can be interacted with using the browser tools:';
149
value += '\n' + formatBrowserEditorList(this.editorService, sharedViews, { agentNetworkFilterService: this.agentNetworkFilterService });
150
} else {
151
value = 'No browser pages are currently shared with you.';
152
}
153
154
if (unsharedCount > 0) {
155
if (value) {
156
value += '\n\n';
157
}
158
value += `${unsharedCount} ${unsharedCount === 1 ? 'page is' : 'pages are'} open but not shared.`;
159
value += `\nUse the 'open_browser_page' tool to open a new page or to help the user share an existing page.`;
160
}
161
162
this.chatContextService.updateWorkspaceContextItems(BrowserChatAgentToolsContribution.CONTEXT_ID, [{
163
handle: 0,
164
label: localize('browserContext.label', "Browser Pages"),
165
value: value
166
}]);
167
}
168
}
169
registerWorkbenchContribution2(BrowserChatAgentToolsContribution.ID, BrowserChatAgentToolsContribution, WorkbenchPhase.AfterRestored);
170
171