From 40dd023e27d0aaaa2a63099bc936559e9037e866 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Apr 2026 09:53:50 +0000 Subject: [PATCH 1/3] Initial plan From 15e8a906f64e3eb4b0e19b5f90f381515a51739b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Apr 2026 10:06:15 +0000 Subject: [PATCH 2/3] initial plan Agent-Logs-Url: https://github.com/microsoft/vscode-pull-request-github/sessions/588c7904-b943-44bc-842d-b78baf609a0a Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- src/@types/vscode.proposed.chatSessionsProvider.d.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/@types/vscode.proposed.chatSessionsProvider.d.ts b/src/@types/vscode.proposed.chatSessionsProvider.d.ts index 06d0649b18..6a394c158b 100644 --- a/src/@types/vscode.proposed.chatSessionsProvider.d.ts +++ b/src/@types/vscode.proposed.chatSessionsProvider.d.ts @@ -594,8 +594,15 @@ declare module 'vscode' { /** * The initial option selections for the session, provided with the first request. * Contains the options the user selected (or defaults) before the session was created. + * + * @deprecated Use `inputState` instead */ readonly initialSessionOptions?: ReadonlyArray<{ optionId: string; value: string | ChatSessionProviderOptionItem }>; + + /** + * The current input state of the chat session. + */ + readonly inputState: ChatSessionInputState; } export interface ChatSessionCapabilities { @@ -692,6 +699,8 @@ declare module 'vscode' { * * These commands will be displayed at the bottom of the group. * + * For extensions using the legacy `commands` API, these commands are passed the sessionResource as the first argument. + * * For extensions that use the new `provideChatSessionInputState` API, these commands are passed a context object * `{ inputState: ChatSessionInputState; sessionResource: Uri | undefined }` that they can use to determine which session and options they are being invoked for. */ From 801127a7fcbb7c60ce8d07ae13f253c20c35f627 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Apr 2026 10:08:39 +0000 Subject: [PATCH 3/3] fix: include event and workflowName in status check deduplication key When the same workflow runs for both push and pull_request events, checks with the same name but different events were incorrectly merged during deduplication. Now the deduplication key includes the event and workflowName fields so distinct checks are preserved. Agent-Logs-Url: https://github.com/microsoft/vscode-pull-request-github/sessions/588c7904-b943-44bc-842d-b78baf609a0a Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- src/github/githubRepository.ts | 12 ++-- src/test/github/githubRepository.test.ts | 79 ++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/src/github/githubRepository.ts b/src/github/githubRepository.ts index 724f9e46a1..dbe093e402 100644 --- a/src/github/githubRepository.ts +++ b/src/github/githubRepository.ts @@ -1916,9 +1916,13 @@ export class GitHubRepository extends Disposable { const statusByContext = new Map(); for (const status of statuses) { - const existing = statusByContext.get(status.context); + // Include event and workflowName in the key so that checks from different + // workflow events (e.g. "push" vs "pull_request") or different workflows + // are not incorrectly merged during deduplication. + const key = `${status.context}\0${status.event ?? ''}\0${status.workflowName ?? ''}`; + const existing = statusByContext.get(key); if (!existing) { - statusByContext.set(status.context, status); + statusByContext.set(key, status); continue; } @@ -1928,7 +1932,7 @@ export class GitHubRepository extends Disposable { if (currentIsPending && !existingIsPending) { // Current is pending, existing is completed - prefer current - statusByContext.set(status.context, status); + statusByContext.set(key, status); } else if (!currentIsPending && existingIsPending) { // Current is completed, existing is pending - keep existing continue; @@ -1936,7 +1940,7 @@ export class GitHubRepository extends Disposable { // Both are same type (both pending or both completed) // Prefer the one with a higher ID (more recent), as GitHub IDs are monotonically increasing if (status.id > existing.id) { - statusByContext.set(status.context, status); + statusByContext.set(key, status); } } } diff --git a/src/test/github/githubRepository.test.ts b/src/test/github/githubRepository.test.ts index 6d41977abf..0e5111026c 100644 --- a/src/test/github/githubRepository.test.ts +++ b/src/test/github/githubRepository.test.ts @@ -15,6 +15,7 @@ import { Uri } from 'vscode'; import { MockExtensionContext } from '../mocks/mockExtensionContext'; import { GitHubManager } from '../../authentication/githubServer'; import { GitHubServerType } from '../../common/authentication'; +import { CheckState, PullRequestCheckStatus } from '../../github/interface'; describe('GitHubRepository', function () { let sinon: SinonSandbox; @@ -52,4 +53,82 @@ describe('GitHubRepository', function () { // assert(! dotcomRepository.isGitHubDotCom); }); }); + + describe('deduplicateStatusChecks', function () { + function createStatus(overrides: Partial & { id: string; context: string }): PullRequestCheckStatus { + return { + databaseId: undefined, + url: undefined, + avatarUrl: undefined, + state: CheckState.Success, + description: null, + targetUrl: null, + workflowName: undefined, + event: undefined, + isRequired: false, + isCheckRun: true, + ...overrides, + }; + } + + function callDeduplicateStatusChecks(repo: GitHubRepository, statuses: PullRequestCheckStatus[]): PullRequestCheckStatus[] { + return (repo as any).deduplicateStatusChecks(statuses); + } + + let repo: GitHubRepository; + + beforeEach(function () { + const url = 'https://github.com/some/repo'; + const remote = new GitHubRemote('origin', url, new Protocol(url), GitHubServerType.GitHubDotCom); + const rootUri = Uri.file('C:\\users\\test\\repo'); + repo = new GitHubRepository(1, remote, rootUri, credentialStore, telemetry); + }); + + it('keeps checks with different events as separate entries', function () { + const statuses = [ + createStatus({ id: '1', context: 'Build Linux / x86-64', event: 'push', workflowName: 'Build Linux' }), + createStatus({ id: '2', context: 'Build Linux / x86-64', event: 'pull_request', workflowName: 'Build Linux' }), + ]; + const result = callDeduplicateStatusChecks(repo, statuses); + assert.strictEqual(result.length, 2); + }); + + it('deduplicates checks with the same name, event, and workflow', function () { + const statuses = [ + createStatus({ id: '1', context: 'Build Linux / x86-64', event: 'push', workflowName: 'Build Linux', state: CheckState.Success }), + createStatus({ id: '2', context: 'Build Linux / x86-64', event: 'push', workflowName: 'Build Linux', state: CheckState.Success }), + ]; + const result = callDeduplicateStatusChecks(repo, statuses); + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].id, '2'); // higher ID preferred + }); + + it('keeps checks from different workflows as separate entries', function () { + const statuses = [ + createStatus({ id: '1', context: 'build', event: 'push', workflowName: 'CI' }), + createStatus({ id: '2', context: 'build', event: 'push', workflowName: 'Nightly' }), + ]; + const result = callDeduplicateStatusChecks(repo, statuses); + assert.strictEqual(result.length, 2); + }); + + it('prefers pending checks over completed ones during deduplication', function () { + const statuses = [ + createStatus({ id: '1', context: 'test', event: 'push', workflowName: 'CI', state: CheckState.Success }), + createStatus({ id: '2', context: 'test', event: 'push', workflowName: 'CI', state: CheckState.Pending }), + ]; + const result = callDeduplicateStatusChecks(repo, statuses); + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].state, CheckState.Pending); + }); + + it('handles status contexts without event or workflowName', function () { + const statuses = [ + createStatus({ id: '1', context: 'ci/jenkins', isCheckRun: false }), + createStatus({ id: '2', context: 'ci/travis', isCheckRun: false }), + ]; + const result = callDeduplicateStatusChecks(repo, statuses); + assert.strictEqual(result.length, 2); + }); + }); });