Skip to content
Open
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export interface ISandboxingDisabledOptions {
}
export type ISandboxingOptions = ISandboxingOnOptions | ISandboxingDisabledOptions;

function createPowerShellModelDescription(shell: string, sandboxingOptions: ISandboxingOptions): string {
function createPowerShellModelDescription(shell: string, sandboxingOptions: ISandboxingOptions, includeElevationGuidance: boolean): string {
const isWinPwsh = isWindowsPowerShell(shell);
const parts = [
`This tool allows you to execute ${isWinPwsh ? 'Windows PowerShell 5.1' : 'PowerShell'} commands in a persistent terminal session, preserving environment variables, working directory, and other context across multiple commands.`,
Expand Down Expand Up @@ -165,6 +165,9 @@ function createPowerShellModelDescription(shell: string, sandboxingOptions: ISan
'- Use Test-Path to check file/directory existence',
'- Be specific with Select-Object properties to avoid excessive output',
'- Avoid printing credentials unless absolutely required',
...(includeElevationGuidance ? [
'- Avoid commands that trigger an interactive elevation prompt, such as Start-Process -Verb RunAs or runas.exe. They block on a UAC/password prompt that cannot be answered in this mode, and secrets must never be routed through the model. If elevated privileges are required, tell the user to run the command themselves and stop — do NOT retry the command with variations.',
] : []),
`- NEVER run Start-Sleep or similar wait commands. You will be automatically notified on your next turn when async terminal commands or timed-out sync commands complete or need input. Do NOT poll for completion.`,
'- NEVER pipe interactive commands through Select-Object, Where-Object, or other filters — this hides prompts and prevents the terminal from detecting when input is needed. Run interactive commands without pipes.',
'',
Expand Down Expand Up @@ -269,7 +272,7 @@ export function createSandboxProperties(sandboxingOptions: ISandboxingOnOptions)
};
}

function createGenericDescription(sandboxingOptions: ISandboxingOptions): string {
function createGenericDescription(sandboxingOptions: ISandboxingOptions, includeElevationGuidance: boolean): string {
const parts = [`
Command Execution:
- Use && to chain simple commands on one line
Expand Down Expand Up @@ -312,7 +315,7 @@ Best Practices:
- Use find with -exec or xargs for file operations
- Be specific with commands to avoid excessive output
- Avoid printing credentials unless absolutely required
- NEVER run sleep or similar wait commands in a terminal. You will be automatically notified on your next turn when async terminal commands or timed-out sync commands complete or need input. Do NOT poll for completion.
${includeElevationGuidance ? '- Avoid commands that require interactive privilege escalation, such as sudo/su/doas without a non-interactive flag (e.g. sudo -n). They block on a password prompt that cannot be answered in this mode, and secrets must never be routed through the model. If a command needs elevated privileges, tell the user to run it themselves in the terminal and stop — do NOT retry the command with variations.\n' : ''}- NEVER run sleep or similar wait commands in a terminal. You will be automatically notified on your next turn when async terminal commands or timed-out sync commands complete or need input. Do NOT poll for completion.
- NEVER pipe interactive commands through tail, head, grep, or other filters — this hides prompts and prevents the terminal from detecting when input is needed. Run interactive commands without pipes.

Interactive Input Handling:
Expand All @@ -325,19 +328,19 @@ Interactive Input Handling:
return parts.join('');
}

function createBashModelDescription(sandboxingOptions: ISandboxingOptions): string {
function createBashModelDescription(sandboxingOptions: ISandboxingOptions, includeElevationGuidance: boolean): string {
return [
'This tool allows you to execute shell commands in a persistent bash terminal session, preserving environment variables, working directory, and other context across multiple commands.',
createGenericDescription(sandboxingOptions),
createGenericDescription(sandboxingOptions, includeElevationGuidance),
'- Use [[ ]] for conditional tests instead of [ ]',
'- Prefer $() over backticks for command substitution'
].join('\n');
}

function createZshModelDescription(sandboxingOptions: ISandboxingOptions): string {
function createZshModelDescription(sandboxingOptions: ISandboxingOptions, includeElevationGuidance: boolean): string {
return [
'This tool allows you to execute shell commands in a persistent zsh terminal session, preserving environment variables, working directory, and other context across multiple commands.',
createGenericDescription(sandboxingOptions),
createGenericDescription(sandboxingOptions, includeElevationGuidance),
'- Use type to check command type (builtin, function, alias)',
'- Use jobs, fg, bg for job control',
'- Use [[ ]] for conditional tests instead of [ ]',
Expand All @@ -350,10 +353,10 @@ function createZshModelDescription(sandboxingOptions: ISandboxingOptions): strin
].join('\n');
}

function createFishModelDescription(sandboxingOptions: ISandboxingOptions): string {
function createFishModelDescription(sandboxingOptions: ISandboxingOptions, includeElevationGuidance: boolean): string {
return [
'This tool allows you to execute shell commands in a persistent fish terminal session, preserving environment variables, working directory, and other context across multiple commands.',
createGenericDescription(sandboxingOptions),
createGenericDescription(sandboxingOptions, includeElevationGuidance),
'- Use type to check command type (builtin, function, alias)',
'- Use jobs, fg, bg for job control',
'- Use test expressions for conditionals (no [[ ]] syntax)',
Expand All @@ -371,6 +374,11 @@ export async function createRunInTerminalToolData(
const configurationService = accessor.get(IConfigurationService);
const allowToRunUnsandboxedCommands = configurationService.getValue<boolean>(AgentSandboxSettingId.AgentSandboxAllowUnsandboxedCommands) === true;
const retryWithAllowNetworkRequestsSetting = configurationService.getValue<boolean>(AgentSandboxSettingId.AgentSandboxRetryWithAllowNetworkRequests) === true;
// Only steer the model away from interactive privilege-escalation commands when auto-approve is
// enabled. In interactive mode the user can focus the terminal and type a password/UAC prompt
// directly (bypassing the model), which is a supported flow; in auto-approve/autopilot mode such
// prompts are cancelled since no human is available to answer them.
const includeElevationGuidance = configurationService.getValue(TerminalChatAgentToolsSettingId.EnableAutoApprove) === true;

const profileFetcher = instantiationService.createInstance(TerminalProfileFetcher);
const [shell, os, isSandboxEnabled, isSandboxAllowNetworkEnabled] = await Promise.all([
Expand Down Expand Up @@ -399,13 +407,13 @@ export async function createRunInTerminalToolData(

let modelDescription: string;
if (shell && os && isPowerShell(shell, os)) {
modelDescription = createPowerShellModelDescription(shell, sandboxingOptions);
modelDescription = createPowerShellModelDescription(shell, sandboxingOptions, includeElevationGuidance);
} else if (shell && os && isZsh(shell, os)) {
modelDescription = createZshModelDescription(sandboxingOptions);
modelDescription = createZshModelDescription(sandboxingOptions, includeElevationGuidance);
} else if (shell && os && isFish(shell, os)) {
modelDescription = createFishModelDescription(sandboxingOptions);
modelDescription = createFishModelDescription(sandboxingOptions, includeElevationGuidance);
} else {
modelDescription = createBashModelDescription(sandboxingOptions);
modelDescription = createBashModelDescription(sandboxingOptions, includeElevationGuidance);
}

const sharedProperties: IJSONSchemaMap = {
Expand Down
Loading