Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/electron-browser/actions/openInAgentsAction.ts
13397 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 './media/openInAgents.css';
7
import { $, append } from '../../../base/browser/dom.js';
8
import { getDefaultHoverDelegate } from '../../../base/browser/ui/hover/hoverDelegateFactory.js';
9
import { BaseActionViewItem, IBaseActionViewItemOptions } from '../../../base/browser/ui/actionbar/actionViewItems.js';
10
import { IAction } from '../../../base/common/actions.js';
11
import { Disposable } from '../../../base/common/lifecycle.js';
12
import { isMacintosh, isWindows } from '../../../base/common/platform.js';
13
import { localize, localize2 } from '../../../nls.js';
14
import { Action2, MenuId, registerAction2 } from '../../../platform/actions/common/actions.js';
15
import { IActionViewItemService } from '../../../platform/actions/browser/actionViewItemService.js';
16
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../../platform/configuration/common/configurationRegistry.js';
17
import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../platform/contextkey/common/contextkey.js';
18
import { IHoverService } from '../../../platform/hover/browser/hover.js';
19
import { IInstantiationService, ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js';
20
import { INativeHostService } from '../../../platform/native/common/native.js';
21
import { IProductService } from '../../../platform/product/common/productService.js';
22
import { Registry } from '../../../platform/registry/common/platform.js';
23
import { ITelemetryService } from '../../../platform/telemetry/common/telemetry.js';
24
import { IWorkspaceContextService, WorkbenchState } from '../../../platform/workspace/common/workspace.js';
25
import { ToggleTitleBarConfigAction, TitleBarLeadingActionsGroup } from '../../browser/parts/titlebar/titlebarActions.js';
26
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../common/contributions.js';
27
import { InEditorZenModeContext, IsAuxiliaryWindowContext, IsSessionsWindowContext } from '../../common/contextkeys.js';
28
import { workbenchConfigurationNodeBase } from '../../common/configuration.js';
29
import { IWorkbenchEnvironmentService } from '../../services/environment/common/environmentService.js';
30
import { ChatEntitlementContextKeys } from '../../services/chat/common/chatEntitlementService.js';
31
32
const OpenInAgentsActionId = 'workbench.action.openInAgents';
33
const OpenInAgentsEnabledSetting = 'workbench.openInAgents.enabled';
34
35
// Context key tracking the current product quality so we can hide the
36
// "Open in Agents" entry in stable builds for now.
37
const OpenInAgentsProductQualityContext = new RawContextKey<string>('openInAgentsProductQuality', '');
38
39
type OpenInAgentsMode = 'siblingApp' | 'newWindow';
40
41
type OpenInAgentsEvent = { mode: OpenInAgentsMode };
42
type OpenInAgentsClassification = {
43
owner: 'osortega';
44
comment: 'Tracks when the user opens the Agents application from the VS Code titlebar.';
45
mode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How the Agents app was opened: siblingApp (launched separate Agents app) or newWindow (in-process agents window).' };
46
};
47
48
const OpenInAgentsVisibility = ContextKeyExpr.and(
49
ContextKeyExpr.equals(`config.${OpenInAgentsEnabledSetting}`, true),
50
IsSessionsWindowContext.toNegated(),
51
IsAuxiliaryWindowContext.toNegated(),
52
InEditorZenModeContext.negate(),
53
// Hide whenever the user has signaled (or policy/workspace trust dictates)
54
// that AI features should not be shown in this window/workspace.
55
ChatEntitlementContextKeys.Setup.hidden.negate(),
56
ChatEntitlementContextKeys.Setup.disabled.negate(),
57
ChatEntitlementContextKeys.Setup.disabledInWorkspace.negate(),
58
ChatEntitlementContextKeys.Setup.untrusted.negate(),
59
// Hide in stable builds for now (insider, exploration and OSS dev are allowed).
60
ContextKeyExpr.notEquals(OpenInAgentsProductQualityContext.key, 'stable'),
61
);
62
63
/**
64
* Action that opens the Agents application for the current workspace.
65
*
66
* In built builds where a sibling Agents app is registered (`darwinSiblingBundleIdentifier`
67
* / `win32SiblingExeBasename`), launches it via {@link INativeHostService.launchSiblingApp}
68
* with `--agents` and the current workspace folder/file. Otherwise falls back to opening
69
* a new in-process Agents window via {@link INativeHostService.openAgentsWindow}.
70
*/
71
class OpenInAgentsAction extends Action2 {
72
73
constructor() {
74
super({
75
id: OpenInAgentsActionId,
76
title: localize2('openInAgents', "Open in Agents"),
77
f1: true,
78
precondition: OpenInAgentsVisibility,
79
menu: [{
80
// Render in the global titlebar tool bar in the dedicated leading
81
// slot so we appear before the layout controls (and stay visible
82
// when layout controls are toggled off).
83
id: MenuId.TitleBar,
84
group: TitleBarLeadingActionsGroup,
85
order: -1000,
86
when: OpenInAgentsVisibility,
87
}]
88
});
89
}
90
91
override async run(accessor: ServicesAccessor): Promise<void> {
92
const nativeHostService = accessor.get(INativeHostService);
93
const productService = accessor.get(IProductService);
94
const environmentService = accessor.get(IWorkbenchEnvironmentService);
95
const workspaceContextService = accessor.get(IWorkspaceContextService);
96
const telemetryService = accessor.get(ITelemetryService);
97
98
const args: string[] = ['--new-window'];
99
100
const workspace = workspaceContextService.getWorkspace();
101
switch (workspaceContextService.getWorkbenchState()) {
102
case WorkbenchState.FOLDER:
103
if (workspace.folders.length > 0) {
104
args.push('--folder-uri', workspace.folders[0].uri.toString());
105
}
106
break;
107
case WorkbenchState.WORKSPACE:
108
if (workspace.configuration) {
109
args.push('--file-uri', workspace.configuration.toString());
110
}
111
break;
112
}
113
114
const hasSibling = !!(
115
productService.darwinSiblingBundleIdentifier ||
116
productService.win32SiblingExeBasename
117
);
118
119
// In built builds with a sibling Agents app available, launch it.
120
// Otherwise (dev / OSS / unsupported platform / no sibling), open a new agents window of
121
// the current Electron app. `launchSiblingApp` is only implemented for macOS/Windows
122
// (see `src/vs/platform/native/node/siblingApp.ts`), so gate on actual platform support.
123
const canLaunchSiblingApp = isMacintosh || isWindows;
124
const mode: OpenInAgentsMode = environmentService.isBuilt && hasSibling && canLaunchSiblingApp ? 'siblingApp' : 'newWindow';
125
telemetryService.publicLog2<OpenInAgentsEvent, OpenInAgentsClassification>('vscode.openInAgents', { mode });
126
127
if (mode === 'siblingApp') {
128
await nativeHostService.launchSiblingApp(args);
129
} else {
130
await nativeHostService.openAgentsWindow({ forceNewWindow: true });
131
}
132
}
133
}
134
135
/**
136
* Renders the "Open in Agents" titlebar entry as an icon-only button that
137
* expands to reveal a label on hover / keyboard focus.
138
*/
139
class OpenInAgentsTitleBarWidget extends BaseActionViewItem {
140
141
constructor(
142
action: IAction,
143
options: IBaseActionViewItemOptions | undefined,
144
@IHoverService private readonly hoverService: IHoverService,
145
) {
146
super(undefined, action, options);
147
}
148
149
override render(container: HTMLElement): void {
150
super.render(container);
151
152
container.classList.add('open-in-agents-titlebar-widget');
153
container.setAttribute('role', 'button');
154
155
const label = this.action.label || localize('openInAgentsLabel', "Open in Agents");
156
container.setAttribute('aria-label', label);
157
this._register(this.hoverService.setupManagedHover(getDefaultHoverDelegate('element'), container, label));
158
159
const icon = append(container, $('span.open-in-agents-titlebar-widget-icon'));
160
icon.setAttribute('aria-hidden', 'true');
161
162
const labelEl = append(container, $('span.open-in-agents-titlebar-widget-label'));
163
labelEl.textContent = label;
164
}
165
}
166
167
class OpenInAgentsContribution extends Disposable implements IWorkbenchContribution {
168
169
static readonly ID = 'workbench.contrib.openInAgents.desktop';
170
171
constructor(
172
@IActionViewItemService actionViewItemService: IActionViewItemService,
173
@IInstantiationService instantiationService: IInstantiationService,
174
@IContextKeyService contextKeyService: IContextKeyService,
175
@IProductService productService: IProductService,
176
) {
177
super();
178
OpenInAgentsProductQualityContext.bindTo(contextKeyService).set(productService.quality ?? '');
179
this._register(actionViewItemService.register(MenuId.TitleBar, OpenInAgentsActionId, (action, options) => {
180
return instantiationService.createInstance(OpenInAgentsTitleBarWidget, action, options);
181
}, undefined));
182
}
183
}
184
185
registerAction2(OpenInAgentsAction);
186
registerWorkbenchContribution2(OpenInAgentsContribution.ID, OpenInAgentsContribution, WorkbenchPhase.BlockRestore);
187
188
// Toggle entry in titlebar context menu (right-click on titlebar)
189
registerAction2(class ToggleOpenInAgents extends ToggleTitleBarConfigAction {
190
constructor() {
191
super(
192
OpenInAgentsEnabledSetting,
193
localize('toggle.openInAgents', 'Open in Agents'),
194
localize('toggle.openInAgentsDescription', "Toggle visibility of the Open in Agents button in title bar"),
195
6,
196
);
197
}
198
});
199
200
// Configuration setting backing the toggle.
201
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
202
...workbenchConfigurationNodeBase,
203
properties: {
204
[OpenInAgentsEnabledSetting]: {
205
type: 'boolean',
206
default: true,
207
markdownDescription: localize('openInAgentsEnabled', "Controls whether the Open in Agents button is shown in the title bar."),
208
}
209
}
210
});
211
212