Skip to content

Commit 8bd5c8b

Browse files
authored
1 parent 09435d2 commit 8bd5c8b

3 files changed

Lines changed: 41 additions & 37 deletions

File tree

src/github/copilotApi.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import { OctokitCommon } from './common';
1212
import { CredentialStore } from './credentials';
1313
import { LoggingOctokit } from './loggingOctokit';
1414
import { hasEnterpriseUri } from './utils';
15+
import Logger from '../common/logger';
16+
17+
const LEARN_MORE_URL = 'https://docs.github.com/en/copilot/how-tos/agents/copilot-coding-agent';
18+
const PREMIUM_REQUESTS_URL = 'https://docs.github.com/en/copilot/concepts/copilot-billing/understanding-and-managing-requests-in-copilot#what-are-premium-requests';
1519

1620
export interface RemoteAgentJobPayload {
1721
problem_statement: string;
@@ -33,6 +37,8 @@ export interface RemoteAgentJobResponse {
3337
}
3438

3539
export class CopilotApi {
40+
protected static readonly ID = 'copilotApi';
41+
3642
constructor(private octokit: LoggingOctokit, private token: string) { }
3743

3844
private get baseUrl(): string {
@@ -57,7 +63,7 @@ export class CopilotApi {
5763
body: JSON.stringify(payload)
5864
});
5965
if (!response.ok) {
60-
throw new Error(this.formatRemoteAgentJobError(response.status));
66+
throw new Error(await this.formatRemoteAgentJobError(response.status, repoSlug, response));
6167
}
6268
const data = await response.json();
6369
this.validateRemoteAgentJobResponse(data);
@@ -66,22 +72,23 @@ export class CopilotApi {
6672

6773

6874
// https://github.com/github/sweagentd/blob/371ea6db280b9aecf790ccc20660e39a7ecb8d1c/internal/api/jobapi/handler.go#L110-L120
69-
private formatRemoteAgentJobError(status: number) {
75+
private async formatRemoteAgentJobError(status: number, repoSlug: string, response: Response): Promise<string> {
7076
switch (status) {
7177
case 400:
7278
return vscode.l10n.t('Bad request');
7379
case 401:
7480
return vscode.l10n.t('Unauthorized');
7581
case 402:
76-
return vscode.l10n.t('Premium request quota exceeded');
82+
return vscode.l10n.t('[Premium request]({0}) quota exceeded', PREMIUM_REQUESTS_URL);
7783
case 403:
78-
return vscode.l10n.t('GitHub Coding Agent is not enabled for this repository');
84+
return vscode.l10n.t('[GitHub Coding Agent]({0}) is not enabled for repository \'{1}\'', LEARN_MORE_URL, repoSlug);
7985
case 404:
80-
return vscode.l10n.t('Repository not found');
86+
return vscode.l10n.t('Repository \'{0}\' not found', repoSlug);
8187
case 409:
8288
return vscode.l10n.t('A Coding Agent pull request already exists');
8389
case 500:
84-
return vscode.l10n.t('Server error');
90+
Logger.error(`Server error in remote agent job: ${await response.text()}`, CopilotApi.ID);
91+
return vscode.l10n.t('Server error. Please try again later.');
8592
default:
8693
return vscode.l10n.t('Error: {0}', status);
8794
}

src/github/copilotRemoteAgent.ts

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ import { AuthProvider } from '../common/authentication';
99
import { COPILOT_LOGINS } from '../common/copilot';
1010
import { commands } from '../common/executeCommands';
1111
import { Disposable } from '../common/lifecycle';
12-
import { Remote } from '../common/remote';
12+
import { GitHubRemote, Remote } from '../common/remote';
1313
import { CODING_AGENT, CODING_AGENT_AUTO_COMMIT_AND_PUSH, CODING_AGENT_ENABLED } from '../common/settingKeys';
1414
import { toOpenPullRequestWebviewUri } from '../common/uri';
1515
import { CopilotApi, RemoteAgentJobPayload } from './copilotApi';
1616
import { CopilotPRWatcher, CopilotStateModel } from './copilotPrWatcher';
1717
import { CredentialStore } from './credentials';
18+
import { FolderRepositoryManager } from './folderRepositoryManager';
1819
import { RepositoriesManager } from './repositoriesManager';
1920

2021
type RemoteAgentSuccessResult = { link: string; state: 'success'; number: number; webviewUri: vscode.Uri; llmDetails: string };
@@ -115,13 +116,12 @@ export class CopilotRemoteAgentManager extends Disposable {
115116
return false;
116117
}
117118

118-
const { owner, repo } = repoInfo;
119-
const folderManager = this.getFolderManagerForRepo(owner, repo);
119+
const { fm } = repoInfo;
120120

121121
try {
122122
// Ensure assignable users are loaded
123-
await folderManager.getAssignableUsers();
124-
const allAssignableUsers = folderManager.getAllAssignableUsers();
123+
await fm.getAssignableUsers();
124+
const allAssignableUsers = fm.getAllAssignableUsers();
125125

126126
if (!allAssignableUsers) {
127127
return false;
@@ -169,33 +169,32 @@ export class CopilotRemoteAgentManager extends Disposable {
169169
.getConfiguration(CODING_AGENT).get(CODING_AGENT_AUTO_COMMIT_AND_PUSH, false);
170170
}
171171

172-
private getFolderManagerForRepo(owner?: string, repo?: string) {
173-
let folderManager = (owner && repo)
174-
? this.repositoriesManager.getManagerForRepository(owner, repo)
175-
: undefined;
176-
if (!folderManager && this.repositoriesManager.folderManagers.length > 0) {
177-
folderManager = this.repositoriesManager.folderManagers[0];
178-
}
179-
if (!folderManager) {
180-
throw new Error('No folder manager found for the repository. Open a workspace with a Git repository.');
172+
async repoInfo(): Promise<{ owner: string; repo: string; baseRef: string; remote: GitHubRemote; repository: Repository; fm: FolderRepositoryManager } | undefined> {
173+
if (!this.repositoriesManager.folderManagers.length) {
174+
return;
181175
}
182-
return folderManager;
183-
}
184-
185-
async repoInfo(): Promise<{ owner: string; repo: string; remote: string; baseRef: string; repository: Repository } | undefined> {
186-
const fm = this.getFolderManagerForRepo();
176+
const fm = this.repositoriesManager.folderManagers[0];
187177
const repository = fm?.repository;
188-
if (!fm || !repository) {
178+
if (!repository) {
189179
return;
190180
}
191-
const { owner, repo } = await fm.getPullRequestDefaults();
192-
const remotes = repository.state.remotes;
181+
193182
const baseRef = repository.state.HEAD?.name; // TODO: Consider edge cases
194-
const remote = remotes.find(r => r.name === 'origin')?.name || remotes.find(r => r.pushUrl)?.name;
195-
if (!owner || !repo || !remote || !baseRef || !repository) {
183+
const ghRemotes = await fm.getGitHubRemotes();
184+
if (!ghRemotes || ghRemotes.length === 0) {
196185
return;
197186
}
198-
return { owner, repo, remote, baseRef, repository };
187+
188+
const remote =
189+
ghRemotes.find(remote => remote.remoteName === 'origin')
190+
|| ghRemotes[0]; // Fallback to the first remote
191+
192+
// Extract repo data from target remote
193+
const { owner, repositoryName: repo } = remote;
194+
if (!owner || !repo || !baseRef || !repository) {
195+
return;
196+
}
197+
return { owner, repo, baseRef, remote, repository, fm };
199198
}
200199

201200
async commandImpl(args?: ICopilotRemoteAgentCommandArgs): Promise<string | undefined> {
@@ -214,9 +213,7 @@ export class CopilotRemoteAgentManager extends Disposable {
214213
}
215214
const { repository, owner, repo } = repoInfo;
216215
const repoName = `${owner}/${repo}`; // TODO: Make sure this is where we'll push to
217-
218216
const hasChanges = repository.state.workingTreeChanges.length > 0 || repository.state.indexChanges.length > 0;
219-
220217
const learnMoreCb = async () => {
221218
vscode.env.openExternal(vscode.Uri.parse('https://docs.github.com/copilot/using-github-copilot/coding-agent'));
222219
};
@@ -301,7 +298,7 @@ export class CopilotRemoteAgentManager extends Disposable {
301298

302299
const repoInfo = await this.repoInfo();
303300
if (!repoInfo) {
304-
return { error: vscode.l10n.t('No repository information found. Please open a workspace with a Git repository.'), state: 'error' };
301+
return { error: vscode.l10n.t('No repository information found. Please open a workspace with a GitHub repository.'), state: 'error' };
305302
}
306303
const { owner, repo, remote, repository, baseRef } = repoInfo;
307304

@@ -327,7 +324,7 @@ export class CopilotRemoteAgentManager extends Disposable {
327324
return { error: vscode.l10n.t('Could not \'git commit\' pending changes. If GPG signing or git hooks are enabled, please first commit or stash your changes and try again. ({0})', e.message), state: 'error' };
328325
}
329326
}
330-
await repository.push(remote, asyncBranch, true);
327+
await repository.push(remote.remoteName, asyncBranch, true);
331328
ref = asyncBranch;
332329
} catch (e) {
333330
return { error: vscode.l10n.t('Could not auto-push pending changes. Manually commit or stash your changes and try again. ({0})', e.message), state: 'error' };

src/lm/tools/copilotRemoteAgentTool.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class CopilotRemoteAgentTool implements vscode.LanguageModelTool<CopilotR
2525

2626
async prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<CopilotRemoteAgentToolParameters>): Promise<vscode.PreparedToolInvocation> {
2727
const { title } = options.input;
28-
28+
2929
// Check if the coding agent is available (enabled and assignable)
3030
const isAvailable = await this.manager.isAvailable();
3131
if (!isAvailable) {
@@ -39,7 +39,7 @@ export class CopilotRemoteAgentTool implements vscode.LanguageModelTool<CopilotR
3939
invocationMessage: vscode.l10n.t('Launching coding agent'),
4040
confirmationMessages: {
4141
message: targetRepo && autoPushEnabled
42-
? vscode.l10n.t('The coding agent will continue work on "**{0}**" in a new branch on "**{1}/{2}**". Any uncommitted changes will be **automatically pushed to your default remote ({3})** and included.', title, targetRepo.owner, targetRepo.repo, targetRepo.remote)
42+
? vscode.l10n.t('The coding agent will continue work on "**{0}**" in a new branch on "**{1}/{2}**". Any uncommitted changes will be **automatically pushed**.', title, targetRepo.owner, targetRepo.repo)
4343
: vscode.l10n.t('The coding agent will start working on "**{0}**"', title),
4444
title: vscode.l10n.t('Start coding agent?'),
4545
}

0 commit comments

Comments
 (0)