Agents - refactor pull request actions#315266
Merged
Merged
Conversation
sandy081
approved these changes
May 8, 2026
hediet
approved these changes
May 8, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
Refactors pull request-related actions in the Agents/Sessions experience by removing the “Sync Pull Request” action and introducing a direct “Create PR / Create Draft PR” flow backed by a new Copilot-side PR creation service and a new GitHub REST API method.
Changes:
- Update Sessions/Changes toolbar action enablement and button rendering to reflect merge vs PR scenarios.
- Add
IOctoKitService.createPullRequest(REST) plumbing and return type for created PRs. - Introduce
PullRequestCreationServiceand wire it into Copilot CLI session commands to commit/push and create PRs.
Show a summary per file
| File | Description |
|---|---|
| src/vs/sessions/contrib/sessions/browser/views/sessionsViewActions.ts | Adjusts “Mark as Done” enablement for merge vs PR scenarios using additional context keys. |
| src/vs/sessions/contrib/changes/browser/changesView.ts | Updates changes toolbar button configuration for commit/sync/PR actions. |
| extensions/copilot/src/platform/github/common/octoKitServiceImpl.ts | Adds createPullRequest implementation using permissive auth. |
| extensions/copilot/src/platform/github/common/githubService.ts | Extends IOctoKitService and BaseOctoKitService with PR creation support and response type. |
| extensions/copilot/src/extension/chatSessions/vscode-node/pullRequestCreationService.ts | New service to push, generate PR title/description context, and create a PR via Octokit. |
| extensions/copilot/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts | Refactors CLI chat commands to use shared sync + new PR creation service (V1 wiring). |
| extensions/copilot/src/extension/chatSessions/vscode-node/copilotCLIChatSessions.ts | Same PR-creation refactor for the newer CLI chat sessions implementation. |
| extensions/copilot/src/extension/chatSessions/vscode-node/chatSessions.ts | Registers PullRequestCreationService and injects it into CLI command registration. |
| extensions/copilot/src/extension/agents/vscode-node/test/mockOctoKitService.ts | Updates Octokit mock to satisfy the new createPullRequest API. |
| extensions/copilot/package.nls.json | Removes the localized label for the deleted “Sync Pull Request” command. |
| extensions/copilot/package.json | Removes the “Sync Pull Request” command contribution and updates enablement/menus for the new PR actions. |
Copilot's findings
- Files reviewed: 11/11 changed files
- Comments generated: 5
Comment on lines
187
to
205
| @@ -198,7 +198,10 @@ class ChangesButtonBarWidget extends Disposable { | |||
| }); | |||
| return { showIcon: false, showLabel: true, isSecondary: false, customLabelObs }; | |||
| } | |||
| if (action.id === 'github.copilot.sessions.sync') { | |||
| if ( | |||
| action.id === 'github.copilot.sessions.sync' || | |||
| action.id === 'github.copilot.sessions.commitAndSync' | |||
| ) { | |||
| const labelWithCount = outgoingChanges > 0 | |||
Comment on lines
+57
to
+85
| async createPullRequest(options: CreatePullRequestOptions, token: vscode.CancellationToken): Promise<string | undefined> { | ||
| const { repositoryUri, branchName, baseBranchName, isDraft } = options; | ||
|
|
||
| const repository = await this.gitService.openRepository(repositoryUri); | ||
| const repositoryContext = await this.gitService.getRepository(repositoryUri); | ||
|
|
||
| if (!repository || !repositoryContext) { | ||
| throw new Error(l10n.t('Could not find the repository for branch \'{0}\'.', branchName)); | ||
| } | ||
|
|
||
| // Resolve owner/repo from the (preferred upstream's) remote. | ||
| const githubRepoInfo = getGitHubRepoInfoFromContext(repositoryContext); | ||
| const remoteInformation = githubRepoInfo | ||
| ? repository.state.remotes.find(remote => | ||
| githubRepoInfo.remoteUrl === remote.fetchUrl || githubRepoInfo.remoteUrl === remote.pushUrl) | ||
| : undefined; | ||
|
|
||
| if (!githubRepoInfo || !remoteInformation) { | ||
| throw new Error(l10n.t('Could not determine the GitHub remote for branch \'{0}\'.', branchName)); | ||
| } | ||
|
|
||
| // Push the branch (set upstream when missing). | ||
| const head = repository.state.HEAD; | ||
| const setUpstream = !head?.upstream; | ||
| await repository.push(remoteInformation.name, branchName, setUpstream); | ||
|
|
||
| if (token.isCancellationRequested) { | ||
| return undefined; | ||
| } |
Comment on lines
+168
to
+179
| } | ||
|
|
||
| const diffChanges = await repository.diffBetweenWithStats(mergeBase, branchName) ?? []; | ||
| const patches: Diff[] = []; | ||
| for (const change of diffChanges) { | ||
| const patch = await repository.diffBetweenPatch(mergeBase, branchName, change.uri.fsPath); | ||
| if (!patch) { | ||
| continue; | ||
| } | ||
|
|
||
| patches.push({ ...change, diff: patch }); | ||
| } |
| head, | ||
| base, | ||
| draft, | ||
| }); |
Comment on lines
+57
to
+134
| async createPullRequest(options: CreatePullRequestOptions, token: vscode.CancellationToken): Promise<string | undefined> { | ||
| const { repositoryUri, branchName, baseBranchName, isDraft } = options; | ||
|
|
||
| const repository = await this.gitService.openRepository(repositoryUri); | ||
| const repositoryContext = await this.gitService.getRepository(repositoryUri); | ||
|
|
||
| if (!repository || !repositoryContext) { | ||
| throw new Error(l10n.t('Could not find the repository for branch \'{0}\'.', branchName)); | ||
| } | ||
|
|
||
| // Resolve owner/repo from the (preferred upstream's) remote. | ||
| const githubRepoInfo = getGitHubRepoInfoFromContext(repositoryContext); | ||
| const remoteInformation = githubRepoInfo | ||
| ? repository.state.remotes.find(remote => | ||
| githubRepoInfo.remoteUrl === remote.fetchUrl || githubRepoInfo.remoteUrl === remote.pushUrl) | ||
| : undefined; | ||
|
|
||
| if (!githubRepoInfo || !remoteInformation) { | ||
| throw new Error(l10n.t('Could not determine the GitHub remote for branch \'{0}\'.', branchName)); | ||
| } | ||
|
|
||
| // Push the branch (set upstream when missing). | ||
| const head = repository.state.HEAD; | ||
| const setUpstream = !head?.upstream; | ||
| await repository.push(remoteInformation.name, branchName, setUpstream); | ||
|
|
||
| if (token.isCancellationRequested) { | ||
| return undefined; | ||
| } | ||
|
|
||
| // Collect commits and patches. | ||
| const context = await collectPullRequestContext(repository, baseBranchName, branchName, token); | ||
| if (token.isCancellationRequested) { | ||
| return undefined; | ||
| } | ||
|
|
||
| let title: string | undefined; | ||
| let description: string | undefined; | ||
| if (context && (context.commitMessages.length > 0 || context.patches.length > 0)) { | ||
| const generator = this.instantiationService.createInstance(GitHubPullRequestTitleAndDescriptionGenerator); | ||
| try { | ||
| const result = await generator.provideTitleAndDescription({ | ||
| commitMessages: context.commitMessages, | ||
| patches: context.patches.map(p => p.diff), | ||
| compareBranch: branchName, | ||
| }, token); | ||
|
|
||
| title = result?.title; | ||
| description = result?.description; | ||
| } finally { | ||
| generator.dispose(); | ||
| } | ||
| } | ||
|
|
||
| if (token.isCancellationRequested) { | ||
| return undefined; | ||
| } | ||
|
|
||
| // Base branch name may contain the remote name as a prefix, so we | ||
| // need to remove it since the API expects just the branch name. | ||
| const normalizedBaseBranchName = baseBranchName.replace( | ||
| new RegExp(`^${escapeRegExpCharacters(remoteInformation.name)}/`), | ||
| '' | ||
| ); | ||
|
|
||
| const createdPullRequest = await this.octoKitService.createPullRequest( | ||
| githubRepoInfo.id.org, | ||
| githubRepoInfo.id.repo, | ||
| title ?? branchName, | ||
| description ?? '', | ||
| branchName, | ||
| normalizedBaseBranchName, | ||
| isDraft, | ||
| {}, | ||
| ); | ||
|
|
||
| return createdPullRequest.url; | ||
| } |
lszomoru
added a commit
that referenced
this pull request
May 8, 2026
* Agents - switch pull request actions away from the agentic loop * Fix compilation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.