diff --git a/packages/core/src/scheduler/policy.test.ts b/packages/core/src/scheduler/policy.test.ts index 34491f788c..057666aada 100644 --- a/packages/core/src/scheduler/policy.test.ts +++ b/packages/core/src/scheduler/policy.test.ts @@ -813,6 +813,33 @@ describe('policy.ts', () => { }), ); }); + + it('should map ProceedAlways to ProceedOnce in Plan Mode', async () => { + const mockConfig = { + getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.PLAN), + setApprovalMode: vi.fn(), + getSessionId: vi.fn().mockReturnValue('test-session-id'), + } as unknown as Mocked; + (mockConfig as unknown as { config: Config }).config = + mockConfig as Config; + const mockMessageBus = { + publish: vi.fn(), + } as unknown as Mocked; + (mockConfig as unknown as { messageBus: MessageBus }).messageBus = + mockMessageBus; + const tool = { name: 'replace' } as AnyDeclarativeTool; + + await updatePolicy( + tool, + ToolConfirmationOutcome.ProceedAlways, + undefined, + mockConfig, + mockMessageBus, + ); + + expect(mockConfig.setApprovalMode).not.toHaveBeenCalled(); + expect(mockMessageBus.publish).not.toHaveBeenCalled(); + }); }); describe('getPolicyDenialError', () => { diff --git a/packages/core/src/scheduler/policy.ts b/packages/core/src/scheduler/policy.ts index 71c5640db9..c5d12ccbdb 100644 --- a/packages/core/src/scheduler/policy.ts +++ b/packages/core/src/scheduler/policy.ts @@ -121,6 +121,14 @@ export async function updatePolicy( ): Promise { const currentMode = context.config.getApprovalMode(); + // If in Plan Mode, map 'Proceed Always' (Allow for this session) to 'Proceed Once' (Allow once) + // to prevent transitioning to AUTO_EDIT mode and updating policy. + if ( + currentMode === ApprovalMode.PLAN && + outcome === ToolConfirmationOutcome.ProceedAlways + ) { + outcome = ToolConfirmationOutcome.ProceedOnce; + } // Mode Transitions (AUTO_EDIT) if (isAutoEditTransition(tool, outcome)) { context.config.setApprovalMode(ApprovalMode.AUTO_EDIT);