Skip to content

Commit aedc615

Browse files
authored
Merge branch 'main' into copilot/fix-e7c8c5a5-dc38-4029-b1a7-504cde95862f
2 parents 4dd3c84 + 17bca28 commit aedc615

22 files changed

Lines changed: 452 additions & 368 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 0.118.2
4+
5+
### Fixes
6+
7+
- Long coding agent problem statement results in unrecoverable error (Truncate coding agent problem_statement). https://github.com/microsoft/vscode-pull-request-github/issues/7861
8+
39
## 0.118.1
410

511
### Fixes

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"activeComment",
1515
"chatParticipantAdditions",
1616
"chatParticipantPrivate",
17-
"chatSessionsProvider",
17+
"chatSessionsProvider@2",
1818
"codiconDecoration",
1919
"codeActionRanges",
2020
"commentingRangeHint",

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

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

6+
// version: 2
7+
68
declare module 'vscode' {
79
/**
810
* Represents the status of a chat session.
@@ -34,6 +36,13 @@ declare module 'vscode' {
3436
readonly onDidChangeChatSessionItems: Event<void>;
3537

3638
/**
39+
* Event that the provider can fire to signal that the current (original) chat session should be replaced with a new (modified) chat session.
40+
* The UI can use this information to gracefully migrate the user to the new session.
41+
*/
42+
readonly onDidCommitChatSessionItem: Event<{ original: ChatSessionItem /** untitled */; modified: ChatSessionItem /** newly created */ }>;
43+
44+
/**
45+
* DEPRECATED: Will be removed!
3746
* Creates a new chat session.
3847
*
3948
* @param options Options for the new session including an optional initial prompt and history
@@ -46,16 +55,6 @@ declare module 'vscode' {
4655
*/
4756
readonly request: ChatRequest;
4857

49-
/**
50-
* Initial prompt to initiate the session
51-
*/
52-
readonly prompt?: string;
53-
54-
/**
55-
* History to initialize the session with
56-
*/
57-
readonly history?: ReadonlyArray<ChatRequestTurn | ChatResponseTurn>;
58-
5958
/**
6059
* Additional metadata to use for session creation
6160
*/
@@ -190,7 +189,16 @@ declare module 'vscode' {
190189
*
191190
* @returns A disposable that unregisters the provider when disposed.
192191
*/
193-
export function registerChatSessionContentProvider(chatSessionType: string, provider: ChatSessionContentProvider, capabilities?: ChatSessionCapabilities): Disposable;
192+
export function registerChatSessionContentProvider(chatSessionType: string, provider: ChatSessionContentProvider, chatParticipant: ChatParticipant, capabilities?: ChatSessionCapabilities): Disposable;
193+
}
194+
195+
export interface ChatContext {
196+
readonly chatSessionContext?: ChatSessionContext;
197+
}
198+
199+
export interface ChatSessionContext {
200+
readonly chatSessionItem: ChatSessionItem; // Maps to URI of chat session editor (could be 'untitled-1', etc..)
201+
readonly isUntitled: boolean;
194202
}
195203

196204
export interface ChatSessionCapabilities {

src/common/uri.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Buffer } from 'buffer';
99
import * as pathUtils from 'path';
1010
import fetch from 'cross-fetch';
1111
import * as vscode from 'vscode';
12+
import { RemoteInfo } from '../../common/types';
1213
import { Repository } from '../api/api';
1314
import { EXTENSION_ID } from '../constants';
1415
import { IAccount, isITeam, ITeam, reviewerId } from '../github/interface';
@@ -675,6 +676,24 @@ export function fromOpenPullRequestWebviewUri(uri: vscode.Uri): OpenPullRequestW
675676
} catch (e) { }
676677
}
677678

679+
export function toQueryUri(params: { remote: RemoteInfo | undefined, isCopilot?: boolean }) {
680+
const uri = vscode.Uri.from({ scheme: Schemes.PRQuery, path: params.isCopilot ? 'copilot' : undefined, query: params.remote ? JSON.stringify({ remote: params.remote }) : undefined });
681+
return uri;
682+
}
683+
684+
export function fromQueryUri(uri: vscode.Uri): { remote: RemoteInfo | undefined, isCopilot?: boolean } | undefined {
685+
if (uri.scheme !== Schemes.PRQuery) {
686+
return;
687+
}
688+
try {
689+
const query = uri.query ? JSON.parse(uri.query) : undefined;
690+
return {
691+
remote: query.remote,
692+
isCopilot: uri.path === 'copilot'
693+
};
694+
} catch (e) { }
695+
}
696+
678697
export enum Schemes {
679698
File = 'file',
680699
Review = 'review', // File content for a checked out PR
@@ -694,8 +713,6 @@ export enum Schemes {
694713
GitHubCommit = 'githubcommit' // file content from GitHub for a commit
695714
}
696715

697-
export const COPILOT_QUERY = vscode.Uri.from({ scheme: Schemes.PRQuery, path: 'copilot' });
698-
699716
export function resolvePath(from: vscode.Uri, to: string) {
700717
if (from.scheme === Schemes.File) {
701718
return pathUtils.resolve(from.fsPath, to);

src/extension.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,11 @@ async function deferredActivate(context: vscode.ExtensionContext, showPRControll
420420
const copilotRemoteAgentManager = new CopilotRemoteAgentManager(credentialStore, reposManager, telemetry, context, apiImpl);
421421
context.subscriptions.push(copilotRemoteAgentManager);
422422
if (vscode.chat?.registerChatSessionItemProvider) {
423+
const chatParticipant = vscode.chat.createChatParticipant(COPILOT_SWE_AGENT, async (request, context, stream, token) =>
424+
await copilotRemoteAgentManager.chatParticipantImpl(request, context, stream, token)
425+
);
426+
context.subscriptions.push(chatParticipant);
427+
423428
const provider = new class implements vscode.ChatSessionContentProvider, vscode.ChatSessionItemProvider {
424429
label = vscode.l10n.t('GitHub Copilot Coding Agent');
425430
provideChatSessionItems = async (token) => {
@@ -429,9 +434,7 @@ async function deferredActivate(context: vscode.ExtensionContext, showPRControll
429434
return await copilotRemoteAgentManager.provideChatSessionContent(id, token);
430435
};
431436
onDidChangeChatSessionItems = copilotRemoteAgentManager.onDidChangeChatSessions;
432-
provideNewChatSessionItem = async (options: { readonly request: vscode.ChatRequest; prompt?: string; history: ReadonlyArray<vscode.ChatRequestTurn | vscode.ChatResponseTurn>; metadata?: any; }, token: vscode.CancellationToken): Promise<vscode.ChatSessionItem> => {
433-
return await copilotRemoteAgentManager.provideNewChatSessionItem(options, token);
434-
};
437+
onDidCommitChatSessionItem = copilotRemoteAgentManager.onDidCommitChatSession;
435438
}();
436439

437440
context.subscriptions.push(vscode.chat?.registerChatSessionItemProvider(
@@ -442,7 +445,8 @@ async function deferredActivate(context: vscode.ExtensionContext, showPRControll
442445
context.subscriptions.push(vscode.chat?.registerChatSessionContentProvider(
443446
COPILOT_SWE_AGENT,
444447
provider,
445-
{ supportsInterruptions: true, }
448+
chatParticipant,
449+
{ supportsInterruptions: true }
446450
));
447451
}
448452

src/github/copilotApi.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import { hasEnterpriseUri } from './utils';
1919

2020
const LEARN_MORE_URL = 'https://aka.ms/coding-agent-docs';
2121
const PREMIUM_REQUESTS_URL = 'https://docs.github.com/en/copilot/concepts/copilot-billing/understanding-and-managing-requests-in-copilot#what-are-premium-requests';
22-
22+
// https://github.com/github/sweagentd/blob/59e7d9210ca3ebba029918387e525eea73cb1f4a/internal/problemstatement/problemstatement.go#L36-L53
23+
export const MAX_PROBLEM_STATEMENT_LENGTH = 30_000 - 50; // 50 character buffer
2324
export interface RemoteAgentJobPayload {
2425
problem_statement: string;
2526
event_type: string;
@@ -45,14 +46,6 @@ export interface ChatSessionWithPR extends vscode.ChatSessionItem {
4546
pullRequest: PullRequestModel;
4647
}
4748

48-
export interface ChatSessionFromSummarizedChat extends vscode.ChatSessionItem {
49-
prompt: string;
50-
summary?: string;
51-
// Cache
52-
pullRequest?: PullRequestModel;
53-
sessionInfo?: SessionInfo;
54-
}
55-
5649
export class CopilotApi {
5750
protected static readonly ID = 'copilotApi';
5851

@@ -79,10 +72,15 @@ export class CopilotApi {
7972
owner: string,
8073
name: string,
8174
payload: RemoteAgentJobPayload,
75+
isTruncated: boolean,
8276
): Promise<RemoteAgentJobResponse> {
8377
const repoSlug = `${owner}/${name}`;
8478
const apiUrl = `/agents/swe/v0/jobs/${repoSlug}`;
8579
let status: number | undefined;
80+
81+
const problemStatementLength = payload.problem_statement.length.toString();
82+
const payloadJson = JSON.stringify(payload);
83+
const payloadLength = payloadJson.length.toString();
8684
Logger.trace(`postRemoteAgentJob: Posting job to ${apiUrl} with payload: ${JSON.stringify(payload)}`, CopilotApi.ID);
8785
try {
8886
const response = await this.makeApiCall(apiUrl, {
@@ -93,7 +91,7 @@ export class CopilotApi {
9391
'Content-Type': 'application/json',
9492
'Accept': 'application/json'
9593
},
96-
body: JSON.stringify(payload)
94+
body: payloadJson
9795
});
9896

9997
status = response.status;
@@ -106,21 +104,33 @@ export class CopilotApi {
106104
/*
107105
__GDPR__
108106
"remoteAgent.postRemoteAgentJob" : {
109-
"status" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
107+
"status" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
108+
"payloadLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
109+
"problemStatementLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
110+
"isTruncated": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
110111
}
111112
*/
112113
this.telemetry.sendTelemetryEvent('remoteAgent.postRemoteAgentJob', {
113114
status: status.toString(),
115+
payloadLength,
116+
problemStatementLength,
117+
isTruncated: isTruncated.toString(),
114118
});
115119
return data;
116120
} catch (error) {
117121
/* __GDPR__
118122
"remoteAgent.postRemoteAgentJob" : {
119-
"status" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
123+
"status" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
124+
"payloadLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
125+
"problemStatementLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
126+
"isTruncated": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
120127
}
121128
*/
122129
this.telemetry.sendTelemetryErrorEvent('remoteAgent.postRemoteAgentJob', {
123130
status: status?.toString() || '999',
131+
payloadLength,
132+
problemStatementLength,
133+
isTruncated: isTruncated.toString(),
124134
});
125135
throw error;
126136
}

src/github/copilotPrWatcher.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { COPILOT_LOGINS, copilotEventToStatus, CopilotPRStatus } from '../common
1010
import { Disposable } from '../common/lifecycle';
1111
import Logger from '../common/logger';
1212
import { PR_SETTINGS_NAMESPACE, QUERIES } from '../common/settingKeys';
13-
import { FolderRepositoryManager } from './folderRepositoryManager';
1413
import { PRType } from './interface';
1514
import { PullRequestModel } from './pullRequestModel';
1615
import { PullRequestOverviewPanel } from './pullRequestOverview';
@@ -42,7 +41,10 @@ export class CopilotStateModel extends Disposable {
4241
this._onRefresh.fire();
4342
}
4443

45-
makeKey(owner: string, repo: string, prNumber: number): string {
44+
makeKey(owner: string, repo: string, prNumber?: number): string {
45+
if (prNumber === undefined) {
46+
return `${owner}/${repo}`;
47+
}
4648
return `${owner}/${repo}#${prNumber}`;
4749
}
4850

@@ -109,6 +111,17 @@ export class CopilotStateModel extends Disposable {
109111
return this._showNotification;
110112
}
111113

114+
getNotificationsCount(owner: string, repo: string): number {
115+
let total = 0;
116+
const partialKey = `${this.makeKey(owner, repo)}#`;
117+
for (const state of this._showNotification.values()) {
118+
if (state.startsWith(partialKey)) {
119+
total++;
120+
}
121+
}
122+
return total;
123+
}
124+
112125
setInitialized() {
113126
this._isInitialized = true;
114127
}
@@ -117,11 +130,14 @@ export class CopilotStateModel extends Disposable {
117130
return this._isInitialized;
118131
}
119132

120-
getCounts(): { total: number; inProgress: number; error: number } {
133+
getCounts(owner: string, repo: string): { total: number; inProgress: number; error: number } {
121134
let inProgressCount = 0;
122135
let errorCount = 0;
123136

124137
for (const state of this._states.values()) {
138+
if (state.item.remote.owner !== owner || state.item.remote.repositoryName !== repo) {
139+
continue;
140+
}
125141
if (state.status === CopilotPRStatus.Started) {
126142
inProgressCount++;
127143
} else if (state.status === CopilotPRStatus.Failed) {
@@ -221,14 +237,6 @@ export class CopilotPRWatcher extends Disposable {
221237
}
222238
}
223239

224-
private _currentUser: string | undefined;
225-
private async _getCurrentUser(folderManager: FolderRepositoryManager): Promise<string> {
226-
if (!this._currentUser) {
227-
this._currentUser = (await folderManager.getCurrentUser()).login;
228-
}
229-
return this._currentUser;
230-
}
231-
232240
private async _updateSingleState(pr: PullRequestModel): Promise<void> {
233241
const changes: { pullRequestModel: PullRequestModel, status: CopilotPRStatus }[] = [];
234242

0 commit comments

Comments
 (0)