diff --git a/extensions/copilot/chat-lib/test/getInlineCompletions.spec.ts b/extensions/copilot/chat-lib/test/getInlineCompletions.spec.ts index bcf4b51601a..05b0fc66877 100644 --- a/extensions/copilot/chat-lib/test/getInlineCompletions.spec.ts +++ b/extensions/copilot/chat-lib/test/getInlineCompletions.spec.ts @@ -119,11 +119,7 @@ class TestAuthService extends Disposable implements IAuthenticationService { private readonly _onDidAdoAuthenticationChange = this._register(new Emitter()); readonly onDidAdoAuthenticationChange = this._onDidAdoAuthenticationChange.event; - async getAnyGitHubSession(options?: AuthenticationGetSessionOptions): Promise { - return undefined; - } - - async getPermissiveGitHubSession(options: AuthenticationGetSessionOptions): Promise { + async getGitHubSession(kind: 'permissive' | 'any', options?: AuthenticationGetSessionOptions): Promise { return undefined; } diff --git a/extensions/copilot/src/extension/agents/copilotcli/node/copilotCli.ts b/extensions/copilot/src/extension/agents/copilotcli/node/copilotCli.ts index b820a199ed1..97d5b5c76db 100644 --- a/extensions/copilot/src/extension/agents/copilotcli/node/copilotCli.ts +++ b/extensions/copilot/src/extension/agents/copilotcli/node/copilotCli.ts @@ -328,7 +328,7 @@ export class CopilotCLISDK implements ICopilotCLISDK { } public async getAuthInfo(): Promise> { - const copilotToken = await this.authentService.getAnyGitHubSession(); + const copilotToken = await this.authentService.getGitHubSession('any', { silent: true }); return { type: 'token', token: copilotToken?.accessToken ?? '', diff --git a/extensions/copilot/src/extension/chatSessions/vscode-node/copilotCLITerminalIntegration.ts b/extensions/copilot/src/extension/chatSessions/vscode-node/copilotCLITerminalIntegration.ts index fa4033d67c9..71f6b61470d 100644 --- a/extensions/copilot/src/extension/chatSessions/vscode-node/copilotCLITerminalIntegration.ts +++ b/extensions/copilot/src/extension/chatSessions/vscode-node/copilotCLITerminalIntegration.ts @@ -299,7 +299,7 @@ async function getCommonTerminalOptions(name: string, authenticationService: IAu location: { viewColumn: ViewColumn.Active }, hideFromUser: false }; - const session = await authenticationService.getAnyGitHubSession(); + const session = await authenticationService.getGitHubSession('any', { silent: true }); if (session) { options.env = { // Old Token name for GitHub integrations (deprecate once the new variable has been adopted widely) diff --git a/extensions/copilot/src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts b/extensions/copilot/src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts index 97bed33dcae..d0211e9ca92 100644 --- a/extensions/copilot/src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts +++ b/extensions/copilot/src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts @@ -875,7 +875,7 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C if (selection.includes(this.AUTHORIZE.toUpperCase())) { stream.progress(vscode.l10n.t('Authorizing')); try { - await this._authenticationService.getPermissiveGitHubSession({ createIfNone: true, silent: false }); + await this._authenticationService.getGitHubSession('permissive', { createIfNone: true }); if (!this._authenticationService.permissiveGitHubSession) { throw new Error('Failed to obtain permissive GitHub session'); } diff --git a/extensions/copilot/src/extension/contextKeys/vscode-node/contextKeys.contribution.ts b/extensions/copilot/src/extension/contextKeys/vscode-node/contextKeys.contribution.ts index da9bbeaec78..5caf08777e3 100644 --- a/extensions/copilot/src/extension/contextKeys/vscode-node/contextKeys.contribution.ts +++ b/extensions/copilot/src/extension/contextKeys/vscode-node/contextKeys.contribution.ts @@ -216,7 +216,7 @@ export class ContextKeysContribution extends Disposable { let missingPermissiveSession = false; if (!this._authenticationService.isMinimalMode) { try { - hasPermissiveSession = !!(await this._authenticationService.getPermissiveGitHubSession({ silent: true })); + hasPermissiveSession = !!(await this._authenticationService.getGitHubSession('permissive', { silent: true })); } catch (error) { if (!(error instanceof MinimalModeError)) { this._logService.trace(`[context keys] Failed to resolve permissive session: ${error instanceof Error ? error.message : String(error)}`); diff --git a/extensions/copilot/src/extension/conversation/vscode-node/remoteAgents.ts b/extensions/copilot/src/extension/conversation/vscode-node/remoteAgents.ts index 38e1f3f0a5b..33c66c8b71d 100644 --- a/extensions/copilot/src/extension/conversation/vscode-node/remoteAgents.ts +++ b/extensions/copilot/src/extension/conversation/vscode-node/remoteAgents.ts @@ -240,7 +240,7 @@ export class RemoteAgentContribution implements IDisposable { } else if (data?.authPermissionPrompted) { request = await this.authenticationChatUpgradeService.handleConfirmationRequest(responseStream, request, context.history); metadata.command = request.command; - accessToken = (await this.authenticationService.getPermissiveGitHubSession({ silent: true }))?.accessToken; + accessToken = (await this.authenticationService.getGitHubSession('permissive', { silent: true }))?.accessToken; if (!accessToken) { responseStream.markdown(l10n.t('The additional permissions are required for this feature.')); return { metadata } satisfies ICopilotChatResult; @@ -679,7 +679,7 @@ export class RemoteAgentContribution implements IDisposable { if (ex instanceof Error && ex.message.includes('Failed to fetch repository info')) { // TODO display a merged confirmation to reauthorize with the repo scope // For now, raise a reauth badge so the user has a way out of this state - void this.authenticationService.getPermissiveGitHubSession({ silent: true }); + void this.authenticationService.getGitHubSession('permissive', { silent: true }); } this.logService.error(ex, 'Failed to fetch info about current GitHub repository'); } diff --git a/extensions/copilot/src/extension/githubMcp/common/githubMcpDefinitionProvider.ts b/extensions/copilot/src/extension/githubMcp/common/githubMcpDefinitionProvider.ts index 92e94d31b62..8d82cf02e93 100644 --- a/extensions/copilot/src/extension/githubMcp/common/githubMcpDefinitionProvider.ts +++ b/extensions/copilot/src/extension/githubMcp/common/githubMcpDefinitionProvider.ts @@ -135,7 +135,7 @@ export class GitHubMcpDefinitionProvider implements McpServerDefinitionProvider< } async resolveMcpServerDefinition(server: McpHttpServerDefinition, token: CancellationToken): Promise { - const session = await this.authenticationService.getPermissiveGitHubSession({ + const session = await this.authenticationService.getGitHubSession('permissive', { createIfNone: { detail: l10n.t('Additional permissions are required to use GitHub MCP Server'), }, diff --git a/extensions/copilot/src/extension/githubMcp/test/node/githubMcpDefinitionProvider.spec.ts b/extensions/copilot/src/extension/githubMcp/test/node/githubMcpDefinitionProvider.spec.ts index bfb4dcb83bf..09e9c855b7f 100644 --- a/extensions/copilot/src/extension/githubMcp/test/node/githubMcpDefinitionProvider.spec.ts +++ b/extensions/copilot/src/extension/githubMcp/test/node/githubMcpDefinitionProvider.spec.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { beforeEach, describe, expect, test } from 'vitest'; -import type { AuthenticationGetSessionOptions, AuthenticationSession } from 'vscode'; +import type { AuthenticationGetSessionOptions, AuthenticationGetSessionPresentationOptions, AuthenticationSession } from 'vscode'; import { BaseAuthenticationService, IAuthenticationService } from '../../../../platform/authentication/common/authentication'; import { CopilotToken } from '../../../../platform/authentication/common/copilotToken'; import { ICopilotTokenManager } from '../../../../platform/authentication/common/copilotTokenManager'; @@ -42,15 +42,17 @@ class TestAuthenticationService extends BaseAuthenticationService { this._onDidAuthenticationChange.fire(); } - getAnyGitHubSession(_options?: AuthenticationGetSessionOptions): Promise { - return Promise.resolve(this._anyGitHubSession); - } - - getPermissiveGitHubSession(options?: AuthenticationGetSessionOptions): Promise { - if (options?.createIfNone && !this._permissiveGitHubSession) { - throw new Error('No permissive GitHub session available'); + override getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions & { createIfNone: boolean | AuthenticationGetSessionPresentationOptions }): Promise; + override getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions & { forceNewSession: boolean | AuthenticationGetSessionPresentationOptions }): Promise; + override getGitHubSession(kind: 'permissive' | 'any', options?: AuthenticationGetSessionOptions): Promise { + if (kind === 'permissive') { + if (options?.createIfNone && !this._permissiveGitHubSession) { + throw new Error('No permissive GitHub session available'); + } + return Promise.resolve(this._permissiveGitHubSession); + } else { + return Promise.resolve(this._anyGitHubSession); } - return Promise.resolve(this._permissiveGitHubSession); } override getAnyAdoSession(_options?: AuthenticationGetSessionOptions): Promise { diff --git a/extensions/copilot/src/extension/onboardDebug/vscode-node/copilotDebugCommandContribution.ts b/extensions/copilot/src/extension/onboardDebug/vscode-node/copilotDebugCommandContribution.ts index f333216054d..a95ac3cb8b3 100644 --- a/extensions/copilot/src/extension/onboardDebug/vscode-node/copilotDebugCommandContribution.ts +++ b/extensions/copilot/src/extension/onboardDebug/vscode-node/copilotDebugCommandContribution.ts @@ -160,7 +160,7 @@ export class CopilotDebugCommandContribution extends Disposable implements vscod rpc.registerMethod('start', async function start(opts: IStartOptions): Promise { if (!authService.copilotToken) { - await authService.getAnyGitHubSession({ createIfNone: true }); + await authService.getGitHubSession('any', { createIfNone: true }); } const result = await factory.start(opts, cts.token); diff --git a/extensions/copilot/src/extension/prompts/node/panel/image.tsx b/extensions/copilot/src/extension/prompts/node/panel/image.tsx index c1c6382e223..6e261335ecc 100644 --- a/extensions/copilot/src/extension/prompts/node/panel/image.tsx +++ b/extensions/copilot/src/extension/prompts/node/panel/image.tsx @@ -59,7 +59,7 @@ export class Image extends PromptElement { const enabled = this.configurationService.getExperimentBasedConfig(ConfigKey.EnableChatImageUpload, this.experimentationService); if (isChatCompletions && enabled && modelCanUseImageURL(this.promptEndpoint)) { try { - const githubToken = (await this.authService.getAnyGitHubSession())?.accessToken; + const githubToken = (await this.authService.getGitHubSession('any', { silent: true }))?.accessToken; const uri = await this.imageService.uploadChatImageAttachment(variable, this.props.variableName, getMimeType(imageSource) ?? 'image/png', githubToken); if (uri) { imageSource = uri.toString(); diff --git a/extensions/copilot/src/extension/prompts/node/panel/toolCalling.tsx b/extensions/copilot/src/extension/prompts/node/panel/toolCalling.tsx index f29806f12e1..fcb4038f38d 100644 --- a/extensions/copilot/src/extension/prompts/node/panel/toolCalling.tsx +++ b/extensions/copilot/src/extension/prompts/node/panel/toolCalling.tsx @@ -523,7 +523,7 @@ class PrimitiveToolResult extends PromptEle } protected async onImage(part: LanguageModelDataPart) { - const githubToken = (await this.authService.getAnyGitHubSession())?.accessToken; + const githubToken = (await this.authService.getGitHubSession('any', { silent: true }))?.accessToken; const uploadsEnabled = this.configurationService && this.experimentationService ? this.configurationService.getExperimentBasedConfig(ConfigKey.EnableChatImageUpload, this.experimentationService) : false; diff --git a/extensions/copilot/src/platform/authentication/common/authentication.ts b/extensions/copilot/src/platform/authentication/common/authentication.ts index 03467b7375a..3b6321734df 100644 --- a/extensions/copilot/src/platform/authentication/common/authentication.ts +++ b/extensions/copilot/src/platform/authentication/common/authentication.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { AuthenticationGetSessionOptions, AuthenticationSession } from 'vscode'; +import type { AuthenticationGetSessionOptions, AuthenticationGetSessionPresentationOptions, AuthenticationSession } from 'vscode'; import { createServiceIdentifier } from '../../../util/common/services'; import { Emitter, Event } from '../../../util/vs/base/common/event'; import { Disposable } from '../../../util/vs/base/common/lifecycle'; @@ -37,8 +37,8 @@ export interface IAuthenticationService { /** * Whether the authentication service is in minimal mode. If true, the authentication service will not attempt to * fetch the permissive token. This means that: - * * {@link getPermissiveGitHubSession} interactive flows will always throw an error - * * {@link getPermissiveGitHubSession} silent flows and {@link permissiveGitHubSession} will always return undefined + * * {@link getGitHubSession} interactive flows with 'permissive' kind will always throw an error + * * {@link getGitHubSession} silent flows with 'permissive' kind and {@link permissiveGitHubSession} will always return undefined */ readonly isMinimalMode: boolean; @@ -59,44 +59,28 @@ export interface IAuthenticationService { * Checks if there is currently any session available in the cache. Does not make any network requests and does not * call out to the underlying authentication provider. * - * @note See {@link getAnyGitHubToken} for more information and for an async version by calling {@link getAnyGitHubSession} with `{ silent: true }`. + * @note See {@link getAnyGitHubToken} for more information and for an async version by calling {@link getGitHubSession} with kind 'any' and `{ silent: true }`. * @note For best practice of handling of the user's authentication state, you should react to {@link onDidAuthenticationChange}. * @note This token will have at least the `user:email` scope to be able to access the minimum Copilot API. */ readonly anyGitHubSession: AuthenticationSession | undefined; - /** - * Returns a currently valid GitHub session, also known as session or auth session. Skips the cache and calls - * the underlying authentication provider using the options passed in. - * - * @note You should typically use the synchronous version {@link anyGitHubToken} if you are fetching a session silently. - * @note For best practice of handling of the user's authentication state, you should react to {@link onDidAuthenticationChange}. - * @note This token will have at least the `user:email` scope to be able to access the minimum Copilot API. - * @returns an auth session or undefined if none is found. - */ - getAnyGitHubSession(options?: AuthenticationGetSessionOptions): Promise; - /** * Checks if there is currently a permissive session available in the cache. Does not make any network requests and does not * call out to the underlying authentication provider. * - * @note See {@link getPermissiveGitHubToken} for more information and for an async version by calling {@link getPermissiveGitHubSession} with `{ silent: true }`. + * @note See {@link getPermissiveGitHubToken} for more information and for an async version by calling {@link getGitHubSession} with kind 'permissive' and `{ silent: true }`. * @note For best practice of handling of the user's authentication state, you should react to {@link onDidAuthenticationChange}. * @returns undefined if no auth session is available or Minimal Mode is enabled. Otherwise, returns an auth session with the `repo` scope. */ readonly permissiveGitHubSession: AuthenticationSession | undefined; + /** - * Returns a currently valid permissive GitHub session, also known as session or auth session. Skips the cache and calls - * the underlying authentication provider using the options passed in. * - * @note We have the {@link IAuthenticationChatUpgradeService} to upgrade the session to a permissive one. Use this for confirmation in Chat/Edits instead of showing the modal. - * @note You should typically use the synchronous version {@link getPermissiveGitHubToken} if you are fetching a session silently. - * @note For best practice of handling of the user's authentication state, you should react to {@link onDidAuthenticationChange}. - * @note This token will have at least the `repo` scope to be able to access the extended features of the Copilot API. - * @returns an auth session or undefined if none is found. - * @throws MinimalModeError {@link MinimalModeError} if the authentication service is in minimal mode. + * @param kind + * @param options */ - getPermissiveGitHubSession(options: AuthenticationGetSessionOptions): Promise; + getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions): Promise; /** * Checks if there is currently a Copilot token available in the cache. Does not make any network requests. @@ -182,7 +166,6 @@ export abstract class BaseAuthenticationService extends Disposable implements IA get anyGitHubSession(): AuthenticationSession | undefined { return this._anyGitHubSession; } - abstract getAnyGitHubSession(options?: AuthenticationGetSessionOptions): Promise; //#endregion @@ -192,7 +175,14 @@ export abstract class BaseAuthenticationService extends Disposable implements IA get permissiveGitHubSession(): AuthenticationSession | undefined { return this._permissiveGitHubSession; } - abstract getPermissiveGitHubSession(options: AuthenticationGetSessionOptions): Promise; + + //#endregion + + //#region GitHub Session + + abstract getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions & { createIfNone: boolean | AuthenticationGetSessionPresentationOptions }): Promise; + abstract getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions & { forceNewSession: boolean | AuthenticationGetSessionPresentationOptions }): Promise; + abstract getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions): Promise; //#endregion @@ -257,8 +247,8 @@ export abstract class BaseAuthenticationService extends Disposable implements IA // Update caches const resolved = await Promise.allSettled([ - this.getAnyGitHubSession({ silent: true }), - this.getPermissiveGitHubSession({ silent: true }), + this.getGitHubSession('any', { silent: true }), + this.getGitHubSession('permissive', { silent: true }), this.getAnyAdoSession({ silent: true }), ]); for (const res of resolved) { diff --git a/extensions/copilot/src/platform/authentication/common/authenticationUpgradeService.ts b/extensions/copilot/src/platform/authentication/common/authenticationUpgradeService.ts index 6a2d780d019..55957c28606 100644 --- a/extensions/copilot/src/platform/authentication/common/authenticationUpgradeService.ts +++ b/extensions/copilot/src/platform/authentication/common/authenticationUpgradeService.ts @@ -63,12 +63,12 @@ export class AuthenticationChatUpgradeService extends Disposable implements IAut return false; } // We already have a permissive session - if (await this._authenticationService.getPermissiveGitHubSession({ silent: true })) { + if (await this._authenticationService.getGitHubSession('permissive', { silent: true })) { reason = 'false - already have permissive session'; return false; } // The user is not signed in at all - if (!(await this._authenticationService.getAnyGitHubSession({ silent: true }))) { + if (!(await this._authenticationService.getGitHubSession('any', { silent: true }))) { reason = 'false - not signed in'; return false; } @@ -91,7 +91,7 @@ export class AuthenticationChatUpgradeService extends Disposable implements IAut this.logService.trace('Requesting permissive session upgrade'); this.hasRequestedPermissiveSessionUpgrade = true; try { - await this._authenticationService.getPermissiveGitHubSession({ + await this._authenticationService.getGitHubSession('permissive', { forceNewSession: { detail: l10n.t('To get more relevant Chat results, we need permission to read the contents of your repository on GitHub.'), learnMore: URI.parse('https://aka.ms/copilotRepoScope'), @@ -101,7 +101,7 @@ export class AuthenticationChatUpgradeService extends Disposable implements IAut return true; } catch (e) { // User cancelled so show the badge - await this._authenticationService.getPermissiveGitHubSession({}); + await this._authenticationService.getGitHubSession('permissive', {}); return false; } } @@ -148,17 +148,17 @@ export class AuthenticationChatUpgradeService extends Disposable implements IAut case `${this._permissionRequestGrant}: "${this._permissionRequest}"`: this.logService.trace('User granted permission'); try { - await this._authenticationService.getPermissiveGitHubSession({ createIfNone: true }); + await this._authenticationService.getGitHubSession('permissive', { createIfNone: true }); this._onDidGrantAuthUpgrade.fire(); } catch (e) { // User cancelled so show the badge - await this._authenticationService.getPermissiveGitHubSession({}); + await this._authenticationService.getGitHubSession('permissive', {}); } break; case `${this._permissionRequestNotNow}: "${this._permissionRequest}"`: this.logService.trace('User declined permission'); stream.markdown(l10n.t("Ok. I won't bother you again for now. If you change your mind, you can react to the authentication request in the Account menu.") + '\n\n'); - await this._authenticationService.getPermissiveGitHubSession({}); + await this._authenticationService.getGitHubSession('permissive', {}); break; case `${this._permissionRequestNeverAskAgain}: "${this._permissionRequest}"`: this.logService.trace('User chose never ask again for permission'); diff --git a/extensions/copilot/src/platform/authentication/common/staticGitHubAuthenticationService.ts b/extensions/copilot/src/platform/authentication/common/staticGitHubAuthenticationService.ts index b23c23e1ed3..7fe65566a98 100644 --- a/extensions/copilot/src/platform/authentication/common/staticGitHubAuthenticationService.ts +++ b/extensions/copilot/src/platform/authentication/common/staticGitHubAuthenticationService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { AuthenticationGetSessionOptions, AuthenticationSession } from 'vscode'; +import type { AuthenticationGetSessionOptions, AuthenticationGetSessionPresentationOptions, AuthenticationSession } from 'vscode'; import { IConfigurationService } from '../../configuration/common/configurationService'; import { ILogService } from '../../log/common/logService'; import { BaseAuthenticationService, GITHUB_SCOPE_ALIGNED, GITHUB_SCOPE_USER_EMAIL, IAuthenticationService, MinimalModeError } from './authentication'; @@ -43,18 +43,21 @@ export class StaticGitHubAuthenticationService extends BaseAuthenticationService } : undefined; } - getAnyGitHubSession(_options?: AuthenticationGetSessionOptions): Promise { - return Promise.resolve(this._anyGitHubSession); - } - - getPermissiveGitHubSession(options: AuthenticationGetSessionOptions): Promise { - if (this.isMinimalMode) { - if (options.createIfNone || options.forceNewSession) { - throw new MinimalModeError(); + override async getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions & { createIfNone: boolean | AuthenticationGetSessionPresentationOptions }): Promise; + override async getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions & { forceNewSession: boolean | AuthenticationGetSessionPresentationOptions }): Promise; + override async getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions): Promise; + override async getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions): Promise { + if (kind === 'permissive') { + if (this.isMinimalMode) { + if (options.createIfNone || options.forceNewSession) { + throw new MinimalModeError(); + } + return undefined; } - return Promise.resolve(undefined); + return this._permissiveGitHubSession; + } else { + return this._anyGitHubSession; } - return Promise.resolve(this._permissiveGitHubSession); } override async getCopilotToken(force?: boolean): Promise { diff --git a/extensions/copilot/src/platform/authentication/test/node/authentication.spec.ts b/extensions/copilot/src/platform/authentication/test/node/authentication.spec.ts index ea7424352bd..a0b0cff1297 100644 --- a/extensions/copilot/src/platform/authentication/test/node/authentication.spec.ts +++ b/extensions/copilot/src/platform/authentication/test/node/authentication.spec.ts @@ -55,13 +55,13 @@ suite('AuthenticationService', function () { }); test('Can get anyGitHubToken', async () => { - const token = await authenticationService.getAnyGitHubSession({ silent: true }); + const token = await authenticationService.getGitHubSession('any', { silent: true }); expect(token?.accessToken).toBe(testToken); expect(authenticationService.anyGitHubSession?.accessToken).toBe(testToken); }); test('Can get permissiveGitHubToken', async () => { - const token = await authenticationService.getPermissiveGitHubSession({ silent: true }); + const token = await authenticationService.getGitHubSession('permissive', { silent: true }); expect(token?.accessToken).toBe(testToken); expect(authenticationService.permissiveGitHubSession?.accessToken).toBe(testToken); }); diff --git a/extensions/copilot/src/platform/authentication/vscode-node/authenticationService.ts b/extensions/copilot/src/platform/authentication/vscode-node/authenticationService.ts index b3b0661aae8..c431ecd17e2 100644 --- a/extensions/copilot/src/platform/authentication/vscode-node/authenticationService.ts +++ b/extensions/copilot/src/platform/authentication/vscode-node/authenticationService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { authentication, AuthenticationGetSessionOptions, AuthenticationSession } from 'vscode'; +import { authentication, AuthenticationGetSessionOptions, AuthenticationGetSessionPresentationOptions, AuthenticationSession } from 'vscode'; import { TaskSingler } from '../../../util/common/taskSingler'; import { AuthProviderId, IConfigurationService } from '../../configuration/common/configurationService'; import { IDomainService } from '../../endpoint/common/domainService'; @@ -40,20 +40,22 @@ export class AuthenticationService extends BaseAuthenticationService { void this._handleAuthChangeEvent(); } - async getAnyGitHubSession(options?: AuthenticationGetSessionOptions): Promise { - const func = () => getAnyAuthSession(this._configurationService, options); - // If we are doing an interactive flow, don't use the singler so that we don't get hung up on the user's choice - const session = options?.createIfNone || options?.forceNewSession ? await func() : await this._taskSingler.getOrCreate('any', func); - this._anyGitHubSession = session; - return session; - } - - async getPermissiveGitHubSession(options: AuthenticationGetSessionOptions): Promise { - const func = () => getAlignedSession(this._configurationService, options); - // If we are doing an interactive flow, don't use the singler so that we don't get hung up on the user's choice - const session = options?.createIfNone || options?.forceNewSession ? await func() : await this._taskSingler.getOrCreate('permissive', func); - this._permissiveGitHubSession = session; - return session; + override async getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions & { createIfNone: boolean | AuthenticationGetSessionPresentationOptions }): Promise; + override async getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions & { forceNewSession: boolean | AuthenticationGetSessionPresentationOptions }): Promise; + override async getGitHubSession(kind: 'permissive' | 'any', options: AuthenticationGetSessionOptions): Promise { + if (kind === 'permissive') { + const func = () => getAlignedSession(this._configurationService, options); + // If we are doing an interactive flow, don't use the singler so that we don't get hung up on the user's choice + const session = options?.createIfNone || options?.forceNewSession ? await func() : await this._taskSingler.getOrCreate('permissive', func); + this._permissiveGitHubSession = session; + return session; + } else { + const func = () => getAnyAuthSession(this._configurationService, options); + // If we are doing an interactive flow, don't use the singler so that we don't get hung up on the user's choice + const session = options?.createIfNone || options?.forceNewSession ? await func() : await this._taskSingler.getOrCreate('any', func); + this._anyGitHubSession = session; + return session; + } } protected async getAnyAdoSession(options?: AuthenticationGetSessionOptions): Promise { diff --git a/extensions/copilot/src/platform/embeddings/common/remoteEmbeddingsComputer.ts b/extensions/copilot/src/platform/embeddings/common/remoteEmbeddingsComputer.ts index bc3d4f3f94b..65306d13f6d 100644 --- a/extensions/copilot/src/platform/embeddings/common/remoteEmbeddingsComputer.ts +++ b/extensions/copilot/src/platform/embeddings/common/remoteEmbeddingsComputer.ts @@ -63,7 +63,7 @@ export class RemoteEmbeddingsComputer implements IEmbeddingsComputer { return embeddings ?? { type: embeddingType, values: [] }; } - const token = (await this._authService.getAnyGitHubSession({ silent: true }))?.accessToken; + const token = (await this._authService.getGitHubSession('any', { silent: true }))?.accessToken; if (!token) { throw new Error('No authentication token available'); } diff --git a/extensions/copilot/src/platform/github/common/octoKitServiceImpl.ts b/extensions/copilot/src/platform/github/common/octoKitServiceImpl.ts index 7bf4989b5b7..f352630a59e 100644 --- a/extensions/copilot/src/platform/github/common/octoKitServiceImpl.ts +++ b/extensions/copilot/src/platform/github/common/octoKitServiceImpl.ts @@ -25,7 +25,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic } async getCurrentAuthedUser(): Promise { - const authToken = (await this._authService.getAnyGitHubSession())?.accessToken; + const authToken = (await this._authService.getGitHubSession('any', { silent: true }))?.accessToken; if (!authToken) { return undefined; } @@ -33,7 +33,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic } async getCopilotPullRequestsForUser(owner: string, repo: string): Promise { - const auth = (await this._authService.getPermissiveGitHubSession({ createIfNone: true })); + const auth = (await this._authService.getGitHubSession('permissive', { createIfNone: true })); if (!auth?.accessToken) { return []; } @@ -48,7 +48,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic async getCopilotSessionsForPR(prId: string): Promise { try { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -74,7 +74,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic async getSessionLogs(sessionId: string): Promise { try { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -96,7 +96,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic async getSessionInfo(sessionId: string): Promise { try { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -122,7 +122,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic async postCopilotAgentJob(owner: string, name: string, apiVersion: string, payload: RemoteAgentJobPayload): Promise { try { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -147,7 +147,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic async getJobByJobId(owner: string, repo: string, jobId: string, userAgent: string): Promise { try { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -169,7 +169,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic async getJobBySessionId(owner: string, repo: string, sessionId: string, userAgent: string): Promise { try { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -190,7 +190,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic } async addPullRequestComment(pullRequestId: string, commentBody: string): Promise { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -199,7 +199,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic async getAllOpenSessions(nwo?: string): Promise { try { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -216,7 +216,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic } async getPullRequestFromGlobalId(globalId: string): Promise { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -225,7 +225,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic async getCustomAgents(owner: string, repo: string, options?: CustomAgentListOptions): Promise { try { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -261,7 +261,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic async getCustomAgentDetails(owner: string, repo: string, agentName: string, version?: string): Promise { try { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No authentication token available'); } @@ -290,7 +290,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic } async getPullRequestFiles(owner: string, repo: string, pullNumber: number): Promise { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { return []; } @@ -298,7 +298,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic } async closePullRequest(owner: string, repo: string, pullNumber: number): Promise { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { return false; } @@ -306,7 +306,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic } async getFileContent(owner: string, repo: string, ref: string, path: string): Promise { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { throw new Error('No GitHub authentication available'); } @@ -314,7 +314,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic } async getUserOrganizations(): Promise { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { return []; } @@ -322,7 +322,7 @@ export class OctoKitService extends BaseOctoKitService implements IOctoKitServic } async getOrganizationRepositories(org: string): Promise { - const authToken = (await this._authService.getPermissiveGitHubSession({ createIfNone: true }))?.accessToken; + const authToken = (await this._authService.getGitHubSession('permissive', { createIfNone: true }))?.accessToken; if (!authToken) { return []; } diff --git a/extensions/copilot/src/platform/ignore/node/remoteContentExclusion.ts b/extensions/copilot/src/platform/ignore/node/remoteContentExclusion.ts index a81d77c66e2..428b994df5f 100644 --- a/extensions/copilot/src/platform/ignore/node/remoteContentExclusion.ts +++ b/extensions/copilot/src/platform/ignore/node/remoteContentExclusion.ts @@ -239,7 +239,7 @@ export class RemoteContentExclusion implements IDisposable { this._ignoreGlobResultCache.clear(); const startTime = Date.now(); const capiClientService = this._capiClientService; - const ghToken = (await this._authService.getAnyGitHubSession({ silent: true }))?.accessToken; + const ghToken = (await this._authService.getGitHubSession('any', { silent: true }))?.accessToken; const remoteFetchUrls = Array.from(this._contentExclusionCache.keys()); const updateRulesForRepos = async (reposToFetch: string[]) => { diff --git a/extensions/copilot/src/platform/remoteCodeSearch/common/githubCodeSearchService.ts b/extensions/copilot/src/platform/remoteCodeSearch/common/githubCodeSearchService.ts index 14ec42ec317..dded6367deb 100644 --- a/extensions/copilot/src/platform/remoteCodeSearch/common/githubCodeSearchService.ts +++ b/extensions/copilot/src/platform/remoteCodeSearch/common/githubCodeSearchService.ts @@ -345,8 +345,8 @@ export class GithubCodeSearchService implements IGithubCodeSearchService { } private async getGithubAccessToken(silent: boolean) { - return (await this._authenticationService.getPermissiveGitHubSession({ silent }))?.accessToken - ?? (await this._authenticationService.getAnyGitHubSession({ silent }))?.accessToken; + return (await this._authenticationService.getGitHubSession('permissive', { silent }))?.accessToken + ?? (await this._authenticationService.getGitHubSession('any', { silent }))?.accessToken; } diff --git a/extensions/copilot/src/platform/remoteCodeSearch/node/codeSearchRepoAuth.ts b/extensions/copilot/src/platform/remoteCodeSearch/node/codeSearchRepoAuth.ts index 6ec4b94d082..f70c28918a0 100644 --- a/extensions/copilot/src/platform/remoteCodeSearch/node/codeSearchRepoAuth.ts +++ b/extensions/copilot/src/platform/remoteCodeSearch/node/codeSearchRepoAuth.ts @@ -32,7 +32,7 @@ export class BasicCodeSearchAuthenticationService implements ICodeSearchAuthenti return; } - await this._authenticationService.getAnyGitHubSession({ createIfNone: true }); + await this._authenticationService.getGitHubSession('any', { createIfNone: true }); } async tryReauthenticating(remoteInfo: ResolvedRepoRemoteInfo | undefined): Promise { @@ -41,7 +41,7 @@ export class BasicCodeSearchAuthenticationService implements ICodeSearchAuthenti return; } - await this._authenticationService.getPermissiveGitHubSession({ createIfNone: true }); + await this._authenticationService.getGitHubSession('permissive', { createIfNone: true }); } async promptForExpandedLocalIndexing(fileCount: number): Promise { diff --git a/extensions/copilot/src/platform/remoteCodeSearch/vscode-node/codeSearchRepoAuth.ts b/extensions/copilot/src/platform/remoteCodeSearch/vscode-node/codeSearchRepoAuth.ts index 24986dd5f91..261db31c8e1 100644 --- a/extensions/copilot/src/platform/remoteCodeSearch/vscode-node/codeSearchRepoAuth.ts +++ b/extensions/copilot/src/platform/remoteCodeSearch/vscode-node/codeSearchRepoAuth.ts @@ -52,7 +52,7 @@ export class VsCodeCodeSearchAuthenticationService implements ICodeSearchAuthent }, signInButton, cancelButton); if (result === signInButton) { - await this._authService.getAnyGitHubSession({ createIfNone: true }); + await this._authService.getGitHubSession('any', { createIfNone: true }); return; } } diff --git a/extensions/copilot/src/platform/remoteSearch/node/codeOrDocsSearchClientImpl.ts b/extensions/copilot/src/platform/remoteSearch/node/codeOrDocsSearchClientImpl.ts index 30e8d07349a..d923900f1b3 100644 --- a/extensions/copilot/src/platform/remoteSearch/node/codeOrDocsSearchClientImpl.ts +++ b/extensions/copilot/src/platform/remoteSearch/node/codeOrDocsSearchClientImpl.ts @@ -112,7 +112,7 @@ export class DocsSearchClient implements IDocsSearchClient { options: ICodeOrDocsSearchOptions, token: CancellationToken ): Promise { - const authToken = (await this._authenticationService.getPermissiveGitHubSession({ silent: true }))?.accessToken ?? (await this._authenticationService.getAnyGitHubSession({ silent: true }))?.accessToken; + const authToken = (await this._authenticationService.getGitHubSession('permissive', { silent: true }))?.accessToken ?? (await this._authenticationService.getGitHubSession('any', { silent: true }))?.accessToken; if (token.isCancellationRequested) { throw new CancellationError(); } diff --git a/extensions/copilot/src/platform/urlChunkSearch/node/urlChunkEmbeddingsIndex.ts b/extensions/copilot/src/platform/urlChunkSearch/node/urlChunkEmbeddingsIndex.ts index ecaef5cde2e..ddf94234aee 100644 --- a/extensions/copilot/src/platform/urlChunkSearch/node/urlChunkEmbeddingsIndex.ts +++ b/extensions/copilot/src/platform/urlChunkSearch/node/urlChunkEmbeddingsIndex.ts @@ -129,7 +129,7 @@ export class UrlChunkEmbeddingsIndex extends Disposable { } private async tryGetAuthToken(createIfNone = true): Promise { - return (await this._authService.getAnyGitHubSession({ createIfNone }))?.accessToken; + return (await this._authService.getGitHubSession('any', { createIfNone }))?.accessToken; } } diff --git a/extensions/copilot/src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes.ts b/extensions/copilot/src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes.ts index 49afc7e5ada..ba91af1a4e1 100644 --- a/extensions/copilot/src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes.ts +++ b/extensions/copilot/src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes.ts @@ -65,7 +65,7 @@ export class GithubAvailableEmbeddingTypesService implements IGithubAvailableEmb @IConfigurationService private readonly _configurationService: IConfigurationService, @IExperimentationService private readonly _experimentationService: IExperimentationService, ) { - this._cached = this._authService.getAnyGitHubSession({ silent: true }).then(session => { + this._cached = this._authService.getGitHubSession('any', { silent: true }).then(session => { if (!session) { return Result.error({ type: 'noSession' }); } @@ -92,7 +92,7 @@ export class GithubAvailableEmbeddingTypesService implements IGithubAvailableEmb } this._cached ??= (async () => { - const anySession = await this._authService.getAnyGitHubSession({ silent }); + const anySession = await this._authService.getGitHubSession('any', { silent }); if (!anySession) { return Result.error({ type: 'noSession' }); } @@ -102,7 +102,7 @@ export class GithubAvailableEmbeddingTypesService implements IGithubAvailableEmb return initialResult; } - const permissiveSession = await this._authService.getPermissiveGitHubSession({ silent, createIfNone: !silent ? true : undefined }); + const permissiveSession = await this._authService.getGitHubSession('permissive', { silent, createIfNone: !silent ? true : undefined }); if (!permissiveSession) { return initialResult; } diff --git a/extensions/copilot/src/platform/workspaceChunkSearch/node/codeSearch/codeSearchChunkSearch.ts b/extensions/copilot/src/platform/workspaceChunkSearch/node/codeSearch/codeSearchChunkSearch.ts index c8ef9a15c58..bd469d6adbc 100644 --- a/extensions/copilot/src/platform/workspaceChunkSearch/node/codeSearch/codeSearchChunkSearch.ts +++ b/extensions/copilot/src/platform/workspaceChunkSearch/node/codeSearch/codeSearchChunkSearch.ts @@ -821,8 +821,8 @@ export class CodeSearchChunkSearch extends Disposable implements IWorkspaceChunk } private async getGithubAuthToken() { - return (await this._authenticationService.getPermissiveGitHubSession({ silent: true }))?.accessToken - ?? (await this._authenticationService.getAnyGitHubSession({ silent: true }))?.accessToken; + return (await this._authenticationService.getGitHubSession('permissive', { silent: true }))?.accessToken + ?? (await this._authenticationService.getGitHubSession('any', { silent: true }))?.accessToken; } private async tryAuthIfNeeded(_telemetryInfo: TelemetryCorrelationId, token: CancellationToken): Promise | undefined> { diff --git a/extensions/copilot/src/platform/workspaceChunkSearch/node/workspaceChunkEmbeddingsIndex.ts b/extensions/copilot/src/platform/workspaceChunkSearch/node/workspaceChunkEmbeddingsIndex.ts index aab16fe5d18..31016c6fe6f 100644 --- a/extensions/copilot/src/platform/workspaceChunkSearch/node/workspaceChunkEmbeddingsIndex.ts +++ b/extensions/copilot/src/platform/workspaceChunkSearch/node/workspaceChunkEmbeddingsIndex.ts @@ -450,6 +450,6 @@ export class WorkspaceChunkEmbeddingsIndex extends Disposable { } private async tryGetAuthToken(options: AuthenticationGetSessionOptions = { createIfNone: true }): Promise { - return (await this._authService.getAnyGitHubSession(options))?.accessToken; + return (await this._authService.getGitHubSession('any', options))?.accessToken; } }