From 20ab418c9ddf0267ac19cbb7f22b271a828b6f85 Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Sat, 7 Feb 2026 15:16:49 -0500 Subject: [PATCH 1/5] fix: allow ask_user in yolo mode --- packages/core/src/policy/config.test.ts | 8 +-- packages/core/src/policy/policies/yolo.toml | 11 +++- .../core/src/policy/policy-engine.test.ts | 56 +++++++++++++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/packages/core/src/policy/config.test.ts b/packages/core/src/policy/config.test.ts index 774214d1011..25f7e4a1500 100644 --- a/packages/core/src/policy/config.test.ts +++ b/packages/core/src/policy/config.test.ts @@ -317,8 +317,8 @@ describe('createPolicyEngineConfig', () => { (r) => r.decision === PolicyDecision.ALLOW && !r.toolName, ); expect(rule).toBeDefined(); - // Priority 999 in default tier → 1.999 - expect(rule?.priority).toBeCloseTo(1.999, 5); + // Priority 998 in default tier → 1.998 (999 reserved for ask_user exception) + expect(rule?.priority).toBeCloseTo(1.998, 5); }); it('should allow edit tool in AUTO_EDIT mode', async () => { @@ -582,8 +582,8 @@ describe('createPolicyEngineConfig', () => { (r) => !r.toolName && r.decision === PolicyDecision.ALLOW, ); expect(wildcardRule).toBeDefined(); - // Priority 999 in default tier → 1.999 - expect(wildcardRule?.priority).toBeCloseTo(1.999, 5); + // Priority 998 in default tier → 1.998 (999 reserved for ask_user exception) + expect(wildcardRule?.priority).toBeCloseTo(1.998, 5); // Write tool ASK_USER rules are present (from write.toml) const writeToolRules = config.rules?.filter( diff --git a/packages/core/src/policy/policies/yolo.toml b/packages/core/src/policy/policies/yolo.toml index 052ca6c4d37..27e44a970a3 100644 --- a/packages/core/src/policy/policies/yolo.toml +++ b/packages/core/src/policy/policies/yolo.toml @@ -25,8 +25,17 @@ # 50: Read-only tools (becomes 1.050 in default tier) # 999: YOLO mode allow-all (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. [[rule]] -decision = "allow" +toolName = "ask_user" +decision = "ask_user" priority = 999 modes = ["yolo"] + +# Allow everything else in YOLO mode +[[rule]] +decision = "allow" +priority = 998 +modes = ["yolo"] allow_redirection = true diff --git a/packages/core/src/policy/policy-engine.test.ts b/packages/core/src/policy/policy-engine.test.ts index dba06550d25..8886980d9e7 100644 --- a/packages/core/src/policy/policy-engine.test.ts +++ b/packages/core/src/policy/policy-engine.test.ts @@ -1998,4 +1998,60 @@ describe('PolicyEngine', () => { 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); + }); + }); }); From 1075dc6d4e54eda2b139f9098aa2852d9311ed4a Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Sat, 7 Feb 2026 16:23:28 -0500 Subject: [PATCH 2/5] chore: add yolo portion to system prompt --- packages/core/src/core/prompts.test.ts | 23 ++++++++++++++++++++ packages/core/src/prompts/promptProvider.ts | 6 +++++ packages/core/src/prompts/snippets.legacy.ts | 22 +++++++++++++++++++ packages/core/src/prompts/snippets.ts | 22 +++++++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/packages/core/src/core/prompts.test.ts b/packages/core/src/core/prompts.test.ts index 649908e77fe..19b230a960e 100644 --- a/packages/core/src/core/prompts.test.ts +++ b/packages/core/src/core/prompts.test.ts @@ -412,6 +412,29 @@ describe('Core System Prompt (prompts.ts)', () => { 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', () => { diff --git a/packages/core/src/prompts/promptProvider.ts b/packages/core/src/prompts/promptProvider.ts index 2a114c3fa86..55ab2140ea2 100644 --- a/packages/core/src/prompts/promptProvider.ts +++ b/packages/core/src/prompts/promptProvider.ts @@ -48,6 +48,7 @@ export class PromptProvider { const interactiveMode = interactiveOverride ?? config.isInteractive(); const approvalMode = config.getApprovalMode?.() ?? ApprovalMode.DEFAULT; const isPlanMode = approvalMode === ApprovalMode.PLAN; + const isYoloMode = approvalMode === ApprovalMode.YOLO; const skills = config.getSkillManager().getSkills(); const toolNames = config.getToolRegistry().getAllToolNames(); const enabledToolNames = new Set(toolNames); @@ -159,6 +160,11 @@ export class PromptProvider { }), ), sandbox: this.withSection('sandbox', () => getSandboxMode()), + yoloMode: this.withSection( + 'yoloMode', + () => true, + isYoloMode && interactiveMode, + ), gitRepo: this.withSection( 'git', () => ({ interactive: interactiveMode }), diff --git a/packages/core/src/prompts/snippets.legacy.ts b/packages/core/src/prompts/snippets.legacy.ts index 16a2a6e6316..cb2224d8df6 100644 --- a/packages/core/src/prompts/snippets.legacy.ts +++ b/packages/core/src/prompts/snippets.legacy.ts @@ -31,6 +31,7 @@ export interface SystemPromptOptions { planningWorkflow?: PlanningWorkflowOptions; operationalGuidelines?: OperationalGuidelinesOptions; sandbox?: SandboxMode; + yoloMode?: boolean; gitRepo?: GitRepoOptions; finalReminder?: FinalReminderOptions; } @@ -106,6 +107,8 @@ ${ ${renderOperationalGuidelines(options.operationalGuidelines)} +${renderYoloMode(options.yoloMode)} + ${renderSandbox(options.sandbox)} ${renderGitRepo(options.gitRepo)} @@ -263,6 +266,25 @@ You are running outside of a sandbox container, directly on the user's system. F } } +export function renderYoloMode(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 { if (!options) return ''; return ` diff --git a/packages/core/src/prompts/snippets.ts b/packages/core/src/prompts/snippets.ts index a4d3adf3aa5..890333c03b9 100644 --- a/packages/core/src/prompts/snippets.ts +++ b/packages/core/src/prompts/snippets.ts @@ -31,6 +31,7 @@ export interface SystemPromptOptions { planningWorkflow?: PlanningWorkflowOptions; operationalGuidelines?: OperationalGuidelinesOptions; sandbox?: SandboxMode; + yoloMode?: boolean; gitRepo?: GitRepoOptions; finalReminder?: FinalReminderOptions; } @@ -106,6 +107,8 @@ ${ ${renderOperationalGuidelines(options.operationalGuidelines)} +${renderYoloMode(options.yoloMode)} + ${renderSandbox(options.sandbox)} ${renderGitRepo(options.gitRepo)} @@ -274,6 +277,25 @@ You are running outside of a sandbox container, directly on the user's system. F } } +export function renderYoloMode(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 { if (!options) return ''; return ` From 72b80b658f088b3bab2acabf70ea68e860a96aa5 Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Sat, 7 Feb 2026 17:16:46 -0500 Subject: [PATCH 3/5] chore: update yolo.toml --- packages/core/src/policy/policies/yolo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/policy/policies/yolo.toml b/packages/core/src/policy/policies/yolo.toml index 27e44a970a3..9bc81c5638a 100644 --- a/packages/core/src/policy/policies/yolo.toml +++ b/packages/core/src/policy/policies/yolo.toml @@ -23,7 +23,8 @@ # 10: Write tools default to ASK_USER (becomes 1.010 in default tier) # 15: Auto-edit tool override (becomes 1.015 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. From 6593bf14a69b856a2c3c92c486ffeb4af9ccf8a4 Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Tue, 10 Feb 2026 10:07:57 -0500 Subject: [PATCH 4/5] chore: reviewer feedback --- packages/core/src/policy/policies/yolo.toml | 1 + packages/core/src/prompts/promptProvider.ts | 4 ++-- packages/core/src/prompts/snippets.legacy.ts | 6 +++--- packages/core/src/prompts/snippets.ts | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/core/src/policy/policies/yolo.toml b/packages/core/src/policy/policies/yolo.toml index 9bc81c5638a..95c3b411f1d 100644 --- a/packages/core/src/policy/policies/yolo.toml +++ b/packages/core/src/policy/policies/yolo.toml @@ -28,6 +28,7 @@ # 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]] toolName = "ask_user" decision = "ask_user" diff --git a/packages/core/src/prompts/promptProvider.ts b/packages/core/src/prompts/promptProvider.ts index c02583f3054..13c4f0374d1 100644 --- a/packages/core/src/prompts/promptProvider.ts +++ b/packages/core/src/prompts/promptProvider.ts @@ -184,8 +184,8 @@ export class PromptProvider { }), ), sandbox: this.withSection('sandbox', () => getSandboxMode()), - yoloMode: this.withSection( - 'yoloMode', + interactiveYoloMode: this.withSection( + 'interactiveYoloMode', () => true, isYoloMode && interactiveMode, ), diff --git a/packages/core/src/prompts/snippets.legacy.ts b/packages/core/src/prompts/snippets.legacy.ts index 670e43b04a2..e962ee8ffc3 100644 --- a/packages/core/src/prompts/snippets.legacy.ts +++ b/packages/core/src/prompts/snippets.legacy.ts @@ -32,7 +32,7 @@ export interface SystemPromptOptions { planningWorkflow?: PlanningWorkflowOptions; operationalGuidelines?: OperationalGuidelinesOptions; sandbox?: SandboxMode; - yoloMode?: boolean; + interactiveYoloMode?: boolean; gitRepo?: GitRepoOptions; finalReminder?: FinalReminderOptions; } @@ -115,7 +115,7 @@ ${ ${renderOperationalGuidelines(options.operationalGuidelines)} -${renderYoloMode(options.yoloMode)} +${renderYoloInteractiveMode(options.interactiveYoloMode)} ${renderSandbox(options.sandbox)} @@ -296,7 +296,7 @@ You are running outside of a sandbox container, directly on the user's system. F } } -export function renderYoloMode(enabled?: boolean): string { +export function renderYoloInteractiveMode(enabled?: boolean): string { if (!enabled) return ''; return ` # Autonomous Mode (YOLO) diff --git a/packages/core/src/prompts/snippets.ts b/packages/core/src/prompts/snippets.ts index 18d74cdc05c..47dfe901b41 100644 --- a/packages/core/src/prompts/snippets.ts +++ b/packages/core/src/prompts/snippets.ts @@ -33,7 +33,7 @@ export interface SystemPromptOptions { planningWorkflow?: PlanningWorkflowOptions; operationalGuidelines?: OperationalGuidelinesOptions; sandbox?: SandboxMode; - yoloMode?: boolean; + interactiveYoloMode?: boolean; gitRepo?: GitRepoOptions; } @@ -112,7 +112,7 @@ ${ ${renderOperationalGuidelines(options.operationalGuidelines)} -${renderYoloMode(options.yoloMode)} +${renderInteractiveYoloMode(options.interactiveYoloMode)} ${renderSandbox(options.sandbox)} @@ -315,7 +315,7 @@ export function renderSandbox(mode?: SandboxMode): string { return ''; } -export function renderYoloMode(enabled?: boolean): string { +export function renderInteractiveYoloMode(enabled?: boolean): string { if (!enabled) return ''; return ` # Autonomous Mode (YOLO) From f1c4d6a2e803d027f9b5ed9c89192edf89949df3 Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Tue, 10 Feb 2026 10:34:52 -0500 Subject: [PATCH 5/5] chore: review comment --- packages/core/src/prompts/snippets.legacy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/prompts/snippets.legacy.ts b/packages/core/src/prompts/snippets.legacy.ts index e962ee8ffc3..8d46fd6a1a4 100644 --- a/packages/core/src/prompts/snippets.legacy.ts +++ b/packages/core/src/prompts/snippets.legacy.ts @@ -115,7 +115,7 @@ ${ ${renderOperationalGuidelines(options.operationalGuidelines)} -${renderYoloInteractiveMode(options.interactiveYoloMode)} +${renderInteractiveYoloMode(options.interactiveYoloMode)} ${renderSandbox(options.sandbox)} @@ -296,7 +296,7 @@ You are running outside of a sandbox container, directly on the user's system. F } } -export function renderYoloInteractiveMode(enabled?: boolean): string { +export function renderInteractiveYoloMode(enabled?: boolean): string { if (!enabled) return ''; return ` # Autonomous Mode (YOLO)