Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/vs/platform/agentHost/node/agentHostTerminalManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface ICommandFinishedEvent {
*/
export interface IAgentHostTerminalManager {
readonly _serviceBrand: undefined;
createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean }): Promise<void>;
createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise<void>;
writeInput(uri: string, data: string): void;
onData(uri: string, cb: (data: string) => void): IDisposable;
onExit(uri: string, cb: (exitCode: number) => void): IDisposable;
Expand Down Expand Up @@ -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<void> {
async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise<void> {
const uri = params.terminal;
if (this._terminals.has(uri)) {
throw new Error(`Terminal already exists: ${uri}`);
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Comment thread
connor4312 marked this conversation as resolved.

const shell: IManagedShell = { id, terminalUri, shellType };
this._shells.set(id, shell);
Expand Down
7 changes: 4 additions & 3 deletions src/vs/platform/agentHost/test/node/copilotShellTools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
async createTerminal(params: ICreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise<void> {
this.created.push({ params, options });
}
writeInput(): void { }
Expand Down Expand Up @@ -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());
Expand All @@ -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', () => {
Expand Down
Loading