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.
This commit is contained in:
Bryan Morgan
2026-02-22 10:09:53 -05:00
parent b5f691577d
commit dd683875d8
2 changed files with 14 additions and 10 deletions

View File

@@ -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 () => {

View File

@@ -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<ServerGeminiStreamEvent, Turn> {
@@ -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 {