Skip to content

Commit dd50d09

Browse files
authored
Merge pull request #311473 from microsoft/connor4312/terminal-nointeract
agentHost: set non-interactive env vars for tool-triggered terminals
1 parent 2b6f58f commit dd50d09

File tree

3 files changed

+19
-6
lines changed

3 files changed

+19
-6
lines changed

src/vs/platform/agentHost/node/agentHostTerminalManager.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface ICommandFinishedEvent {
3838
*/
3939
export interface IAgentHostTerminalManager {
4040
readonly _serviceBrand: undefined;
41-
createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean }): Promise<void>;
41+
createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise<void>;
4242
writeInput(uri: string, data: string): void;
4343
onData(uri: string, cb: (data: string) => void): IDisposable;
4444
onExit(uri: string, cb: (exitCode: number) => void): IDisposable;
@@ -172,7 +172,7 @@ export class AgentHostTerminalManager extends Disposable implements IAgentHostTe
172172
* Create a new terminal backed by node-pty.
173173
* Spawns the user's default shell.
174174
*/
175-
async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean }): Promise<void> {
175+
async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise<void> {
176176
const uri = params.terminal;
177177
if (this._terminals.has(uri)) {
178178
throw new Error(`Terminal already exists: ${uri}`);
@@ -199,6 +199,18 @@ export class AgentHostTerminalManager extends Disposable implements IAgentHostTe
199199
// prevents agent-executed commands from polluting the user's shell history.
200200
env['VSCODE_PREVENT_SHELL_HISTORY'] = '1';
201201
}
202+
if (options?.nonInteractive) {
203+
// Suppress paging and interactive prompts so that tool-spawned
204+
// terminals produce clean, machine-friendly output. An empty
205+
// string disables paging in git, less, and most CLI tools and
206+
// is safe on all platforms (unlike 'cat' which isn't on Windows PATH).
207+
env['LC_ALL'] = 'C.UTF-8';
208+
env['PAGER'] = '';
209+
env['GIT_PAGER'] = '';
210+
env['GH_PAGER'] = '';
211+
env['GIT_TERMINAL_PROMPT'] = '0';
212+
env['DEBIAN_FRONTEND'] = 'noninteractive';
213+
}
202214
let shellArgs: string[] = [];
203215

204216
const injection = await getShellIntegrationInjection(

src/vs/platform/agentHost/node/copilot/copilotShellTools.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export class ShellManager {
109109
claim,
110110
name: shellDisplayName,
111111
cwd: cwd ?? this._workingDirectory?.fsPath,
112-
}, { shell: getShellExecutable(shellType), preventShellHistory: true });
112+
}, { shell: getShellExecutable(shellType), preventShellHistory: true, nonInteractive: true });
113113

114114
const shell: IManagedShell = { id, terminalUri, shellType };
115115
this._shells.set(id, shell);

src/vs/platform/agentHost/test/node/copilotShellTools.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import { ShellManager, prefixForHistorySuppression } from '../../node/copilot/co
1919
class TestAgentHostTerminalManager implements IAgentHostTerminalManager {
2020
declare readonly _serviceBrand: undefined;
2121

22-
readonly created: { params: ICreateTerminalParams; options?: { shell?: string; preventShellHistory?: boolean } }[] = [];
22+
readonly created: { params: ICreateTerminalParams; options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean } }[] = [];
2323

24-
async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean }): Promise<void> {
24+
async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise<void> {
2525
this.created.push({ params, options });
2626
}
2727
writeInput(): void { }
@@ -66,7 +66,7 @@ suite('CopilotShellTools', () => {
6666
]);
6767
});
6868

69-
test('opts every managed shell into shell-history suppression', async () => {
69+
test('opts every managed shell into shell-history suppression and non-interactive mode', async () => {
7070
const terminalManager = new TestAgentHostTerminalManager();
7171
const services = new ServiceCollection();
7272
services.set(ILogService, new NullLogService());
@@ -79,6 +79,7 @@ suite('CopilotShellTools', () => {
7979

8080
assert.strictEqual(terminalManager.created.length, 1);
8181
assert.strictEqual(terminalManager.created[0].options?.preventShellHistory, true);
82+
assert.strictEqual(terminalManager.created[0].options?.nonInteractive, true);
8283
});
8384

8485
test('prefixForHistorySuppression prepends a space for POSIX shells, no-op for PowerShell', () => {

0 commit comments

Comments
 (0)