mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-19 16:18:58 +01:00
Preserve $TMPDIR when retrying terminal commands outside the sandbox (#304601)
* Changes to include environment variables for retry without sandboxing * adding e2e tests * fixing code formatting
This commit is contained in:
@@ -71,7 +71,7 @@ function extractTextContent(result: vscode.LanguageModelToolResult): string {
|
||||
participantRegistered = false;
|
||||
pendingResult = undefined;
|
||||
pendingCommand = undefined;
|
||||
pendingTimeout = undefined;
|
||||
pendingOptions = undefined;
|
||||
|
||||
const chatToolsConfig = vscode.workspace.getConfiguration('chat.tools.global');
|
||||
await chatToolsConfig.update('autoApprove', undefined, vscode.ConfigurationTarget.Global);
|
||||
@@ -82,10 +82,16 @@ function extractTextContent(result: vscode.LanguageModelToolResult): string {
|
||||
* Helper: invokes run_in_terminal via a chat participant and returns the tool result text.
|
||||
* Each call creates a new chat session to avoid participant re-registration issues.
|
||||
*/
|
||||
interface RunInTerminalOptions {
|
||||
timeout?: number;
|
||||
requestUnsandboxedExecution?: boolean;
|
||||
requestUnsandboxedExecutionReason?: string;
|
||||
}
|
||||
|
||||
let participantRegistered = false;
|
||||
let pendingResult: DeferredPromise<vscode.LanguageModelToolResult> | undefined;
|
||||
let pendingCommand: string | undefined;
|
||||
let pendingTimeout: number | undefined;
|
||||
let pendingOptions: RunInTerminalOptions | undefined;
|
||||
|
||||
function setupParticipant() {
|
||||
if (participantRegistered) {
|
||||
@@ -98,10 +104,10 @@ function extractTextContent(result: vscode.LanguageModelToolResult): string {
|
||||
}
|
||||
const currentResult = pendingResult;
|
||||
const currentCommand = pendingCommand;
|
||||
const currentTimeout = pendingTimeout ?? 15000;
|
||||
const currentOptions = pendingOptions ?? {};
|
||||
pendingResult = undefined;
|
||||
pendingCommand = undefined;
|
||||
pendingTimeout = undefined;
|
||||
pendingOptions = undefined;
|
||||
try {
|
||||
const result = await vscode.lm.invokeTool('run_in_terminal', {
|
||||
input: {
|
||||
@@ -109,7 +115,11 @@ function extractTextContent(result: vscode.LanguageModelToolResult): string {
|
||||
explanation: 'Integration test command',
|
||||
goal: 'Test run_in_terminal output',
|
||||
isBackground: false,
|
||||
timeout: currentTimeout
|
||||
timeout: currentOptions.timeout ?? 15000,
|
||||
...currentOptions.requestUnsandboxedExecution ? {
|
||||
requestUnsandboxedExecution: true,
|
||||
requestUnsandboxedExecutionReason: currentOptions.requestUnsandboxedExecutionReason,
|
||||
} : {},
|
||||
},
|
||||
toolInvocationToken: request.toolInvocationToken,
|
||||
});
|
||||
@@ -122,13 +132,18 @@ function extractTextContent(result: vscode.LanguageModelToolResult): string {
|
||||
disposables.push(participant);
|
||||
}
|
||||
|
||||
async function invokeRunInTerminal(command: string, timeout = 15000): Promise<string> {
|
||||
async function invokeRunInTerminal(command: string, options?: RunInTerminalOptions): Promise<string>;
|
||||
async function invokeRunInTerminal(command: string, timeout?: number): Promise<string>;
|
||||
async function invokeRunInTerminal(command: string, optionsOrTimeout?: RunInTerminalOptions | number): Promise<string> {
|
||||
setupParticipant();
|
||||
|
||||
const opts: RunInTerminalOptions = typeof optionsOrTimeout === 'number'
|
||||
? { timeout: optionsOrTimeout }
|
||||
: optionsOrTimeout ?? {};
|
||||
const resultPromise = new DeferredPromise<vscode.LanguageModelToolResult>();
|
||||
pendingResult = resultPromise;
|
||||
pendingCommand = command;
|
||||
pendingTimeout = timeout;
|
||||
pendingOptions = opts;
|
||||
|
||||
await vscode.commands.executeCommand('workbench.action.chat.newChat');
|
||||
vscode.commands.executeCommand('workbench.action.chat.open', { query: '@participant test' });
|
||||
@@ -326,6 +341,27 @@ function extractTextContent(result: vscode.LanguageModelToolResult): string {
|
||||
assert.ok(acceptable.includes(output.trim()), `Unexpected output: ${JSON.stringify(output.trim())}`);
|
||||
});
|
||||
|
||||
test('requestUnsandboxedExecution preserves sandbox $TMPDIR', async function () {
|
||||
this.timeout(60000);
|
||||
|
||||
const marker = `SANDBOX_UNSANDBOX_${Date.now()}`;
|
||||
const sentinelName = `sentinel-${marker}.txt`;
|
||||
|
||||
// Step 1: Write a sentinel file into the sandbox-provided $TMPDIR.
|
||||
const writeOutput = await invokeRunInTerminal(`echo ${marker} > "$TMPDIR/${sentinelName}" && echo ${marker}`);
|
||||
assert.strictEqual(writeOutput.trim(), marker);
|
||||
|
||||
// Step 2: Retry with requestUnsandboxedExecution=true while sandbox
|
||||
// stays enabled. The tool should preserve $TMPDIR from the sandbox so
|
||||
// the sentinel file created in step 1 is still accessible.
|
||||
const retryOutput = await invokeRunInTerminal(`cat "$TMPDIR/${sentinelName}"`, {
|
||||
timeout: 30000,
|
||||
requestUnsandboxedExecution: true,
|
||||
requestUnsandboxedExecutionReason: 'Need to verify $TMPDIR persists on unsandboxed retry',
|
||||
});
|
||||
assert.strictEqual(retryOutput.trim(), marker);
|
||||
});
|
||||
|
||||
test('cannot write to /tmp', async function () {
|
||||
this.timeout(60000);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user