fix(sdk): prevent interactive tool calls in headless self-discuss mode

The discuss step loaded the full interactive workflow prompt which
instructs the agent to use AskUserQuestion, Skill(), and area selection
UIs. In headless auto mode, the agent followed these instructions and
tried to interact with a non-existent user.

Fix: prepend a mandatory headless override BEFORE the workflow prompt
that explicitly forbids interactive tools and instructs the agent to
make all decisions autonomously. Prepending (not appending) ensures
the override takes priority over conflicting instructions later in
the prompt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lex Christopherson
2026-04-25 13:57:43 -06:00
parent 25d9763878
commit f0953dec0c
2 changed files with 29 additions and 4 deletions

View File

@@ -1989,8 +1989,8 @@ Use TypeScript.`, 'utf-8');
);
expect(discussCalls.length).toBeGreaterThanOrEqual(1);
const prompt = discussCalls[0][0] as string;
expect(prompt).toContain('Self-Discuss Mode');
expect(prompt).toContain('No human is present');
expect(prompt).toContain('HEADLESS MODE');
expect(prompt).toContain('no human present');
});
it('skips self-discuss when context already exists even in auto-mode', async () => {

View File

@@ -438,9 +438,34 @@ export class PhaseRunner {
const contextFiles = await this.contextEngine.resolveContextFiles(PhaseType.Discuss);
let prompt = await this.promptFactory.buildPrompt(PhaseType.Discuss, null, contextFiles);
// Supplement with self-discuss instructions with pass cap
// Prepend self-discuss override BEFORE the workflow prompt.
// The workflow prompt contains interactive patterns (user questions, area selection)
// that the agent will follow unless explicitly overridden up front.
const maxPasses = this.config.workflow.max_discuss_passes ?? 3;
prompt += `\n\n## Self-Discuss Mode\n\nYou are the AI discussing decisions with yourself. No human is present. Identify 3-5 gray areas in the project scope, reason through each one, make opinionated choices, and write CONTEXT.md with your decisions.\n\n**CRITICAL: Single-pass only.** You MUST complete all decisions in ONE pass and write CONTEXT.md once. Do NOT re-read your own CONTEXT.md to find "gaps" and do additional passes. The maximum allowed passes is ${maxPasses} — if you have already written CONTEXT.md, you are DONE. Proceed to the next workflow step. Self-referential gap-finding loops waste resources without adding value.`;
const selfDiscussOverride = [
'## HEADLESS MODE — MANDATORY OVERRIDE',
'',
'**This session is running headless with no human present.**',
'',
'You MUST NOT:',
'- Use AskUserQuestion or any interactive tools',
'- Invoke Skill() or SlashCommand()',
'- Ask the user anything or wait for input',
'- Use multi-select, checkbox, or prompt UIs',
'',
'You MUST:',
'- Make all decisions autonomously and opinionatedly',
'- Identify 3-5 gray areas in the project scope',
'- Reason through each one yourself and pick the best option',
'- Write CONTEXT.md with your decisions in a single pass',
`- Complete within ${maxPasses} pass(es) maximum — do not re-read your own output to find gaps`,
'',
'Any instructions below about "asking the user", "discussing with the user", or "interactive" should be read as "decide yourself."',
'',
'---',
'',
].join('\n');
prompt = selfDiscussOverride + prompt;
planResult = await runPhaseStepSession(
prompt,