From 4739495e39ebe216b126c53e0a790f563649420f Mon Sep 17 00:00:00 2001 From: Sri Pasumarthi <111310667+sripasg@users.noreply.github.com> Date: Mon, 11 May 2026 10:38:20 -0700 Subject: [PATCH] fix(cli/acp): prevent infinite thought loop in ACP mode by disablig nextSpeakerCheck (#26874) --- packages/cli/src/config/config.test.ts | 22 ++++++++++++++++++++++ packages/cli/src/config/config.ts | 6 +++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts index e2c223b55c..fd1e0a4de9 100644 --- a/packages/cli/src/config/config.test.ts +++ b/packages/cli/src/config/config.test.ts @@ -1043,6 +1043,28 @@ describe('loadCliConfig', () => { expect(config.isInteractive()).toBe(false); }); + + describe('isAcpMode', () => { + it('should force skipNextSpeakerCheck to true when in ACP mode', async () => { + process.argv = ['node', 'script.js', '--acp']; + const argv = await parseArguments(createTestMergedSettings()); + const settings = createTestMergedSettings({ + model: { skipNextSpeakerCheck: false }, + }); + const config = await loadCliConfig(settings, 'test-session', argv); + expect(config.getSkipNextSpeakerCheck()).toBe(true); + }); + + it('should respect settings.model.skipNextSpeakerCheck when not in ACP mode', async () => { + process.argv = ['node', 'script.js']; + const argv = await parseArguments(createTestMergedSettings()); + const settings = createTestMergedSettings({ + model: { skipNextSpeakerCheck: false }, + }); + const config = await loadCliConfig(settings, 'test-session', argv); + expect(config.getSkipNextSpeakerCheck()).toBe(false); + }); + }); }); describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => { diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index d683084413..e7b332711d 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -1105,7 +1105,11 @@ export async function loadCliConfig( shellToolInactivityTimeout: settings.tools?.shell?.inactivityTimeout, enableShellOutputEfficiency: settings.tools?.shell?.enableShellOutputEfficiency ?? true, - skipNextSpeakerCheck: settings.model?.skipNextSpeakerCheck, + // In ACP mode, always skip the next-speaker check. This check triggers + // recursive continuation turns inside GeminiClient.processTurn() that + // conflict with ACP's explicit turn management via session/prompt, + // causing infinite agent_thought_chunk loops. + skipNextSpeakerCheck: isAcpMode || settings.model?.skipNextSpeakerCheck, truncateToolOutputThreshold: settings.tools?.truncateToolOutputThreshold, eventEmitter: coreEvents, useWriteTodos: argv.useWriteTodos ?? settings.useWriteTodos,