agentHostPullRequestOperationHandler.ts ×16

Frontier kind: Code frontier

unlabeled · c_e4f1185796a5

498 tests · 23569 LOC · 104 files · introduces 0 tests · 113 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
16 ranges113 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1951 ranges23569 lines · 104 files · Browse complete extent
All tests (intent)
498 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 113 introduced LOC across 16 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/agentHost/node/agentHostPullRequestOperationHandler.ts 113 introduced LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- agentHostPullRequestOperationHandler.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationToken } from '../../../base/common/cancellation.js';
7 > import { URI } from '../../../base/common/uri.js';
8 > import { localize } from '../../../nls.js';
9 > import { IAgentService } from '../common/agentService.js';
10 > import { IAgentHostGitHubEndpointService } from './agentHostGitHubEndpointService.js';
11 > import { parseChangesetUri } from '../common/changesetUri.js';
12 > import { AHP_AUTH_REQUIRED, AHP_SESSION_NOT_FOUND, JsonRpcErrorCodes, ProtocolError } from '../common/state/sessionProtocol.js';
13 > import { readSessionGitHubState, readSessionGitState, type ChangesetOperationFollowUp, type ISessionFileDiff, type ISessionWithDefaultChat } from '../common/state/sessionState.js';
14 > import { ILogService } from '../../log/common/log.js';
15 > import { IAgentHostGitService } from '../common/agentHostGitService.js';
16 > import { type IChangesetOperationHandler } from '../common/agentHostChangesetOperationService.js';
17 > import { type AutoMergeMethod, type CreatedPullRequest, IAgentHostOctoKitService } from './shared/agentHostOctoKitService.js';
18 > import type { InvokeChangesetOperationParams, InvokeChangesetOperationResult } from '../common/state/protocol/channels-changeset/commands.js';
19 > import { ICopilotApiService, type ICopilotUtilityChatMessage } from './shared/copilotApiService.js';
20 > import { buildConversationContext } from '../common/agentHostConversationContext.js';
21 >
22 > /**
23 > * Soft upper bound, in characters, for the conversation context fed to the
24 > * utility model when generating a PR title and description. Sized to stay
25 > * within the small model's context window while leaving room for the changed
26 > * file summary and prompt scaffolding.
27 > */
28 > const MAX_PR_CONVERSATION_CONTEXT_CHARS = 12_000;
29 >
30 > /**
31 > * Soft upper bound, in characters, for the changed-file summary fed to the
32 > * utility model when generating a PR title and description.
33 > */
34 > const MAX_PR_CHANGE_SUMMARY_CHARS = 4_000;
35 >
36 > export interface PullRequestCreatedEvent {
37 > readonly sessionKey: string;
38 > readonly pullRequestUrl: string;
39 > }
40 >
41 > /**
42 > * Server-side handler for the `create-pr` and `create-draft-pr` changeset
43 > * operations advertised on git-backed sessions whose working directory has
44 > * a GitHub remote. Operation availability is recomputed by
45 > * `AgentHostChangesetOperationService.updateOperations`.
46 > *
47 > * The flow mirrors the Copilot CLI extension's `createPullRequest` helper
48 > * (`extensions/copilot/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts`):
49 > *
50 > * 1. Resolve session → working directory + current/base branch from
51 > * {@link ISessionGitState}.
52 > * 2. Commit any uncommitted working-tree changes.
53 > * 3. Push the current branch to `origin` (with `--set-upstream` when missing).
54 > * 4. Resolve `owner` / `repo` from {@link ISessionGitState.githubOwner}
55 > * / {@link ISessionGitState.githubRepo} (populated by the git probe).
56 > * 5. Reuse an existing PR for the branch, or POST `/repos/{owner}/{repo}/pulls`
57 > * via {@link IAgentHostOctoKitService}.
58 > * 6. Return the PR URL as an {@link InvokeChangesetOperationResult.followUp}.
59 > */
60 > export class AgentHostPullRequestOperationHandler implements IChangesetOperationHandler {
61 >
62 > public static readonly OPERATION_CREATE_PR = 'create-pr';
63 > public static readonly OPERATION_CREATE_DRAFT_PR = 'create-draft-pr';
64 > public static readonly OPERATION_CREATE_PR_AUTO_MERGE = 'create-pr-auto-merge';
65 > public static readonly OPERATION_CREATE_PR_AUTO_SQUASH = 'create-pr-auto-squash';
66 > public static readonly OPERATION_CREATE_PR_AUTO_REBASE = 'create-pr-auto-rebase';
67 >
68 > constructor(
69 private readonly _draft: boolean,
70 private readonly _autoMergeMethod: AutoMergeMethod | undefined,
78 @ILogService private readonly _logService: ILogService,
79 ) { }
81 > async invoke(params: InvokeChangesetOperationParams, token: CancellationToken): Promise<InvokeChangesetOperationResult> {
82 const abortController = new AbortController();
83 if (token.isCancellationRequested) {
91 }
92 }
94 > private async _invoke(params: InvokeChangesetOperationParams, token: CancellationToken, signal: AbortSignal): Promise<InvokeChangesetOperationResult> {
95 const parsed = parseChangesetUri(params.channel);
96 if (!parsed) {
221 return await this._finalize(created, false, sessionUri, gitHubState.owner, gitHubState.repo, authToken, signal, token);
222 }
224 > /**
225 > * Notifies listeners that the pull request now exists, optionally enables
226 > * auto-merge with the configured {@link AutoMergeMethod} (best-effort: a
227 > * failure to enable auto-merge does not fail the operation), and builds the
228 > * result message describing what happened.
229 > */
230 > private async _finalize(
231 pr: CreatedPullRequest,
232 isExisting: boolean,
266 return this._createResult(pr, this._buildMessage(pr, isExisting, autoMergeOutcome, autoMergeError));
267 }
269 > private _buildMessage(pr: CreatedPullRequest, isExisting: boolean, autoMergeOutcome: 'none' | 'enabled' | 'failed', autoMergeError: string | undefined): string {
270 let mergeMethodLabel: string | undefined;
271 switch (this._autoMergeMethod) {
303 }
304 }
306 > private _throwIfCancelled(token: CancellationToken): void {
307 if (token.isCancellationRequested) {
308 throw new ProtocolError(JsonRpcErrorCodes.InternalError, localize('agentHost.changeset.pr.cancelled', "Pull request operation was cancelled."));
309 }
310 }
312 > private _formatTitle(branchName: string): string {
313 // Beautify a branch name like `feat/foo-bar` into `feat: foo bar`.
314 const idx = branchName.indexOf('/');
320 return branchName.replace(/[-_]+/g, ' ');
321 }
323 > private _formatCommitMessage(branchName: string): string {
324 return localize('agentHost.changeset.pr.commitMessage', "Agent Host changes for {0}", branchName);
325 }
327 > private _formatBody(branchName: string, baseBranchName: string): string {
328 return localize('agentHost.changeset.pr.body', "Created from `{0}` targeting `{1}`.", branchName, baseBranchName);
329 }
331 > /**
332 > * Best-effort generation of a PR title and description using the utility
333 > * model. The model is given the main session conversation (only the
334 > * markdown text of user requests and agent responses — tool calls,
335 > * subagents, and reasoning are excluded and the text is character-bounded)
336 > * along with a summary of the changed files. Returns `undefined` when no
337 > * Copilot token is available or generation fails, so the caller can fall
338 > * back to the branch-name based title/description. PR creation must never
339 > * fail just because the model is unavailable.
340 > */
341 > private async _generateTitleAndDescription(
342 sessionState: ISessionWithDefaultChat,
343 branchName: string,
376 }
377 }
379 > private _buildTitleAndDescriptionPrompt(branchName: string, base: string, conversation: string | undefined, changeSummary: string): ICopilotUtilityChatMessage[] {
380 const userSections: string[] = [
381 `Branch: ${branchName}`,
405 ];
406 }
408 > private _summarizeDiffsForPrompt(diffs: readonly ISessionFileDiff[]): string {
409 const lines: string[] = [];
410 let length = 0;
441 }
442 }
444 > private _parseTitleAndDescription(raw: string): { title: string; description: string } | undefined {
445 let text = raw.trim().replace(/\r\n/g, '\n');
446 const fenced = /^```(?:markdown|md|text)?\s*([\s\S]*?)\s*```$/i.exec(text);
474 return { title, description };
475 }
477 > private _createResult(created: { readonly url: string; readonly number: number }, message: string): InvokeChangesetOperationResult {
478 const followUp: ChangesetOperationFollowUp = {
479 content: { uri: created.url, contentType: 'text/html' },