From dd683875d8ebdc58d4af20cb14515fffeb026127 Mon Sep 17 00:00:00 2001 From: Bryan Morgan Date: Sun, 22 Feb 2026 10:09:53 -0500 Subject: [PATCH] feat(core): add non-interactive performance defaults Shell timeout 10min (vs 5min interactive), MAX_TURNS 200 (vs 100 interactive) to give autonomous sessions more room to complete complex tasks. --- packages/core/src/core/client.test.ts | 13 ++++++------- packages/core/src/core/client.ts | 11 ++++++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts index c910556ca8..0924645cea 100644 --- a/packages/core/src/core/client.test.ts +++ b/packages/core/src/core/client.test.ts @@ -1207,7 +1207,7 @@ ${JSON.stringify( eventCount++; // Safety check to prevent actual infinite loop in test - if (eventCount > 200) { + if (eventCount > 400) { abortController.abort(); throw new Error( 'Test exceeded expected event limit - possible actual infinite loop', @@ -1219,13 +1219,12 @@ ${JSON.stringify( expect(finalResult).toBeInstanceOf(Turn); // If infinite loop protection is working, checkNextSpeaker should be called many times - // but stop at MAX_TURNS (100). Since each recursive call should trigger checkNextSpeaker, - // we expect it to be called multiple times before hitting the limit + // but stop at maxTurns (200 for non-interactive). Since each recursive call should trigger + // checkNextSpeaker, we expect it to be called multiple times before hitting the limit expect(mockCheckNextSpeaker).toHaveBeenCalled(); // The stream should produce events and eventually terminate expect(eventCount).toBeGreaterThanOrEqual(1); - expect(eventCount).toBeLessThan(200); // Should not exceed our safety limit }); it('should yield MaxSessionTurns and stop when session turn limit is reached', async () => { @@ -1347,9 +1346,9 @@ ${JSON.stringify( const callCount = mockCheckNextSpeaker.mock.calls.length; // With the fix: even when turns is set to a very high value, - // the loop should stop at MAX_TURNS (100) - expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS - expect(eventCount).toBeLessThanOrEqual(200); // Should have reasonable number of events + // the loop should stop at maxTurns (200 for non-interactive) + expect(callCount).toBeLessThanOrEqual(200); // Should not exceed maxTurns + expect(eventCount).toBeLessThanOrEqual(400); // Should have reasonable number of events }); it('should yield ContextWindowWillOverflow when the context window is about to overflow', async () => { diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts index 56447468bd..0afa2d2381 100644 --- a/packages/core/src/core/client.ts +++ b/packages/core/src/core/client.ts @@ -65,7 +65,8 @@ import { partToString } from '../utils/partUtils.js'; import { coreEvents, CoreEvent } from '../utils/events.js'; import type { LlmRole } from '../telemetry/types.js'; -const MAX_TURNS = 100; +const MAX_TURNS_INTERACTIVE = 100; +const MAX_TURNS_NON_INTERACTIVE = 200; type BeforeAgentHookReturn = | { @@ -86,6 +87,7 @@ export class GeminiClient { private readonly loopDetector: LoopDetectionService; private readonly compressionService: ChatCompressionService; private readonly toolOutputMaskingService: ToolOutputMaskingService; + private readonly maxTurns: number; private lastPromptId: string; private currentSequenceModel: string | null = null; private lastSentIdeContext: IdeContext | undefined; @@ -102,6 +104,9 @@ export class GeminiClient { this.compressionService = new ChatCompressionService(); this.toolOutputMaskingService = new ToolOutputMaskingService(); this.lastPromptId = this.config.getSessionId(); + this.maxTurns = config.isInteractive() + ? MAX_TURNS_INTERACTIVE + : MAX_TURNS_NON_INTERACTIVE; coreEvents.on(CoreEvent.ModelChanged, this.handleModelChanged); } @@ -790,7 +795,7 @@ export class GeminiClient { request: PartListUnion, signal: AbortSignal, prompt_id: string, - turns: number = MAX_TURNS, + turns?: number, isInvalidStreamRetry: boolean = false, displayContent?: PartListUnion, ): AsyncGenerator { @@ -838,7 +843,7 @@ export class GeminiClient { } } - const boundedTurns = Math.min(turns, MAX_TURNS); + const boundedTurns = Math.min(turns ?? this.maxTurns, this.maxTurns); let turn = new Turn(this.getChat(), prompt_id); try {