diff --git a/package-lock.json b/package-lock.json index b14a6d39f8e28..f2c0285665aa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@anthropic-ai/sandbox-runtime": "0.0.23", "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", "@parcel/watcher": "^2.5.4", @@ -178,6 +179,35 @@ "node": ">=6.0.0" } }, + "node_modules/@anthropic-ai/sandbox-runtime": { + "version": "0.0.23", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sandbox-runtime/-/sandbox-runtime-0.0.23.tgz", + "integrity": "sha512-Np0VRH6D71cGoJZvd8hCz1LMfwg9ERJovrOJSCz5aSQSQJPWPNIFPV1wfc8oAhJpStOuYkot+EmXOkRRxuGMCQ==", + "license": "Apache-2.0", + "dependencies": { + "@pondwader/socks5-server": "^1.0.10", + "@types/lodash-es": "^4.17.12", + "commander": "^12.1.0", + "lodash-es": "^4.17.21", + "shell-quote": "^1.8.3", + "zod": "^3.24.1" + }, + "bin": { + "srt": "dist/cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@anthropic-ai/sandbox-runtime/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@azure-rest/ai-translation-text": { "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/@azure-rest/ai-translation-text/-/ai-translation-text-1.0.0-beta.1.tgz", @@ -2009,6 +2039,12 @@ "node": ">=18" } }, + "node_modules/@pondwader/socks5-server": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz", + "integrity": "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg==", + "license": "MIT" + }, "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", @@ -2305,6 +2341,21 @@ "@types/node": "*" } }, + "node_modules/@types/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -11811,6 +11862,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash-es": { + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz", + "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==", + "license": "MIT" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -15386,10 +15443,16 @@ } }, "node_modules/shell-quote": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", - "dev": true + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/sigmund": { "version": "1.0.1", @@ -18310,6 +18373,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zx": { "version": "8.8.5", "resolved": "https://registry.npmjs.org/zx/-/zx-8.8.5.tgz", diff --git a/package.json b/package.json index f698bd3808f68..32aef3605e73e 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "update-build-ts-version": "npm install -D typescript@next && npm install -D @typescript/native-preview && (cd build && npm run typecheck)" }, "dependencies": { + "@anthropic-ai/sandbox-runtime": "0.0.23", "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", "@parcel/watcher": "^2.5.4", @@ -240,4 +241,4 @@ "optionalDependencies": { "windows-foreground-love": "0.5.0" } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/terminal/terminalContribExports.ts b/src/vs/workbench/contrib/terminal/terminalContribExports.ts index 59f7bae295e0d..1fd3bb600a07d 100644 --- a/src/vs/workbench/contrib/terminal/terminalContribExports.ts +++ b/src/vs/workbench/contrib/terminal/terminalContribExports.ts @@ -43,7 +43,7 @@ export const enum TerminalContribSettingId { AutoApprove = TerminalChatAgentToolsSettingId.AutoApprove, EnableAutoApprove = TerminalChatAgentToolsSettingId.EnableAutoApprove, ShellIntegrationTimeout = TerminalChatAgentToolsSettingId.ShellIntegrationTimeout, - OutputLocation = TerminalChatAgentToolsSettingId.OutputLocation + OutputLocation = TerminalChatAgentToolsSettingId.OutputLocation, } // HACK: Export some context key strings from `terminalContrib/` that are depended upon elsewhere. diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/terminal.chatAgentTools.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/terminal.chatAgentTools.contribution.ts index e9f18f15afac7..db7751a57b091 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/terminal.chatAgentTools.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/terminal.chatAgentTools.contribution.ts @@ -29,6 +29,14 @@ import { RunInTerminalTool, createRunInTerminalToolData } from './tools/runInTer import { CreateAndRunTaskTool, CreateAndRunTaskToolData } from './tools/task/createAndRunTaskTool.js'; import { GetTaskOutputTool, GetTaskOutputToolData } from './tools/task/getTaskOutputTool.js'; import { RunTaskTool, RunTaskToolData } from './tools/task/runTaskTool.js'; +import { InstantiationType, registerSingleton } from '../../../../../platform/instantiation/common/extensions.js'; +import { ITerminalSandboxService, TerminalSandboxService } from '../common/terminalSandboxService.js'; + +// #region Services + +registerSingleton(ITerminalSandboxService, TerminalSandboxService, InstantiationType.Delayed); + +// #endregion Services class ShellIntegrationTimeoutMigrationContribution extends Disposable implements IWorkbenchContribution { static readonly ID = 'terminal.shellIntegrationTimeoutMigration'; diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts index fdfbcb4308f39..594fa998d6bee 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -20,7 +20,7 @@ import { localize } from '../../../../../../nls.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { IInstantiationService, type ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../../../platform/storage/common/storage.js'; -import { TerminalCapability } from '../../../../../../platform/terminal/common/capabilities/capabilities.js'; +import { ICommandDetectionCapability, TerminalCapability } from '../../../../../../platform/terminal/common/capabilities/capabilities.js'; import { ITerminalLogService, ITerminalProfile } from '../../../../../../platform/terminal/common/terminal.js'; import { IRemoteAgentService } from '../../../../../services/remote/common/remoteAgentService.js'; import { TerminalToolConfirmationStorageKeys } from '../../../../chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolConfirmationSubPart.js'; @@ -45,6 +45,7 @@ import { CommandLineAutoApproveAnalyzer } from './commandLineAnalyzer/commandLin import { CommandLineFileWriteAnalyzer } from './commandLineAnalyzer/commandLineFileWriteAnalyzer.js'; import { OutputMonitor } from './monitoring/outputMonitor.js'; import { IPollingResult, OutputMonitorState } from './monitoring/types.js'; +import { ITerminalSandboxService } from '../../common/terminalSandboxService.js'; import { chatSessionResourceToId, LocalChatSessionUri } from '../../../../chat/common/model/chatUri.js'; import { URI } from '../../../../../../base/common/uri.js'; import type { ICommandLineRewriter } from './commandLineRewriter/commandLineRewriter.js'; @@ -308,6 +309,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { @ITerminalService private readonly _terminalService: ITerminalService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, + @ITerminalSandboxService private readonly _sandboxService: ITerminalSandboxService, ) { super(); @@ -336,6 +338,15 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { this._storageService.remove(TerminalToolConfirmationStorageKeys.TerminalAutoApproveWarningAccepted, StorageScope.APPLICATION); } } + // If terminal sandbox settings changed, update sandbox config. + if ( + e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxEnabled) || + e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetwork) || + e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem) || + e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem) + ) { + this._sandboxService.setNeedsForceUpdateConfigFile(); + } })); // Restore terminal associations from storage @@ -417,6 +428,15 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { }; } + // If in sandbox mode, skip confirmation logic. In sandbox mode, commands are run in a restricted environment and explicit + // user confirmation is not required. + if (this._sandboxService.isEnabled()) { + toolSpecificData.autoApproveInfo = new MarkdownString(localize('autoApprove.sandbox', 'In sandbox mode')); + return { + toolSpecificData + }; + } + // Determine auto approval, this happens even when auto approve is off to that reasoning // can be reviewed in the terminal channel. It also allows gauging the effective set of // commands that would be auto approved if it were enabled. @@ -530,11 +550,11 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { const args = invocation.parameters as IRunInTerminalInputParams; this._logService.debug(`RunInTerminalTool: Invoking with options ${JSON.stringify(args)}`); - let toolResultMessage: string | undefined; + let toolResultMessage: string | IMarkdownString | undefined; const chatSessionResource = invocation.context?.sessionResource ?? LocalChatSessionUri.forSession(invocation.context?.sessionId ?? 'no-chat-session'); const chatSessionId = chatSessionResourceToId(chatSessionResource); - const command = toolSpecificData.commandLine.userEdited ?? toolSpecificData.commandLine.toolEdited ?? toolSpecificData.commandLine.original; + let command = toolSpecificData.commandLine.userEdited ?? toolSpecificData.commandLine.toolEdited ?? toolSpecificData.commandLine.original; const didUserEditCommand = ( toolSpecificData.commandLine.userEdited !== undefined && toolSpecificData.commandLine.userEdited !== toolSpecificData.commandLine.original @@ -545,6 +565,12 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { toolSpecificData.commandLine.toolEdited !== toolSpecificData.commandLine.original ); + if (this._sandboxService.isEnabled()) { + await this._sandboxService.getSandboxConfigPath(); + this._logService.info(`RunInTerminalTool: Sandboxing is enabled, wrapping command with srt.`); + command = this._sandboxService.wrapCommand(command); + } + if (token.isCancellationRequested) { throw new CancellationError(); } @@ -688,21 +714,9 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } try { - let strategy: ITerminalExecuteStrategy; - switch (toolTerminal.shellIntegrationQuality) { - case ShellIntegrationQuality.None: { - strategy = this._instantiationService.createInstance(NoneExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false); - toolResultMessage = '$(info) Enable [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) to improve command detection'; - break; - } - case ShellIntegrationQuality.Basic: { - strategy = this._instantiationService.createInstance(BasicExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false, commandDetection!); - break; - } - case ShellIntegrationQuality.Rich: { - strategy = this._instantiationService.createInstance(RichExecuteStrategy, toolTerminal.instance, commandDetection!); - break; - } + const strategy: ITerminalExecuteStrategy = this._getExecuteStrategy(toolTerminal.shellIntegrationQuality, toolTerminal, commandDetection!); + if (toolTerminal.shellIntegrationQuality === ShellIntegrationQuality.None) { + toolResultMessage = '$(info) Enable [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) to improve command detection'; } this._logService.debug(`RunInTerminalTool: Using \`${strategy.type}\` execute strategy for command \`${command}\``); store.add(strategy.onDidCreateStartMarker(startMarker => { @@ -762,7 +776,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } terminalResult = resultArr.join('\n\n'); } - } catch (e) { // Handle timeout case - get output collected so far and return it if (didTimeout && e instanceof CancellationError) { @@ -1006,6 +1019,21 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } + private _getExecuteStrategy(shellIntegrationQuality: ShellIntegrationQuality, toolTerminal: IToolTerminal, commandDetection: ICommandDetectionCapability): ITerminalExecuteStrategy { + let strategy: ITerminalExecuteStrategy; + switch (shellIntegrationQuality) { + case ShellIntegrationQuality.None: + strategy = this._instantiationService.createInstance(NoneExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false); + break; + case ShellIntegrationQuality.Basic: + strategy = this._instantiationService.createInstance(BasicExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false, commandDetection!); + break; + case ShellIntegrationQuality.Rich: + strategy = this._instantiationService.createInstance(RichExecuteStrategy, toolTerminal.instance, commandDetection!); + break; + } + return strategy; + } // #endregion } diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts index 57351d2b06590..fd6cb45f4178f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -20,6 +20,10 @@ export const enum TerminalChatAgentToolsSettingId { ShellIntegrationTimeout = 'chat.tools.terminal.shellIntegrationTimeout', AutoReplyToPrompts = 'chat.tools.terminal.autoReplyToPrompts', OutputLocation = 'chat.tools.terminal.outputLocation', + TerminalSandboxEnabled = 'chat.tools.terminal.sandbox.enabled', + TerminalSandboxNetwork = 'chat.tools.terminal.sandbox.network', + TerminalSandboxLinuxFileSystem = 'chat.tools.terminal.sandbox.linuxFileSystem', + TerminalSandboxMacFileSystem = 'chat.tools.terminal.sandbox.macFileSystem', PreventShellHistory = 'chat.tools.terminal.preventShellHistory', EnforceTimeoutFromModel = 'chat.tools.terminal.enforceTimeoutFromModel', @@ -503,6 +507,99 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary('terminalSandboxService'); + +export interface ITerminalSandboxService { + readonly _serviceBrand: undefined; + isEnabled(): boolean; + wrapCommand(command: string): string; + getSandboxConfigPath(forceRefresh?: boolean): Promise; + getTempDir(): URI | undefined; + setNeedsForceUpdateConfigFile(): void; +} + +export class TerminalSandboxService implements ITerminalSandboxService { + readonly _serviceBrand: undefined; + private _srtPath: string; + private _sandboxConfigPath: string | undefined; + private _needsForceUpdateConfigFile = true; + private _tempDir: URI | undefined; + private _sandboxSettingsId: string | undefined; + private _os: OperatingSystem = OS; + + constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IFileService private readonly _fileService: IFileService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @ILogService private readonly _logService: ILogService, + @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, + ) { + const appRoot = dirname(FileAccess.asFileUri('').fsPath); + this._srtPath = join(appRoot, 'node_modules', '.bin', 'srt'); + this._sandboxSettingsId = generateUuid(); + this._initTempDir(); + this._remoteAgentService.getEnvironment().then(remoteEnv => this._os = remoteEnv?.os ?? OS); + } + + public isEnabled(): boolean { + if (this._os === OperatingSystem.Windows) { + return false; + } + return this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandboxEnabled); + } + + public wrapCommand(command: string): string { + if (!this._sandboxConfigPath || !this._tempDir) { + throw new Error('Sandbox config path or temp dir not initialized'); + } + return `"${this._srtPath}" TMPDIR=${this._tempDir.fsPath} --settings "${this._sandboxConfigPath}" "${command}"`; + } + + public getTempDir(): URI | undefined { + return this._tempDir; + } + + public setNeedsForceUpdateConfigFile(): void { + this._needsForceUpdateConfigFile = true; + } + + public async getSandboxConfigPath(forceRefresh: boolean = false): Promise { + if (!this._sandboxConfigPath || forceRefresh || this._needsForceUpdateConfigFile) { + this._sandboxConfigPath = await this._createSandboxConfig(); + this._needsForceUpdateConfigFile = false; + } + return this._sandboxConfigPath; + } + + private async _createSandboxConfig(): Promise { + + if (this.isEnabled() && !this._tempDir) { + this._initTempDir(); + } + if (this._tempDir) { + const networkSetting = this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandboxNetwork) ?? {}; + const linuxFileSystemSetting = this._os === OperatingSystem.Linux + ? this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem) ?? {} + : {}; + const macFileSystemSetting = this._os === OperatingSystem.Macintosh + ? this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem) ?? {} + : {}; + const configFileUri = joinPath(this._tempDir, `vscode-sandbox-settings-${this._sandboxSettingsId}.json`); + const sandboxSettings = { + network: { + allowedDomains: networkSetting.allowedDomains ?? [], + deniedDomains: networkSetting.deniedDomains ?? [] + }, + filesystem: { + denyRead: this._os === OperatingSystem.Macintosh ? macFileSystemSetting.denyRead : linuxFileSystemSetting.denyRead, + allowWrite: this._os === OperatingSystem.Macintosh ? macFileSystemSetting.allowWrite : linuxFileSystemSetting.allowWrite, + denyWrite: this._os === OperatingSystem.Macintosh ? macFileSystemSetting.denyWrite : linuxFileSystemSetting.denyWrite, + } + }; + this._sandboxConfigPath = configFileUri.fsPath; + await this._fileService.createFile(configFileUri, VSBuffer.fromString(JSON.stringify(sandboxSettings, null, '\t')), { overwrite: true }); + return this._sandboxConfigPath; + } + return undefined; + } + + private _initTempDir(): void { + if (this.isEnabled() && isNative) { + this._needsForceUpdateConfigFile = true; + const environmentService = this._environmentService as IEnvironmentService & { tmpDir?: URI }; + this._tempDir = environmentService.tmpDir; + if (!this._tempDir) { + this._logService.warn('TerminalSandboxService: Cannot create sandbox settings file because no tmpDir is available in this environment'); + return; + } + } + } +} diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/electron-browser/runInTerminalTool.test.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/electron-browser/runInTerminalTool.test.ts index 25a032e8e2448..305cf806b9a6f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/electron-browser/runInTerminalTool.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/electron-browser/runInTerminalTool.test.ts @@ -32,6 +32,7 @@ import { TestIPCFileSystemProvider } from '../../../../../test/electron-browser/ import { TerminalToolConfirmationStorageKeys } from '../../../../chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolConfirmationSubPart.js'; import { IChatService, type IChatTerminalToolInvocationData } from '../../../../chat/common/chatService/chatService.js'; import { LocalChatSessionUri } from '../../../../chat/common/model/chatUri.js'; +import { ITerminalSandboxService } from '../../common/terminalSandboxService.js'; import { ILanguageModelToolsService, IPreparedToolInvocation, IToolInvocationPreparationContext, type ToolConfirmationAction } from '../../../../chat/common/tools/languageModelToolsService.js'; import { ITerminalChatService, ITerminalService, type ITerminalInstance } from '../../../../terminal/browser/terminal.js'; import { ITerminalProfileResolverService } from '../../../../terminal/common/terminal.js'; @@ -91,6 +92,14 @@ suite('RunInTerminalTool', () => { instantiationService.stub(IHistoryService, { getLastActiveWorkspaceRoot: () => undefined }); + instantiationService.stub(ITerminalSandboxService, { + _serviceBrand: undefined, + isEnabled: () => false, + wrapCommand: command => command, + getSandboxConfigPath: async () => undefined, + getTempDir: () => undefined, + setNeedsForceUpdateConfigFile: () => { } + }); const treeSitterLibraryService = store.add(instantiationService.createInstance(TreeSitterLibraryService)); treeSitterLibraryService.isTest = true;