diff --git a/src/vs/platform/agentHost/node/agentHostTerminalManager.ts b/src/vs/platform/agentHost/node/agentHostTerminalManager.ts index 3193302c67255..f0a77734e4029 100644 --- a/src/vs/platform/agentHost/node/agentHostTerminalManager.ts +++ b/src/vs/platform/agentHost/node/agentHostTerminalManager.ts @@ -38,7 +38,7 @@ export interface ICommandFinishedEvent { */ export interface IAgentHostTerminalManager { readonly _serviceBrand: undefined; - createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean }): Promise; + createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise; writeInput(uri: string, data: string): void; onData(uri: string, cb: (data: string) => void): IDisposable; onExit(uri: string, cb: (exitCode: number) => void): IDisposable; @@ -172,7 +172,7 @@ export class AgentHostTerminalManager extends Disposable implements IAgentHostTe * Create a new terminal backed by node-pty. * Spawns the user's default shell. */ - async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean }): Promise { + async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise { const uri = params.terminal; if (this._terminals.has(uri)) { throw new Error(`Terminal already exists: ${uri}`); @@ -199,6 +199,18 @@ export class AgentHostTerminalManager extends Disposable implements IAgentHostTe // prevents agent-executed commands from polluting the user's shell history. env['VSCODE_PREVENT_SHELL_HISTORY'] = '1'; } + if (options?.nonInteractive) { + // Suppress paging and interactive prompts so that tool-spawned + // terminals produce clean, machine-friendly output. An empty + // string disables paging in git, less, and most CLI tools and + // is safe on all platforms (unlike 'cat' which isn't on Windows PATH). + env['LC_ALL'] = 'C.UTF-8'; + env['PAGER'] = ''; + env['GIT_PAGER'] = ''; + env['GH_PAGER'] = ''; + env['GIT_TERMINAL_PROMPT'] = '0'; + env['DEBIAN_FRONTEND'] = 'noninteractive'; + } let shellArgs: string[] = []; const injection = await getShellIntegrationInjection( diff --git a/src/vs/platform/agentHost/node/copilot/copilotShellTools.ts b/src/vs/platform/agentHost/node/copilot/copilotShellTools.ts index c944e862b4441..9282fbd2ef529 100644 --- a/src/vs/platform/agentHost/node/copilot/copilotShellTools.ts +++ b/src/vs/platform/agentHost/node/copilot/copilotShellTools.ts @@ -109,7 +109,7 @@ export class ShellManager { claim, name: shellDisplayName, cwd: cwd ?? this._workingDirectory?.fsPath, - }, { shell: getShellExecutable(shellType), preventShellHistory: true }); + }, { shell: getShellExecutable(shellType), preventShellHistory: true, nonInteractive: true }); const shell: IManagedShell = { id, terminalUri, shellType }; this._shells.set(id, shell); diff --git a/src/vs/platform/agentHost/test/node/copilotShellTools.test.ts b/src/vs/platform/agentHost/test/node/copilotShellTools.test.ts index 59386ce3b3fa6..ef56bf3e74f22 100644 --- a/src/vs/platform/agentHost/test/node/copilotShellTools.test.ts +++ b/src/vs/platform/agentHost/test/node/copilotShellTools.test.ts @@ -19,9 +19,9 @@ import { ShellManager, prefixForHistorySuppression } from '../../node/copilot/co class TestAgentHostTerminalManager implements IAgentHostTerminalManager { declare readonly _serviceBrand: undefined; - readonly created: { params: ICreateTerminalParams; options?: { shell?: string; preventShellHistory?: boolean } }[] = []; + readonly created: { params: ICreateTerminalParams; options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean } }[] = []; - async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean }): Promise { + async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise { this.created.push({ params, options }); } writeInput(): void { } @@ -66,7 +66,7 @@ suite('CopilotShellTools', () => { ]); }); - test('opts every managed shell into shell-history suppression', async () => { + test('opts every managed shell into shell-history suppression and non-interactive mode', async () => { const terminalManager = new TestAgentHostTerminalManager(); const services = new ServiceCollection(); services.set(ILogService, new NullLogService()); @@ -79,6 +79,7 @@ suite('CopilotShellTools', () => { assert.strictEqual(terminalManager.created.length, 1); assert.strictEqual(terminalManager.created[0].options?.preventShellHistory, true); + assert.strictEqual(terminalManager.created[0].options?.nonInteractive, true); }); test('prefixForHistorySuppression prepends a space for POSIX shells, no-op for PowerShell', () => {