From 38334b1a2c1125e1cc3284996ff874f5b5b1aa4f Mon Sep 17 00:00:00 2001 From: Octopus Date: Fri, 3 Apr 2026 14:34:50 +0800 Subject: [PATCH 1/2] fix: downgrade console.error to console.debug in DocumentHistoryTracker.push() When onDidChangeVisibleTextEditors fires, deleteChain() calls push() on the DocumentHistoryTracker for the previous request filepath. Because onDidOpenTextDocument is currently disabled (pending PR #8364 merge), documents are never pre-added via addDocument(), so push() always takes the fallback path and logs a console.error on every editor switch. The fallback in push() already handles this gracefully by calling addDocument(), making the error log misleading noise. Downgrade it to console.debug so it remains accessible for debugging without polluting the Extension Host output. Fixes #11919 --- core/nextEdit/DocumentHistoryTracker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/nextEdit/DocumentHistoryTracker.ts b/core/nextEdit/DocumentHistoryTracker.ts index 7cca91f5653..436149b48f5 100644 --- a/core/nextEdit/DocumentHistoryTracker.ts +++ b/core/nextEdit/DocumentHistoryTracker.ts @@ -61,7 +61,7 @@ export class DocumentHistoryTracker { const documentHistory = this.documentContentHistoryMap.get(documentPath); if (!astHistory || !documentHistory) { - console.error(`Document ${documentPath} not found in AST tracker`); + console.debug(`Document ${documentPath} not found in AST tracker, adding it`); this.addDocument(documentPath, documentContent, ast); return; // Early return - document was added with initial state } From 26c16ad36e6f6bc273f6aabed287dce6ed5037d0 Mon Sep 17 00:00:00 2001 From: Octopus Date: Sat, 4 Apr 2026 13:03:03 +0800 Subject: [PATCH 2/2] fix: fall back to cmd.exe when PowerShell unavailable on Windows (fixes #11960) On Windows environments where PowerShell is blocked by corporate security policy (e.g., AppLocker), run_terminal_command would fail with 'spawn UNKNOWN'. This change introduces a module-level flag that is set when PowerShell fails to spawn (UNKNOWN or ENOENT error code). Once set, getShellCommand() falls back to cmd.exe via COMSPEC for all subsequent calls in the session, allowing the tool to work without further user intervention after the first failure. --- .../implementations/runTerminalCommand.ts | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/core/tools/implementations/runTerminalCommand.ts b/core/tools/implementations/runTerminalCommand.ts index 8f6201bd12c..5493bd7e290 100644 --- a/core/tools/implementations/runTerminalCommand.ts +++ b/core/tools/implementations/runTerminalCommand.ts @@ -21,13 +21,27 @@ function getDecodedOutput(data: Buffer): string { } else { return data.toString(); } -} // Simple helper function to use login shell on Unix/macOS and PowerShell on Windows +} + +// Tracks whether PowerShell failed to spawn on Windows (e.g., blocked by corporate policy) +// so subsequent calls can fall back to cmd.exe without retrying PowerShell. +let _powershellUnavailable = false; + +// Simple helper function to use login shell on Unix/macOS and PowerShell on Windows function getShellCommand(command: string): { shell: string; args: string[] } { if (process.platform === "win32") { - // Windows: Use PowerShell + if (!_powershellUnavailable) { + // Windows: prefer PowerShell for richer command support + return { + shell: "powershell.exe", + args: ["-NoLogo", "-ExecutionPolicy", "Bypass", "-Command", command], + }; + } + // PowerShell unavailable (e.g., blocked by corporate security policy); + // fall back to cmd.exe via COMSPEC. return { - shell: "powershell.exe", - args: ["-NoLogo", "-ExecutionPolicy", "Bypass", "-Command", command], + shell: process.env.COMSPEC || "cmd.exe", + args: ["/D", "/S", "/C", command], }; } else { // Unix/macOS: Use login shell to source .bashrc/.zshrc etc. @@ -339,7 +353,7 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => { } }); - childProc.on("error", (error) => { + childProc.on("error", (error: NodeJS.ErrnoException) => { // Clear timeout on error if (timeoutId) { clearTimeout(timeoutId); @@ -360,6 +374,15 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => { removeRunningProcess(toolCallId); } + // If PowerShell failed to spawn on Windows (e.g., blocked by corporate policy), + // mark it unavailable so future calls fall back to cmd.exe automatically. + if ( + process.platform === "win32" && + (error.code === "UNKNOWN" || error.code === "ENOENT") + ) { + _powershellUnavailable = true; + } + reject(error); }); }); @@ -458,7 +481,7 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => { } }); - childProc.on("error", (error) => { + childProc.on("error", (error: NodeJS.ErrnoException) => { // Clear timeout on error if (timeoutId) { clearTimeout(timeoutId); @@ -473,6 +496,16 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => { if (toolCallId) { removeRunningProcess(toolCallId); } + + // If PowerShell failed to spawn on Windows (e.g., blocked by corporate policy), + // mark it unavailable so future calls fall back to cmd.exe automatically. + if ( + process.platform === "win32" && + (error.code === "UNKNOWN" || error.code === "ENOENT") + ) { + _powershellUnavailable = true; + } + reject(error); }); }, @@ -521,10 +554,17 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => { } }); - childProc.on("error", () => { + childProc.on("error", (error: NodeJS.ErrnoException) => { if (isProcessBackgrounded(toolCallId)) { removeBackgroundedProcess(toolCallId); } + // If PowerShell failed to spawn on Windows, mark it unavailable for future calls. + if ( + process.platform === "win32" && + (error.code === "UNKNOWN" || error.code === "ENOENT") + ) { + _powershellUnavailable = true; + } }); // Unref the child to allow the Node.js process to exit