feat(acp): support set_mode interface (#18890) (#18891)

Co-authored-by: Mervap <megavaprold@gmail.com>
This commit is contained in:
Valery Teplyakov
2026-02-19 17:07:46 +01:00
committed by GitHub
parent b79e5ce56d
commit 966eef14ee
3 changed files with 184 additions and 2 deletions
@@ -35,6 +35,7 @@ import {
import { loadCliConfig, type CliArgs } from '../config/config.js';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { ApprovalMode } from '@google/gemini-cli-core/src/policy/types.js';
vi.mock('../config/config.js', () => ({
loadCliConfig: vi.fn(),
@@ -119,6 +120,8 @@ describe('GeminiAgent', () => {
subscribe: vi.fn(),
unsubscribe: vi.fn(),
}),
getApprovalMode: vi.fn().mockReturnValue('default'),
isPlanEnabled: vi.fn().mockReturnValue(false),
} as unknown as Mocked<Awaited<ReturnType<typeof loadCliConfig>>>;
mockSettings = {
merged: {
@@ -185,6 +188,59 @@ describe('GeminiAgent', () => {
expect(mockConfig.getGeminiClient).toHaveBeenCalled();
});
it('should return modes without plan mode when plan is disabled', async () => {
mockConfig.getContentGeneratorConfig = vi.fn().mockReturnValue({
apiKey: 'test-key',
});
mockConfig.isPlanEnabled = vi.fn().mockReturnValue(false);
mockConfig.getApprovalMode = vi.fn().mockReturnValue('default');
const response = await agent.newSession({
cwd: '/tmp',
mcpServers: [],
});
expect(response.modes).toEqual({
availableModes: [
{ id: 'default', name: 'Default', description: 'Prompts for approval' },
{
id: 'autoEdit',
name: 'Auto Edit',
description: 'Auto-approves edit tools',
},
{ id: 'yolo', name: 'YOLO', description: 'Auto-approves all tools' },
],
currentModeId: 'default',
});
});
it('should return modes with plan mode when plan is enabled', async () => {
mockConfig.getContentGeneratorConfig = vi.fn().mockReturnValue({
apiKey: 'test-key',
});
mockConfig.isPlanEnabled = vi.fn().mockReturnValue(true);
mockConfig.getApprovalMode = vi.fn().mockReturnValue('plan');
const response = await agent.newSession({
cwd: '/tmp',
mcpServers: [],
});
expect(response.modes).toEqual({
availableModes: [
{ id: 'default', name: 'Default', description: 'Prompts for approval' },
{
id: 'autoEdit',
name: 'Auto Edit',
description: 'Auto-approves edit tools',
},
{ id: 'yolo', name: 'YOLO', description: 'Auto-approves all tools' },
{ id: 'plan', name: 'Plan', description: 'Read-only mode' },
],
currentModeId: 'plan',
});
});
it('should fail session creation if Gemini API key is missing', async () => {
(loadSettings as unknown as Mock).mockImplementation(() => ({
merged: {
@@ -306,6 +362,32 @@ describe('GeminiAgent', () => {
expect(session.prompt).toHaveBeenCalled();
expect(result).toEqual({ stopReason: 'end_turn' });
});
it('should delegate setMode to session', async () => {
await agent.newSession({ cwd: '/tmp', mcpServers: [] });
const session = (
agent as unknown as { sessions: Map<string, Session> }
).sessions.get('test-session-id');
if (!session) throw new Error('Session not found');
session.setMode = vi.fn().mockReturnValue({});
const result = await agent.setSessionMode({
sessionId: 'test-session-id',
modeId: 'plan',
});
expect(session.setMode).toHaveBeenCalledWith('plan');
expect(result).toEqual({});
});
it('should throw error when setting mode on non-existent session', async () => {
await expect(
agent.setSessionMode({
sessionId: 'unknown',
modeId: 'plan',
}),
).rejects.toThrow('Session not found: unknown');
});
});
describe('Session', () => {
@@ -352,6 +434,8 @@ describe('Session', () => {
getEnableRecursiveFileSearch: vi.fn().mockReturnValue(false),
getDebugMode: vi.fn().mockReturnValue(false),
getMessageBus: vi.fn().mockReturnValue(mockMessageBus),
setApprovalMode: vi.fn(),
isPlanEnabled: vi.fn().mockReturnValue(false),
} as unknown as Mocked<Config>;
mockConnection = {
sessionUpdate: vi.fn(),
@@ -822,4 +906,17 @@ describe('Session', () => {
].value;
expect(mockInstance.build).toHaveBeenCalled();
});
it('should set mode on config', () => {
session.setMode(ApprovalMode.AUTO_EDIT);
expect(mockConfig.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.AUTO_EDIT,
);
});
it('should throw error for invalid mode', () => {
expect(() => session.setMode('invalid-mode')).toThrow(
'Invalid or unavailable mode: invalid-mode',
);
});
});