chat - enable skeleton prompt in more areas (#243857)

This commit is contained in:
Benjamin Pasero
2025-03-18 14:05:05 +01:00
committed by GitHub
parent c0b410d696
commit 1deaa6ffd6
3 changed files with 69 additions and 45 deletions
@@ -107,24 +107,42 @@ class SetupChatAgentImplementation extends Disposable implements IChatAgentImple
ChatContextKeys.Setup.fromDialog
);
const id = location === ChatAgentLocation.Panel ? 'setup.chat' : isToolsAgent ? 'setup.agent' : 'setup.edits';
const welcomeMessageContent: IChatWelcomeMessageContent = location === ChatAgentLocation.Panel ?
{
title: localize('chatTitle', "Ask Copilot"),
message: new MarkdownString(localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use.")),
icon: Codicon.copilotLarge
} : isToolsAgent ?
{
title: localize('editsTitle', "Edit with Copilot"),
message: new MarkdownString(localize('agentMessage', "Ask Copilot to edit your files in agent mode. Copilot will automatically use multiple requests to pick files to edit, run terminal commands, and iterate on errors.")),
icon: Codicon.copilotLarge
} :
{
title: localize('editsTitle', "Edit with Copilot"),
message: new MarkdownString(localize('editsMessage', "Start your editing session by defining a set of files that you want to work with. Then ask Copilot for the changes you want to make.")),
let id: string;
let description = localize('chatDescription', "Ask Copilot");
let welcomeMessageContent: IChatWelcomeMessageContent | undefined;
switch (location) {
case ChatAgentLocation.Panel:
id = 'setup.chat';
welcomeMessageContent = {
title: description,
message: new MarkdownString(localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use.")),
icon: Codicon.copilotLarge
};
break;
case ChatAgentLocation.EditingSession:
id = isToolsAgent ? 'setup.agent' : 'setup.edits';
description = isToolsAgent ? localize('agentDescription', "Edit files in your workspace in agent mode (Experimental)") : localize('editsDescription', "Edit files in your workspace");
welcomeMessageContent = isToolsAgent ?
{
title: localize('editsTitle', "Edit with Copilot"),
message: new MarkdownString(localize('agentMessage', "Ask Copilot to edit your files in agent mode. Copilot will automatically use multiple requests to pick files to edit, run terminal commands, and iterate on errors.")),
icon: Codicon.copilotLarge
} :
{
title: localize('editsTitle', "Edit with Copilot"),
message: new MarkdownString(localize('editsMessage', "Start your editing session by defining a set of files that you want to work with. Then ask Copilot for the changes you want to make.")),
icon: Codicon.copilotLarge
};
break;
case ChatAgentLocation.Terminal:
id = 'setup.terminal';
break;
case ChatAgentLocation.Editor:
id = 'setup.editor';
break;
default:
throw new Error(`Unsupported location: ${location}`);
}
const disposable = new DisposableStore();
@@ -141,7 +159,7 @@ class SetupChatAgentImplementation extends Disposable implements IChatAgentImple
welcomeMessageContent,
helpTextPrefix: SetupChatAgentImplementation.SETUP_WARNING
},
description: location === ChatAgentLocation.Panel ? localize('chatDescription', "Ask Copilot") : isToolsAgent ? localize('agentDescription', "Edit files in your workspace in agent mode (Experimental)") : localize('editsDescription', "Edit files in your workspace"),
description,
extensionId: nullExtensionDescription.identifier,
extensionDisplayName: nullExtensionDescription.name,
extensionPublisherId: nullExtensionDescription.publisher
@@ -204,7 +222,7 @@ class SetupChatAgentImplementation extends Disposable implements IChatAgentImple
let success = undefined;
try {
success = await setup.run();
success = await setup.run({ disableCopilotViewReveal: true /* preserve the context we are currently in */ });
} catch (error) {
this.logService.error(localize('setupError', "Error during setup: {0}", toErrorMessage(error)));
} finally {
@@ -281,12 +299,12 @@ class ChatSetup {
@ILogService private readonly logService: ILogService,
) { }
async run(): Promise<boolean | undefined> {
async run(options?: { disableCopilotViewReveal?: boolean }): Promise<boolean | undefined> {
if (this.pendingRun) {
return this.pendingRun;
}
this.pendingRun = this.doRun();
this.pendingRun = this.doRun(options);
try {
return await this.pendingRun;
@@ -295,7 +313,7 @@ class ChatSetup {
}
}
private async doRun(): Promise<boolean | undefined> {
private async doRun(options?: { disableCopilotViewReveal?: boolean }): Promise<boolean | undefined> {
let setupStrategy: ChatSetupStrategy;
if (this.chatEntitlementService.entitlement === ChatEntitlement.Pro || this.chatEntitlementService.entitlement === ChatEntitlement.Limited) {
setupStrategy = ChatSetupStrategy.DefaultSetup; // existing pro/free users setup without a dialog
@@ -307,13 +325,13 @@ class ChatSetup {
try {
switch (setupStrategy) {
case ChatSetupStrategy.SetupWithEnterpriseProvider:
success = await this.controller.value.setupWithProvider(true);
success = await this.controller.value.setupWithProvider({ ...options, useEnterpriseProvider: true });
break;
case ChatSetupStrategy.SetupWithoutEnterpriseProvider:
success = await this.controller.value.setupWithProvider(false);
success = await this.controller.value.setupWithProvider({ ...options, useEnterpriseProvider: false });
break;
case ChatSetupStrategy.DefaultSetup:
success = await this.controller.value.setup();
success = await this.controller.value.setup(options);
break;
}
} catch (error) {
@@ -454,6 +472,8 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr
if (!disabled && !registration.value) {
registration.value = combinedDisposable(
SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Panel, false, context, controller),
SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Terminal, false, context, controller),
SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Editor, false, context, controller),
SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.EditingSession, false, context, controller),
SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.EditingSession, true, context, controller)
);
@@ -748,7 +768,7 @@ class ChatSetupController extends Disposable {
this._onDidChange.fire();
}
async setup(options?: { forceSignIn?: boolean }): Promise<boolean> {
async setup(options?: { forceSignIn?: boolean; disableCopilotViewReveal?: boolean }): Promise<boolean> {
const watch = new StopWatch(false);
const title = localize('setupChatProgress', "Getting Copilot ready...");
const badge = this.activityService.showViewContainerActivity(preferCopilotEditsView(this.viewsService) ? CHAT_EDITING_SIDEBAR_PANEL_ID : CHAT_SIDEBAR_PANEL_ID, {
@@ -760,13 +780,13 @@ class ChatSetupController extends Disposable {
location: ProgressLocation.Window,
command: CHAT_OPEN_ACTION_ID,
title,
}, () => this.doSetup(options?.forceSignIn ?? false, watch));
}, () => this.doSetup(options ?? {}, watch));
} finally {
badge.dispose();
}
}
private async doSetup(forceSignIn: boolean, watch: StopWatch): Promise<boolean> {
private async doSetup(options: { forceSignIn?: boolean; disableCopilotViewReveal?: boolean }, watch: StopWatch): Promise<boolean> {
this.context.suspend(); // reduces flicker
let focusChatInput = false;
@@ -778,9 +798,9 @@ class ChatSetupController extends Disposable {
let entitlement: ChatEntitlement | undefined;
// Entitlement Unknown or `forceSignIn`: we need to sign-in user
if (this.context.state.entitlement === ChatEntitlement.Unknown || forceSignIn) {
if (this.context.state.entitlement === ChatEntitlement.Unknown || options.forceSignIn) {
this.setStep(ChatSetupStep.SigningIn);
const result = await this.signIn(providerId);
const result = await this.signIn(providerId, options);
if (!result.session) {
this.telemetryService.publicLog2<InstallChatEvent, InstallChatClassification>('commandCenter.chatInstall', { installResult: 'failedNotSignedIn', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog });
return false;
@@ -802,7 +822,7 @@ class ChatSetupController extends Disposable {
// Install
this.setStep(ChatSetupStep.Installing);
success = await this.install(session, entitlement ?? this.context.state.entitlement, providerId, watch);
success = await this.install(session, entitlement ?? this.context.state.entitlement, providerId, options, watch);
const currentActiveElement = getActiveElement();
focusChatInput = activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body;
@@ -811,18 +831,20 @@ class ChatSetupController extends Disposable {
this.context.resume();
}
if (focusChatInput) {
if (focusChatInput && !options.disableCopilotViewReveal) {
(await showCopilotView(this.viewsService, this.layoutService))?.focusInput();
}
return success;
}
private async signIn(providerId: string): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> {
private async signIn(providerId: string, options?: { disableCopilotViewReveal?: boolean }): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> {
let session: AuthenticationSession | undefined;
let entitlements;
try {
showCopilotView(this.viewsService, this.layoutService);
if (!options?.disableCopilotViewReveal) {
showCopilotView(this.viewsService, this.layoutService);
}
({ session, entitlements } = await this.requests.signIn());
} catch (e) {
@@ -838,20 +860,22 @@ class ChatSetupController extends Disposable {
});
if (confirmed) {
return this.signIn(providerId);
return this.signIn(providerId, options);
}
}
return { session, entitlement: entitlements?.entitlement };
}
private async install(session: AuthenticationSession | undefined, entitlement: ChatEntitlement, providerId: string, watch: StopWatch,): Promise<boolean> {
private async install(session: AuthenticationSession | undefined, entitlement: ChatEntitlement, providerId: string, options: { disableCopilotViewReveal?: boolean }, watch: StopWatch): Promise<boolean> {
const wasInstalled = this.context.state.installed;
let signUpResult: boolean | { errorCode: number } | undefined = undefined;
const setupFromDialog = Boolean(this.configurationService.getValue('chat.experimental.setupFromDialog'));
try {
showCopilotView(this.viewsService, this.layoutService);
if (!options?.disableCopilotViewReveal) {
showCopilotView(this.viewsService, this.layoutService);
}
if (
entitlement !== ChatEntitlement.Limited && // User is not signed up to Copilot Free
@@ -932,7 +956,7 @@ class ChatSetupController extends Disposable {
}
}
async setupWithProvider(useEnterpriseProvider: boolean): Promise<boolean> {
async setupWithProvider(options: { useEnterpriseProvider: boolean; disableCopilotViewReveal?: boolean }): Promise<boolean> {
const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
registry.registerConfiguration({
'id': 'copilot.setup',
@@ -952,7 +976,7 @@ class ChatSetupController extends Disposable {
}
});
if (useEnterpriseProvider) {
if (options.useEnterpriseProvider) {
const success = await this.handleEnterpriseInstance();
if (!success) {
return false; // not properly configured, abort
@@ -964,7 +988,7 @@ class ChatSetupController extends Disposable {
existingAdvancedSetting = {};
}
if (useEnterpriseProvider) {
if (options.useEnterpriseProvider) {
await this.configurationService.updateValue(`${defaultChat.completionsAdvancedSetting}`, {
...existingAdvancedSetting,
'authProvider': defaultChat.enterpriseProviderId
@@ -977,7 +1001,7 @@ class ChatSetupController extends Disposable {
await this.configurationService.updateValue(defaultChat.providerUriSetting, undefined, ConfigurationTarget.USER);
}
return this.setup({ forceSignIn: true });
return this.setup({ ...options, forceSignIn: true });
}
private async handleEnterpriseInstance(): Promise<boolean /* success */> {
@@ -1102,8 +1126,8 @@ class ChatSetupWelcomeContent extends Disposable {
buttonContainer.classList.add('button-container');
const button = this._register(new ButtonWithDropdown(buttonContainer, {
actions: [
toAction({ id: 'chatSetup.setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => this.controller.setupWithProvider(false) }),
toAction({ id: 'chatSetup.setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => this.controller.setupWithProvider(true) })
toAction({ id: 'chatSetup.setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => this.controller.setupWithProvider({ useEnterpriseProvider: false }) }),
toAction({ id: 'chatSetup.setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => this.controller.setupWithProvider({ useEnterpriseProvider: true }) })
],
addPrimaryActionToDropdown: false,
contextMenuProvider: this.contextMenuService,
@@ -410,7 +410,7 @@ export class InlineChatEnabler {
const updateAgent = () => {
const agent = chatAgentService.getDefaultAgent(ChatAgentLocation.Editor);
if (agent?.id === 'github.copilot.editor') {
if (agent?.id === 'github.copilot.editor' || agent?.id === 'setup.editor') {
this._ctxHasProvider.set(true);
this._ctxHasProvider2.reset();
} else if (agent?.id === 'github.copilot.editingSessionEditor') {
@@ -3,13 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from '../../../../../base/common/event.js';
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';
import { IChatAgentService } from '../../../chat/common/chatAgents.js';
import { ChatAgentLocation } from '../../../chat/common/constants.js';
import { TerminalChatContextKeys } from './terminalChat.js';
export class TerminalChatEnabler {
static Id = 'terminalChat.enabler';
@@ -23,7 +23,7 @@ export class TerminalChatEnabler {
@IContextKeyService contextKeyService: IContextKeyService,
) {
this._ctxHasProvider = TerminalChatContextKeys.hasChatAgent.bindTo(contextKeyService);
this._store.add(chatAgentService.onDidChangeAgents(() => {
this._store.add(Event.runAndSubscribe(chatAgentService.onDidChangeAgents, () => {
const hasTerminalAgent = Boolean(chatAgentService.getDefaultAgent(ChatAgentLocation.Terminal));
this._ctxHasProvider.set(hasTerminalAgent);
}));