From 303598f0146b09b867a909940fc20391c8fbeeb4 Mon Sep 17 00:00:00 2001 From: cte Date: Wed, 7 Jan 2026 11:13:37 -0800 Subject: [PATCH] More progress --- apps/cli/src/constants.ts | 5 ++ apps/cli/src/extension-host.ts | 122 +++++++++------------------------ apps/cli/src/ui/App.tsx | 4 +- 3 files changed, 41 insertions(+), 90 deletions(-) create mode 100644 apps/cli/src/constants.ts diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts new file mode 100644 index 0000000000..98c1b09122 --- /dev/null +++ b/apps/cli/src/constants.ts @@ -0,0 +1,5 @@ +/** + * Default timeout in seconds for auto-approving followup questions. + * Used in both the TUI (App.tsx) and the extension host (extension-host.ts). + */ +export const FOLLOWUP_TIMEOUT_SECONDS = 60 diff --git a/apps/cli/src/extension-host.ts b/apps/cli/src/extension-host.ts index 6f7ec6e01e..227f0780eb 100644 --- a/apps/cli/src/extension-host.ts +++ b/apps/cli/src/extension-host.ts @@ -25,6 +25,7 @@ import { } from "@roo-code/types" import { createVSCodeAPI, setRuntimeConfigValues } from "@roo-code/vscode-shim" import { debugLog } from "@roo-code/core/debug-log" +import { FOLLOWUP_TIMEOUT_SECONDS } from "./constants.js" // Get the CLI package root directory (for finding node_modules/@vscode/ripgrep) // When bundled, import.meta.url points to dist/index.js, so go up to package root @@ -549,46 +550,36 @@ export class ExtensionHost extends EventEmitter { this.messageListener = (message: ExtensionMessage) => this.handleExtensionMessage(message) this.on("extensionWebviewMessage", this.messageListener) - let defaultSettings: RooCodeSettings - - if (this.options.nonInteractive) { - defaultSettings = { - autoApprovalEnabled: true, - alwaysAllowReadOnly: true, - alwaysAllowReadOnlyOutsideWorkspace: true, - alwaysAllowWrite: true, - alwaysAllowWriteOutsideWorkspace: true, - alwaysAllowWriteProtected: false, - alwaysAllowBrowser: true, - alwaysAllowMcp: true, - alwaysAllowModeSwitch: true, - alwaysAllowSubtasks: true, - alwaysAllowExecute: true, - alwaysAllowFollowupQuestions: true, - // NOTE: Setting to 0 should disable extension's internal timeout, - // but we need to verify this is working correctly. - followupAutoApproveTimeoutMs: 0, - allowedCommands: ["*"], - commandExecutionTimeout: 20, - enableCheckpoints: false, // Checkpoints disabled until CLI UI is implemented. - } - } else { - defaultSettings = { - autoApprovalEnabled: false, - enableCheckpoints: false, - } + const baseSettings: RooCodeSettings = { + commandExecutionTimeout: 30, + enableCheckpoints: false, // Checkpoints disabled until CLI UI is implemented. + ...this.buildApiConfiguration(), } - const settings = { ...defaultSettings, ...this.buildApiConfiguration() } + const settings: RooCodeSettings = this.options.nonInteractive + ? { + autoApprovalEnabled: true, + alwaysAllowReadOnly: true, + alwaysAllowReadOnlyOutsideWorkspace: true, + alwaysAllowWrite: true, + alwaysAllowWriteOutsideWorkspace: true, + alwaysAllowWriteProtected: false, + alwaysAllowBrowser: true, + alwaysAllowMcp: true, + alwaysAllowModeSwitch: true, + alwaysAllowSubtasks: true, + alwaysAllowExecute: true, + allowedCommands: ["*"], + ...baseSettings, + } + : { + autoApprovalEnabled: false, + ...baseSettings, + } + this.applyRuntimeSettings(settings) - - this.sendToExtension({ - type: "updateSettings", - updatedSettings: settings, - }) - + this.sendToExtension({ type: "updateSettings", updatedSettings: settings }) await new Promise((resolve) => setTimeout(resolve, 100)) - this.sendToExtension({ type: "newTask", text: prompt }) await this.waitForCompletion() } @@ -641,50 +632,6 @@ export class ExtensionHost extends EventEmitter { process.stderr.write(text + "\n") } - /** - * Get the expected model ID from CLI options - */ - private getExpectedModelId(): string | undefined { - return this.options.model - } - - /** - * Get the current model ID from state's apiConfiguration - */ - private getStateModelId(apiConfig: Record | undefined): string | undefined { - if (!apiConfig) { - return undefined - } - - const provider = apiConfig.apiProvider as string | undefined - switch (provider) { - case "openrouter": - return apiConfig.openRouterModelId as string | undefined - case "openai": - return apiConfig.openAiModelId as string | undefined - case "ollama": - return apiConfig.ollamaModelId as string | undefined - case "litellm": - return apiConfig.litellmModelId as string | undefined - case "lmstudio": - return apiConfig.lmStudioModelId as string | undefined - case "huggingface": - return apiConfig.huggingFaceModelId as string | undefined - case "unbound": - return apiConfig.unboundModelId as string | undefined - case "requesty": - return apiConfig.requestyModelId as string | undefined - case "deepinfra": - return apiConfig.deepInfraModelId as string | undefined - case "vercel-ai-gateway": - return apiConfig.vercelAiGatewayModelId as string | undefined - case "io-intelligence": - return apiConfig.ioIntelligenceModelId as string | undefined - default: - return apiConfig.apiModelId as string | undefined - } - } - /** * Handle state update messages from the extension. */ @@ -1232,8 +1179,7 @@ export class ExtensionHost extends EventEmitter { const firstSuggestion = suggestions.length > 0 ? suggestions[0] : null const defaultAnswer = firstSuggestion?.answer ?? "" - // Default timeout is 10 seconds for testing (will be configurable later). - const timeoutMs = 60_000 + const timeoutMs = FOLLOWUP_TIMEOUT_SECONDS * 1000 try { const answer = await this.promptForInputWithTimeout( @@ -1280,7 +1226,7 @@ export class ExtensionHost extends EventEmitter { this.restoreConsole() } - // Put stdin in raw mode to detect individual keypresses + // Put stdin in raw mode to detect individual keypresses. const wasRaw = process.stdin.isRaw if (process.stdin.isTTY) { @@ -1293,7 +1239,7 @@ export class ExtensionHost extends EventEmitter { let timeoutCancelled = false let resolved = false - // Set up the timeout + // Set up the timeout. const timeout = setTimeout(() => { if (!resolved) { resolved = true @@ -1303,10 +1249,10 @@ export class ExtensionHost extends EventEmitter { } }, timeoutMs) - // Show the prompt + // Show the prompt. process.stdout.write(prompt) - // Cleanup function + // Cleanup function. const cleanup = () => { clearTimeout(timeout) process.stdin.removeListener("data", onData) @@ -1700,7 +1646,7 @@ export class ExtensionHost extends EventEmitter { this.once("taskComplete", completeHandler) this.once("taskError", errorHandler) - // Set a timeout (10 minutes by default) + // Set a timeout (10 minutes by default). const timeout = setTimeout( () => { cleanup() @@ -1709,7 +1655,7 @@ export class ExtensionHost extends EventEmitter { 10 * 60 * 1000, ) - // Clear timeout on completion + // Clear timeout on completion. this.once("taskComplete", () => clearTimeout(timeout)) this.once("taskError", () => clearTimeout(timeout)) }) diff --git a/apps/cli/src/ui/App.tsx b/apps/cli/src/ui/App.tsx index ff43b7b886..b0f4c5ec73 100644 --- a/apps/cli/src/ui/App.tsx +++ b/apps/cli/src/ui/App.tsx @@ -34,6 +34,7 @@ import { ScrollArea, useScrollToBottom } from "./components/ScrollArea.js" import ScrollIndicator from "./components/ScrollIndicator.js" import { TerminalSizeProvider, useTerminalSize } from "./hooks/TerminalSizeContext.js" import * as theme from "./utils/theme.js" +import { FOLLOWUP_TIMEOUT_SECONDS } from "../constants.js" import type { AppProps, TUIMessage, @@ -230,8 +231,7 @@ function AppInner({ const exitHintTimeout = useRef(null) const pendingExit = useRef(false) - // Countdown timer for auto-accepting followup questions (10 seconds for testing) - const FOLLOWUP_TIMEOUT_SECONDS = 10 + // Countdown timer for auto-accepting followup questions const [countdownSeconds, setCountdownSeconds] = useState(null) const countdownIntervalRef = useRef(null)