From 483193257edbc5f0956b79e216f9a33dd95ca8b0 Mon Sep 17 00:00:00 2001 From: Hadi Minooei Date: Thu, 19 Mar 2026 08:43:02 -0700 Subject: [PATCH] feat: add user simulator and integrate into CLI --- package-lock.json | 292 +++++++++++++-------- package.json | 2 +- packages/cli/src/config/config.ts | 7 + packages/cli/src/gemini.test.tsx | 1 + packages/cli/src/interactiveCli.tsx | 14 +- packages/cli/src/services/UserSimulator.ts | 103 ++++++++ packages/core/src/config/config.ts | 7 + packages/core/src/telemetry/llmRole.ts | 1 + packages/vscode-ide-companion/package.json | 2 +- 9 files changed, 320 insertions(+), 109 deletions(-) create mode 100644 packages/cli/src/services/UserSimulator.ts diff --git a/package-lock.json b/package-lock.json index 914d66d3ac..a7a130c038 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,7 +80,7 @@ "@lydell/node-pty-win32-arm64": "1.1.0", "@lydell/node-pty-win32-x64": "1.1.0", "keytar": "^7.9.0", - "node-pty": "^1.0.0" + "node-pty": "^1.1.0" } }, "node_modules/@agentclientprotocol/sdk": { @@ -4906,53 +4906,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@vscode/vsce": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.6.0.tgz", - "integrity": "sha512-u2ZoMfymRNJb14aHNawnXJtXHLXDVKc1oKZaH4VELKT/9iWKRVgtQOdwxCgtwSxJoqYvuK4hGlBWQJ05wxADhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@azure/identity": "^4.1.0", - "@secretlint/node": "^10.1.1", - "@secretlint/secretlint-formatter-sarif": "^10.1.1", - "@secretlint/secretlint-rule-no-dotenv": "^10.1.1", - "@secretlint/secretlint-rule-preset-recommend": "^10.1.1", - "@vscode/vsce-sign": "^2.0.0", - "azure-devops-node-api": "^12.5.0", - "chalk": "^4.1.2", - "cheerio": "^1.0.0-rc.9", - "cockatiel": "^3.1.2", - "commander": "^12.1.0", - "form-data": "^4.0.0", - "glob": "^11.0.0", - "hosted-git-info": "^4.0.2", - "jsonc-parser": "^3.2.0", - "leven": "^3.1.0", - "markdown-it": "^14.1.0", - "mime": "^1.3.4", - "minimatch": "^3.0.3", - "parse-semver": "^1.1.1", - "read": "^1.0.7", - "secretlint": "^10.1.1", - "semver": "^7.5.2", - "tmp": "^0.2.3", - "typed-rest-client": "^1.8.4", - "url-join": "^4.0.1", - "xml2js": "^0.5.0", - "yauzl": "^2.3.1", - "yazl": "^2.2.2" - }, - "bin": { - "vsce": "vsce" - }, - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "keytar": "^7.7.0" - } - }, "node_modules/@vscode/vsce-sign": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.6.tgz", @@ -5098,52 +5051,6 @@ "win32" ] }, - "node_modules/@vscode/vsce/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@vscode/vsce/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@vscode/vsce/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@vscode/vsce/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/@vue/compiler-core": { "version": "3.5.26", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz", @@ -6655,6 +6562,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -12193,13 +12107,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nan": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", - "integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==", - "license": "MIT", - "optional": true - }, "node_modules/nano-spawn": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.3.tgz", @@ -12343,16 +12250,23 @@ } }, "node_modules/node-pty": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz", - "integrity": "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz", + "integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==", "hasInstallScript": true, "license": "MIT", "optional": true, "dependencies": { - "nan": "^2.17.0" + "node-addon-api": "^7.1.0" } }, + "node_modules/node-pty/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, "node_modules/node-sarif-builder": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.2.0.tgz", @@ -17996,6 +17910,23 @@ "node": ">=20" } }, + "packages/snake-game": { + "name": "@google/gemini-cli-snake-game", + "version": "0.1.0", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "lucide-react": "^0.471.0" + }, + "devDependencies": { + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "esbuild": "^0.25.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "typescript": "^5.7.3" + } + }, "packages/test-utils": { "name": "@google/gemini-cli-test-utils", "version": "0.36.0-nightly.20260317.2f90b4653", @@ -18030,7 +17961,7 @@ "@types/vscode": "^1.99.0", "@typescript-eslint/eslint-plugin": "^8.31.1", "@typescript-eslint/parser": "^8.31.1", - "@vscode/vsce": "^3.6.0", + "@vscode/vsce": "^3.7.1", "esbuild": "^0.25.3", "eslint": "^9.25.1", "npm-run-all2": "^8.0.2", @@ -18047,6 +17978,155 @@ "integrity": "sha512-30sjmas1hQ0gVbX68LAWlm/YYlEqUErunPJJKLpEl+xhK0mKn+jyzlCOpsdTwfkZfPy4U6CDkmygBLC3AB8W9Q==", "dev": true, "license": "MIT" + }, + "packages/vscode-ide-companion/node_modules/@vscode/vsce": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.7.1.tgz", + "integrity": "sha512-OTm2XdMt2YkpSn2Nx7z2EJtSuhRHsTPYsSK59hr3v8jRArK+2UEoju4Jumn1CmpgoBLGI6ReHLJ/czYltNUW3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@vscode/vsce-sign": "^2.0.0", + "azure-devops-node-api": "^12.5.0", + "chalk": "^4.1.2", + "cheerio": "^1.0.0-rc.9", + "cockatiel": "^3.1.2", + "commander": "^12.1.0", + "form-data": "^4.0.0", + "glob": "^11.0.0", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^14.1.0", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "secretlint": "^10.1.2", + "semver": "^7.5.2", + "tmp": "^0.2.3", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "packages/vscode-ide-companion/node_modules/@vscode/vsce/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "packages/vscode-ide-companion/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "packages/vscode-ide-companion/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "packages/vscode-ide-companion/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/vscode-ide-companion/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "packages/vscode-ide-companion/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "packages/vscode-ide-companion/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "packages/vscode-ide-companion/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" } } } diff --git a/package.json b/package.json index 531f9f75d9..ddc3c09904 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,7 @@ "@lydell/node-pty-win32-arm64": "1.1.0", "@lydell/node-pty-win32-x64": "1.1.0", "keytar": "^7.9.0", - "node-pty": "^1.0.0" + "node-pty": "^1.1.0" }, "lint-staged": { "*.{js,jsx,ts,tsx}": [ diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 80c1e19443..eab45e744a 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -97,6 +97,7 @@ export interface CliArgs { rawOutput: boolean | undefined; acceptRawOutputRisk: boolean | undefined; isCommand: boolean | undefined; + simulateUser: boolean | undefined; } /** @@ -298,6 +299,11 @@ export async function parseArguments( .option('accept-raw-output-risk', { type: 'boolean', description: 'Suppress the security warning when using --raw-output.', + }) + .option('simulate-user', { + type: 'boolean', + description: + 'Run the user simulation agent in the background for evaluation purposes.', }), ) // Register MCP subcommands @@ -799,6 +805,7 @@ export async function loadCliConfig( approvalMode, disableYoloMode: settings.security?.disableYoloMode || settings.admin?.secureModeEnabled, + simulateUser: !!argv.simulateUser, disableAlwaysAllow: settings.security?.disableAlwaysAllow || settings.admin?.secureModeEnabled, diff --git a/packages/cli/src/gemini.test.tsx b/packages/cli/src/gemini.test.tsx index 31fec36db0..81b08540bd 100644 --- a/packages/cli/src/gemini.test.tsx +++ b/packages/cli/src/gemini.test.tsx @@ -513,6 +513,7 @@ describe('gemini.tsx main function kitty protocol', () => { rawOutput: undefined, acceptRawOutputRisk: undefined, isCommand: undefined, + simulateUser: undefined, }); await act(async () => { diff --git a/packages/cli/src/interactiveCli.tsx b/packages/cli/src/interactiveCli.tsx index a6337ef29c..e93351eb07 100644 --- a/packages/cli/src/interactiveCli.tsx +++ b/packages/cli/src/interactiveCli.tsx @@ -9,7 +9,9 @@ import { render } from 'ink'; import { basename } from 'node:path'; import { AppContainer } from './ui/AppContainer.js'; 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'; import { type StartupWarning, type Config, @@ -132,6 +134,9 @@ export async function startInteractiveUI( await new Promise((resolve) => setTimeout(resolve, 100)); } + const simulateUser = config.getSimulateUser(); + const simulatedStdin = new PassThrough({ encoding: 'utf8' }); + const instance = render( process.env['DEBUG'] ? ( @@ -143,7 +148,8 @@ export async function startInteractiveUI( { stdout: inkStdout, stderr: inkStderr, - stdin: process.stdin, + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-assignment + stdin: (simulateUser ? simulatedStdin : process.stdin) as any, exitOnCtrlC: false, isScreenReaderEnabled: config.getScreenReader(), onRender: ({ renderTime }: { renderTime: number }) => { @@ -179,6 +185,12 @@ export async function startInteractiveUI( } }); + if (simulateUser) { + const simulator = new UserSimulator(config, instance, simulatedStdin); + simulator.start(); + registerCleanup(() => simulator.stop()); + } + registerCleanup(() => instance.unmount()); registerCleanup(setupTtyCheck()); diff --git a/packages/cli/src/services/UserSimulator.ts b/packages/cli/src/services/UserSimulator.ts new file mode 100644 index 0000000000..903838a398 --- /dev/null +++ b/packages/cli/src/services/UserSimulator.ts @@ -0,0 +1,103 @@ +/** + * @license + * 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 { Writable } from 'node:stream'; +import type { Instance } from 'ink'; + +export class UserSimulator { + private isRunning = false; + private timer: NodeJS.Timeout | null = null; + private lastScreenContent = ''; + private isProcessing = false; + + constructor( + private readonly config: Config, + private readonly instance: Instance, + private readonly stdinBuffer: Writable, + ) {} + + start() { + if (!this.config.getSimulateUser()) { + return; + } + this.isRunning = true; + this.timer = setInterval(() => this.tick(), 2000); + } + + stop() { + this.isRunning = false; + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + } + debugLogger.log('User simulator stopped'); + } + + private async tick() { + if (!this.isRunning || this.isProcessing) return; + + 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; + if (!screen || screen === this.lastScreenContent) return; + + const strippedScreen = screen.replace( + // eslint-disable-next-line no-control-regex + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, + '', + ); + + const contentGenerator = this.config.getContentGenerator(); + if (!contentGenerator) return; + + const prompt = `You are evaluating a CLI agent by simulating a user sitting at the terminal. +Here is the current terminal screen output: + + +${strippedScreen} + + +Look at the screen. Is the CLI waiting for your input (e.g., asking for tool confirmation, presenting a multi-choice question, or waiting for a text prompt with an indicator like "❯")? +If it is NOT waiting for input (e.g., it is streaming a response or showing a spinner), you MUST output exactly: + +If it IS waiting for your input, output ONLY the exact raw characters you would type. +Read the screen context carefully. Sometimes you may need to choose a numbered option, answer yes/no, or provide a text explanation. +For example: +- To choose an option from a multi-choice list, output the number: 1 +- To answer a simple yes/no confirmation, output: y +- To provide an explanation or answer an open-ended question, output the text followed by \n: Because it is required for the project\n +- 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 response = await contentGenerator.generateContent( + { + model: this.config.getModel(), + contents: [ + { + role: 'user', + parts: [{ text: prompt }], + }, + ], + }, + 'simulator-prompt', + LlmRole.UTILITY_SIMULATOR, + ); + + const responseText = (response.text || '').trim(); + if (responseText && responseText !== '') { + const keys = responseText.replace(/\\n/g, '\n'); + this.stdinBuffer.write(keys); + this.lastScreenContent = screen; + } + } catch (e: unknown) { + debugLogger.error('UserSimulator tick failed', e); + } finally { + this.isProcessing = false; + } + } +} diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index aa3e9aa5b6..32b954deb1 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -649,6 +649,7 @@ export interface ConfigParameters { billing?: { overageStrategy?: OverageStrategy; }; + simulateUser?: boolean; } export class Config implements McpContext, AgentLoopContext { @@ -866,6 +867,7 @@ export class Config implements McpContext, AgentLoopContext { private lastModeSwitchTime: number = performance.now(); readonly injectionService: InjectionService; private approvedPlanPath: string | undefined; + private readonly simulateUser: boolean; constructor(params: ConfigParameters) { this._sessionId = params.sessionId; @@ -1095,6 +1097,7 @@ export class Config implements McpContext, AgentLoopContext { this.fileExclusions = new FileExclusions(this); this.eventEmitter = params.eventEmitter; this.enableConseca = params.enableConseca ?? false; + this.simulateUser = params.simulateUser ?? false; // Initialize Safety Infrastructure const contextBuilder = new ContextBuilder(this); @@ -2509,6 +2512,10 @@ export class Config implements McpContext, AgentLoopContext { return this.usageStatisticsEnabled; } + getSimulateUser(): boolean { + return this.simulateUser; + } + getAcpMode(): boolean { return this.acpMode; } diff --git a/packages/core/src/telemetry/llmRole.ts b/packages/core/src/telemetry/llmRole.ts index 843ac4123c..b846dd8a39 100644 --- a/packages/core/src/telemetry/llmRole.ts +++ b/packages/core/src/telemetry/llmRole.ts @@ -16,4 +16,5 @@ export enum LlmRole { UTILITY_EDIT_CORRECTOR = 'utility_edit_corrector', UTILITY_AUTOCOMPLETE = 'utility_autocomplete', UTILITY_FAST_ACK_HELPER = 'utility_fast_ack_helper', + UTILITY_SIMULATOR = 'utility_simulator', } diff --git a/packages/vscode-ide-companion/package.json b/packages/vscode-ide-companion/package.json index ac47bbf0be..8947a3f487 100644 --- a/packages/vscode-ide-companion/package.json +++ b/packages/vscode-ide-companion/package.json @@ -129,7 +129,7 @@ "@types/vscode": "^1.99.0", "@typescript-eslint/eslint-plugin": "^8.31.1", "@typescript-eslint/parser": "^8.31.1", - "@vscode/vsce": "^3.6.0", + "@vscode/vsce": "^3.7.1", "esbuild": "^0.25.3", "eslint": "^9.25.1", "npm-run-all2": "^8.0.2",