Skip to content

Commit f7696ee

Browse files
justschenCopilot
andauthored
plan widget fixes (#312631)
* plan widget fixes * Update extensions/copilot/src/extension/chatSessions/copilotcli/node/exitPlanModeHandler.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update plan review action label tests to match new labels --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 6743fb4 commit f7696ee

4 files changed

Lines changed: 31 additions & 23 deletions

File tree

extensions/copilot/src/extension/chatSessions/copilotcli/node/exitPlanModeHandler.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import { IToolsService } from '../../../tools/common/toolsService';
1717
type ExitPlanModeActionType = Parameters<NonNullable<SessionOptions['onExitPlanMode']>>[0]['actions'][number];
1818

1919
const actionDescriptions: Record<ExitPlanModeActionType, { label: string; description: string }> = {
20-
'autopilot': { label: 'Autopilot', description: l10n.t('Auto-approve all tool calls and continue until the task is done') },
21-
'interactive': { label: 'Approve and Implement', description: l10n.t('Let the agent continue in interactive mode, asking for input and approval for each action.') },
22-
'exit_only': { label: 'Approve', description: l10n.t('Approve plan, but do not execute the plan. I will execute the plan myself.') },
23-
'autopilot_fleet': { label: 'Autopilot Fleet', description: l10n.t('Auto-approve all tool calls, including fleet management actions, and continue until the task is done.') },
20+
'autopilot': { label: l10n.t("Implement with Autopilot"), description: l10n.t('Auto-approve all tool calls and continue until the task is done.') },
21+
'autopilot_fleet': { label: l10n.t("Implement with Autopilot Fleet"), description: l10n.t('Auto-approve all tool calls, including fleet management actions, and continue until the task is done.') },
22+
'interactive': { label: l10n.t("Implement Plan"), description: l10n.t('Implement the plan, asking for input and approval for each action.') },
23+
'exit_only': { label: l10n.t("Approve Plan Only"), description: l10n.t('Approve the plan without executing it. I will implement it myself.') },
2424
};
2525

2626
/**

extensions/copilot/src/extension/chatSessions/copilotcli/node/test/exitPlanModeHandler.spec.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,43 +197,43 @@ describe('handleExitPlanMode', () => {
197197
});
198198

199199
it('returns approved with selected action mapped from label', async () => {
200-
toolService.setResult({ rejected: false, action: 'Autopilot' });
200+
toolService.setResult({ rejected: false, action: 'Implement with Autopilot' });
201201
const event = makeEvent();
202202
const result = await handleExitPlanMode(event, session as unknown as Session, 'interactive', FAKE_TOKEN, workspaceService, logService, toolService, CANCEL_TOKEN);
203203
expect(result).toEqual<ExitPlanModeResponse>({ approved: true, selectedAction: 'autopilot', autoApproveEdits: undefined });
204204
});
205205

206-
it('maps "Approve and exit" label to exit_only', async () => {
207-
toolService.setResult({ rejected: false, action: 'Approve' });
206+
it('maps "Approve Plan Only" label to exit_only', async () => {
207+
toolService.setResult({ rejected: false, action: 'Approve Plan Only' });
208208
const event = makeEvent();
209209
const result = await handleExitPlanMode(event, session as unknown as Session, 'interactive', FAKE_TOKEN, workspaceService, logService, toolService, CANCEL_TOKEN);
210210
expect(result.selectedAction).toBe('exit_only');
211211
});
212212

213213
it('sets autoApproveEdits when permissionLevel is autoApprove', async () => {
214-
toolService.setResult({ rejected: false, action: 'Interactive' });
214+
toolService.setResult({ rejected: false, action: 'Implement Plan' });
215215
const event = makeEvent();
216216
const result = await handleExitPlanMode(event, session as unknown as Session, 'autoApprove', FAKE_TOKEN, workspaceService, logService, toolService, CANCEL_TOKEN);
217217
expect(result.autoApproveEdits).toBe(true);
218218
});
219219

220220
it('does not set autoApproveEdits when permissionLevel is interactive', async () => {
221-
toolService.setResult({ rejected: false, action: 'Interactive' });
221+
toolService.setResult({ rejected: false, action: 'Implement Plan' });
222222
const event = makeEvent();
223223
const result = await handleExitPlanMode(event, session as unknown as Session, 'interactive', FAKE_TOKEN, workspaceService, logService, toolService, CANCEL_TOKEN);
224224
expect(result.autoApproveEdits).toBeUndefined();
225225
});
226226

227227
it('passes actions with labels and recommended flag to tool', async () => {
228-
toolService.setResult({ rejected: false, action: 'Interactive' });
228+
toolService.setResult({ rejected: false, action: 'Implement Plan' });
229229
const event = makeEvent({ actions: ['autopilot', 'exit_only'], recommendedAction: 'exit_only' });
230230
await handleExitPlanMode(event, session as unknown as Session, 'interactive', FAKE_TOKEN, workspaceService, logService, toolService, CANCEL_TOKEN);
231231
const call = toolService.invokeToolCalls[0];
232232
expect(call.name).toBe('vscode_reviewPlan');
233233
const input = call.input as any;
234234
expect(input.actions).toHaveLength(2);
235-
expect(input.actions[0]).toEqual(expect.objectContaining({ label: 'Autopilot', default: false }));
236-
expect(input.actions[1]).toEqual(expect.objectContaining({ label: 'Approve', default: true }));
235+
expect(input.actions[0]).toEqual(expect.objectContaining({ label: 'Implement with Autopilot', default: false }));
236+
expect(input.actions[1]).toEqual(expect.objectContaining({ label: 'Approve Plan Only', default: true }));
237237
});
238238

239239
it('includes plan path in tool input when plan path exists', async () => {

src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatPlanReviewPart.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -322,24 +322,28 @@ export class ChatPlanReviewPart extends Disposable implements IChatContentPart {
322322
}
323323
this._buttonStore.add(approveButton.onDidClick(() => this.submitApproval(primary)));
324324

325+
// Reject button (grey secondary) immediately after the approve button
326+
// so the primary Approve / Reject pair stays grouped together —
327+
// omitted in the collapsed title bar (parity with
328+
// chatToolConfirmationCarouselPart which only surfaces the primary
329+
// action when collapsed).
330+
if (includeReject) {
331+
const rejectButton = new Button(container, { ...defaultButtonStyles, secondary: true });
332+
rejectButton.label = localize('chat.planReview.reject', 'Reject');
333+
this._buttonStore.add(rejectButton);
334+
this._buttonStore.add(rejectButton.onDidClick(() => this.submitRejection()));
335+
}
336+
325337
// Provide Feedback button (grey secondary) — shown only when feedback
326-
// is enabled and we are not in collapsed mode.
338+
// is enabled and we are not in collapsed mode. Right-aligned via CSS
339+
// so the primary Approve / Reject pair stays grouped on the left.
327340
if (this.review.canProvideFeedback && includeReject) {
328341
const feedbackButton = new Button(container, { ...defaultButtonStyles, secondary: true });
342+
feedbackButton.element.classList.add('chat-plan-review-feedback-button');
329343
feedbackButton.label = localize('chat.planReview.provideFeedback', 'Provide Feedback');
330344
this._buttonStore.add(feedbackButton);
331345
this._buttonStore.add(feedbackButton.onDidClick(() => this.enterFeedbackMode()));
332346
}
333-
334-
// Reject button (grey secondary) after the approve button — omitted in
335-
// the collapsed title bar (parity with chatToolConfirmationCarouselPart
336-
// which only surfaces the primary action when collapsed).
337-
if (includeReject) {
338-
const rejectButton = new Button(container, { ...defaultButtonStyles, secondary: true });
339-
rejectButton.label = localize('chat.planReview.reject', 'Reject');
340-
this._buttonStore.add(rejectButton);
341-
this._buttonStore.add(rejectButton.onDidClick(() => this.submitRejection()));
342-
}
343347
}
344348

345349
private toggleCollapsed(): void {

src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatPlanReview.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@
273273
align-items: center;
274274
}
275275

276+
.interactive-session .chat-plan-review-container > .chat-confirmation-widget2.chat-plan-review > .chat-confirmation-widget-buttons.chat-plan-review-footer .chat-buttons .monaco-button.chat-plan-review-feedback-button {
277+
margin-left: auto;
278+
}
279+
276280
/* ---------- Hidden helper ---------- */
277281
.interactive-session .chat-plan-review-container .monaco-button.chat-plan-review-hidden {
278282
display: none;

0 commit comments

Comments
 (0)