Align Cloud Agent with Mission Control PR-decouple behavior#5078
Align Cloud Agent with Mission Control PR-decouple behavior#5078Oregand wants to merge 3 commits into
Conversation
The VS Code Cloud Agent experience was out of sync with Mission Control's CCA PR-decoupling flow. This aligns VS Code with MC behavior: - Drop the delegate / commit / push modal. Codex and Claude don't show one; Mission Control doesn't either. Permissive GitHub auth now uses the standard non-modal sign-in prompt via createIfNone. - Send the raw user prompt as problem_statement (no chat-history summary injection). This fixes the 'agent invents its own prompt' behavior Luke reported. - Send a minimal pull_request block (title/base_ref/[head_ref]) to match what sweagentd + Mission Control send. Drop body_placeholder and the 'Created from VS Code' body_suffix. - Drop the pre-delegation git preflight (uncommitted-changes detection, remote base-branch check, branch push). delegate() already falls back to the repo's default_branch when base_ref is not supplied. - Delete the now-unreachable CopilotCloudGitOperationsManager and the unused body_suffix / formatBodyPlaceholder helpers. Server-side verified: sweagentd v1 API (docs/adr/0001-create-job-api.md) makes pull_request optional and returns job_id + session_id before the PR exists; Mission Control's sweagentd client sends the minimal pull_request shape used here. Full no-PR (non-PR) workflow is on the sweagentd roadmap; this change is compatible with it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Aligns the VS Code Copilot Cloud Agent delegation flow with Mission Control’s PR-decoupling behavior by removing pre-delegation UI/preflight work and sending a minimal job payload (raw user prompt + resolved references, minimal pull_request block).
Changes:
- Removes the delegate/commit/push confirmation modal and associated git preflight / workspace-state tracking.
- Stops summarizing chat history for
problem_statement; sends the raw prompt with resolved references only. - Simplifies the
pull_requestpayload totitle,base_ref, and optionalhead_ref, and deletes now-unreachable git operations code.
Show a summary per file
| File | Description |
|---|---|
src/extension/chatSessions/vscode/copilotCodingAgentUtils.ts |
Removes PR body placeholder/suffix helpers now that cloud sessions no longer send those fields. |
src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts |
Removes confirmation + git preflight flow; delegates directly, removes history summarization, and trims pull_request payload fields. |
src/extension/chatSessions/vscode-node/copilotCloudGitOperationsManager.ts |
Deletes unreachable git operations helper previously used by the modal/preflight path. |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 1
| await this._authenticationService.getGitHubSession('permissive', { createIfNone: { detail: l10n.t('Sign in to GitHub with additional permissions to use Copilot cloud sessions.') } }); | ||
| } catch (error) { | ||
| this.logService.error(`Authorization failed: ${error}`); | ||
| throw new Error(vscode.l10n.t('Authorization failed. Please sign into GitHub and try again.')); | ||
| } |
There was a problem hiding this comment.
The new permissive-auth path treats any error from getGitHubSession('permissive', { createIfNone: … }) as an authorization failure. getGitHubSession can throw a cancellation error when the user dismisses the sign-in prompt, so this will surface as an error instead of cleanly cancelling the request. Consider handling isCancellationError(error) (and returning {} / streaming a cancellation message) separately, and log via logService.error(error, 'Authorization failed') rather than interpolating the error into the message (per ILogService.error guidance).
There was a problem hiding this comment.
Good catch, both addressed in aecc4a7:
- Cancellation is now re-thrown as a
CancellationErrorwhenisCancellationError(error)(ortoken.isCancellationRequested) so a dismissed sign-in prompt cancels cleanly instead of surfacing as an auth failure. - Switched to
logService.error(error, 'Authorization failed')per theILogService.error(error: string | Error, message?: string)signature.
Address review feedback on PR microsoft#5078: - Handle isCancellationError (and token.isCancellationRequested) by throwing CancellationError instead of the generic auth-failed error, so user-dismissed sign-in prompts cancel cleanly. - Use logService.error(error, 'Authorization failed') per ILogService guidance rather than interpolating the error into the message string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Next logical PR: migrate to Mission Control's public Tasks APIToday (both before and after this PR): VS Code calls What MC ships today: create_pull_request:
type: boolean
default: false # ← public callers get no-PR by default// handler_task.go
// CreatePullRequest precedence:
// - Use the explicit request value when provided.
// - Otherwise default to false for public API requests.
// - Otherwise default to true for internal requests.Session identity is a Proposed follow-up (stacked on this PR)
That PR is the one that delivers full PR-decoupling end-to-end — this PR is the minimum viable step to stop the modal/preflight/summary work that's misaligned with Mission Control's contract today. |
|
@microsoft-github-policy-service agree company="GitHub" |
Follow-up to the initial alignment commit. Independent review caught four real issues; this addresses all of them: 1. Current remote-tracking branch was being ignored. delegate() now checks RepoContext.upstreamBranchName — if the user's checked-out branch is pushed to remote, use it as base_ref. If it's unpushed, fall back to default_branch and emit a progress notice so the user knows which branch the agent will start from. 2. Auth cancellation was only handled in chatParticipantImpl, leaving CLI /delegate callers (copilotCLIChatSessions.ts, copilotCLIChatSessionsContribution.ts) with a generic "invalid response" error when the user dismissed the sign-in prompt. Moved the permissive-session acquisition + CancellationError normalization into delegate() itself so every caller benefits. 3. Uncommitted-changes warning now fires from the shared delegate() path, covering both staged (indexChanges) and unstaged (workingTree) changes. Aligns with Mission Control / Codex / Claude behavior — warn the user, but don't block. 4. Base-branch fallback is no longer silent: when we fall back to default_branch because the current branch isn't on remote, emit a stream.progress message telling the user what will happen. Also fixed the existing CLI-path uncommitted-changes checks in copilotCLIChatSessions.ts:1463 and copilotCLIChatSessionsContribution.ts:1742 which only looked at indexChanges; now they check workingTree too. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Did an independent critique pass after the initial round and it caught four real regressions that the first commit introduced. Pushed 1. Current remote-tracking branch was being ignored. The old preflight path used the user's checked-out branch as 2. Auth cancellation was only normalized in 3. Uncommitted-changes warning. Main path warned on neither; the CLI paths only checked 4. Silent base-branch fallback. When we fall back to Judgment calls I deliberately didn't revert:
474 tests still pass, tsc + eslint clean. |
|
Thanks for the contribution! This repository has been archived because the project has moved into the main VS Code repository. Could you please reopen/recreate this PR against: We’ll continue reviewing contributions there. Thanks! |
VS Code's Cloud Agent flow was still doing a bunch of pre-PR work that Mission Control / CCA PR Decoupling no longer need.
Problems
copilot-instructions.mdand appears to invent its own prompt.The "strange prompt" behavior was caused by us summarizing the chat history and appending a
body_suffixtoproblem_statement/pull_request.body_suffixbefore sending. Mission Control sends the raw prompt with a minimalpull_requestblock; this change matches that.Changes
chatParticipantImplgoes straight todelegate(). Permissive GitHub auth now kicks in via the standard non-modalcreateIfNonesign-in prompt.IChatDelegationSummaryService.summarize(andtrackSummaryUsage) on the delegation path.problem_statementis now the user prompt + resolved references only.extractPromptusage for reading back PR bodies is unchanged.pull_requestpayload. Dropbody_placeholderandbody_suffix— keeptitle,base_ref, and optionalhead_ref. Matchesinternal/sweagentd/sweagentd.goingithub/copilot-mission-control.detectedUncommittedChanges,checkBaseBranchPresentOnRemote,buildConfirmation, andhandleConfirmationData.delegate()already falls back to the repo'sdefault_branchwhenbase_refisn't supplied.CopilotCloudGitOperationsManageris now unreachable — removed the whole file.body_suffix/formatBodyPlaceholderremoved fromcopilotCodingAgentUtils.ts.SEEN_DELEGATION_PROMPT_KEY,validateMetadata,hasHistoryToSummarize, and the modal string constants are also gone.Regressions fixed during review
After self-audit + independent critique, four behavior regressions from the first pass were addressed in a follow-up commit:
default_branch, which was broader than the unpushed-WIP case it was meant to cover. Now: if the active repo matches the selected repo andRepoContext.upstreamBranchNameis set (branch exists on remote),headBranchNameis used asbase_ref. Only unpushed branches fall back.copilotCLIChatSessions.tsandcopilotCLIChatSessionsContribution.tscalldelegate()directly; they would have surfaced a generic "invalid response" error on cancellation. The permissive-auth check now lives insidedelegate()itself so every caller benefits.indexChanges(staged); the main path warned on neither.delegate()and both CLI handlers now check bothworkingTreeandindexChanges.default_branchinstead of the current branch. Astream.progressnotice now fires when that fallback happens.Server-side verification
docs/adr/0001-create-job-api.md: onlyproblem_statementis required;pull_requestis[Optional]; v1POST /jobsreturns{job_id, session_id, actor, created_at, updated_at}immediately, without waiting for the PR.internal/sweagentd/sweagentd.go: MC sendspull_request = {title, base_ref, head_ref}only — nobody_placeholder, nobody_suffix, no history/summary injection inproblem_statement. This is our target behavior.Non-PR (no PR at all) workflow is on the sweagentd roadmap ("Supporting non-PR based workflow is on our radar but didn't get a chance to look into this yet"). This PR is forward-compatible: once the server supports it, we can drop
pull_requestentirely from the payload.Tests
npx vitest run src/extension/chatSessions/vscode-node/test/— 474 passed, 3 skipped, 14 test files.npx tsc -p tsconfig.json --noEmit— clean.npx eslinton the changed files — clean.Existing spec (
copilotCloudSessionsProvider.spec.ts) covers helpers +ChatSessionContentBuilder; dedicated coverage for the newdelegate()paths (auth cancellation, upstream-branch detection, uncommitted-changes warning) is flagged as a follow-up rather than being bundled here.Follow-ups (separate PRs)
/jobsto Mission Control's public Tasks API (POST /repos/{owner}/{repo}/tasks), which defaultscreate_pull_request: falsefor public callers — aligning us with the non-PR workflow once@vscode/copilot-apiexposes a client. OpenAPI confirms the default inapi/generated-sessions-openapi.yaml.session-by-idURI scheme and aSessionIdForJobresource type, so nothing in the provider assumes a PR ever exists. Waits on sweagentd.waitForJobWithPullRequestwait indelegate()in a follow-up once we're happy to return a streamed/placeholderChatResponsePullRequestPart.delegate()paths (auth cancellation, upstream branch detection, uncommitted-changes warning).Risk
Low. All touched code was reachable only from the modal path we're removing, from the summary path we're removing, or from the rich-PR-payload path that MC already proves is unnecessary. The authenticated (
permissiveGitHubSession) pre-check keeps the same auth requirement — just without the modal wrapper.