mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
fix(acp): resolve agent mode disconnect and improve mode awareness (#26332)
This commit is contained in:
@@ -33,6 +33,10 @@ export class GeminiAgent {
|
|||||||
this.sessionManager = new AcpSessionManager(settings, argv, connection);
|
this.sessionManager = new AcpSessionManager(settings, argv, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
this.sessionManager.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
async initialize(
|
async initialize(
|
||||||
args: acp.InitializeRequest,
|
args: acp.InitializeRequest,
|
||||||
): Promise<acp.InitializeResponse> {
|
): Promise<acp.InitializeResponse> {
|
||||||
|
|||||||
@@ -564,4 +564,26 @@ describe('Session', () => {
|
|||||||
|
|
||||||
expect(result.stopReason).toBe('max_turn_requests');
|
expect(result.stopReason).toBe('max_turn_requests');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should send sessionUpdate when approval mode changes', async () => {
|
||||||
|
const { coreEvents, CoreEvent, ApprovalMode } = await import(
|
||||||
|
'@google/gemini-cli-core'
|
||||||
|
);
|
||||||
|
|
||||||
|
coreEvents.emit(CoreEvent.ApprovalModeChanged, {
|
||||||
|
sessionId: 'session-1',
|
||||||
|
mode: ApprovalMode.PLAN,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockConnection.sessionUpdate).toHaveBeenCalledWith({
|
||||||
|
sessionId: 'session-1',
|
||||||
|
update: {
|
||||||
|
sessionUpdate: 'agent_message_chunk',
|
||||||
|
content: {
|
||||||
|
type: 'text',
|
||||||
|
text: `[MODE_UPDATE] ${ApprovalMode.PLAN}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import {
|
|||||||
type ApprovalMode,
|
type ApprovalMode,
|
||||||
type ConversationRecord,
|
type ConversationRecord,
|
||||||
CoreToolCallStatus,
|
CoreToolCallStatus,
|
||||||
|
coreEvents,
|
||||||
|
CoreEvent,
|
||||||
|
type ApprovalModeChangedPayload,
|
||||||
logToolCall,
|
logToolCall,
|
||||||
convertToFunctionResponse,
|
convertToFunctionResponse,
|
||||||
ToolConfirmationOutcome,
|
ToolConfirmationOutcome,
|
||||||
@@ -69,7 +72,31 @@ export class Session {
|
|||||||
private readonly context: AgentLoopContext,
|
private readonly context: AgentLoopContext,
|
||||||
private readonly connection: acp.AgentSideConnection,
|
private readonly connection: acp.AgentSideConnection,
|
||||||
private readonly settings: LoadedSettings,
|
private readonly settings: LoadedSettings,
|
||||||
) {}
|
) {
|
||||||
|
coreEvents.on(
|
||||||
|
CoreEvent.ApprovalModeChanged,
|
||||||
|
this.handleApprovalModeChanged,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleApprovalModeChanged = (payload: ApprovalModeChangedPayload) => {
|
||||||
|
if (payload.sessionId === this.id) {
|
||||||
|
void this.sendUpdate({
|
||||||
|
sessionUpdate: 'agent_message_chunk',
|
||||||
|
content: {
|
||||||
|
type: 'text',
|
||||||
|
text: `[MODE_UPDATE] ${payload.mode}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
coreEvents.off(
|
||||||
|
CoreEvent.ApprovalModeChanged,
|
||||||
|
this.handleApprovalModeChanged,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async cancelPendingPrompt(): Promise<void> {
|
async cancelPendingPrompt(): Promise<void> {
|
||||||
if (!this.pendingPrompt) {
|
if (!this.pendingPrompt) {
|
||||||
|
|||||||
@@ -48,6 +48,13 @@ export class AcpSessionManager {
|
|||||||
return this.sessions.get(sessionId);
|
return this.sessions.get(sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
for (const session of this.sessions.values()) {
|
||||||
|
session.dispose();
|
||||||
|
}
|
||||||
|
this.sessions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
async newSession(
|
async newSession(
|
||||||
{ cwd, mcpServers }: acp.NewSessionRequest,
|
{ cwd, mcpServers }: acp.NewSessionRequest,
|
||||||
authDetails: AuthDetails,
|
authDetails: AuthDetails,
|
||||||
@@ -183,6 +190,12 @@ export class AcpSessionManager {
|
|||||||
this.connection,
|
this.connection,
|
||||||
this.settings,
|
this.settings,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const existingSession = this.sessions.get(sessionId);
|
||||||
|
if (existingSession) {
|
||||||
|
existingSession.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
this.sessions.set(sessionId, session);
|
this.sessions.set(sessionId, session);
|
||||||
|
|
||||||
// Stream history back to client
|
// Stream history back to client
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ const mockCoreEvents = vi.hoisted(() => ({
|
|||||||
emitConsoleLog: vi.fn(),
|
emitConsoleLog: vi.fn(),
|
||||||
emitQuotaChanged: vi.fn(),
|
emitQuotaChanged: vi.fn(),
|
||||||
on: vi.fn(),
|
on: vi.fn(),
|
||||||
|
emit: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockSetGlobalProxy = vi.hoisted(() => vi.fn());
|
const mockSetGlobalProxy = vi.hoisted(() => vi.fn());
|
||||||
|
|||||||
@@ -2697,17 +2697,18 @@ export class Config implements McpContext, AgentLoopContext {
|
|||||||
this,
|
this,
|
||||||
new ApprovalModeSwitchEvent(currentMode, mode),
|
new ApprovalModeSwitchEvent(currentMode, mode),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
this.policyEngine.setApprovalMode(mode);
|
this.policyEngine.setApprovalMode(mode);
|
||||||
this.refreshSandboxManager();
|
this.refreshSandboxManager();
|
||||||
|
coreEvents.emit(CoreEvent.ApprovalModeChanged, {
|
||||||
|
sessionId: this.getSessionId(),
|
||||||
|
mode,
|
||||||
|
});
|
||||||
|
|
||||||
const isPlanModeTransition =
|
const isPlanModeTransition =
|
||||||
currentMode !== mode &&
|
currentMode === ApprovalMode.PLAN || mode === ApprovalMode.PLAN;
|
||||||
(currentMode === ApprovalMode.PLAN || mode === ApprovalMode.PLAN);
|
|
||||||
const isYoloModeTransition =
|
const isYoloModeTransition =
|
||||||
currentMode !== mode &&
|
currentMode === ApprovalMode.YOLO || mode === ApprovalMode.YOLO;
|
||||||
(currentMode === ApprovalMode.YOLO || mode === ApprovalMode.YOLO);
|
|
||||||
|
|
||||||
if (isPlanModeTransition || isYoloModeTransition) {
|
if (isPlanModeTransition || isYoloModeTransition) {
|
||||||
if (this._geminiClient?.isInitialized()) {
|
if (this._geminiClient?.isInitialized()) {
|
||||||
@@ -2719,6 +2720,7 @@ export class Config implements McpContext, AgentLoopContext {
|
|||||||
this.updateSystemInstructionIfInitialized();
|
this.updateSystemInstructionIfInitialized();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the duration of the current approval mode.
|
* Logs the duration of the current approval mode.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > ApprovalMode in System Prompt > Approved Plan in Plan Mode > should NOT include approved plan section if no plan is set in config 1`] = `
|
exports[`Core System Prompt (prompts.ts) > ApprovalMode in System Prompt > Approved Plan in Plan Mode > should NOT include approved plan section if no plan is set in config 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Plan** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ ONLY use the built-in \`exit_plan_mode\` tool to present the plan for formal app
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > ApprovalMode in System Prompt > Approved Plan in Plan Mode > should include approved plan path when set in config 1`] = `
|
exports[`Core System Prompt (prompts.ts) > ApprovalMode in System Prompt > Approved Plan in Plan Mode > should include approved plan path when set in config 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Plan** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -482,7 +482,7 @@ Your core function is efficient and safe assistance. Balance extreme conciseness
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > ApprovalMode in System Prompt > should include PLAN mode instructions 1`] = `
|
exports[`Core System Prompt (prompts.ts) > ApprovalMode in System Prompt > should include PLAN mode instructions 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Plan** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -662,7 +662,7 @@ ONLY use the built-in \`exit_plan_mode\` tool to present the plan for formal app
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should append userMemory with separator when provided 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should append userMemory with separator when provided 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -843,7 +843,7 @@ Be extra polite.
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should handle CodebaseInvestigator (enabled=false) 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should handle CodebaseInvestigator (enabled=false) 1`] = `
|
||||||
"You are Gemini CLI, an autonomous CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an autonomous CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -976,7 +976,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should handle CodebaseInvestigator (enabled=true) 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should handle CodebaseInvestigator (enabled=true) 1`] = `
|
||||||
"You are Gemini CLI, an autonomous CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an autonomous CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -1591,7 +1591,7 @@ Your core function is efficient and safe assistance. Balance extreme conciseness
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should include available_skills with updated verbiage for preview models 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should include available_skills with updated verbiage for preview models 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -1768,7 +1768,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should include correct sandbox instructions for SANDBOX=sandbox-exec 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should include correct sandbox instructions for SANDBOX=sandbox-exec 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -1936,7 +1936,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should include correct sandbox instructions for SANDBOX=true 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should include correct sandbox instructions for SANDBOX=true 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -2104,7 +2104,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should include correct sandbox instructions for SANDBOX=undefined 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should include correct sandbox instructions for SANDBOX=undefined 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -2268,7 +2268,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should include mandate to distinguish between Directives and Inquiries 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should include mandate to distinguish between Directives and Inquiries 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -2432,7 +2432,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should include modern approved plan instructions with completion in DEFAULT mode when approvedPlanPath is set 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should include modern approved plan instructions with completion in DEFAULT mode when approvedPlanPath is set 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -2590,7 +2590,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should include planning phase suggestion when enter_plan_mode tool is enabled 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should include planning phase suggestion when enter_plan_mode tool is enabled 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -2722,7 +2722,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should include sub-agents in XML for preview models when invoke_agent tool is enabled 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should include sub-agents in XML for preview models when invoke_agent tool is enabled 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -3014,7 +3014,7 @@ Your core function is efficient and safe assistance. Balance extreme conciseness
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should include the TASK MANAGEMENT PROTOCOL when task tracker is enabled 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should include the TASK MANAGEMENT PROTOCOL when task tracker is enabled 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -3436,7 +3436,7 @@ project context
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should return the base prompt when userMemory is empty string 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should return the base prompt when userMemory is empty string 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -3600,7 +3600,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should return the base prompt when userMemory is whitespace only 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should return the base prompt when userMemory is whitespace only 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -3878,7 +3878,7 @@ Your core function is efficient and safe assistance. Balance extreme conciseness
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should use chatty system prompt for preview flash model 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should use chatty system prompt for preview flash model 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
@@ -4042,7 +4042,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Core System Prompt (prompts.ts) > should use chatty system prompt for preview model 1`] = `
|
exports[`Core System Prompt (prompts.ts) > should use chatty system prompt for preview model 1`] = `
|
||||||
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.
|
"You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. You are currently operating in **Default** mode. Your primary goal is to help users safely and effectively.
|
||||||
|
|
||||||
# Core Mandates
|
# Core Mandates
|
||||||
|
|
||||||
|
|||||||
@@ -2055,6 +2055,29 @@ ${JSON.stringify(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update system instruction when ApprovalModeChanged event is emitted', async () => {
|
||||||
|
const { ApprovalMode } = await import('../policy/types.js');
|
||||||
|
|
||||||
|
vi.mocked(mockConfig.getSessionId).mockReturnValue('session-1');
|
||||||
|
vi.mocked(mockConfig.getSystemInstructionMemory).mockReturnValue(
|
||||||
|
'Current Memory',
|
||||||
|
);
|
||||||
|
|
||||||
|
const { getCoreSystemPrompt } = await import('./prompts.js');
|
||||||
|
const mockGetCoreSystemPrompt = vi.mocked(getCoreSystemPrompt);
|
||||||
|
mockGetCoreSystemPrompt.mockClear();
|
||||||
|
|
||||||
|
coreEvents.emit(CoreEvent.ApprovalModeChanged, {
|
||||||
|
sessionId: 'session-1',
|
||||||
|
mode: ApprovalMode.YOLO,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockGetCoreSystemPrompt).toHaveBeenCalledWith(
|
||||||
|
mockConfig,
|
||||||
|
'Current Memory',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should propagate InvalidStream events without injecting "Please continue." or recursing', async () => {
|
it('should propagate InvalidStream events without injecting "Please continue." or recursing', async () => {
|
||||||
// Arrange: a single turn that yields an InvalidStream event.
|
// Arrange: a single turn that yields an InvalidStream event.
|
||||||
const mockStream = (async function* () {
|
const mockStream = (async function* () {
|
||||||
|
|||||||
@@ -67,7 +67,11 @@ import {
|
|||||||
} from '../availability/policyHelpers.js';
|
} from '../availability/policyHelpers.js';
|
||||||
import { getDisplayString, resolveModel } from '../config/models.js';
|
import { getDisplayString, resolveModel } from '../config/models.js';
|
||||||
import { partToString } from '../utils/partUtils.js';
|
import { partToString } from '../utils/partUtils.js';
|
||||||
import { coreEvents, CoreEvent } from '../utils/events.js';
|
import {
|
||||||
|
coreEvents,
|
||||||
|
CoreEvent,
|
||||||
|
type ApprovalModeChangedPayload,
|
||||||
|
} from '../utils/events.js';
|
||||||
import { initializeContextManager } from '../context/initializer.js';
|
import { initializeContextManager } from '../context/initializer.js';
|
||||||
|
|
||||||
const MAX_TURNS = 100;
|
const MAX_TURNS = 100;
|
||||||
@@ -116,6 +120,10 @@ export class GeminiClient {
|
|||||||
|
|
||||||
coreEvents.on(CoreEvent.ModelChanged, this.handleModelChanged);
|
coreEvents.on(CoreEvent.ModelChanged, this.handleModelChanged);
|
||||||
coreEvents.on(CoreEvent.MemoryChanged, this.handleMemoryChanged);
|
coreEvents.on(CoreEvent.MemoryChanged, this.handleMemoryChanged);
|
||||||
|
coreEvents.on(
|
||||||
|
CoreEvent.ApprovalModeChanged,
|
||||||
|
this.handleApprovalModeChanged,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get config(): Config {
|
private get config(): Config {
|
||||||
@@ -130,6 +138,12 @@ export class GeminiClient {
|
|||||||
this.updateSystemInstruction();
|
this.updateSystemInstruction();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleApprovalModeChanged = (payload: ApprovalModeChangedPayload) => {
|
||||||
|
if (payload.sessionId === this.config.getSessionId()) {
|
||||||
|
this.updateSystemInstruction();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
clearCurrentSequenceModel(): void {
|
clearCurrentSequenceModel(): void {
|
||||||
this.currentSequenceModel = null;
|
this.currentSequenceModel = null;
|
||||||
}
|
}
|
||||||
@@ -314,6 +328,10 @@ export class GeminiClient {
|
|||||||
dispose() {
|
dispose() {
|
||||||
coreEvents.off(CoreEvent.ModelChanged, this.handleModelChanged);
|
coreEvents.off(CoreEvent.ModelChanged, this.handleModelChanged);
|
||||||
coreEvents.off(CoreEvent.MemoryChanged, this.handleMemoryChanged);
|
coreEvents.off(CoreEvent.MemoryChanged, this.handleMemoryChanged);
|
||||||
|
coreEvents.off(
|
||||||
|
CoreEvent.ApprovalModeChanged,
|
||||||
|
this.handleApprovalModeChanged,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async resumeChat(
|
async resumeChat(
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ export class PromptProvider {
|
|||||||
const options: snippets.SystemPromptOptions = {
|
const options: snippets.SystemPromptOptions = {
|
||||||
preamble: this.withSection('preamble', () => ({
|
preamble: this.withSection('preamble', () => ({
|
||||||
interactive: interactiveMode,
|
interactive: interactiveMode,
|
||||||
|
approvalMode,
|
||||||
})),
|
})),
|
||||||
coreMandates: this.withSection('coreMandates', () => ({
|
coreMandates: this.withSection('coreMandates', () => ({
|
||||||
interactive: interactiveMode,
|
interactive: interactiveMode,
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
} from '../tools/tool-names.js';
|
} from '../tools/tool-names.js';
|
||||||
import type { HierarchicalMemory } from '../config/memory.js';
|
import type { HierarchicalMemory } from '../config/memory.js';
|
||||||
import { DEFAULT_CONTEXT_FILENAME } from '../tools/memoryTool.js';
|
import { DEFAULT_CONTEXT_FILENAME } from '../tools/memoryTool.js';
|
||||||
|
import type { ApprovalMode } from '../policy/types.js';
|
||||||
|
|
||||||
// --- Options Structs ---
|
// --- Options Structs ---
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ export interface SystemPromptOptions {
|
|||||||
|
|
||||||
export interface PreambleOptions {
|
export interface PreambleOptions {
|
||||||
interactive: boolean;
|
interactive: boolean;
|
||||||
|
approvalMode: ApprovalMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CoreMandatesOptions {
|
export interface CoreMandatesOptions {
|
||||||
@@ -188,9 +190,17 @@ ${renderUserMemory(userMemory, contextFilenames)}
|
|||||||
|
|
||||||
export function renderPreamble(options?: PreambleOptions): string {
|
export function renderPreamble(options?: PreambleOptions): string {
|
||||||
if (!options) return '';
|
if (!options) return '';
|
||||||
return options.interactive
|
|
||||||
? 'You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.'
|
let modeStr = 'Default';
|
||||||
: 'You are Gemini CLI, an autonomous CLI agent specializing in software engineering tasks. Your primary goal is to help users safely and effectively.';
|
if (options.approvalMode === 'plan') modeStr = 'Plan';
|
||||||
|
if (options.approvalMode === 'yolo') modeStr = 'YOLO';
|
||||||
|
if (options.approvalMode === 'autoEdit') modeStr = 'Auto-Edit';
|
||||||
|
|
||||||
|
const base = options.interactive
|
||||||
|
? 'You are Gemini CLI, an interactive CLI agent specializing in software engineering tasks.'
|
||||||
|
: 'You are Gemini CLI, an autonomous CLI agent specializing in software engineering tasks.';
|
||||||
|
|
||||||
|
return `${base} You are currently operating in **${modeStr}** mode. Your primary goal is to help users safely and effectively.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderCoreMandates(options?: CoreMandatesOptions): string {
|
export function renderCoreMandates(options?: CoreMandatesOptions): string {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import type {
|
|||||||
KeychainAvailabilityEvent,
|
KeychainAvailabilityEvent,
|
||||||
} from '../telemetry/types.js';
|
} from '../telemetry/types.js';
|
||||||
import { debugLogger } from './debugLogger.js';
|
import { debugLogger } from './debugLogger.js';
|
||||||
|
import type { ApprovalMode } from '../policy/types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the severity level for user-facing feedback.
|
* Defines the severity level for user-facing feedback.
|
||||||
@@ -52,6 +53,20 @@ export interface ModelChangedPayload {
|
|||||||
model: string;
|
model: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Payload for the 'approval-mode-changed' event.
|
||||||
|
*/
|
||||||
|
export interface ApprovalModeChangedPayload {
|
||||||
|
/**
|
||||||
|
* The session ID associated with the mode change.
|
||||||
|
*/
|
||||||
|
sessionId: string;
|
||||||
|
/**
|
||||||
|
* The new approval mode.
|
||||||
|
*/
|
||||||
|
mode: ApprovalMode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payload for the 'console-log' event.
|
* Payload for the 'console-log' event.
|
||||||
*/
|
*/
|
||||||
@@ -181,6 +196,7 @@ export interface QuotaChangedPayload {
|
|||||||
export enum CoreEvent {
|
export enum CoreEvent {
|
||||||
UserFeedback = 'user-feedback',
|
UserFeedback = 'user-feedback',
|
||||||
ModelChanged = 'model-changed',
|
ModelChanged = 'model-changed',
|
||||||
|
ApprovalModeChanged = 'approval-mode-changed',
|
||||||
ConsoleLog = 'console-log',
|
ConsoleLog = 'console-log',
|
||||||
Output = 'output',
|
Output = 'output',
|
||||||
MemoryChanged = 'memory-changed',
|
MemoryChanged = 'memory-changed',
|
||||||
@@ -215,6 +231,7 @@ export interface EditorSelectedPayload {
|
|||||||
export interface CoreEvents extends ExtensionEvents {
|
export interface CoreEvents extends ExtensionEvents {
|
||||||
[CoreEvent.UserFeedback]: [UserFeedbackPayload];
|
[CoreEvent.UserFeedback]: [UserFeedbackPayload];
|
||||||
[CoreEvent.ModelChanged]: [ModelChangedPayload];
|
[CoreEvent.ModelChanged]: [ModelChangedPayload];
|
||||||
|
[CoreEvent.ApprovalModeChanged]: [ApprovalModeChangedPayload];
|
||||||
[CoreEvent.ConsoleLog]: [ConsoleLogPayload];
|
[CoreEvent.ConsoleLog]: [ConsoleLogPayload];
|
||||||
[CoreEvent.Output]: [OutputPayload];
|
[CoreEvent.Output]: [OutputPayload];
|
||||||
[CoreEvent.MemoryChanged]: [MemoryChangedPayload];
|
[CoreEvent.MemoryChanged]: [MemoryChangedPayload];
|
||||||
@@ -327,6 +344,14 @@ export class CoreEventEmitter extends EventEmitter<CoreEvents> {
|
|||||||
this.emit(CoreEvent.ModelChanged, payload);
|
this.emit(CoreEvent.ModelChanged, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies subscribers that the approval mode has changed.
|
||||||
|
*/
|
||||||
|
emitApprovalModeChanged(sessionId: string, mode: ApprovalMode): void {
|
||||||
|
const payload: ApprovalModeChangedPayload = { sessionId, mode };
|
||||||
|
this.emit(CoreEvent.ApprovalModeChanged, payload);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies subscribers that settings have been modified.
|
* Notifies subscribers that settings have been modified.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user