diff --git a/packages/cli/src/interactiveCli.tsx b/packages/cli/src/interactiveCli.tsx index e93351eb07..87997220d3 100644 --- a/packages/cli/src/interactiveCli.tsx +++ b/packages/cli/src/interactiveCli.tsx @@ -12,6 +12,12 @@ import { ConsolePatcher } from './ui/utils/ConsolePatcher.js'; import { UserSimulator } from './services/UserSimulator.js'; import { registerCleanup, setupTtyCheck } from './utils/cleanup.js'; import { PassThrough } from 'node:stream'; + +interface RenderMetrics { + renderTime: number; + output: string; + staticOutput?: string; +} import { type StartupWarning, type Config, @@ -137,6 +143,7 @@ export async function startInteractiveUI( const simulateUser = config.getSimulateUser(); const simulatedStdin = new PassThrough({ encoding: 'utf8' }); + let lastFrame: string | undefined; const instance = render( process.env['DEBUG'] ? ( @@ -152,9 +159,10 @@ export async function startInteractiveUI( stdin: (simulateUser ? simulatedStdin : process.stdin) as any, exitOnCtrlC: false, isScreenReaderEnabled: config.getScreenReader(), - onRender: ({ renderTime }: { renderTime: number }) => { - if (renderTime > SLOW_RENDER_MS) { - recordSlowRender(config, renderTime); + onRender: (metrics: RenderMetrics) => { + lastFrame = metrics.output; + if (metrics.renderTime > SLOW_RENDER_MS) { + recordSlowRender(config, metrics.renderTime); } profiler.reportFrameRendered(); }, @@ -186,7 +194,11 @@ export async function startInteractiveUI( }); if (simulateUser) { - const simulator = new UserSimulator(config, instance, simulatedStdin); + const simulator = new UserSimulator( + config, + () => lastFrame, + simulatedStdin, + ); simulator.start(); registerCleanup(() => simulator.stop()); } diff --git a/packages/cli/src/services/UserSimulator.ts b/packages/cli/src/services/UserSimulator.ts index 903838a398..f4c3acdaa3 100644 --- a/packages/cli/src/services/UserSimulator.ts +++ b/packages/cli/src/services/UserSimulator.ts @@ -3,10 +3,9 @@ * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -import type { Config} from '@google/gemini-cli-core'; -import { debugLogger, LlmRole } from '@google/gemini-cli-core'; +import type { Config } from '@google/gemini-cli-core'; +import { debugLogger, LlmRole, resolveModel } from '@google/gemini-cli-core'; import type { Writable } from 'node:stream'; -import type { Instance } from 'ink'; export class UserSimulator { private isRunning = false; @@ -16,7 +15,7 @@ export class UserSimulator { constructor( private readonly config: Config, - private readonly instance: Instance, + private readonly getScreen: () => string | undefined, private readonly stdinBuffer: Writable, ) {} @@ -42,8 +41,7 @@ export class UserSimulator { try { this.isProcessing = true; - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-explicit-any - const screen = (this.instance as any).lastFrame() as string | undefined; + const screen = this.getScreen(); if (!screen || screen === this.lastScreenContent) return; const strippedScreen = screen.replace( @@ -74,9 +72,17 @@ For example: - To enter a new prompt, output the text followed by \n. Do NOT output markdown, explanations of your thought process, or quotes. Output ONLY the raw characters to send or .`; + const model = resolveModel( + this.config.getModel(), + false, // useGemini3_1 + false, // useCustomToolModel + this.config.getHasAccessToPreviewModel?.() ?? true, + this.config, + ); + const response = await contentGenerator.generateContent( { - model: this.config.getModel(), + model, contents: [ { role: 'user',