Skip to content

Commit 278c00d

Browse files
authored
Guide users to sign in from more extension entry points (#7615)
* Guide users to sign in from more extension entry points Part of #7614 * Fix yarn.lock * Fix tests
1 parent 2f2d47c commit 278c00d

12 files changed

Lines changed: 99 additions & 43 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4089,7 +4089,7 @@
40894089
"@types/react-dom": "^16.8.2",
40904090
"@types/sinon": "7.0.11",
40914091
"@types/temp": "0.8.34",
4092-
"@types/vscode": "1.89.0",
4092+
"@types/vscode": "1.103.0",
40934093
"@types/webpack-env": "^1.16.0",
40944094
"@typescript-eslint/eslint-plugin": "6.10.0",
40954095
"@typescript-eslint/parser": "6.10.0",

src/@types/vscode.proposed.chatParticipantAdditions.d.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ declare module 'vscode' {
273273
*/
274274
progress(value: string, task?: (progress: Progress<ChatResponseWarningPart | ChatResponseReferencePart>) => Thenable<string | void>): void;
275275

276-
thinkingProgress(value: string, id?: string, metadata?: string): void;
276+
thinkingProgress(thinkingDelta: ThinkingDelta): void;
277277

278278
textEdit(target: Uri, edits: TextEdit | TextEdit[]): void;
279279

@@ -327,6 +327,21 @@ declare module 'vscode' {
327327
Omitted = 3
328328
}
329329

330+
export type ThinkingDelta = {
331+
text?: string;
332+
id: string;
333+
metadata?: string;
334+
} | {
335+
text?: string;
336+
id?: string;
337+
metadata: string;
338+
} |
339+
{
340+
text: string;
341+
id?: string;
342+
metadata?: string;
343+
};
344+
330345
export enum ChatResponseClearToPreviousToolInvocationReason {
331346
NoReason = 0,
332347
FilteredContentRetry = 1,
@@ -553,6 +568,8 @@ declare module 'vscode' {
553568
kind: 'chatEditingHunkAction';
554569
uri: Uri;
555570
lineCount: number;
571+
linesAdded: number;
572+
linesRemoved: number;
556573
outcome: ChatEditingSessionActionOutcome;
557574
hasRemainingEdits: boolean;
558575
}

src/@types/vscode.proposed.chatParticipantPrivate.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
// version: 9
6+
// version: 10
77

88
declare module 'vscode' {
99

src/commands.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { NotificationProvider } from './github/notifications';
3030
import { GHPRComment, GHPRCommentThread, TemporaryComment } from './github/prComment';
3131
import { PullRequestModel } from './github/pullRequestModel';
3232
import { PullRequestOverviewPanel } from './github/pullRequestOverview';
33+
import { chooseItem } from './github/quickPicks';
3334
import { RepositoriesManager } from './github/repositoriesManager';
3435
import { getIssuesUrl, getPullsUrl, isInCodespaces, ISSUE_OR_URL_EXPRESSION, parseIssueExpressionOutput, vscodeDevPrLink } from './github/utils';
3536
import { CodingAgentContext, OverviewContext } from './github/views';
@@ -173,26 +174,6 @@ export async function openDescription(
173174

174175
}
175176

176-
async function chooseItem<T>(
177-
activePullRequests: T[],
178-
propertyGetter: (itemValue: T) => string,
179-
options?: vscode.QuickPickOptions,
180-
): Promise<T | undefined> {
181-
if (activePullRequests.length === 1) {
182-
return activePullRequests[0];
183-
}
184-
interface Item extends vscode.QuickPickItem {
185-
itemValue: T;
186-
}
187-
const items: Item[] = activePullRequests.map(currentItem => {
188-
return {
189-
label: propertyGetter(currentItem),
190-
itemValue: currentItem,
191-
};
192-
});
193-
return (await vscode.window.showQuickPick(items, options))?.itemValue;
194-
}
195-
196177
export async function openPullRequestOnGitHub(e: PRNode | RepositoryChangesNode | IssueModel | NotificationTreeItem, telemetry: ITelemetry) {
197178
if (e instanceof PRNode || e instanceof RepositoryChangesNode) {
198179
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(e.pullRequestModel.html_url));

src/extension.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,7 @@ async function deferredActivate(context: vscode.ExtensionContext, showPRControll
393393
const experimentationService = await createExperimentationService(context, telemetry);
394394
await experimentationService.initializePromise;
395395
await experimentationService.isCachedFlightEnabled('githubaa');
396-
const showBadge = (vscode.env.appHost === 'desktop');
397-
await credentialStore.create(showBadge ? undefined : { silent: true });
396+
await credentialStore.create();
398397

399398
const reposManager = new RepositoriesManager(credentialStore, telemetry);
400399
context.subscriptions.push(reposManager);

src/github/copilotRemoteAgent.ts

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import * as nodePath from 'path';
6+
import * as pathLib from 'path';
77
import vscode from 'vscode';
88
import { parseSessionLogs, parseToolCallDetails } from '../../common/sessionParsing';
99
import { COPILOT_ACCOUNTS } from '../common/comment';
@@ -24,11 +24,12 @@ import { CopilotPRWatcher, CopilotStateModel } from './copilotPrWatcher';
2424
import { ChatSessionContentBuilder } from './copilotRemoteAgent/chatSessionContentBuilder';
2525
import { GitOperationsManager } from './copilotRemoteAgent/gitOperationsManager';
2626
import { CredentialStore } from './credentials';
27-
import { ReposManagerState } from './folderRepositoryManager';
27+
import { FolderRepositoryManager, ReposManagerState } from './folderRepositoryManager';
2828
import { GitHubRepository } from './githubRepository';
2929
import { GithubItemStateEnum } from './interface';
3030
import { issueMarkdown } from './markdownUtils';
3131
import { PullRequestModel } from './pullRequestModel';
32+
import { chooseItem } from './quickPicks';
3233
import { RepositoriesManager } from './repositoriesManager';
3334

3435
const LEARN_MORE = vscode.l10n.t('Learn about coding agent');
@@ -166,6 +167,11 @@ export class CopilotRemoteAgentManager extends Disposable {
166167
return false;
167168
}
168169

170+
if (!this.credentialStore.isAnyAuthenticated()) {
171+
// If not signed in, then we optimistically say it's available.
172+
return true;
173+
}
174+
169175
const repoInfo = await this.repoInfo();
170176
if (!repoInfo) {
171177
return false;
@@ -189,14 +195,18 @@ export class CopilotRemoteAgentManager extends Disposable {
189195
}
190196
}
191197

192-
async repoInfo(): Promise<RepoInfo | undefined> {
193-
if (!this.repositoriesManager.folderManagers.length) {
194-
return;
195-
}
196-
const fm = this.repositoriesManager.folderManagers[0];
198+
private chooseFolderManager(): Promise<FolderRepositoryManager | undefined> {
199+
return chooseItem<FolderRepositoryManager>(
200+
this.repositoriesManager.folderManagers,
201+
itemValue => pathLib.basename(itemValue.repository.rootUri.fsPath),
202+
);
203+
}
204+
205+
async repoInfo(fm?: FolderRepositoryManager): Promise<RepoInfo | undefined> {
206+
fm = fm || (await this.chooseFolderManager());
197207
const repository = fm?.repository;
198208
const ghRepository = fm?.gitHubRepositories.find(repo => repo.remote instanceof GitHubRemote) as GitHubRepository | undefined;
199-
if (!repository || !ghRepository) {
209+
if (!fm || !repository || !ghRepository) {
200210
return;
201211
}
202212

@@ -246,11 +256,37 @@ export class CopilotRemoteAgentManager extends Disposable {
246256
}
247257
}
248258

259+
private async tryAcquireAuth(): Promise<FolderRepositoryManager | undefined> {
260+
if (this.credentialStore.isAnyAuthenticated()) {
261+
return undefined;
262+
}
263+
264+
const result = await this.credentialStore.create({ createIfNone: { detail: vscode.l10n.t('Sign in to start delegating tasks to the GitHub coding agent.') } });
265+
266+
/* __GDPR__
267+
"remoteAgent.command.auth" : {
268+
"succeeded" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
269+
}
270+
*/
271+
this.telemetry.sendTelemetryEvent('remoteAgent.command.auth', {
272+
succeeded: result.canceled ? 'false' : 'true'
273+
});
274+
275+
if (result.canceled) {
276+
return undefined;
277+
}
278+
// Wait for repos to update
279+
const fm = await this.chooseFolderManager();
280+
await fm?.updateRepositories();
281+
return fm;
282+
}
283+
249284
async commandImpl(args?: ICopilotRemoteAgentCommandArgs): Promise<string | ICopilotRemoteAgentCommandResponse | undefined> {
250285
if (!args) {
251286
return;
252287
}
253288
const { userPrompt, summary, source, followup, _version } = args;
289+
const fm = await this.tryAcquireAuth();
254290

255291
/* __GDPR__
256292
"remoteAgent.command.args" : {
@@ -273,7 +309,7 @@ export class CopilotRemoteAgentManager extends Disposable {
273309
return;
274310
}
275311

276-
const repoInfo = await this.repoInfo();
312+
const repoInfo = await this.repoInfo(fm);
277313
if (!repoInfo) {
278314
/* __GDPR__
279315
"remoteAgent.command.result" : {
@@ -1062,7 +1098,7 @@ export class CopilotRemoteAgentManager extends Disposable {
10621098
if (toolDetails.toolSpecificData) {
10631099
if ('command' in toolDetails.toolSpecificData) {
10641100
if ((toolDetails.toolSpecificData.command === 'view' || toolDetails.toolSpecificData.command === 'edit') && toolDetails.toolSpecificData.fileLabel) {
1065-
const uri = vscode.Uri.file(nodePath.join(pullRequest.githubRepository.rootUri.fsPath, toolDetails.toolSpecificData.fileLabel));
1101+
const uri = vscode.Uri.file(pathLib.join(pullRequest.githubRepository.rootUri.fsPath, toolDetails.toolSpecificData.fileLabel));
10661102
toolPart.invocationMessage = new vscode.MarkdownString(`${toolPart.toolName} [](${uri.toString()})`);
10671103
toolPart.invocationMessage.supportHtml = true;
10681104
toolPart.pastTenseMessage = new vscode.MarkdownString(`${toolPart.toolName} [](${uri.toString()})`);

src/github/quickPicks.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,29 @@ import { AccountType, IAccount, ILabel, IMilestone, IProject, isISuggestedReview
1818
import { IssueModel } from './issueModel';
1919
import { DisplayLabel } from './views';
2020

21+
export async function chooseItem<T>(
22+
itemsToChooseFrom: T[],
23+
propertyGetter: (itemValue: T) => string,
24+
options?: vscode.QuickPickOptions,
25+
): Promise<T | undefined> {
26+
if (itemsToChooseFrom.length === 0) {
27+
return undefined;
28+
}
29+
if (itemsToChooseFrom.length === 1) {
30+
return itemsToChooseFrom[0];
31+
}
32+
interface Item extends vscode.QuickPickItem {
33+
itemValue: T;
34+
}
35+
const items: Item[] = itemsToChooseFrom.map(currentItem => {
36+
return {
37+
label: propertyGetter(currentItem),
38+
itemValue: currentItem,
39+
};
40+
});
41+
return (await vscode.window.showQuickPick(items, options))?.itemValue;
42+
}
43+
2144
async function getItems<T extends IAccount | ITeam | ISuggestedReviewer>(context: vscode.ExtensionContext, skipList: Set<string>, users: T[], picked: boolean, tooManyAssignable: boolean = false): Promise<(vscode.QuickPickItem & { user?: T })[]> {
2245
const alreadyAssignedItems: (vscode.QuickPickItem & { user?: T })[] = [];
2346
// Address skip list before first await

src/view/treeNodes/categoryNode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export enum PRCategoryActionType {
3232

3333
export class PRCategoryActionNode extends TreeNode implements vscode.TreeItem {
3434
public collapsibleState: vscode.TreeItemCollapsibleState;
35-
public iconPath?: { light: string | vscode.Uri; dark: string | vscode.Uri };
35+
public iconPath?: { light: vscode.Uri; dark: vscode.Uri };
3636
public type: PRCategoryActionType;
3737
public command?: vscode.Command;
3838

src/view/treeNodes/fileChangeNode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export class FileChangeNode extends TreeNode implements vscode.TreeItem {
6868
public iconPath?:
6969
| string
7070
| vscode.Uri
71-
| { light: string | vscode.Uri; dark: string | vscode.Uri }
71+
| { light: vscode.Uri; dark: vscode.Uri }
7272
| vscode.ThemeIcon;
7373
public fileChangeResourceUri: vscode.Uri;
7474
public contextValue: string;

src/view/treeNodes/pullRequestNode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2
265265
});
266266
}
267267

268-
private async _getIcon(): Promise<vscode.Uri | vscode.ThemeIcon | { light: string | vscode.Uri; dark: string | vscode.Uri }> {
268+
private async _getIcon(): Promise<vscode.Uri | vscode.ThemeIcon | { light: vscode.Uri; dark: vscode.Uri }> {
269269
const copilotWorkingStatus = await this.pullRequestModel.copilotWorkingStatus(this.pullRequestModel);
270270
const theme = this._folderReposManager.themeWatcher.themeData;
271271
if (!theme || copilotWorkingStatus === CopilotWorkingStatus.NotCopilotIssue) {

0 commit comments

Comments
 (0)