From a0d52d4d598853544bb9f49f2d649f1bb2716d9f Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 20 Feb 2026 11:16:29 -0800 Subject: [PATCH] cli yolo mode should not persist yolo setting to disk ever (#9370) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - added a method to StateManager, setSessionOverride, which overrides state settings while the statemanager lives in memory Co-authored-by: Max Paulus 🥪 --- cli/src/index.ts | 32 +++----------------------------- src/core/storage/StateManager.ts | 22 +++++++++++++++++++++- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/cli/src/index.ts b/cli/src/index.ts index 87287781ea..a184a8fcd0 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -73,24 +73,7 @@ async function disposeTelemetryServices(): Promise { await Promise.allSettled([telemetryService.dispose(), PostHogClientProvider.getInstance().dispose()]) } -/** - * Restore yoloModeToggled to its original value from before this CLI session. - * This ensures the --yolo flag is session-only and doesn't leak into future runs. - * Must be called before flushPendingState so the restored value gets persisted. - */ -function restoreYoloState(): void { - if (savedYoloModeToggled !== null) { - try { - StateManager.get().setGlobalState("yoloModeToggled", savedYoloModeToggled) - savedYoloModeToggled = null - } catch { - // StateManager may not be initialized (e.g., early exit before init) - } - } -} - async function disposeCliContext(ctx: CliContext): Promise { - restoreYoloState() await ctx.controller.stateManager.flushPendingState() await ctx.controller.dispose() await ErrorService.get().dispose() @@ -203,12 +186,10 @@ function applyTaskOptions(options: TaskOptions): void { telemetryService.captureHostEvent("max_consecutive_mistakes_flag", String(maxConsecutiveMistakes)) } - // Override yolo mode only if --yolo flag is explicitly passed. - // The original value is saved in initializeCli and restored on exit. + // Set yolo mode as a session-scoped override so AutoApprove picks it up, + // but it is never persisted to disk (setSessionOverride never touches pendingGlobalState). if (options.yolo) { - const state = StateManager.get() - savedYoloModeToggled = state.getGlobalSettingsKey("yoloModeToggled") ?? false - state.setGlobalState("yoloModeToggled", true) + StateManager.get().setSessionOverride("yoloModeToggled", true) telemetryService.captureHostEvent("yolo_flag", "true") } @@ -313,9 +294,6 @@ let activeContext: CliContext | null = null let isShuttingDown = false // Track if we're in plain text mode (no Ink UI) - set by runTask when piped stdin detected let isPlainTextMode = false -// Track the original yoloModeToggled value from before this CLI session so we can restore it on exit. -// The --yolo flag should only affect the current invocation, not persist across runs. -let savedYoloModeToggled: boolean | null = null /** * Wait for stdout to fully drain before exiting. @@ -357,10 +335,6 @@ function setupSignalHandlers() { printWarning(`${signal} received, shutting down...`) try { - // Restore yolo state before any cleanup - this is idempotent and safe - // even if disposeCliContext also calls it (restoreYoloState checks savedYoloModeToggled !== null) - restoreYoloState() - if (activeContext) { const task = activeContext.controller.task if (task) { diff --git a/src/core/storage/StateManager.ts b/src/core/storage/StateManager.ts index d6e0bc6e63..e4084c745e 100644 --- a/src/core/storage/StateManager.ts +++ b/src/core/storage/StateManager.ts @@ -59,6 +59,7 @@ export class StateManager { private globalStateCache: GlobalStateAndSettings = {} as GlobalStateAndSettings private taskStateCache: Partial = {} + private sessionOverrideCache: Partial = {} private remoteConfigCache: Partial = {} as RemoteConfigFields private secretsCache: Secrets = {} as Secrets private workspaceStateCache: LocalState = {} as LocalState @@ -379,6 +380,21 @@ export class StateManager { this.scheduleDebouncedPersistence() } + /** + * Set a session-scoped override for a settings key. + * Session overrides are in-memory only and are NEVER persisted to disk. + * They take precedence after remote config but before task-specific and global settings. + * + * Use this for CLI flags like --yolo that should apply for the current + * process lifetime only, without modifying the user's saved settings. + */ + setSessionOverride(key: K, value: Settings[K]): void { + if (!this.isInitialized) { + throw new Error(STATE_MANAGER_NOT_INITIALIZED) + } + this.sessionOverrideCache[key] = value + } + /** * Set method for remote config field - updates cache immediately (no persistence) * Remote config is read-only from the extension's perspective and only stored in memory @@ -604,7 +620,7 @@ export class StateManager { /** * Get method for global settings keys - reads from in-memory cache - * Precedence: remote config > task settings > global settings + * Precedence: remote config > session override > task settings > global settings */ getGlobalSettingsKey(key: K): Settings[K] { if (!this.isInitialized) { @@ -613,6 +629,9 @@ export class StateManager { if (this.remoteConfigCache[key] !== undefined) { return this.remoteConfigCache[key] as Settings[K] } + if (this.sessionOverrideCache[key] !== undefined) { + return this.sessionOverrideCache[key] as Settings[K] + } if (this.taskStateCache[key] !== undefined) { return this.taskStateCache[key] } @@ -696,6 +715,7 @@ export class StateManager { this.workspaceStateCache = {} as LocalState this.taskStateCache = {} this.remoteConfigCache = {} as GlobalStateAndSettings + this.sessionOverrideCache = {} this.isInitialized = false }