mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
fix: allow ask_user tool in yolo mode (#18541)
This commit is contained in:
@@ -483,6 +483,29 @@ describe('Core System Prompt (prompts.ts)', () => {
|
|||||||
expect(prompt).toMatchSnapshot();
|
expect(prompt).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should include YOLO mode instructions in interactive mode', () => {
|
||||||
|
vi.mocked(mockConfig.getApprovalMode).mockReturnValue(ApprovalMode.YOLO);
|
||||||
|
vi.mocked(mockConfig.isInteractive).mockReturnValue(true);
|
||||||
|
const prompt = getCoreSystemPrompt(mockConfig);
|
||||||
|
expect(prompt).toContain('# Autonomous Mode (YOLO)');
|
||||||
|
expect(prompt).toContain('Only use the `ask_user` tool if');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT include YOLO mode instructions in non-interactive mode', () => {
|
||||||
|
vi.mocked(mockConfig.getApprovalMode).mockReturnValue(ApprovalMode.YOLO);
|
||||||
|
vi.mocked(mockConfig.isInteractive).mockReturnValue(false);
|
||||||
|
const prompt = getCoreSystemPrompt(mockConfig);
|
||||||
|
expect(prompt).not.toContain('# Autonomous Mode (YOLO)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT include YOLO mode instructions for DEFAULT mode', () => {
|
||||||
|
vi.mocked(mockConfig.getApprovalMode).mockReturnValue(
|
||||||
|
ApprovalMode.DEFAULT,
|
||||||
|
);
|
||||||
|
const prompt = getCoreSystemPrompt(mockConfig);
|
||||||
|
expect(prompt).not.toContain('# Autonomous Mode (YOLO)');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Platform-specific and Background Process instructions', () => {
|
describe('Platform-specific and Background Process instructions', () => {
|
||||||
|
|||||||
@@ -317,8 +317,8 @@ describe('createPolicyEngineConfig', () => {
|
|||||||
(r) => r.decision === PolicyDecision.ALLOW && !r.toolName,
|
(r) => r.decision === PolicyDecision.ALLOW && !r.toolName,
|
||||||
);
|
);
|
||||||
expect(rule).toBeDefined();
|
expect(rule).toBeDefined();
|
||||||
// Priority 999 in default tier → 1.999
|
// Priority 998 in default tier → 1.998 (999 reserved for ask_user exception)
|
||||||
expect(rule?.priority).toBeCloseTo(1.999, 5);
|
expect(rule?.priority).toBeCloseTo(1.998, 5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow edit tool in AUTO_EDIT mode', async () => {
|
it('should allow edit tool in AUTO_EDIT mode', async () => {
|
||||||
@@ -582,8 +582,8 @@ describe('createPolicyEngineConfig', () => {
|
|||||||
(r) => !r.toolName && r.decision === PolicyDecision.ALLOW,
|
(r) => !r.toolName && r.decision === PolicyDecision.ALLOW,
|
||||||
);
|
);
|
||||||
expect(wildcardRule).toBeDefined();
|
expect(wildcardRule).toBeDefined();
|
||||||
// Priority 999 in default tier → 1.999
|
// Priority 998 in default tier → 1.998 (999 reserved for ask_user exception)
|
||||||
expect(wildcardRule?.priority).toBeCloseTo(1.999, 5);
|
expect(wildcardRule?.priority).toBeCloseTo(1.998, 5);
|
||||||
|
|
||||||
// Write tool ASK_USER rules are present (from write.toml)
|
// Write tool ASK_USER rules are present (from write.toml)
|
||||||
const writeToolRules = config.rules?.filter(
|
const writeToolRules = config.rules?.filter(
|
||||||
|
|||||||
@@ -23,10 +23,21 @@
|
|||||||
# 10: Write tools default to ASK_USER (becomes 1.010 in default tier)
|
# 10: Write tools default to ASK_USER (becomes 1.010 in default tier)
|
||||||
# 15: Auto-edit tool override (becomes 1.015 in default tier)
|
# 15: Auto-edit tool override (becomes 1.015 in default tier)
|
||||||
# 50: Read-only tools (becomes 1.050 in default tier)
|
# 50: Read-only tools (becomes 1.050 in default tier)
|
||||||
# 999: YOLO mode allow-all (becomes 1.999 in default tier)
|
# 998: YOLO mode allow-all (becomes 1.998 in default tier)
|
||||||
|
# 999: Ask-user tool (becomes 1.999 in default tier)
|
||||||
|
|
||||||
|
# Ask-user tool always requires user interaction, even in YOLO mode.
|
||||||
|
# This ensures the model can gather user preferences/decisions when needed.
|
||||||
|
# Note: In non-interactive mode, this decision is converted to DENY by the policy engine.
|
||||||
[[rule]]
|
[[rule]]
|
||||||
decision = "allow"
|
toolName = "ask_user"
|
||||||
|
decision = "ask_user"
|
||||||
priority = 999
|
priority = 999
|
||||||
modes = ["yolo"]
|
modes = ["yolo"]
|
||||||
|
|
||||||
|
# Allow everything else in YOLO mode
|
||||||
|
[[rule]]
|
||||||
|
decision = "allow"
|
||||||
|
priority = 998
|
||||||
|
modes = ["yolo"]
|
||||||
allow_redirection = true
|
allow_redirection = true
|
||||||
|
|||||||
@@ -2030,4 +2030,60 @@ describe('PolicyEngine', () => {
|
|||||||
expect(result.decision).toBe(PolicyDecision.DENY);
|
expect(result.decision).toBe(PolicyDecision.DENY);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('YOLO mode with ask_user tool', () => {
|
||||||
|
it('should return ASK_USER for ask_user tool even in YOLO mode', async () => {
|
||||||
|
const rules: PolicyRule[] = [
|
||||||
|
{
|
||||||
|
toolName: 'ask_user',
|
||||||
|
decision: PolicyDecision.ASK_USER,
|
||||||
|
priority: 999,
|
||||||
|
modes: [ApprovalMode.YOLO],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decision: PolicyDecision.ALLOW,
|
||||||
|
priority: 998,
|
||||||
|
modes: [ApprovalMode.YOLO],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
engine = new PolicyEngine({
|
||||||
|
rules,
|
||||||
|
approvalMode: ApprovalMode.YOLO,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await engine.check(
|
||||||
|
{ name: 'ask_user', args: {} },
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
expect(result.decision).toBe(PolicyDecision.ASK_USER);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return ALLOW for other tools in YOLO mode', async () => {
|
||||||
|
const rules: PolicyRule[] = [
|
||||||
|
{
|
||||||
|
toolName: 'ask_user',
|
||||||
|
decision: PolicyDecision.ASK_USER,
|
||||||
|
priority: 999,
|
||||||
|
modes: [ApprovalMode.YOLO],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decision: PolicyDecision.ALLOW,
|
||||||
|
priority: 998,
|
||||||
|
modes: [ApprovalMode.YOLO],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
engine = new PolicyEngine({
|
||||||
|
rules,
|
||||||
|
approvalMode: ApprovalMode.YOLO,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await engine.check(
|
||||||
|
{ name: 'run_shell_command', args: { command: 'ls' } },
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
expect(result.decision).toBe(PolicyDecision.ALLOW);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export class PromptProvider {
|
|||||||
const interactiveMode = interactiveOverride ?? config.isInteractive();
|
const interactiveMode = interactiveOverride ?? config.isInteractive();
|
||||||
const approvalMode = config.getApprovalMode?.() ?? ApprovalMode.DEFAULT;
|
const approvalMode = config.getApprovalMode?.() ?? ApprovalMode.DEFAULT;
|
||||||
const isPlanMode = approvalMode === ApprovalMode.PLAN;
|
const isPlanMode = approvalMode === ApprovalMode.PLAN;
|
||||||
|
const isYoloMode = approvalMode === ApprovalMode.YOLO;
|
||||||
const skills = config.getSkillManager().getSkills();
|
const skills = config.getSkillManager().getSkills();
|
||||||
const toolNames = config.getToolRegistry().getAllToolNames();
|
const toolNames = config.getToolRegistry().getAllToolNames();
|
||||||
const enabledToolNames = new Set(toolNames);
|
const enabledToolNames = new Set(toolNames);
|
||||||
@@ -183,6 +184,11 @@ export class PromptProvider {
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
sandbox: this.withSection('sandbox', () => getSandboxMode()),
|
sandbox: this.withSection('sandbox', () => getSandboxMode()),
|
||||||
|
interactiveYoloMode: this.withSection(
|
||||||
|
'interactiveYoloMode',
|
||||||
|
() => true,
|
||||||
|
isYoloMode && interactiveMode,
|
||||||
|
),
|
||||||
gitRepo: this.withSection(
|
gitRepo: this.withSection(
|
||||||
'git',
|
'git',
|
||||||
() => ({ interactive: interactiveMode }),
|
() => ({ interactive: interactiveMode }),
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export interface SystemPromptOptions {
|
|||||||
planningWorkflow?: PlanningWorkflowOptions;
|
planningWorkflow?: PlanningWorkflowOptions;
|
||||||
operationalGuidelines?: OperationalGuidelinesOptions;
|
operationalGuidelines?: OperationalGuidelinesOptions;
|
||||||
sandbox?: SandboxMode;
|
sandbox?: SandboxMode;
|
||||||
|
interactiveYoloMode?: boolean;
|
||||||
gitRepo?: GitRepoOptions;
|
gitRepo?: GitRepoOptions;
|
||||||
finalReminder?: FinalReminderOptions;
|
finalReminder?: FinalReminderOptions;
|
||||||
}
|
}
|
||||||
@@ -114,6 +115,8 @@ ${
|
|||||||
|
|
||||||
${renderOperationalGuidelines(options.operationalGuidelines)}
|
${renderOperationalGuidelines(options.operationalGuidelines)}
|
||||||
|
|
||||||
|
${renderInteractiveYoloMode(options.interactiveYoloMode)}
|
||||||
|
|
||||||
${renderSandbox(options.sandbox)}
|
${renderSandbox(options.sandbox)}
|
||||||
|
|
||||||
${renderGitRepo(options.gitRepo)}
|
${renderGitRepo(options.gitRepo)}
|
||||||
@@ -293,6 +296,25 @@ You are running outside of a sandbox container, directly on the user's system. F
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderInteractiveYoloMode(enabled?: boolean): string {
|
||||||
|
if (!enabled) return '';
|
||||||
|
return `
|
||||||
|
# Autonomous Mode (YOLO)
|
||||||
|
|
||||||
|
You are operating in **autonomous mode**. The user has requested minimal interruption.
|
||||||
|
|
||||||
|
**Only use the \`${ASK_USER_TOOL_NAME}\` tool if:**
|
||||||
|
- A wrong decision would cause significant re-work
|
||||||
|
- The request is fundamentally ambiguous with no reasonable default
|
||||||
|
- The user explicitly asks you to confirm or ask questions
|
||||||
|
|
||||||
|
**Otherwise, work autonomously:**
|
||||||
|
- Make reasonable decisions based on context and existing code patterns
|
||||||
|
- Follow established project conventions
|
||||||
|
- If multiple valid approaches exist, choose the most robust option
|
||||||
|
`.trim();
|
||||||
|
}
|
||||||
|
|
||||||
export function renderGitRepo(options?: GitRepoOptions): string {
|
export function renderGitRepo(options?: GitRepoOptions): string {
|
||||||
if (!options) return '';
|
if (!options) return '';
|
||||||
return `
|
return `
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export interface SystemPromptOptions {
|
|||||||
planningWorkflow?: PlanningWorkflowOptions;
|
planningWorkflow?: PlanningWorkflowOptions;
|
||||||
operationalGuidelines?: OperationalGuidelinesOptions;
|
operationalGuidelines?: OperationalGuidelinesOptions;
|
||||||
sandbox?: SandboxMode;
|
sandbox?: SandboxMode;
|
||||||
|
interactiveYoloMode?: boolean;
|
||||||
gitRepo?: GitRepoOptions;
|
gitRepo?: GitRepoOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +112,8 @@ ${
|
|||||||
|
|
||||||
${renderOperationalGuidelines(options.operationalGuidelines)}
|
${renderOperationalGuidelines(options.operationalGuidelines)}
|
||||||
|
|
||||||
|
${renderInteractiveYoloMode(options.interactiveYoloMode)}
|
||||||
|
|
||||||
${renderSandbox(options.sandbox)}
|
${renderSandbox(options.sandbox)}
|
||||||
|
|
||||||
${renderGitRepo(options.gitRepo)}
|
${renderGitRepo(options.gitRepo)}
|
||||||
@@ -312,6 +315,25 @@ export function renderSandbox(mode?: SandboxMode): string {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderInteractiveYoloMode(enabled?: boolean): string {
|
||||||
|
if (!enabled) return '';
|
||||||
|
return `
|
||||||
|
# Autonomous Mode (YOLO)
|
||||||
|
|
||||||
|
You are operating in **autonomous mode**. The user has requested minimal interruption.
|
||||||
|
|
||||||
|
**Only use the \`${ASK_USER_TOOL_NAME}\` tool if:**
|
||||||
|
- A wrong decision would cause significant re-work
|
||||||
|
- The request is fundamentally ambiguous with no reasonable default
|
||||||
|
- The user explicitly asks you to confirm or ask questions
|
||||||
|
|
||||||
|
**Otherwise, work autonomously:**
|
||||||
|
- Make reasonable decisions based on context and existing code patterns
|
||||||
|
- Follow established project conventions
|
||||||
|
- If multiple valid approaches exist, choose the most robust option
|
||||||
|
`.trim();
|
||||||
|
}
|
||||||
|
|
||||||
export function renderGitRepo(options?: GitRepoOptions): string {
|
export function renderGitRepo(options?: GitRepoOptions): string {
|
||||||
if (!options) return '';
|
if (!options) return '';
|
||||||
return `
|
return `
|
||||||
|
|||||||
Reference in New Issue
Block a user