feat(plan): add "Yes, restore previous mode" option when exiting plan mode (#3008)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run

* feat(plan): add "Yes, restore previous mode" option when exiting plan mode

When exiting plan mode, users previously had no way to restore their
original approval mode (e.g. YOLO). Add a new default option that
restores the pre-plan approval mode, with a dynamic label showing
which mode will be restored.

Closes #3002

* test: add fallback test for RestorePrevious when no prePlanMode recorded

* fix: handle RestorePrevious in telemetry and ACP mode notification

- Add RestorePrevious to telemetry decision mapping as ACCEPT
- Fix sendCurrentModeUpdateNotification to read actual mode for
  RestorePrevious instead of defaulting to 'default'

* test: add plan confirmation tests for RestorePrevious in permissionUtils
This commit is contained in:
zhangxy-zju 2026-04-09 14:25:38 +08:00 committed by GitHub
parent 1356c05e3f
commit 9c0bbfba6c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 142 additions and 0 deletions

View file

@ -697,6 +697,10 @@ export class Session implements SessionContext {
case ToolConfirmationOutcome.ProceedAlways:
newModeId = 'auto-edit';
break;
case ToolConfirmationOutcome.RestorePrevious:
// onConfirm has already restored the mode; read the actual current mode
newModeId = this.config.getApprovalMode() as ApprovalModeValue;
break;
case ToolConfirmationOutcome.ProceedOnce:
default:
newModeId = 'default';
@ -1045,6 +1049,7 @@ export class Session implements SessionContext {
case ToolConfirmationOutcome.ProceedAlwaysServer:
case ToolConfirmationOutcome.ProceedAlwaysTool:
case ToolConfirmationOutcome.ModifyWithEditor:
case ToolConfirmationOutcome.RestorePrevious:
break;
default: {
const resultOutcome: never = outcome;

View file

@ -34,6 +34,49 @@ describe('permissionUtils', () => {
);
});
it('returns plan options with RestorePrevious including prePlanMode', () => {
const options = toPermissionOptions({
type: 'plan',
title: 'Would you like to proceed?',
plan: 'Test plan',
prePlanMode: 'yolo',
onConfirm: async () => undefined,
});
expect(options).toHaveLength(4);
expect(options[0]).toMatchObject({
optionId: ToolConfirmationOutcome.RestorePrevious,
name: 'Yes, restore previous mode (yolo)',
kind: 'allow_once',
});
expect(options[1]).toMatchObject({
optionId: ToolConfirmationOutcome.ProceedAlways,
name: 'Yes, and auto-accept edits',
});
expect(options[2]).toMatchObject({
optionId: ToolConfirmationOutcome.ProceedOnce,
name: 'Yes, and manually approve edits',
});
expect(options[3]).toMatchObject({
optionId: ToolConfirmationOutcome.Cancel,
name: 'No, keep planning (esc)',
});
});
it('defaults prePlanMode to "default" when not provided in plan options', () => {
const options = toPermissionOptions({
type: 'plan',
title: 'Would you like to proceed?',
plan: 'Test plan',
onConfirm: async () => undefined,
});
expect(options[0]).toMatchObject({
optionId: ToolConfirmationOutcome.RestorePrevious,
name: 'Yes, restore previous mode (default)',
});
});
it('falls back to rootCommand when exec permissionRules are unavailable', () => {
const options = toPermissionOptions({
type: 'exec',

View file

@ -171,6 +171,11 @@ export function toPermissionOptions(
);
case 'plan':
return [
{
optionId: ToolConfirmationOutcome.RestorePrevious,
name: `Yes, restore previous mode (${confirmation.prePlanMode ?? 'default'})`,
kind: 'allow_once',
},
{
optionId: ToolConfirmationOutcome.ProceedAlways,
name: 'Yes, and auto-accept edits',

View file

@ -1184,6 +1184,8 @@ export default {
'Always allow for this user': 'Für diesen Benutzer immer erlauben',
'Always allow {{action}} for this user':
'{{action}} für diesen Benutzer immer erlauben',
'Yes, restore previous mode ({{mode}})':
'Ja, vorherigen Modus wiederherstellen ({{mode}})',
'Yes, and auto-accept edits': 'Ja, und Änderungen automatisch akzeptieren',
'Yes, and manually approve edits': 'Ja, und Änderungen manuell genehmigen',
'No, keep planning (esc)': 'Nein, weiter planen (Esc)',

View file

@ -1236,6 +1236,8 @@ export default {
'Always allow for this user': 'Always allow for this user',
'Always allow {{action}} for this user':
'Always allow {{action}} for this user',
'Yes, restore previous mode ({{mode}})':
'Yes, restore previous mode ({{mode}})',
'Yes, and auto-accept edits': 'Yes, and auto-accept edits',
'Yes, and manually approve edits': 'Yes, and manually approve edits',
'No, keep planning (esc)': 'No, keep planning (esc)',

View file

@ -918,6 +918,8 @@ export default {
'このプロジェクトで{{action}}を常に許可',
'Always allow for this user': 'このユーザーに常に許可',
'Always allow {{action}} for this user': 'このユーザーに{{action}}を常に許可',
'Yes, restore previous mode ({{mode}})':
'はい、以前のモードに戻す ({{mode}})',
'Yes, and auto-accept edits': 'はい、編集を自動承認',
'Yes, and manually approve edits': 'はい、編集を手動承認',
'No, keep planning (esc)': 'いいえ、計画を続ける (Esc)',

View file

@ -1190,6 +1190,8 @@ export default {
'Always allow for this user': 'Sempre permitir para este usuário',
'Always allow {{action}} for this user':
'Sempre permitir {{action}} para este usuário',
'Yes, restore previous mode ({{mode}})':
'Sim, restaurar modo anterior ({{mode}})',
'Yes, and auto-accept edits': 'Sim, e aceitar edições automaticamente',
'Yes, and manually approve edits': 'Sim, e aprovar edições manualmente',
'No, keep planning (esc)': 'Não, continuar planejando (esc)',

View file

@ -1114,6 +1114,8 @@ export default {
'Always allow for this user': 'Всегда разрешать для этого пользователя',
'Always allow {{action}} for this user':
'Всегда разрешать {{action}} для этого пользователя',
'Yes, restore previous mode ({{mode}})':
'Да, восстановить предыдущий режим ({{mode}})',
'Yes, and auto-accept edits': 'Да, и автоматически принимать правки',
'Yes, and manually approve edits': 'Да, и вручную подтверждать правки',
'No, keep planning (esc)': 'Нет, продолжить планирование (esc)',

View file

@ -1170,6 +1170,7 @@ export default {
'Always allow {{action}} in this project': '在本项目中总是允许{{action}}',
'Always allow for this user': '对该用户总是允许',
'Always allow {{action}} for this user': '对该用户总是允许{{action}}',
'Yes, restore previous mode ({{mode}})': '是,恢复之前的模式 ({{mode}})',
'Yes, and auto-accept edits': '是,并自动接受编辑',
'Yes, and manually approve edits': '是,并手动批准编辑',
'No, keep planning (esc)': '否,继续规划 (esc)',

View file

@ -302,6 +302,13 @@ export const ToolConfirmationMessage: React.FC<
const planProps = confirmationDetails;
question = planProps.title;
options.push({
key: 'restore-previous',
label: t('Yes, restore previous mode ({{mode}})', {
mode: planProps.prePlanMode ?? 'default',
}),
value: ToolConfirmationOutcome.RestorePrevious,
});
options.push({
key: 'proceed-always',
label: t('Yes, and auto-accept edits'),